Created
July 12, 2014 05:58
-
-
Save psylone/4084aa26c2be1c0a2312 to your computer and use it in GitHub Desktop.
Indicator class for control player parameters (experience, energy, attack etc.)
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 Indicator < ActiveRecord::Base | |
include CorruptionResponder | |
include CorruptionIncrement | |
include CorruptionDecrement | |
include PartnerShip | |
include GiveUnit | |
PARAM_NAMES = %w{attack defence health energy clashes} | |
attr_accessible :experience, :skill_points, :level, :current_health, :max_health, :current_energy, :max_energy, :current_attack, :max_attack | |
belongs_to :player, inverse_of: :indicator | |
validates :player, presence: true | |
before_save :update_level, if: :experience_changed? | |
before_save :reset_timers | |
def update_level | |
return unless time_to_update_level? | |
inc :level, value: 1 | |
inc :skill_points, value: 3 if level > 3 | |
Thread.new{ perform_offer } if level == 5 | |
revive | |
rise_unit if respond_to? :rise_unit | |
return update_level if time_to_update_level? | |
Activity.publish!(player: player, action: :update_level) | |
end | |
def reset_timers | |
%w{health energy attack}.each do |value| | |
player.reset_timer value if changes["current_#{value}"].try(:first) == self["max_#{value}"] | |
end | |
end | |
# Распределение очков умения | |
# | |
# @param [Hash] params | |
def distribute_skill_points(params) | |
return Failure.new message: I18n.tkey('indicators.distribute_skill_points.failure.cannot_distribute_during_study') if level <= 3 | |
return Failure.new message: I18n.tkey('indicators.distribute_skill_points.failure.have_no_skill_points') unless have_skill_points? | |
return Failure.new message: I18n.tkey('indicators.distribute_skill_points.failure.cannot_distribute_more') if overmuch?(params) | |
return Failure.new message: I18n.tkey('indicators.distribute_skill_points.failure.cannot_distribute_more') if params[:clashes].to_i[0] == 1 | |
params.each do |key, value| | |
value = value.to_i.abs | |
case key.to_sym | |
when :attack | |
player.modifier.inc(:attack, value: 1 * value) | |
when :defence | |
player.modifier.inc(:defence, value: 1 * value) | |
when :health | |
%w(max_health current_health).each do |attr| | |
player.indicator.inc(attr, value: 10 * value) | |
end | |
when :energy | |
%w(max_energy current_energy).each do |attr| | |
player.indicator.inc(attr, value: 10 * value) | |
end | |
when :clashes | |
%w(max_attack current_attack).each do |attr| | |
player.indicator.inc(attr, value: value / 2) | |
end | |
end | |
player.indicator.dec(:skill_points, value: value) if PARAM_NAMES.include? key.to_s | |
end | |
player.save! | |
Success.new(message: I18n.tkey('indicators.distribute_skill_points.success')) | |
end | |
# Количество опыта, необходимое для перехода на следующий уровень. | |
# | |
# @return [Integer] | |
def experience_for_next_level | |
Level.new(level).next.exp | |
end | |
def experience_for_prev_level | |
Level.new(level).pred.exp | |
end | |
def time_to_update_level? | |
experience >= experience_for_next_level | |
end | |
# Возвращает true, если произошла попытка потратить больше очков, чем имеется. | |
# | |
# @param [Hash] params очки умения | |
def overmuch? params | |
params.values_at(*PARAM_NAMES).map{ |i| i.to_i.abs }.sum > skill_points | |
end | |
# Возвращает true, если у игрока есть очки умения. | |
# TODO replace with skill_points? | |
def have_skill_points? | |
skill_points > 0 | |
end | |
def revive *values | |
default = [:energy, :health, :attack] | |
return nil unless (values - default).blank? | |
values = default if values.blank? | |
values.each{ |stat| self["current_#{stat}"] = self["max_#{stat}"] } | |
end | |
def revive! *values | |
revive(*values) and save! | |
end | |
def perk_slots | |
case level | |
when 1...10 then 0 | |
when 10...30 then 1 | |
when 30...50 then 2 | |
when 50...70 then 3 | |
when 70...90 then 4 | |
else 5 | |
end | |
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
# encoding: utf-8 | |
require 'spec_helper' | |
describe Indicator do | |
def boost_exp amount = 10 | |
@indicator.experience += amount | |
end | |
def set_level value | |
@indicator.level = value | |
end | |
def set_sp amount | |
@indicator.skill_points = amount | |
end | |
def decrease *stats | |
options = stats.extract_options! | |
options.reverse_merge! amount: 10 | |
stats.each do |stat| | |
stat = stat.to_sym | |
attack_amount = stat == :attack ? 1 : nil | |
@indicator.decrement "current_#{stat}", attack_amount || options[:amount] | |
@indicator.save! if options[:save] | |
end | |
end | |
before :all do | |
FactoryGirl.create :special_item | |
end | |
describe "has valid factories for" do | |
it "new indicator" do | |
FactoryGirl.create(:indicator).should be_valid | |
end | |
end | |
describe "has validations" do | |
context "for player" do | |
it "should present" do | |
FactoryGirl.build(:indicator, player: nil).should_not be_valid | |
end | |
end | |
end | |
describe "has callbacks" do | |
describe "before save" do | |
before :each do | |
@indicator = FactoryGirl.create(:indicator) | |
end | |
let(:run_callbacks){ @indicator.run_callbacks(:save) } | |
context "#update_level" do | |
it "skip if experience wasn't change" do | |
@indicator.should_not_receive(:update_level) | |
run_callbacks | |
end | |
it "invoke if experience changed" do | |
boost_exp | |
@indicator.should_receive(:update_level) | |
run_callbacks | |
end | |
it "don't increase level if experience is not enough" do | |
boost_exp | |
expect{ run_callbacks }.to_not change{ @indicator.level } | |
end | |
it "increase level by 1 if experience is enough" do | |
boost_exp 11 | |
expect{ run_callbacks }.to change{ @indicator.level }.by(1) | |
end | |
it "increase level several times if experience is enough" do | |
boost_exp 45 | |
expect{ run_callbacks }.to change{ @indicator.level }.by(2) | |
end | |
it "increase skill points by 3 after the 3rd level" do | |
boost_exp 46 | |
expect{ run_callbacks }.to change{ @indicator.skill_points }.by(3) | |
end | |
it "send activity" do | |
boost_exp 45 | |
expect{ run_callbacks }.to change{ Cache::List[:activities].length }.by(1) | |
end | |
it "restore stats" do | |
boost_exp 11 | |
@indicator.should_receive(:revive).with(no_args) | |
run_callbacks | |
end | |
it "apply partnership offer" do | |
boost_exp 75 | |
@indicator.should_receive(:perform_offer) | |
run_callbacks | |
end | |
if Indicator.included_modules.include? GiveUnit | |
before :all do | |
FactoryGirl.create(:special_item).update_attributes level: 21 | |
end | |
it "rise special unit up to 21st level" do | |
boost_exp 3014 # 21st level | |
run_callbacks | |
@indicator.player.items.where(title: "Коррупционный Дед Мороз")[0].level.should eq(21) | |
end | |
it "rise special unit up to 21st level if there's no one" do | |
@indicator.player.properties.destroy_all | |
boost_exp 3014 | |
run_callbacks | |
@indicator.player.items.where(title: "Коррупционный Дед Мороз")[0].level.should eq(21) | |
end | |
end | |
end | |
context "#reset_timers" do | |
def should_reset_timer stat | |
@indicator.decrement "current_#{stat}", 10 | |
@indicator.player.should_receive(:reset_timer).with(stat) | |
run_callbacks | |
end | |
it "reset health timer if health lost" do | |
should_reset_timer "health" | |
end | |
it "reset energy timer if energy lost" do | |
should_reset_timer "energy" | |
end | |
it "reset attack timer if attack lost" do | |
should_reset_timer "attack" | |
end | |
end | |
end | |
end | |
describe "has instance methods" do | |
def should_restore *stats | |
stats.each{ |stat| @indicator["current_#{stat}"].should eq(@indicator["max_#{stat}"]) } | |
end | |
before :each do | |
@indicator = FactoryGirl.create :indicator | |
end | |
context "#experience_for_next_level" do | |
it "up to 2 level" do | |
@indicator.experience_for_next_level.should eq(11) | |
end | |
it "up to 4 level" do | |
set_level 3 | |
@indicator.experience_for_next_level.should eq(46) | |
end | |
it "up to 13 level" do | |
set_level 12 | |
@indicator.experience_for_next_level.should eq(804) | |
end | |
end | |
context "#experience_for_prev_level" do | |
it "return 0 for 1 level" do | |
@indicator.experience_for_prev_level.should eq(0) | |
end | |
it "down to 1 level" do | |
set_level 2 | |
@indicator.experience_for_prev_level.should eq(11) | |
end | |
it "down to 3 level" do | |
set_level 4 | |
@indicator.experience_for_prev_level.should eq(46) | |
end | |
it "down to 12 level" do | |
set_level 13 | |
@indicator.experience_for_prev_level.should eq(804) | |
end | |
end | |
context "#time_to_update_level?" do | |
it "return true if experience corresponds new level" do | |
boost_exp(11) | |
@indicator.send(:time_to_update_level?).should be_true | |
end | |
end | |
context "#overmuch?" do | |
let!(:sp){ set_sp 19 } | |
it "return true if distribute skill points exceed availables" do | |
distribute_sp = { "attack" => "2", "defence" => "3", "health" => "1", "energy" => "10", "clashes" => "4" } | |
@indicator.send(:overmuch?, distribute_sp).should be_true | |
end | |
it "return false if distribute skill points less than availables" do | |
distribute_sp = { "attack" => "2", "health" => "1", "energy" => "10", "clashes" => "4" } | |
@indicator.send(:overmuch?, distribute_sp).should be_false | |
end | |
end | |
context "#have_skill_points?" do | |
it "return true if skill points are available" do | |
set_sp 3 | |
@indicator.send(:have_skill_points?).should be_true | |
end | |
it "return false if there aren't skill points" do | |
@indicator.send(:have_skill_points?).should be_false | |
end | |
end | |
context "#revive" do | |
before :each do | |
decrease :energy, :health, :attack | |
end | |
it "return nil for unknown stat" do | |
@indicator.revive(:superforce).should be_nil | |
end | |
it "don't change stat for unknown one" do | |
@indicator.revive(:superforce) | |
@indicator.current_energy.should eq(190) | |
end | |
it "restore energy with :energy parameter" do | |
@indicator.revive :energy | |
should_restore(:energy) | |
end | |
it "restore health with :health parameter" do | |
@indicator.revive :health | |
should_restore(:health) | |
end | |
it "restore attack with :attack parameter" do | |
@indicator.revive :attack | |
should_restore(:attack) | |
end | |
it "restore all stats without parameter" do | |
@indicator.revive | |
should_restore(:energy, :health, :attack) | |
end | |
it "restore several stats at once" do | |
@indicator.revive :energy, :attack | |
should_restore(:energy, :attack) | |
end | |
it "restore only necessary stat" do | |
@indicator.revive :attack | |
@indicator.current_energy.should eq(190) | |
end | |
end | |
context "#revive!" do | |
def revive_and_reload *stats | |
@indicator.revive! *stats | |
@indicator.reload | |
end | |
before :each do | |
decrease :energy, :health, :attack | |
@indicator.save | |
end | |
it "invoke #revive with correct arguments" do | |
@indicator.should_receive(:revive).with(:energy, :attack) | |
@indicator.revive! :energy, :attack | |
end | |
it "restore one stat and save" do | |
revive_and_reload :energy | |
should_restore :energy | |
end | |
it "restore several stats at once and save" do | |
revive_and_reload :energy, :attack | |
should_restore :energy, :attack | |
end | |
it "restore all stats without parameters and save" do | |
revive_and_reload | |
should_restore :energy, :health, :attack | |
end | |
it "don't change stats for unknown stat" do | |
revive_and_reload :superforce | |
@indicator.current_energy.should eq(190) | |
end | |
it "return nil for unknown stat" do | |
@indicator.revive!(:superforce).should be_nil | |
end | |
end | |
context "#perk_slots" do | |
it "return 0 for 1 up to 9 level" do | |
set_level 9 | |
@indicator.perk_slots.should eq(0) | |
end | |
it "return 1 for 10 up to 29 level" do | |
set_level 29 | |
@indicator.perk_slots.should eq(1) | |
end | |
it "return 2 for 30 up to 49 level" do | |
set_level 49 | |
@indicator.perk_slots.should eq(2) | |
end | |
it "return 3 for 50 up to 69 level" do | |
set_level 69 | |
@indicator.perk_slots.should eq(3) | |
end | |
it "return 4 for 70 up to 89 level" do | |
set_level 89 | |
@indicator.perk_slots.should eq(4) | |
end | |
it "return 5 for 90 level and more" do | |
set_level 90 | |
@indicator.perk_slots.should eq(5) | |
end | |
end | |
end | |
describe "provides #distribute_skill_points" do | |
def distribute params = {} | |
sp_params.update params | |
@indicator.distribute_skill_points(sp_params) | |
end | |
before :each do | |
@indicator = FactoryGirl.create :indicator, level: 4, skill_points: 22 | |
end | |
let(:sp_params){ { "attack" => "2", "defence" => "3", "health" => "1", "energy" => "10", "clashes" => "4" }.with_indifferent_access } | |
context "which fails when" do | |
it "player level less than 4" do | |
set_level 3 | |
distribute.message.should eq(I18n.t "indicators.distribute_skill_points.failure.cannot_distribute_during_study") | |
end | |
it "havn't skill points" do | |
set_sp 0 | |
distribute.message.should eq(I18n.t "indicators.distribute_skill_points.failure.have_no_skill_points") | |
end | |
it "not enough skill points" do | |
set_sp 19 | |
distribute.message.should eq(I18n.t "indicators.distribute_skill_points.failure.cannot_distribute_more") | |
end | |
it "clashes is odd (prevents skill point gap)" do | |
distribute("clashes" => 5).message.should eq(I18n.t "indicators.distribute_skill_points.failure.cannot_distribute_more") | |
end | |
end | |
context "with" do | |
it "handle negative values" do | |
expect{ distribute "energy" => -2 }.to change{ @indicator.max_energy }.by(20) | |
end | |
it "increase attack by 1 per skill point" do | |
expect{ distribute "attack" => 3 }.to change{ @indicator.player.modifier.attack }.by(3) | |
end | |
it "increase defence by 1 per skill point" do | |
expect{ distribute "defence" => 3 }.to change{ @indicator.player.modifier.defence }.by(3) | |
end | |
it "increase max health by 10 per skill point" do | |
expect{ distribute "health" => 3 }.to change{ @indicator.max_health }.by(30) | |
end | |
it "increase current health by 10 per skill point" do | |
decrease :health, amount: 20 | |
distribute "health" => 3 | |
@indicator.current_health.should eq(110) | |
end | |
it "increase max energy by 10 per skill point" do | |
expect{ distribute "energy" => 3 }.to change{ @indicator.max_energy }.by(30) | |
end | |
it "restore current energy by 10 per skill point" do | |
decrease :energy, amount: 20 | |
distribute "energy" => 3 | |
@indicator.current_energy.should eq(210) | |
end | |
it "increase max attack by 1 per 2 skill points" do | |
expect{ distribute "attack" => 4 }.to change{ @indicator.max_attack }.by(2) | |
end | |
it "restore current attack by 1 per 2 skill points" do | |
decrease :attack | |
distribute "attack" => 4 | |
@indicator.current_attack.should eq(3) | |
end | |
it "decrease skill points" do | |
expect{ distribute }.to change{ @indicator.skill_points }.from(22).to(2) | |
end | |
it "saving the results" do | |
distribute "attack" => 4 | |
@indicator.reload.max_attack.should eq(4) | |
end | |
it "successful response" do | |
distribute.message.should eq(I18n.t "indicators.distribute_skill_points.success") | |
end | |
end | |
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
class Level | |
attr_reader :level, :exp | |
def initialize level = 1 | |
@level = level | |
@exp = start_exp | |
end | |
def next | |
Level.new @level + 1 | |
end | |
def pred | |
Level.new @level - 1 | |
end | |
def next! | |
@level += 1 | |
@exp = start_exp | |
end | |
def pred! | |
@level -= 1 | |
@exp = start_exp | |
end | |
private | |
def next_exp | |
(((((@level + 2) / 1.2) ** 3) / 2.1) + (@level * 4)).to_i | |
end | |
def start_exp | |
@level == 1 ? 0 : (((((@level + 1) / 1.2) ** 3) / 2.1) + ((@level - 1) * 4)).to_i | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment