From 28da8fe5eb0e851db38a89f7051d4bdc920d6abc Mon Sep 17 00:00:00 2001 From: Ari Timonen Date: Thu, 9 Apr 2020 04:25:08 +0300 Subject: [PATCH] Add log and event functionality back --- app/controllers/log_events_controller.rb | 85 ++++++++++ app/controllers/log_files_controller.rb | 42 +++++ app/controllers/rounds_controller.rb | 26 +++ app/models/log.rb | 197 +++++++++++++++++++++++ app/models/log_event.rb | 25 +++ app/models/log_file.rb | 95 +++++++++++ app/models/round.rb | 128 +++++++++++++++ app/models/rounder.rb | 52 ++++++ app/views/log_events/edit.html.erb | 16 ++ app/views/log_events/index.html.erb | 20 +++ app/views/log_events/new.html.erb | 15 ++ app/views/log_events/show.html.erb | 8 + app/views/rounds/_rounds.html.erb | 23 +++ app/views/rounds/index.html.erb | 7 + app/views/rounds/show.html.erb | 150 +++++++++++++++++ 15 files changed, 889 insertions(+) create mode 100644 app/controllers/log_events_controller.rb create mode 100644 app/controllers/log_files_controller.rb create mode 100644 app/controllers/rounds_controller.rb create mode 100644 app/models/log.rb create mode 100644 app/models/log_event.rb create mode 100644 app/models/log_file.rb create mode 100644 app/models/round.rb create mode 100644 app/models/rounder.rb create mode 100644 app/views/log_events/edit.html.erb create mode 100644 app/views/log_events/index.html.erb create mode 100644 app/views/log_events/new.html.erb create mode 100644 app/views/log_events/show.html.erb create mode 100644 app/views/rounds/_rounds.html.erb create mode 100644 app/views/rounds/index.html.erb create mode 100644 app/views/rounds/show.html.erb diff --git a/app/controllers/log_events_controller.rb b/app/controllers/log_events_controller.rb new file mode 100644 index 0000000..e1064ee --- /dev/null +++ b/app/controllers/log_events_controller.rb @@ -0,0 +1,85 @@ +class LogEventsController < ApplicationController + # GET /log_events + # GET /log_events.xml + def index + @log_events = LogEvent.all + + respond_to do |format| + format.html # index.html.erb + format.xml { render xml: @log_events } + end + end + + # GET /log_events/1 + # GET /log_events/1.xml + def show + @log_event = LogEvent.find(params[:id]) + + respond_to do |format| + format.html # show.html.erb + format.xml { render xml: @log_event } + end + end + + # GET /log_events/new + # GET /log_events/new.xml + def new + @log_event = LogEvent.new + + respond_to do |format| + format.html # new.html.erb + format.xml { render xml: @log_event } + end + end + + # GET /log_events/1/edit + def edit + @log_event = LogEvent.find(params[:id]) + end + + # POST /log_events + # POST /log_events.xml + def create + @log_event = LogEvent.new(params[:log_event]) + + respond_to do |format| + if @log_event.save + flash[:notice] = t(:logevent_create) + format.html { redirect_to(@log_event) } + format.xml { render xml: @log_event, :status => :created, :location => @log_event } + else + format.html { render :new } + format.xml { render xml: @log_event.errors, :status => :unprocessable_entity } + end + end + end + + # PUT /log_events/1 + # PUT /log_events/1.xml + def update + @log_event = LogEvent.find(params[:id]) + + respond_to do |format| + if @log_event.update_attributes(params[:log_event]) + flash[:notice] = t(:logevent_update) + format.html { redirect_to(@log_event) } + format.xml { head :ok } + else + format.html { render :edit } + format.xml { render xml: @log_event.errors, :status => :unprocessable_entity } + end + end + end + + # DELETE /log_events/1 + # DELETE /log_events/1.xml + def destroy + @log_event = LogEvent.find(params[:id]) + @log_event.destroy + + respond_to do |format| + format.html { redirect_to(log_events_url) } + format.xml { head :ok } + end + end +end diff --git a/app/controllers/log_files_controller.rb b/app/controllers/log_files_controller.rb new file mode 100644 index 0000000..dda7433 --- /dev/null +++ b/app/controllers/log_files_controller.rb @@ -0,0 +1,42 @@ +class LogFilesController < ApplicationController + def index + LogFile.process + render text: 'Ok' + end + + def handle + LogFile.find(params[:id]).deal + render text: 'Ok' + end + + def refresh + LogFile.unhandled.each do |lf| + lf.deal + end + end + + def fix + Rounder.find_in_batches(batch_size: 100) do |rounders| + rounders.each do |r| + r.team_id = nil + if r.user and t = Teamer.historic(r.user, r.round.start).first + r.team_id = t.team_id + end + r.save + end + end + end + + def pix + Round.all.each do |r| + r.team1_id = nil + r.team2_id = nil + [1, 2].each do |team| + if s = r.rounders.team(team).stats.first + r["team#{team}_id"] = s["team_id"] + end + end + r.save + end + end +end diff --git a/app/controllers/rounds_controller.rb b/app/controllers/rounds_controller.rb new file mode 100644 index 0000000..b09511b --- /dev/null +++ b/app/controllers/rounds_controller.rb @@ -0,0 +1,26 @@ +class RoundsController < ApplicationController + def index + sort = case params['sort'] + when "start" then "start" + when "server" then "server_id" + when "team1" then "team1_id" + when "team2" then "team2_id" + when "map" then "map_name" + when "commander" then "commander_id" + end + + @rounds = Round.basic.paginate \ + order: sort, + page: params[:page], + per_page: 30 + + if params[:ajax] + render partial: 'list', layout: false + return + end + end + + def show + @round = Round.find(params[:id]) + end +end diff --git a/app/models/log.rb b/app/models/log.rb new file mode 100644 index 0000000..71e0794 --- /dev/null +++ b/app/models/log.rb @@ -0,0 +1,197 @@ +# == Schema Information +# +# Table name: logs +# +# id :integer not null, primary key +# server_id :integer +# text :text +# domain :integer +# created_at :datetime +# round_id :integer +# details :string(255) +# actor_id :integer +# target_id :integer +# specifics1 :string(255) +# specifics2 :string(255) +# log_file_id :integer +# + +class Log < ActiveRecord::Base + include Extra + attr_accessor :text + + DOMAIN_LOG = 1 + DOMAIN_INFO = 4 + + TEAM_MARINES = 1 + TEAM_ALIENS = 2 + + RE_PLAYER = /".*?<\d*><\w*>"/ + RE_PLAYER_ID = /".*?<\d*><\w*>"/ + RE_PLAYER_ID_NAME_TEAM = /"(.*?)<\d*><([a-z]*)1team>"/ + RE_PLAYER_NAME = /"(.*?)<\d*><[a-z]*1team>"/ + RE_PLAYER_NAME_TEAM = /"(.*?)<\d*><([a-z]*)1team>"/ + + scope :recent, :order => "id DESC", :limit => 5 + scope :ordered, :order => "created_at ASC, id ASC" + scope :with_details, + lambda { |details| {:conditions => ["details LIKE ?", details]} } + scope :unhandled, :conditions => {:details => nil} + scope :stats, + :select => "id, details, COUNT(*) as num", + :group => "details", + :order => "details" + + belongs_to :details, :class_name => "LogEvent" + belongs_to :server + belongs_to :round + belongs_to :server + belongs_to :log_file + belongs_to :actor, :class_name => "Rounder" + belongs_to :target, :class_name => "Rounder" + + def since + (created_at - round.start).to_i + end + + def time + return sprintf("%02d:%02d", since/60, since%60) + end + + def frag + text.match(/^#{RE_PLAYER_NAME_TEAM} killed #{RE_PLAYER_NAME_TEAM} with "([a-z0-9_]*)"$/) + end + + def role + text.match(/^#{RE_PLAYER_NAME} changed role to "([a-z0-9_]*)"$/) + end + + def match_map vars + if m = text.match(/^Started map "([A-Za-z0-9_]*)"/) + vars[:map] = m[1] + self.details = LogEvent.get "map" + self.specifics1 = m[1] + end + end + + def match_start vars + if text.match(/^Game reset complete.$/) + vars[:round] = Round.new + vars[:round].server = server + vars[:round].start = created_at + vars[:round].map_name = vars[:map] + vars[:round].map = Map.with_name(vars[:map]).first + vars[:round].save + vars[:lifeforms] = {} + self.details = LogEvent.get "start" + end + end + + def match_end vars + if m = text.match(/^Team ([1-2]) has lost.$/) + vars[:round].winner = (m[1].to_i == 1 ? 2 : 1) + vars[:round].end = created_at + [1, 2].each do |team| + if s = vars[:round].rounders.team(team).stats.first + vars[:round]["team#{team}_id"] = s["team_id"] + end + end + vars[:round].save + vars[:round] = nil + self.details = LogEvent.get "end" + end + end + + def match_join vars + if m = text.match(/^#{RE_PLAYER_ID_NAME_TEAM} .*$/) and !(self.actor = vars[:round].rounders.match(m[2]).first) + self.actor = Rounder.new + self.actor.round = vars[:round] + self.actor.name = m[1] + self.actor.steamid = m[2] + self.actor.user = User.first(:conditions => {:steamid => m[2]}) or User.historic(m[2]) + self.actor.team = (m[3] == "marine" ? TEAM_MARINES : TEAM_ALIENS) + if self.actor.user and t = Teamer.historic(actor.user, vars[:round].start).first + self.actor.ensl_team = t.team + end + self.actor.kills = 0 + self.actor.deaths = 0 + self.actor.save + self.details = LogEvent.get "join" + end + end + + def match_kill vars + if m = text.match(/^#{RE_PLAYER} killed #{RE_PLAYER_ID} with "([a-z0-9_]*)"$/) + if self.actor + actor.increment :kills + actor.save + end + if self.target = vars[:round].rounders.match(m[1]).first + target.increment :deaths + target.save + end + self.details = LogEvent.get "kill" + self.specifics1 = m[3] + save + end + end + + def match_say vars + if m = text.match(/^#{RE_PLAYER} (say(_team)?) ".*"$/) + self.details = "say" + self.specifics1 = m[1] + end + end + + def match_built vars + if m = text.match(/^#{RE_PLAYER} triggered "structure_built" \(type "([a-z0-9_]*)"\)$/) + self.details = "built_" + m[1] + end + end + + def match_destroyed vars + if m = text.match(/^#{RE_PLAYER} triggered "structure_destroyed" \(type "([a-z0-9_]*)"\)$/) + self.details = "destroyed_" + m[1] + end + end + + def match_research_start vars + if m = text.match(/^#{RE_PLAYER} triggered "research_start" \(type "([a-z0-9_]*)"\)$/) + self.details = m[1] + end + end + + def match_research_cancel vars + if m = text.match(/^#{RE_PLAYER} triggered "research_cancel" \(type "([a-z0-9_]*)"\)$/) + self.details = "research_cancel" + end + end + + def match_role vars + if m = text.match(/^#{RE_PLAYER_ID} changed role to "([a-z0-9_]*)"$/) + if m[2] == "gestate" + self.details = "gestate" + elsif actor + if m[2] == "commander" and !vars[:round].commander + vars[:round].commander = actor + vars[:round].save + end + if !actor.roles + actor.update_attribute :roles, m[2] + elsif !self.actor.roles.include?(m[2]) + actor.update_attribute :roles, actor.roles + ", " + m[2] + end + self.details = ((vars[:lifeforms].include? actor.id and vars[:lifeforms][actor.id] == m[2]) ? "upgrade" : m[2]) + vars[:lifeforms][actor.id] = m[2] + end + end + end + + def self.add server, domain, text + log = new + log.server = server + log.domain = domain + log.text = text + log.save + end +end diff --git a/app/models/log_event.rb b/app/models/log_event.rb new file mode 100644 index 0000000..65b0b4f --- /dev/null +++ b/app/models/log_event.rb @@ -0,0 +1,25 @@ +# == Schema Information +# +# Table name: log_events +# +# id :integer not null, primary key +# name :string(255) +# description :string(255) +# team :integer +# created_at :datetime +# updated_at :datetime +# + +class LogEvent < ActiveRecord::Base + def self.get search, team = nil + if f = first({:conditions => {:name => search}}) + return f + else + f = LogEvent.new + f.name = "get" + f.team = team if team + f.save + return f + end + end +end diff --git a/app/models/log_file.rb b/app/models/log_file.rb new file mode 100644 index 0000000..f430d05 --- /dev/null +++ b/app/models/log_file.rb @@ -0,0 +1,95 @@ +# encoding: US-ASCII +# == Schema Information +# +# Table name: log_files +# +# id :integer not null, primary key +# name :string(255) +# md5 :string(255) +# size :integer +# server_id :integer +# updated_at :datetime +# + + +require 'digest/md5' + +class LogFile < ActiveRecord::Base + NON_ASCII = /[\x80-\xff]/ + LOGS = File.join(Rails.root, "tmp", "logs") + + attr_accessor :path + belongs_to :server + + has_many :logs + has_many :rounds, :through => :logs + + def after_create + Pathname(path).each_line do |line| + if m = line.gsub(NON_ASCII, "").match(/\d{2}:\d{2}:\d{2}: (.*)/) + log = Log.new + log.server = server + log.domain = Log::DOMAIN_LOG + log.log_file = self + log.text = m[1].strip + next if log.text.match(/^Server cvar/) + next if log.text.match(/^\[ENSL\]/) + next if log.text.match(/STEAM USERID validated/) + next if log.text.match(/^\[META\]/) + l.created_at = DateTime.parse(line.match(/\d{2}\/\d{2}\/\d{4} \- \d{2}:\d{2}:\d{2}:/)[0]) + vars = {} + log.match_map vars or log.match_start vars + if vars[:round] and !log.details + log.match_end vars \ + or log.match_join vars \ + or log.match_kill vars \ + or log.match_say vars \ + or log.match_built vars \ + or log.match_destroyed vars \ + or log.match_research_start vars \ + or log.match_research_cancel vars \ + or log.match_role vars \ + end + if log.details + log.round = vars[:round] if vars[:round] + log.save + end + end + end + rounds.each do |r| + unless r.end + r.destroy + end + end + Log.delete_all(["details IS NULL AND log_file_id = ?", self.id]) + end + + def format path + self.name = File.basename(path) + self.size = File.size(path) + self.md5 = Digest::MD5.hexdigest(File.read(path)) + self.updated_at = File.mtime(path) + self.path = path + end + + def deal + # TODO + end + + def self.process + Dir.glob("#{LOGS}/*").each do |entry| + dir = File.basename(entry).to_i + if File.directory?(entry) and dir > 0 and Server.find(dir) + Dir.glob("#{entry}/*.log").each do |file| + lf = LogFile.new + lf.format file + lf.server_id = dir + + unless LogFile.first(:conditions => {:name => lf.name, :size => lf.size, :server_id => dir.to_i}) + lf.save + end + end + end + end + end +end diff --git a/app/models/round.rb b/app/models/round.rb new file mode 100644 index 0000000..3263114 --- /dev/null +++ b/app/models/round.rb @@ -0,0 +1,128 @@ +# == Schema Information +# +# Table name: rounds +# +# id :integer not null, primary key +# server_id :integer +# start :datetime +# end :datetime +# winner :integer +# match_id :integer +# commander_id :integer +# team1_id :integer +# team2_id :integer +# map_name :string(255) +# map_id :integer +# + +class Round < ActiveRecord::Base + scope :basic, :include => [:commander, :map, :server, :team1, :team2], :order => "start DESC" + scope :team_stats, + :select => "team1_id AS team_id, COUNT(*) as rounds, AVG(winner) AS wlr, teams.name", + :joins => "LEFT JOIN teams ON teams.id = team1_id", + :group => "team_id", + :order => "rounds DESC", + :having => "rounds > 10 AND team_id IS NOT NULL", + :limit => 100 + + has_many :rounders, :dependent => :destroy + has_many :logs, :dependent => :destroy + + belongs_to :team1, :class_name => "Team" + belongs_to :team2, :class_name => "Team" + belongs_to :server + belongs_to :match + belongs_to :commander, :class_name => "Rounder" + belongs_to :map + + def length + sprintf("%02d:%02d", (self.end - self.start).to_i/60, (self.end - self.start).to_i%60) + end + + def winner_s + winner == 1 ? "Marines" : "Aliens" + end + + def marine_stats + {"built_resourcetower" => "Marine RTs", + "built_item_genericammo" => "Ammo Packs", + "built_item_health" => "Medpacks"} + end + + def alien_stats + {"built_alienresourcetower" => "Alien RTs", + "lerk" => "Lerks", + "fade" => "Fades", + "onos" => "Onoses"} + end + + def self.marine_events + {"built_resourcetower" => "RT Built", + "built_phasegate" => "PG Built", + "built_team_infportal" => "IP Built", + "built_team_armory" => "Armory Built", + "built_team_observatory" => "Obs Built", + "built_team_armslab" => "ARMS Built", + "built_team_command" => "CC Built", + "built_team_prototypelab" => "Proto Built", + "built_team_turretfactory" => "TF Built", + "built_team_turret" => "Turret Built", + "built_team_siegeturret" => "Siege Built", + "destroyed_resourcetower" => "RT Destroyed", + "destroyed_phasegate" => "PG Destroyed", + "destroyed_team_infportal" => "IP Destroyed", + "destroyed_team_armory" => "Armory Destroyed", + "destroyed_team_observatory" => "Obs Destroyed", + "destroyed_team_armslab" => "ARMS Destroyed", + "destroyed_team_command" => "CC Destroyed", + "destroyed_team_prototypelab" => "Proto Destroyed", + "destroyed_team_turretfactory" => "TF Destroyed", + "destroyed_team_turret" => "Turret Destroyed", + "destroyed_team_siegeturret" => "Siege Destroyed", + "scan" => "Scan", + "research_motiontracking" => "MT Tech", + "research_phasetech" => "PG Tech", + "research_distressbeacon" => "Beacon", + "research_armorl1" => "Armor 1", + "research_armorl2" => "Armor 2", + "research_armorl3" => "Armor 3", + "research_weaponsl1" => "Weapons 1", + "research_weaponsl2" => "Weapons 2", + "research_weaponsl3" => "Weapons 3", + "research_advarmory" => "AA", + "research_electrical" => "Electrify", + "research_advturretfactory" => "A-TFAC", + "research_cancel" => "Research Cancel", + "kill" => "Frag"} + end + + def self.alien_events + {"built_team_hive" => "Hive Built", + "built_movementchamber" => "MC Built", + "built_sensorychamber" => "SC Built", + "built_defensechamber" => "DC Built", + "built_offensechamber" => "OC Built", + "built_alienresourcetower" => "RT Built", + "destroyed_team_hive" => "Hive Destroyed", + "destroyed_movementchamber" => "MC Destroyed", + "destroyed_sensorychamber" => "SC Destroyed", + "destroyed_defensechamber" => "DC Destroyed", + "destroyed_offensechamber" => "OC Destroyed", + "destroyed_alienresourcetower" => "RT Destroyed", + "gorge" => "Gorge up", + "lerk" => "Lerk up", + "fade" => "Fade up", + "onos" => "Onos up", + "kill" => "Frag"} + end + + def self.alien_event event + return alien_events[event] if alien_events.include?(event) + return nil + end + + def self.marine_event event + return marine_events[event] if marine_events.include?(event) + return nil + end +end diff --git a/app/models/rounder.rb b/app/models/rounder.rb new file mode 100644 index 0000000..0914f5d --- /dev/null +++ b/app/models/rounder.rb @@ -0,0 +1,52 @@ +# == Schema Information +# +# Table name: rounders +# +# id :integer not null, primary key +# round_id :integer +# user_id :integer +# team :integer +# roles :string(255) +# kills :integer +# deaths :integer +# name :string(255) +# steamid :string(255) +# team_id :integer +# + +class Rounder < ActiveRecord::Base + attr_accessor :lifeform + + scope :team, lambda { |team| {:conditions => {:team => team}} } + scope :match, lambda { |steamid| {:conditions => {:steamid => steamid}} } + scope :ordered, :order => "kills DESC, deaths ASC" + scope :stats, + :select => "id, team_id, COUNT(*) as num", + :group => "team_id", + :order => "num DESC", + :having => "num > 3" + scope :player_stats, + :select => "id, user_id, SUM(kills)/SUM(deaths) as kpd, COUNT(*) as rounds", + :group => "user_id", + :order => "kpd DESC", + :having => "rounds > 30 AND kpd > 0 AND user_id IS NOT NULL", + :limit => 100 + scope :team_stats, + :select => "id, team_id, SUM(kills)/SUM(deaths) as kpd, COUNT(DISTINCT round_id) as rounds", + :group => "team_id", + :order => "kpd DESC", + :having => "rounds > 30 AND kpd > 0 AND team_id IS NOT NULL", + :limit => 100 + scope :extras, :include => [:round, :user] + scope :within, + lambda { |from, to| + {:conditions => ["created_at > ? AND created_at < ?", from.utc, to.utc]} } + + belongs_to :round + belongs_to :user + belongs_to :ensl_team, :class_name => "Team", :foreign_key => "team_id" + + def to_s + user ? user.username : name + end +end diff --git a/app/views/log_events/edit.html.erb b/app/views/log_events/edit.html.erb new file mode 100644 index 0000000..bd1c0d8 --- /dev/null +++ b/app/views/log_events/edit.html.erb @@ -0,0 +1,16 @@ +

Editing log_event

+ +<%= form_for(@log_event) do |f| %> + <%= f.error_messages %> + +

+ <%= f.label :name %>
+ <%= f.text_field :name %> +

+

+ <%= f.submit 'Update' %> +

+<% end %> + +<%= link_to 'Show', @log_event %> | +<%= link_to 'Back', log_events_path %> \ No newline at end of file diff --git a/app/views/log_events/index.html.erb b/app/views/log_events/index.html.erb new file mode 100644 index 0000000..5619d98 --- /dev/null +++ b/app/views/log_events/index.html.erb @@ -0,0 +1,20 @@ +

Listing log_events

+ + + + + + + <% @log_events.each do |log_event| %> + + + + + + + <% end %> +
Name
<%=h log_event.name %><%= link_to 'Show', log_event %><%= link_to 'Edit', edit_log_event_path(log_event) %><%= link_to 'Destroy', log_event, :confirm => 'Are you sure?', :method => :delete %>
+ +
+ +<%= link_to 'New log_event', new_log_event_path %> diff --git a/app/views/log_events/new.html.erb b/app/views/log_events/new.html.erb new file mode 100644 index 0000000..d2e38e1 --- /dev/null +++ b/app/views/log_events/new.html.erb @@ -0,0 +1,15 @@ +

New log_event

+ +<%= form_for(@log_event) do |f| %> + <%= f.error_messages %> + +

+ <%= f.label :name %>
+ <%= f.text_field :name %> +

+

+ <%= f.submit 'Create' %> +

+<% end %> + +<%= link_to 'Back', log_events_path %> \ No newline at end of file diff --git a/app/views/log_events/show.html.erb b/app/views/log_events/show.html.erb new file mode 100644 index 0000000..9c64c19 --- /dev/null +++ b/app/views/log_events/show.html.erb @@ -0,0 +1,8 @@ +

+ Name: + <%=h @log_event.name %> +

+ + +<%= link_to 'Edit', edit_log_event_path(@log_event) %> | +<%= link_to 'Back', log_events_path %> \ No newline at end of file diff --git a/app/views/rounds/_rounds.html.erb b/app/views/rounds/_rounds.html.erb new file mode 100644 index 0000000..a807b82 --- /dev/null +++ b/app/views/rounds/_rounds.html.erb @@ -0,0 +1,23 @@ + + + + + + + + + + + <% rounds.each do |round| %> + + + + + + + + + <% end %> +
<%= sort_link "Date", :start %><%= sort_link "Server", :server %><%= sort_link "Team 1", :team1 %><%= sort_link "Team 2", :team2 %><%= sort_link "Map", :map %><%= sort_link "commander", :commander %>
<%= link_to (shorttime round.start), round %><%= namelink round.server %><%= namelink round.team1 if round.team1 %><%= namelink round.team2 if round.team2 %><%= round.map.nil? ? h(round.map_name) : namelink(round.map) %><%= (round.commander.nil? or round.commander.user.nil?) ? h(round.commander) : namelink(round.commander.user) %>
+ +<%= will_paginate rounds %> diff --git a/app/views/rounds/index.html.erb b/app/views/rounds/index.html.erb new file mode 100644 index 0000000..e3ce175 --- /dev/null +++ b/app/views/rounds/index.html.erb @@ -0,0 +1,7 @@ +

