Created
April 19, 2012 11:28
-
-
Save arturaz/2420357 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class RaidSpawner | |
# It's a regular planet. | |
KEY_PLANET = 'planet' | |
# It's a battleground planet. | |
KEY_BATTLEGROUND = 'battleground' | |
# Apocalypse is raging in the galaxy. | |
KEY_APOCALYPSE = 'apocalypse' | |
def initialize(planet) | |
@planet = planet | |
end | |
# Creates raiders and raids planet. Registers new raid. | |
def raid! | |
raiders = units | |
unless raiders.blank? | |
Unit.save_all_units(raiders, nil, EventBroker::CREATED) | |
Cooldown.create_unless_exists( | |
@planet.location_point, Cfg.after_spawn_cooldown | |
) | |
end | |
register! | |
end | |
private | |
# Registers next raid on planet. | |
def register! | |
@planet.next_raid_at += Cfg.raiding_delay_random | |
@planet.raid_arg = generate_arg | |
CallbackManager.register_or_update( | |
@planet, CallbackManager::EVENT_RAID, @planet.next_raid_at | |
) | |
@planet.delayed_fire(@planet, EventBroker::CHANGED, | |
EventBroker::REASON_OWNER_PROP_CHANGE) | |
@planet.save! | |
end | |
# Return raid configuration key for spawners planet. | |
def key | |
return KEY_APOCALYPSE if apocalypse? | |
return KEY_BATTLEGROUND if battleground? | |
KEY_PLANET | |
end | |
# Returns raid argument that should be stored to database. | |
def generate_arg | |
return 0 if @planet.player_id.nil? || apocalypse? || | |
! without_locking { @planet.solar_system }.player_id.nil? | |
arg = battleground? \ | |
? @planet.player.bg_planets_count : @planet.player.planets_count | |
[arg, Cfg.raiding_max_arg(key)].min | |
end | |
# Return array of built (but not saved) units. | |
def units | |
definitions = unit_counts | |
units = [] | |
definitions.each do |type, count, flank| | |
klass = "Unit::#{type.camelcase}".constantize | |
count.times do | |
unit = klass.new(:level => 1, :location => @planet, :flank => flank) | |
unit.skip_validate_technologies = true | |
units.push unit | |
end | |
end | |
units | |
end | |
# Return what units will raid player if he has _planets_ planets. | |
# | |
# Returns Array: | |
# [ | |
# [type, count, flank], | |
# ... | |
# ] | |
# | |
def unit_counts | |
# No raiders if apocalypse and planet is not occupied. | |
return [] if apocalypse? && @planet.player_id.nil? | |
units = {} | |
max_flanks = Cfg.max_flanks | |
raid_arg = apocalypse? ? apocalypse_raid_arg : @planet.raid_arg | |
params = {'arg' => raid_arg} | |
calc = lambda do |formula| | |
begin | |
value = CONFIG.safe_eval(formula, params) | |
# If we went to complex numbers, that means we did something strange and | |
# the result is bad. | |
return 0 if value.is_a?(Complex) | |
[0, value.round].max | |
rescue FloatDomainError; 0; end | |
end | |
Cfg.raiding_config(key).each do | |
|type, (from_formula, to_formula, chance_formula)| | |
units[type] = {} | |
max_flanks.times { |flank| units[type][flank] = 0 } | |
from = calc[from_formula] | |
to = calc[to_formula] | |
chance = [calc[chance_formula], 1.0].min | |
# Add the minimum. | |
from.times do | |
flank = rand(max_flanks) | |
units[type][flank] += 1 | |
end | |
# Add additional ones. | |
(to - from).times do | |
if rand <= chance | |
flank = rand(max_flanks) | |
units[type][flank] += 1 | |
end | |
end | |
end | |
raiders = [] | |
units.each do |type, flanks| | |
flanks.each do |index, count| | |
raiders.push [type, count, index] unless count == 0 | |
end | |
end | |
raiders | |
end | |
# Has the apocalypse started? | |
def apocalypse? | |
@_apocalypse ||= without_locking do | |
@planet.solar_system.galaxy.apocalypse_started? | |
end | |
end | |
# Current apocalypse day. | |
def apocalypse_raid_arg | |
without_locking do | |
@planet.solar_system.galaxy.apocalypse_day(@planet.next_raid_at) | |
end | |
end | |
def battleground? | |
@_battleground ||= without_locking { @planet.solar_system.battleground? } | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require File.expand_path( | |
File.join(File.dirname(__FILE__), '..', 'spec_helper.rb') | |
) | |
describe RaidSpawner do | |
methods = %w{register! key generate_arg units unit_counts} | |
before(:all) { RaidSpawner.class_eval { public *methods } } | |
after(:all) { RaidSpawner.class_eval { private *methods } } | |
let(:battleground_planet) do | |
ss = Factory.build(:battleground) | |
Factory.build(:planet, :solar_system => ss, | |
:player => Factory.create(:player, :bg_planets_count => 3)) | |
end | |
let(:apocalyptic_planet) do | |
galaxy = Factory.build(:galaxy, :apocalypse_start => 5.6.days.ago) | |
ss = Factory.build(:solar_system, :galaxy => galaxy) | |
Factory.build(:planet, :solar_system => ss, | |
:next_raid_at => 5.days.from_now, :player => Factory.create(:player)) | |
end | |
describe "#raid!" do | |
let(:planet) { Factory.create(:planet, :next_raid_at => Time.now) } | |
let(:spawner) { RaidSpawner.new(planet) } | |
describe "when raiders exist" do | |
let(:units) { [Factory.build(:unit_built)] } | |
before(:each) do | |
spawner.stub(:units).and_return(units) | |
end | |
it "should save raiders to database" do | |
Unit.should_receive(:save_all_units). | |
with(units, nil, EventBroker::CREATED) | |
spawner.raid! | |
end | |
it "should create cooldown in planet" do | |
Cooldown.should_receive(:create_unless_exists).and_return do |lp, time| | |
lp.should == planet.location_point | |
time.should be_within(SPEC_TIME_PRECISION). | |
of(Cfg.after_spawn_cooldown) | |
end | |
spawner.raid! | |
end | |
end | |
describe "when raiders do not exist" do | |
before(:each) do | |
spawner.stub(:units).and_return([]) | |
end | |
it "should not try to save raiders to database" do | |
Unit.should_not_receive(:save_all_units) | |
spawner.raid! | |
end | |
it "should not check location for combat" do | |
Combat::LocationChecker.should_not_receive(:check_location) | |
spawner.raid! | |
end | |
end | |
it "should register new raid" do | |
spawner.should_receive(:register!) | |
spawner.raid! | |
end | |
end | |
describe "#register!" do | |
let(:planet) { Factory.create(:planet, :next_raid_at => 10.days.ago) } | |
let(:spawner) { RaidSpawner.new(planet) } | |
it "should shift #next_raid_at to the future" do | |
next_raid_at = planet.next_raid_at | |
spawner.register! | |
planet.reload | |
range = Cfg.raiding_delay_range | |
range = (next_raid_at + range.first)..(next_raid_at + range.last) | |
range.should cover(planet.next_raid_at) | |
end | |
it "should set #raid_arg" do | |
spawner.stub!(:generate_arg).and_return(115) | |
lambda do | |
spawner.register! | |
planet.reload | |
end.should change(planet, :raid_arg).to(115) | |
end | |
it "should register a callback" do | |
spawner.register! | |
planet.should have_callback(CallbackManager::EVENT_RAID, | |
planet.next_raid_at) | |
end | |
it "should dispatch a changed event" do | |
should_fire_event(planet, EventBroker::CHANGED, | |
EventBroker::REASON_OWNER_PROP_CHANGE) do | |
spawner.register! | |
end | |
end | |
end | |
describe "#key" do | |
it "should return apocalypse key it it has started" do | |
RaidSpawner.new(apocalyptic_planet).key. | |
should == RaidSpawner::KEY_APOCALYPSE | |
end | |
it "should return battleground key if planet is in bg solar system" do | |
RaidSpawner.new(battleground_planet).key. | |
should == RaidSpawner::KEY_BATTLEGROUND | |
end | |
it "should return planet key if planet is in regular solar system" do | |
RaidSpawner.new(Factory.build(:planet)).key. | |
should == RaidSpawner::KEY_PLANET | |
end | |
end | |
describe "#generate_arg" do | |
it "should return 0 if planet is npc" do | |
RaidSpawner.new(Factory.build(:planet)).generate_arg.should == 0 | |
end | |
it "should return 0 if apocalypse has started" do | |
RaidSpawner.new(apocalyptic_planet).generate_arg.should == 0 | |
end | |
it "should return 0 if planet is in player owned solar system" do | |
solar_system = Factory.create(:home_ss) | |
planet = Factory.create(:planet, :solar_system => solar_system) | |
RaidSpawner.new(planet).generate_arg.should == 0 | |
end | |
describe "if it's a battleground planet" do | |
it "should return player bg planet count" do | |
RaidSpawner.new(battleground_planet).generate_arg. | |
should == battleground_planet.player.bg_planets_count | |
end | |
it "should return limit max arg" do | |
max = Cfg.raiding_max_arg(RaidSpawner::KEY_BATTLEGROUND) | |
battleground_planet.player.bg_planets_count = max + 1 | |
RaidSpawner.new(battleground_planet).generate_arg. | |
should == max | |
end | |
end | |
describe "if it's a regular planet" do | |
let(:planet) do | |
Factory.build( | |
:planet, :player => Factory.create(:player, :planets_count => 5) | |
) | |
end | |
it "should return player planet count " do | |
RaidSpawner.new(planet).generate_arg. | |
should == planet.player.planets_count | |
end | |
it "should return limit max arg" do | |
max = Cfg.raiding_max_arg(RaidSpawner::KEY_PLANET) | |
planet.player.planets_count = max + 1 | |
RaidSpawner.new(planet).generate_arg.should == max | |
end | |
end | |
end | |
describe "#units" do | |
before(:all) do | |
@planet = Factory.create(:planet) | |
spawner = RaidSpawner.new(@planet) | |
def spawner.unit_counts | |
[ | |
["gnat", 2, 0], | |
["glancer", 1, 1], | |
] | |
end | |
@units = spawner.units | |
end | |
it "should place units in planet" do | |
@units.each { |unit| unit.location.should == @planet.location_point } | |
end | |
it "should set unit flanks" do | |
@units.map(&:flank).should == [0, 0, 1] | |
end | |
it "should set unit levels" do | |
@units.each { |unit| unit.level.should == 1 } | |
end | |
it "should set unit to full hp" do | |
@units.each { |unit| unit.hp.should == unit.hit_points } | |
end | |
it "should not belong to any player" do | |
@units.each { |unit| unit.player.should be_nil } | |
end | |
it "should be correct types" do | |
@units.map { |u| u.class.to_s }.should == [ | |
"Unit::Gnat", | |
"Unit::Gnat", | |
"Unit::Glancer", | |
] | |
end | |
it "should not be saved" do | |
@units.each { |unit| unit.id.should be_nil } | |
end | |
end | |
describe "#unit_counts" do | |
describe "in normal conditions" do | |
it "should return data using SsObject#raid_arg" do | |
with_config_values('raiding.raiders.planet' => { | |
"gnat" => [ | |
"arg * 2", "arg * 4", "0.3 + (arg - 1) * 0.2" | |
] | |
}) do | |
spawner = RaidSpawner.new(Factory.build(:planet, :raid_arg => 2)) | |
counts = spawner.unit_counts.inject({}) do | |
|hash, (type, count, flank)| | |
hash[type] ||= 0 | |
hash[type] += count | |
hash | |
end | |
(4..8).should cover(counts['gnat']) | |
end | |
end | |
end | |
describe "in apocalypse" do | |
it "should return data using RaidSpawner#apocalypse_raid_arg" do | |
with_config_values('raiding.raiders.apocalypse' => { | |
"gnat" => [ | |
"arg", "arg", "0.3 + (arg - 1) * 0.2" | |
] | |
}) do | |
# Set to nonsense to check that we're not using it. | |
apocalyptic_planet.raid_arg = 9999 | |
spawner = RaidSpawner.new(apocalyptic_planet) | |
counts = spawner.unit_counts.inject({}) do | |
|hash, (type, count, flank)| | |
hash[type] ||= 0 | |
hash[type] += count | |
hash | |
end | |
counts['gnat'].should == 7 + 5 | |
end | |
end | |
it "should return [] if planet is unoccupied" do | |
apocalyptic_planet.player = nil | |
spawner = RaidSpawner.new(apocalyptic_planet) | |
spawner.unit_counts.should == [] | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment