Skip to content

Commit 61f87cf

Browse files
committed
Implement refreshing auras declaratively
This is the fifth version of the Aura API We are not deprecating the old system yet, although this is preferrable over attribute-script based modifiers where relevant. Previously, we would create an attribute script for atk/cost for some cards and return a modified value based on the board. For example, Old Murk-Eye would consistently have its attack depend on the number of murlocs on the board. This is a problem for three reasons: - It's ugly in the DSL. You'd have to filter the board with race=Race.MURLOC, exclude self, etc. The new system allows for reuse of selectors. - Attribute scripts are constantly "on". Murk-Eye would then have 3 atk in the hand, Mountain Giant would have its cost reduced on the board, etc. - We currently have to create custom cards for every single buff. That's a huge hassle, and problematic for Kettle.
1 parent 97faa53 commit 61f87cf

File tree

4 files changed

+75
-3
lines changed

4 files changed

+75
-3
lines changed

fireplace/aura.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from .managers import CardManager
12
from .utils import CardList, fireplace_logger as logger
23

34

@@ -71,3 +72,63 @@ def destroy(self):
7172
del self._buffs
7273
del self._buffed
7374
self.source.auras.remove(self)
75+
76+
77+
class AuraBuff:
78+
def __init__(self, source, entity):
79+
self.source = source
80+
self.entity = entity
81+
self.tags = CardManager(self)
82+
83+
def __repr__(self):
84+
return "<AuraBuff on %r from %r>" % (self.entity, self.source)
85+
86+
def update_tags(self, tags):
87+
self.tags.update(tags)
88+
self.tick = self.source.game.tick
89+
90+
def destroy(self):
91+
self.entity.slots.remove(self)
92+
self.source.game.active_aura_buffs.remove(self)
93+
94+
def _getattr(self, attr, i):
95+
value = getattr(self, attr, 0)
96+
if callable(value):
97+
return value(self.entity, i)
98+
return i + value
99+
100+
101+
class Refresh:
102+
"""
103+
Refresh a buff or a set of tags on an entity
104+
"""
105+
def __init__(self, selector, tags):
106+
self.selector = selector
107+
self.tags = tags
108+
109+
def trigger(self, source):
110+
entities = self.selector.eval(source.game, source)
111+
for entity in entities:
112+
tags = {}
113+
for tag, value in self.tags.items():
114+
if not isinstance(value, int) and not callable(value):
115+
value = value.evaluate(source)
116+
tags[tag] = value
117+
118+
entity.refresh_buff(source, tags)
119+
120+
121+
class TargetableByAuras:
122+
def refresh_buff(self, source, tags):
123+
for slot in self.slots[:]:
124+
if isinstance(slot, AuraBuff) and slot.source is source:
125+
slot.update_tags(tags)
126+
# Move the buff position at the end again
127+
self.slots.remove(slot)
128+
self.slots.append(slot)
129+
break
130+
else:
131+
buff = AuraBuff(source, self)
132+
buff.update_tags(tags)
133+
self.slots.append(buff)
134+
source.game.active_aura_buffs.append(buff)

fireplace/card.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from itertools import chain
22
from . import cards as CardDB, rules
33
from .actions import Damage, Deaths, Destroy, Heal, Morph, Play, Shuffle, SetCurrentHealth
4-
from .aura import Aura
4+
from .aura import Aura, TargetableByAuras
55
from .entity import Entity, boolean_property, int_property
66
from .enums import CardType, PlayReq, Race, Rarity, Zone
77
from .managers import CardManager
@@ -120,7 +120,7 @@ def buff(self, target, buff, **kwargs):
120120
return ret
121121

122122

123-
class PlayableCard(BaseCard):
123+
class PlayableCard(BaseCard, TargetableByAuras):
124124
windfury = boolean_property("windfury")
125125

126126
def __init__(self, id, data):

fireplace/game.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ def __init__(self, players):
3333
self.auras = []
3434
self.minions_killed_this_turn = CardList()
3535
self.no_aura_refresh = False
36+
self.tick = 0
37+
self.active_aura_buffs = []
3638

3739
def __repr__(self):
3840
return "%s(players=%r)" % (self.__class__.__name__, self.players)
@@ -211,6 +213,14 @@ def refresh_auras(self):
211213
return
212214
for aura in self.auras:
213215
aura.update()
216+
for entity in self.entities:
217+
if entity.data and hasattr(entity.data.scripts, "update"):
218+
if not entity.silenced:
219+
entity.data.scripts.update.trigger(entity)
220+
for buff in self.active_aura_buffs[:]:
221+
if buff.tick < self.tick:
222+
buff.destroy()
223+
self.tick += 1
214224

215225
def prepare(self):
216226
self.players[0].opponent = self.players[1]

fireplace/player.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import random
22
from itertools import chain
33
from .actions import Draw, Give, Steal, Summon
4+
from .aura import TargetableByAuras
45
from .card import Card
56
from .deck import Deck
67
from .entity import Entity
@@ -11,7 +12,7 @@
1112
from .utils import CardList
1213

1314

14-
class Player(Entity):
15+
class Player(Entity, TargetableByAuras):
1516
Manager = PlayerManager
1617
extra_deathrattles = slot_property("extra_deathrattles")
1718
healing_double = slot_property("healing_double", sum)

0 commit comments

Comments
 (0)