+ ENSL Round Archive +

+ +
+ <%= render :partial => "rounds", :object => @rounds %> +
diff --git a/app/views/rounds/show.html.erb b/app/views/rounds/show.html.erb new file mode 100644 index 0000000..0d74984 --- /dev/null +++ b/app/views/rounds/show.html.erb @@ -0,0 +1,150 @@ +
+

+ <%= namelink @round.server %> : <%= longtime @round.start %> +

+
+ +
+

+ General Info and Statistics +

+ +
+ <%= cascade @round, ["winner_s", "length", "commander", "map_name"] %> +
+ +
+ <% @round.logs.stats.each do |s| %> + <% next unless @round.marine_stats.include? s.details %> + <%= @round.marine_stats[s.details] %>: <%= s.num %>
+ <% end %> +
+
+ <% @round.logs.stats.each do |s| %> + <% next unless @round.alien_stats.include? s.details %> + <%= @round.alien_stats[s.details] %>: <%= s.num %>
+ <% end %> +
+
+ +<% {Log::TEAM_MARINES => "Marines", Log::TEAM_ALIENS => "Aliens"}.each do |team, name| %> +
+

+ <%= name %> +

+ + + + + + + + + + + <% @round.rounders.team(team).ordered.each do |rounder| %> + + + + + + + + + <% end %> +
NameENSLTeamRolesKillsDeaths
<%= h rounder.name %><%= namelink rounder.user if rounder.user %><%= namelink rounder.ensl_team if rounder.ensl_team %><%= h rounder.roles %><%= rounder.kills %><%= rounder.deaths %>
+
+<% end %> + +
+

