def test_buff_cancelling_when_other_buff_triggering(self): buffable = Buffable() buff_id_1 = 1 buff_id_2 = 2 buff_1 = BuffBuilder(buff_id_1).modify("+", 50, Attributes.ATK).whenever(DamageEvent)\ .just_if("not has_buff 2").build() buff_2 = BuffBuilder(buff_id_2).modify( "+", 75, Attributes.ATK).whenever(DamageEvent).build() # A condition that can be triggered by a MockEvent @buffspecs.AddConditionFor([BuffEvent]) def has_buff(event, buff_id): return buff_id in event.buffable.active_buffs add_buff(buffable, buff_1, CompleteBuildingEvent()) call_event(DamageEvent(buffable)) # Player does not have buff 2 so he should be getting buff 1 assert buffable.attributes[Attributes.ATK] == 50 assert buff_id_1 in buffable.active_buffs add_buff(buffable, buff_2, CompleteBuildingEvent()) call_event(DamageEvent(buffable)) # Now that he got buff 2 applied, buff 1 should be removed and only buff 2 is applied assert buffable.attributes[Attributes.ATK] == 75 assert buff_id_1 not in buffable.active_buffs assert buff_id_2 in buffable.active_buffs
def test_increasing_propagation_source_attribute_repropagates_derivation( self): player = Player() player.attributes[Attributes.ATK] = 200 equipment = Equipment() equipment.attributes[Attributes.ATK] = 100 equipment.owner = player # 50% of equipment attack goes to player DEF add_buff(equipment, BuffBuilder().modify("%", 0.5, Attributes.ATK)\ .propagates_to_attribute(Attributes.DEF).propagates_to(Player).build(), CompleteBuildingEvent() ) # 50% of the EQUIPMENT ATK (100) should have gone to player DEF assert equipment.attributes[Attributes.ATK] == 100 assert player.attributes[ Attributes.DEF] == equipment.attributes[Attributes.ATK] * 0.5 # +100 to Equipment ATTACK, it should propagate 50% of it to player DEF add_buff(equipment, BuffBuilder().modify("+", 100, Attributes.ATK).build(), CompleteBuildingEvent()) assert equipment.attributes[Attributes.ATK] == 200 assert player.attributes[ Attributes.DEF] == equipment.attributes[Attributes.ATK] * 0.5
def test_basic_buff_dependency(self): buffable = Buffable() buff_id_1 = 1 buff_id_2 = 2 buff_1 = BuffBuilder().modify( "+", 50, Attributes.ATK).just_if("has_buff 2").build() buff_2 = BuffBuilder().modify("+", 50, Attributes.ATK).build() # A condition that can be triggered by a MockEvent @buffspecs.AddConditionFor([BuffEvent]) def has_buff(event, buff_id): return buff_id in event.buffable.active_buffs add_buff(buffable, buff_1, CompleteBuildingEvent()) # Player should not be modified because he did not have buff 2 assert buffable.attributes[Attributes.ATK] == 0 # Adding the second buff should trigger the first one because his condidion matched add_buff(buffable, buff_2, CompleteBuildingEvent()) # Both buffs should be applied now assert buffable.attributes[Attributes.ATK] == 100 assert buff_id_1 in buffable.active_buffs assert buff_id_2 in buffable.active_buffs
def test_changing_source_attribute_affects_derivated_attribute(self): buffable = Buffable() buffable.attributes[Attributes.ATK] = 100 # Simple buff that 50% of your attack goes to your def buff = BuffBuilder().modify("%", 0.5, Attributes.ATK).to_attribute( Attributes.DEF).build() add_buff(buffable, buff, CompleteBuildingEvent()) # Player should have got 50% of his atk (50) to def assert buffable.attributes[Attributes.DEF] == 50 assert buffable.attributes[Attributes.ATK] == 100 # Now we add +100 attack to the player, his 50% buff instead of giving 50 def should give 100 def buff_2 = BuffBuilder().modify("+", 100, Attributes.ATK).build() add_buff(buffable, buff_2, CompleteBuildingEvent()) # Player defense should be updated as his attack increased assert buffable.attributes[Attributes.DEF] == 100 assert buffable.attributes[Attributes.ATK] == 200 # Now removing the buff should re-calculate the derivation as well remove_buff(buffable, buff_2.buff_id) # Player should have got 50% of his atk (50) to def as it should be derivating from 100 atk again assert buffable.attributes[Attributes.DEF] == 50 assert buffable.attributes[Attributes.ATK] == 100
def test_propagation_registering_triggers_properly(self): player = Player() player.attributes[Attributes.ATK] = 50 player.attributes[Attributes.DEF] = 75 equipment = Equipment() equipment.attributes[Attributes.ATK] = 100 equipment.owner = player castle = Castle() castle.players = [player] # 50% of player def becomes player HP add_buff(equipment, BuffBuilder(1).modify("%", 0.5, Attributes.DEF).to_attribute(Attributes.HP) \ .propagates_to(Player).build(), CompleteBuildingEvent() ) assert len(equipment.activation_triggers) == 0 assert len(player.activation_triggers) == 0 assert len(equipment.propagation_triggers) == 0 add_buff(equipment, BuffBuilder(2).modify("%", 0.5, Attributes.ATK).propagates_to_attribute(Attributes.HP) \ .propagates_to(Player).build(), CompleteBuildingEvent() ) assert len(equipment.propagation_triggers) == 0 assert len(player.activation_triggers) == 0
def test_inactivating_propagation_target(self): player = Player() player.attributes[Attributes.HP] = 10 castle = Castle() castle.players.append(player) # A global castle buff of +50 ATK castle_buff = BuffBuilder().modify("+", 50, Attributes.ATK).propagates_to(Player)\ .whenever(FartEvent).just_if("is_healthy").build() @buffspecs.AddConditionFor([BuffEvent]) def is_healthy(event): return event.buffable.attributes[Attributes.HP] > 0 add_buff(castle, castle_buff, CompleteBuildingEvent()) # Call the event triggering the buff call_event(FartEvent(player)) assert player.attributes[Attributes.ATK] == 50 inactivate_buff(player, castle_buff, None) assert player.attributes[Attributes.ATK] == 0 assert castle_buff.buff_id not in player.active_buffs # Activation trigger should be registered because buff had a condition that can change assert len(player.activation_triggers["FartEvent"]) == 1
def test_propagation_with_triggers(self): player = Player() player.attributes[Attributes.ATK] = 100 # A global castle buff of 50% ATK castle_buff = BuffBuilder().modify("%", 0.5, Attributes.ATK)\ .propagates_when(RecruitPlayerEvent).propagates_to(Player).build() castle = Castle() castle.players.append(player) add_buff(castle, castle_buff, CompleteBuildingEvent()) # Castle should have a propagation trigger not a activation trigger assert castle_buff.buff_id not in castle.activation_triggers[ "RecruitPlayerEvent"] assert castle_buff.buff_id in castle.propagation_triggers[ "RecruitPlayerEvent"] # Event was not triggered, so player should not get modified assert player.attributes[Attributes.ATK] == 100 # Now we trigger the event and expect the propagation to happen call_event(RecruitPlayerEvent(castle)) # Now the buff should have been applied assert player.attributes[Attributes.ATK] == 150 assert castle_buff.buff_id in player.active_buffs assert castle_buff.buff_id in castle.active_buffs # The propagation trigger should not consumed assert castle_buff.buff_id in castle.propagation_triggers[ "RecruitPlayerEvent"]
def test_derivating_the_propagated_value(self): player = Player() player.attributes[Attributes.ATK] = 200 equipment = Equipment() equipment.attributes[Attributes.ATK] = 100 equipment.owner = player # 50% of equipment attack goes to player DEF equipment_buff_2 = BuffBuilder().modify("%", 0.5, Attributes.ATK)\ .propagates_to_attribute(Attributes.DEF).propagates_to(Player).build() add_buff(equipment, equipment_buff_2, CompleteBuildingEvent()) # Atk should not be modified assert player.attributes[Attributes.ATK] == 200 # 50% of the EQUIPMENT ATK (100) should have gone to player DEF assert player.attributes[ Attributes.DEF] == equipment.attributes[Attributes.ATK] * 0.5 # Equipment should not be affected assert equipment.attributes[Attributes.ATK] == 100 assert equipment.attributes[Attributes.DEF] == 0 # Derivation should be stored in the target, not the propagator assert len(equipment.attributes.get_data( Attributes.ATK).derivations) == 0 assert len(player.attributes.get_data(Attributes.ATK).derivations) == 1
def test_derivation_debug_history(self): buffable = Buffable() buffable.attributes[Attributes.ATK] = 100 buff = BuffBuilder().modify("%", 0.5, Attributes.ATK).to_attribute( Attributes.DEF).build() add_buff(buffable, buff, CompleteBuildingEvent()) atk_modification_history = list( buffable.attributes.get_data(Attributes.DEF).history.values()) last_modification = atk_modification_history[0] # The original modifier should be in history as well assert last_modification.modifier.value == 0.5 assert last_modification.modifier.attribute_id == Attributes.ATK assert last_modification.modifier.operator == "%" # It should have created an modifier of 50% of user atk, since atk was 100, thats +50 DEF assert last_modification.derivated_modifier.value == 50 assert last_modification.derivated_modifier.attribute_id == Attributes.DEF assert last_modification.derivated_modifier.operator == "+" assert last_modification.buff_id == buff.buff_id event_chain = last_modification.source_event.get_event_chain() assert isinstance(event_chain[0], AddBuffEvent) assert isinstance(event_chain[1], CompleteBuildingEvent)
def test_propagation_with_stacks(self): player = Player() castle = Castle() castle.players.append(player) # A global castle buff of +50 ATK castle_buff = BuffBuilder().modify("+", 50, Attributes.ATK).propagates_to(Player).stacks(3).build() # Add the first stack add_buff(castle, castle_buff, CompleteBuildingEvent()) assert player.attributes[Attributes.ATK] == 50 assert player.active_buffs[castle_buff.buff_id].stack == 1 assert castle.active_buffs[castle_buff.buff_id].stack == 1 # Add a second stack add_buff(castle, castle_buff, CompleteBuildingEvent()) assert player.active_buffs[castle_buff.buff_id].stack == 2 assert castle.active_buffs[castle_buff.buff_id].stack == 2 assert player.attributes[Attributes.ATK] == 100 # Add a the third stack add_buff(castle, castle_buff, CompleteBuildingEvent()) assert player.attributes[Attributes.ATK] == 150 # An extra stack should not propagate the buff anymore add_buff(castle, castle_buff, CompleteBuildingEvent()) assert player.attributes[Attributes.ATK] == 150
def test_propagating_a_derivation_buff(self): # Simple derivation buff buffable = Buffable() buffable.attributes[Attributes.ATK] = 100 buff = BuffBuilder().modify("%", 0.5, Attributes.ATK).to_attribute( Attributes.DEF).build() # We want to track the performance of this with TrackStack() as track: add_buff(buffable, buff, CompleteBuildingEvent())
def test_propagating_a_derivation_buff(self): player = Player() player.attributes[Attributes.ATK] = 50 player.attributes[Attributes.DEF] = 75 equipment = Equipment() equipment.attributes[Attributes.ATK] = 100 equipment.owner = player castle = Castle() castle.players = [player] # 50% of player def becomes player HP add_buff(equipment, BuffBuilder(1).modify("%", 0.5, Attributes.DEF).to_attribute(Attributes.HP) \ .propagates_to(Player).build(), CompleteBuildingEvent() ) assert player.attributes[Attributes.DEF] == 75 assert player.attributes[Attributes.HP] == 75 / 2 # 50% of equipment attack goes to player DEF add_buff(equipment, BuffBuilder(2).modify("%", 0.5, Attributes.ATK).propagates_to_attribute(Attributes.DEF) \ .propagates_to(Player).build(), CompleteBuildingEvent() ) assert player.attributes[Attributes.DEF] == 125 assert player.attributes[Attributes.HP] == 125 / 2 # Remove the player DEF -> HP derivation remove_buff(equipment, 1) foka = (125 + 40 + 40 + 50) / 2 asd = player.attributes[Attributes.HP] assert player.attributes[Attributes.DEF] == 125 assert player.attributes[Attributes.HP] == 0
def test_double_propagation(self): player = Player() player.attributes[Attributes.ATK] = 100 castle = Castle() castle.players.append(player) equipment = Equipment() equipment.owner = player # A global castle buff of 50% ATK castle_buff = BuffBuilder().modify( "%", 0.5, Attributes.ATK).propagates_to(Player).build() # Equipment buff of 100% bonus atk equipment_buff = BuffBuilder().modify( "%", 1, Attributes.ATK).propagates_to(Player).build() add_buff(castle, castle_buff, CompleteBuildingEvent()) add_buff(equipment, equipment_buff, CompleteBuildingEvent()) assert len(player.active_buffs) == 2 # flat bonus = 100, 250% total bonus from propagations, so 250 final value assert player.attributes[Attributes.ATK] == 250
def test_removing_derivation_buff_updates_derivated_attributes(self): buffable = Buffable() buffable.attributes[Attributes.ATK] = 100 buffable.attributes[Attributes.DEF] = 100 # Simple buff that 50% of your attack goes to your def buff = BuffBuilder().modify("%", 0.5, Attributes.ATK).to_attribute( Attributes.DEF).build() add_buff(buffable, buff, CompleteBuildingEvent()) # 50% of your def, goes to your HP buff_2 = BuffBuilder().modify("%", 0.5, Attributes.DEF).to_attribute( Attributes.HP).build() add_buff(buffable, buff_2, CompleteBuildingEvent()) # Player defense should be updated as his attack increased assert buffable.attributes[Attributes.DEF] == 150 assert buffable.attributes[Attributes.HP] == 75 remove_buff(buffable, buff.buff_id) assert buffable.attributes[Attributes.DEF] == 100 assert buffable.attributes[Attributes.HP] == 50
def test_removing_propagation(self): player = Player() player.attributes[Attributes.ATK] = 100 castle_buff = BuffBuilder().modify( "%", 0.5, Attributes.ATK).propagates_to(Player).build() castle = Castle() castle.players.append(player) add_buff(castle, castle_buff, CompleteBuildingEvent()) assert player.attributes[Attributes.ATK] == 150 remove_buff(castle, castle_buff.buff_id) assert player.attributes[Attributes.ATK] == 100
def test_chaining_propagation(self): player = Player() player.attributes[Attributes.ATK] = 100 equipment = Equipment() equipment.attributes[Attributes.ATK] = 100 equipment.owner = player # 50% of player DEF becomes player HP add_buff(equipment, BuffBuilder().modify("%", 0.5, Attributes.DEF).to_attribute(Attributes.HP) \ .propagates_to(Player).build(), CompleteBuildingEvent() ) # 50% of equipment attack goes to player DEF add_buff(equipment, BuffBuilder().modify("%", 0.5, Attributes.ATK).propagates_to_attribute(Attributes.DEF) \ .propagates_to(Player).build(), CompleteBuildingEvent() ) assert player.attributes[Attributes.DEF] == 50 assert player.attributes[Attributes.HP] == 25
def test_propagation_condition_and_trigger(self): player1 = Player() player1.attributes[Attributes.ATK] = 100 castle = Castle() castle.players.append(player1) # A global castle buff that propagates that 50% ATK to players if a castle has 3 or more players castle_buff = BuffBuilder().modify("%", 0.5, Attributes.ATK)\ .propagates_when(RecruitPlayerEvent).only_propagates_if("cond_has_qtd_players 2").propagates_to(Player).build() @buffspecs.AddConditionFor([BuffEvent]) def cond_has_qtd_players(event, amt): return len(event.buffable.players) >= amt add_buff(castle, castle_buff, CompleteBuildingEvent()) # The buff is not active on the player assert castle_buff.buff_id in castle.active_buffs assert castle_buff.buff_id not in player1.active_buffs # Castle should have registered the trigger assert "RecruitPlayerEvent" not in castle.activation_triggers assert "RecruitPlayerEvent" in castle.propagation_triggers # add another player to the castle player2 = Player() player2.attributes[Attributes.ATK] = 200 castle.players.append(player2) call_event(RecruitPlayerEvent(castle)) # Now that we matched the condition all 3 players should have been propagated and have 50% bonus assert player1.attributes[Attributes.ATK] == 100 * 1.5 assert player2.attributes[Attributes.ATK] == 200 * 1.5 # Adding a new player should propagate the buff to the new player as well player3 = Player() player3.attributes[Attributes.ATK] = 300 castle.players.append(player3) call_event(RecruitPlayerEvent(castle)) # Should not affect the other players assert player1.attributes[Attributes.ATK] == 100 * 1.5 assert player2.attributes[Attributes.ATK] == 200 * 1.5 # Should propagate to the new player assert player3.attributes[Attributes.ATK] == 300 * 1.5
def test_inactivating_propagator(self): player = Player() castle = Castle() castle.players.append(player) # A global castle buff of +50 ATK castle_buff = BuffBuilder().modify( "+", 50, Attributes.ATK).propagates_to(Player).build() add_buff(castle, castle_buff, CompleteBuildingEvent()) assert player.attributes[Attributes.ATK] == 50 inactivate_buff(castle, castle_buff, None) assert player.attributes[Attributes.ATK] == 0
def test_propating_two_targets(self): player = Player() player.attributes[Attributes.ATK] = 100 castle = Castle() player.castle = castle castle.players.append(player) # A global castle buff of bonus coins collected that can affect both players and castles castle_buff = BuffBuilder().modify( "+", 10, Attributes.BONUS_COINS_COLLECTED).propagates_to(Player, Castle).build() add_buff(castle, castle_buff, CompleteBuildingEvent()) # Both players and castles shall have bonus coins collected assert player.attributes[Attributes.BONUS_COINS_COLLECTED] == 10 assert castle.attributes[Attributes.BONUS_COINS_COLLECTED] == 10
def test_simple_derivation(self): buffable = Buffable() buffable.attributes[Attributes.ATK] = 100 # Simple buff that 50% of your attack goes to your def buff = BuffBuilder().modify("%", 0.5, Attributes.ATK).to_attribute( Attributes.DEF).build() add_buff(buffable, buff, CompleteBuildingEvent()) # Player should have got 50% of his atk (50) to def assert buffable.attributes[Attributes.DEF] == 50 assert buffable.attributes[Attributes.ATK] == 100 assert buff.buff_id in buffable.active_buffs # Checking the derivation has been registered assert list( buffable.attributes.get_data( Attributes.ATK).derivations.keys())[0] == Attributes.DEF
def test_performance(self): player1 = Player() player1_equips = [] for i in range(10): equip = Equipment() equip.owner = player1 player1_equips.append(equip) player2 = Player() player2_equips = [] for i in range(10): equip = Equipment() equip.owner = player2 player2_equips.append(equip) castle = Castle() castle.players = [player1, player2] seed = 1234 random.seed(seed) buff_targets = {} for i in range(10): builder = BuffBuilder() modifier = random_modifier() builder.modify(*modifier) propagates = False apply_to, propagate_to = random_targets() if chance_pct(25): builder.to_attribute(random_attribute(exlude=modifier[2])) if chance_pct(25): propagates = True builder.propagates_to_attribute(random_attribute())
def test_propagation_wont_duplicate_buffs(self): player = Player() player.attributes[Attributes.ATK] = 100 castle_buff = BuffBuilder().modify("%", 0.5, Attributes.ATK) \ .propagates_when(RecruitPlayerEvent).propagates_to(Player).build() castle = Castle() castle.players.append(player) add_buff(castle, castle_buff, CompleteBuildingEvent()) # Trigger the event twice call_event(RecruitPlayerEvent(castle)) call_event(RecruitPlayerEvent(castle)) # The propagation should have happened only once assert len(player.active_buffs) == 1 assert player.attributes[Attributes.ATK] == 150
def test_buff_event_modifications_log(self): buffable = Buffable() # Building a buff with the builder buff = BuffBuilder().modify( "+", 5, Attributes.ATK).whenever(FartEvent).build() add_buff(buffable, buff, CompleteBuildingEvent()) fart_event = FartEvent(buffable) event_result = call_event(fart_event) modifications = event_result.added_modifications # Check if the event returns the correct modifications that happened assert len(modifications) == 1 assert modifications[0].buff_id == buff.buff_id assert modifications[0].source_event == fart_event assert modifications[0].modifier.operator == "+" assert modifications[0].modifier.value == 5 assert modifications[0].modifier.attribute_id == Attributes.ATK
def test_removing_propagation_source(self): player = Player() player.attributes[Attributes.ATK] = 200 equipment = Equipment() equipment.attributes[Attributes.ATK] = 100 equipment.owner = player # 50% of equipment attack goes to player DEF equipment_buff_2 = BuffBuilder().modify("%", 0.5, Attributes.ATK) \ .propagates_to_attribute(Attributes.DEF).propagates_to(Player).build() add_buff(equipment, equipment_buff_2, CompleteBuildingEvent()) # 50% of the EQUIPMENT ATK (100) should have gone to player DEF assert player.attributes[ Attributes.DEF] == equipment.attributes[Attributes.ATK] * 0.5 # If we remove the propagation source, the propagation targets attributes needs to be updated remove_buff(equipment, equipment_buff_2.buff_id) assert player.attributes[Attributes.DEF] == 0
def test_propagation_also_modifyng_source(self): player = Player() player.attributes[Attributes.ATK] = 100 castle = Castle() castle.players.append(player) # A global castle buff of +50 ATK but also affects the castle castle_buff = BuffBuilder().modify("+", 50, Attributes.ATK)\ .propagates_to(Player, Castle).build() add_buff(castle, castle_buff, CompleteBuildingEvent()) # The buff should be active in the owner and target assert castle_buff.buff_id in castle.active_buffs assert castle_buff.buff_id in player.active_buffs # Since castle buffs are propagated to the players in that castle, the player should have got +50% ATK assert player.attributes[Attributes.ATK] == 150 # However even tho the castle has this buff as active, since its not a target it did not change ATK assert castle.attributes[Attributes.ATK] == 50
def test_propagation_debug_tracking(self): player = Player() player.attributes[Attributes.ATK] = 100 castle = Castle() castle.players.append(player) # A global castle buff of 50% ATK castle_buff = BuffBuilder().modify( "%", 0.5, Attributes.ATK).propagates_to(Player).build() event_result = add_buff(castle, castle_buff, CompleteBuildingEvent()) # The event result should let us know about the propagation propagated_modifications = event_result.propagated_modifications added_modifications_on_propagation = propagated_modifications[ player.id][0].added_modifications[0] # We also have the history in the attribute modification history attribute_history_modification = list( player.attributes.get_data(Attributes.ATK).history.values())[0] for modification in [ added_modifications_on_propagation, attribute_history_modification ]: # Asserting this stored the modifier correctly assert modification.modifier.value == 0.5 assert modification.modifier.operator == "%" assert modification.modifier.attribute_id == Attributes.ATK # Asserting the buff chain, we added a buff, caused a propagation to add a buff in another buffable event_chain = modification.source_event.get_event_chain() assert isinstance(event_chain[0], AddBuffEvent) assert isinstance(event_chain[1], BuffPropagatedEvent) assert event_chain[1].source_buffable == castle assert isinstance(event_chain[2], AddBuffEvent) assert isinstance(event_chain[3], CompleteBuildingEvent)
def test_inactivating_propagated_buff_stack_from_source(self): player = Player() castle = Castle() castle.players.append(player) # A global castle buff of +50 ATK castle_buff = BuffBuilder().modify("+", 50, Attributes.ATK).propagates_to(Player).stacks(3).build() # Add thre stacks of the buff add_buff(castle, castle_buff, CompleteBuildingEvent()) add_buff(castle, castle_buff, CompleteBuildingEvent()) add_buff(castle, castle_buff, CompleteBuildingEvent()) assert player.attributes[Attributes.ATK] == 150 # Stacks are added both to source as well to propagation target assert player.active_buffs[castle_buff.buff_id].stack == 3 assert castle.active_buffs[castle_buff.buff_id].stack == 3 inactivate_buff(castle, castle_buff, None) assert player.attributes[Attributes.ATK] == 100 assert castle.active_buffs[castle_buff.buff_id].stack == 2 assert player.active_buffs[castle_buff.buff_id].stack == 2
def test_pulling_propagated_buffs(self): player = Player() player.attributes[Attributes.ATK] = 100 castle_buff = BuffBuilder().modify( "%", 0.5, Attributes.ATK).propagates_to(Player).build() castle = Castle() castle.players.append(player) add_buff(castle, castle_buff, CompleteBuildingEvent()) # First player should be updated assert player.attributes[Attributes.ATK] == 150 # Adding another player player2 = Player() player2.attributes[Attributes.ATK] = 100 # We can enforce a propagation to happen in case we just created a new buffable and we have "global buffs" pull_propagated_buffs(castle, player2, CompleteBuildingEvent()) # Player should have pulled the propagation from his castle assert player2.attributes[Attributes.ATK] == 150
def test_propagating_a_derivation_buff(self): player = Player() player.attributes[Attributes.ATK] = 50 player.attributes[Attributes.DEF] = 75 equipment = Equipment() equipment.attributes[Attributes.ATK] = 100 equipment.owner = player castle = Castle() castle.players = [player] # 50% of player def becomes player HP add_buff(equipment, BuffBuilder(1).modify("%", 0.5, Attributes.DEF).to_attribute(Attributes.HP) \ .propagates_to(Player).build(), CompleteBuildingEvent() ) assert player.attributes[Attributes.HP] == 75 / 2 # 50% of equipment attack goes to player DEF add_buff(equipment, BuffBuilder(2).modify("%", 0.5, Attributes.ATK).propagates_to_attribute(Attributes.DEF)\ .propagates_to(Player).build(), CompleteBuildingEvent() ) assert player.attributes[Attributes.DEF] == 125 assert player.attributes[Attributes.HP] == 125 / 2 # 25% of castle DEF becomes player DEF add_buff(castle, BuffBuilder(3).modify("%", 0.5, Attributes.DEF).propagates_to_attribute(Attributes.DEF) \ .propagates_to(Player).build(), CompleteBuildingEvent() ) assert castle.attributes[Attributes.DEF] == 0 assert player.attributes[Attributes.DEF] == 125 assert player.attributes[Attributes.HP] == 125 / 2 # Castle buff of +80 DEF, 50% should derivate to player add_buff(castle, BuffBuilder(4).modify("+", 80, Attributes.DEF).build(), CompleteBuildingEvent()) assert castle.attributes[Attributes.DEF] == 80 assert player.attributes[Attributes.DEF] == 125 + 40 assert player.attributes[Attributes.HP] == (125 + 40) / 2 # Another Castle buff of +80 DEF, 50% should derivate to player add_buff(castle, BuffBuilder(5).modify("+", 80, Attributes.DEF).build(), CompleteBuildingEvent()) assert castle.attributes[Attributes.DEF] == 80 + 80 assert player.attributes[Attributes.DEF] == 125 + 40 + 40 assert player.attributes[Attributes.HP] == (125 + 40 + 40) / 2 # Just bumping player + 100 ATK. To remember: add_buff(equipment, BuffBuilder(6).modify("+", 100, Attributes.ATK).build(), CompleteBuildingEvent()) assert castle.attributes[Attributes.DEF] == 80 + 80 assert player.attributes[Attributes.DEF] == 125 + 40 + 40 + 50 assert player.attributes[Attributes.HP] == (125 + 40 + 40 + 50) / 2 # 100% of castle DEF goes to player HP add_buff( castle, BuffBuilder(7).modify( "%", 1, Attributes.DEF).propagates_to_attribute( Attributes.HP).propagates_to(Player).build(), CompleteBuildingEvent()) assert castle.attributes[Attributes.DEF] == 80 + 80 assert player.attributes[Attributes.DEF] == 125 + 40 + 40 + 50 assert player.attributes[Attributes.HP] == ( (125 + 40 + 40 + 50) / 2) + castle.attributes[Attributes.DEF] # 50% of HP to CRIT_DAMAGE on player add_buff( player, BuffBuilder(8).modify("%", 0.5, Attributes.HP).to_attribute( Attributes.CRIT_DAMAGE).build(), CompleteBuildingEvent()) assert player.attributes[Attributes.HP] == ( (125 + 40 + 40 + 50) / 2) + castle.attributes[Attributes.DEF] assert player.attributes[ Attributes.CRIT_DAMAGE] == player.attributes[Attributes.HP] / 2 assert castle.attributes[Attributes.DEF] == 80 + 80 assert player.attributes[Attributes.DEF] == 125 + 40 + 40 + 50 with TrackStack() as track: # 50% of castle def to player def add_buff(castle, BuffBuilder(9).modify("%", 0.5, Attributes.DEF).propagates_to_attribute(Attributes.DEF) \ .propagates_to(Player).build(), CompleteBuildingEvent() ) # Printing the stack, this buff should trigger a 4 step derivation chain track.print_stack() assert player.attributes[Attributes.DEF] == 125 + 40 + 40 + 50 + 80 assert player.attributes[Attributes.HP] == ( (125 + 40 + 40 + 50 + 80) / 2) + castle.attributes[Attributes.DEF] assert player.attributes[ Attributes.CRIT_DAMAGE] == player.attributes[Attributes.HP] / 2 assert castle.attributes[Attributes.DEF] == 80 + 80
def test_reuse_conditions_with_abstraction(self): # Lets say our game has an economy, of multiple types of coins. example_coin_types = ["gold", "silver", "copper"] class EconomyEvent(BuffEvent): def __init__(self, buffable, coin_type_changed, coin_amount): super(EconomyEvent, self).__init__(buffable) self.coin_type_changed = coin_type_changed self.coin_amount = coin_amount # The player can get coins by mining for instance class PlayerMineCoinsEvent(EconomyEvent): def __init__(self, buffable, coin_type_changed, coin_amount): super(PlayerMineCoinsEvent, self).__init__(buffable, coin_type_changed, coin_amount) # The player can also get coins by looting enemies class PlayerLootEnemyEvent(EconomyEvent): def __init__(self, buffable, coin_type_changed, coin_amount): super(PlayerLootEnemyEvent, self).__init__(buffable, coin_type_changed, coin_amount) # Now this condition works for any economy event generically @buffspecs.AddConditionFor([EconomyEvent]) def is_coin_type(event, *coin_types): return event.coin_type_changed in coin_types # Now we create a buff that buffable = Buffable() # Making a buff that gives player DEF when if he mines a gold coin bdr = BuffBuilder().modify("+", 5, Attributes.DEF).whenever( PlayerMineCoinsEvent).just_if("is_coin_type gold") bonus_mine_gold_buff = bdr.build() # Making a buff that gives player ATK if he loots silver or copper coins bdr = BuffBuilder().modify( "+", 5, Attributes.ATK ).whenever(PlayerLootEnemyEvent).just_if( "is_coin_type copper silver" # Multiple parameters in the condition ) bonus_loot_copper_silver_buff = bdr.build() # Add the buffs add_buff(buffable, bonus_loot_copper_silver_buff, CompleteBuildingEvent()) add_buff(buffable, bonus_mine_gold_buff, CompleteBuildingEvent()) # Now we mine some silver call_event(PlayerMineCoinsEvent(buffable, "silver", 10)) # No buffs should be applied as no conditions matched assert len(buffable.active_buffs) == 0 # Now he mines gold, should activate the buff cause the condition matched call_event(PlayerMineCoinsEvent(buffable, "gold", 10)) assert bonus_mine_gold_buff.buff_id in buffable.active_buffs # Now he will loot gold, condition should not match call_event(PlayerLootEnemyEvent(buffable, "gold", 10)) assert bonus_loot_copper_silver_buff.buff_id not in buffable.active_buffs # Now he finally loots silver or copper (in this case, silver) and the buff should apply call_event(PlayerLootEnemyEvent(buffable, "silver", 10)) assert bonus_loot_copper_silver_buff.buff_id in buffable.active_buffs