+ Round Timeline +

+ +
+
+

+ Aliens +

+ + <% alien_total = 0 %> + <% consecutives = 0 %> + <% @round.logs.each do |log| %> + <% next unless e = Round.alien_event(log.details) %> + <% next if log.details == "kill" and (m = log.frag)[2] != "alien" %> + <% add = log.since*8 - alien_total %> + <% consecutives = add < 0 ? (consecutives + 1) : 0 %> + <% left = 145 - (add < 0 ? 105*consecutives : 0) %> + + <% if log.details == "kill" %> +
+ + <%= Verification.uncrap(m[1])[0, 7] %> + + <%= image_tag 'weapons/' + m[5] + '.gif', :width => 22, :height => 14 %> + + <%= Verification.uncrap(m[3])[0, 7] %> + +
+ <% else %> +
+ <% if ["onos", "fade", "lerk", "gorge"].include? log.details %> + <%= Verification.uncrap(log.role[1])[0, 7] %> <%= log.role[2] %>s + <% else %> + <%= h e %> + <% end %> +
+ <% end %> + + <% add = add + 15 %> + <% alien_total = alien_total + (add > 0 ? add : 0) %> + <% end %> +
+ +
+ +
+

+ Marines +

+ + <% marine_total = 0 %> + <% consecutives = 0 %> + <% @round.logs.each do |log| %> + <% next unless e = Round.marine_event(log.details) %> + <% next if log.details == "kill" and (m = log.frag)[2] != "marine" %> + <% add = log.since*8 - marine_total %> + <% consecutives = add < 0 ? (consecutives + 1) : 0 %> + <% left = 3 + (add < 0 ? 105*consecutives : 0) %> + + <% if log.details == "kill" %> +
+ + <%= Verification.uncrap(m[1])[0, 7] %> + + <%= image_tag 'weapons/' + m[5] + '.gif', :width => 30, :height => 14 %> + + <%= Verification.uncrap(m[3])[0, 7] %> + +
+ <% else %> +
+ <%= h e %> +
+ <% end %> + + <% add = add + 15 %> + <% marine_total = marine_total + (add > 0 ? add : 0) %> + <% end %> +
+ + <% total = marine_total > alien_total ? marine_total : alien_total %> + +
+
+ +