class Buff(object): MAXHP_CAP = 1.30 _static = Static({ 'all_buffs': [], 'adv': None }) def __init__(self, name='<buff_noname>', value=0, duration=0, mtype='att', morder=None, modifier=None, hidden=False): self.name = name self.__value = value self.duration = duration self.mod_type = mtype self.mod_order = morder or ('chance' if self.mod_type == 'crit' else 'buff') self.bufftype = 'misc' if hidden else 'self' self.bufftime = self._bufftime if self.duration > 0 else self._no_bufftime self.buff_end_timer = Timer(self.buff_end_proc) if modifier: self.modifier = modifier self.get = self.modifier.get elif mtype != 'effect': self.modifier = Modifier('mod_' + self.name, self.mod_type, self.mod_order, 0) self.modifier.get = self.get else: self.modifier = None self.dmg_test_event = Event('dmg_formula') self.dmg_test_event.dmg_coef = 1 self.dmg_test_event.dname = 'test' self.hidden = hidden self.__stored = 0 self.__active = 0 # self.on() def logwrapper(self, *args): if not self.hidden: log('buff', *args) def _no_bufftime(self): return 1 def _ex_bufftime(self): return 1 + self._static.adv.sub_mod('buff', 'ex') def _bufftime(self): return self._static.adv.mod('buff', operator=operator.add) def _debufftime(self): return self._static.adv.mod('debuff', operator=operator.add) def no_bufftime(self): self.bufftime = self._no_bufftime return self def ex_bufftime(self): self.bufftime = self._ex_bufftime return self def value(self, newvalue=None): if newvalue: self.logwrapper(self.name, f'{self.mod_type}({self.mod_order}): {newvalue:.02f}', 'buff value change') return self.set(newvalue) else: return self.get() def get(self): if self.__active: return self.__value else: return 0 def set(self, v, d=None): self.__value = v if d != None: self.duration = d return self def stack(self): stack = 0 for i in self._static.all_buffs: if i.name == self.name: if i.__active != 0: stack += 1 return stack def valuestack(self): stack = 0 value = 0 for i in self._static.all_buffs: if i.name == self.name: if i.__active != 0: stack += 1 value += i.__value return value, stack def effect_on(self): return self.modifier and self.modifier.on() def effect_off(self): return self.modifier and self.modifier.off() def buff_end_proc(self, e): self.logwrapper(self.name, f'{self.mod_type}({self.mod_order}): {self.value():.02f}', 'buff end <timeout>') self.__active = 0 if self.__stored: idx = len(self._static.all_buffs) while 1: idx -= 1 if idx < 0: break if self == self._static.all_buffs[idx]: self._static.all_buffs.pop(idx) break self.__stored = 0 value, stack = self.valuestack() if stack > 0: self.logwrapper(self.name, f'{self.mod_type}({self.mod_order}): {value:.02f}', f'buff stack <{stack}>') self.effect_off() def count_team_buff(self): base_mods = [ Modifier('base_cc', 'crit', 'chance', 0.12), Modifier('base_killer', 'killer','passive', 0.30) ] self.dmg_test_event.modifiers = ModifierDict() for mod in base_mods: self.dmg_test_event.modifiers.append(mod) for b in filter(lambda b: b.get() and b.bufftype == 'simulated_def', self._static.all_buffs): self.dmg_test_event.modifiers.append(b.modifier) self.dmg_test_event() no_team_buff_dmg = self.dmg_test_event.dmg placeholders = [] for b in filter(lambda b: b.get() and b.bufftype in ('team', 'debuff'), self._static.all_buffs): placehold = None if b.modifier.mod_type == 's': placehold = Modifier('placehold_sd', 'att', 'sd', b.modifier.get() / 2) elif b.modifier.mod_type == 'spd': placehold = Modifier('placehold_spd', 'att', 'spd', b.modifier.get()) elif b.modifier.mod_type.endswith('_killer'): placehold = Modifier('placehold_k', 'killer', 'passive', b.modifier.get()) if placehold: self.dmg_test_event.modifiers.append(placehold) placeholders.append(placehold) else: self.dmg_test_event.modifiers.append(b.modifier) self.dmg_test_event() team_buff_dmg = self.dmg_test_event.dmg log('buff', 'team', team_buff_dmg / no_team_buff_dmg - 1) for mod in chain(base_mods, placeholders): mod.off() def on(self, duration=None): if self.mod_type == 'maxhp': max_hp = self._static.adv.mod('maxhp') if max_hp >= Buff.MAXHP_CAP: return self value = self.__value mod_val = min(value, max(Buff.MAXHP_CAP-max_hp, 0)) self._static.adv.set_hp((self._static.adv.hp*max_hp+value*100)/(max_hp+mod_val)) d = (duration or self.duration) * self.bufftime() if self.__active == 0: self.__active = 1 if self.__stored == 0: self._static.all_buffs.append(self) self.__stored = 1 if d >= 0: self.buff_end_timer.on(d) proc_type = 'start' else: if d >= 0: self.buff_end_timer.on(d) proc_type = 'refresh' self.logwrapper(self.name, f'{self.mod_type}({self.mod_order}): {self.value():.02f}', f'buff {proc_type} <{d:.02f}s>') value, stack = self.valuestack() if stack > 1: log('buff', self.name, f'{self.mod_type}({self.mod_order}): {value:.02f}', f'buff stack <{stack}>') if self.mod_type == 'defense': Event('defchain').on() if self.bufftype == 'team': log('buff', 'team_defense', 'proc team doublebuffs') if self.mod_type == 'regen': # may need to make this part global since game always regen all stacks at same ticks self.set_hp_event = Event('set_hp') self.set_hp_event.delta = self.get() self.regen_timer = Timer(self.hp_regen, 3.9, True) else: self.effect_on() return self def hp_regen(self, t): self.set_hp_event() def off(self): if self.__active == 0: return self.logwrapper(self.name, f'{self.mod_type}({self.mod_order}): {self.value():.02f}', f'buff end <turn off>') self.__active = 0 self.effect_off() self.buff_end_timer.off() return self def timeleft(self): return -1 if self.duration == -1 else (self.buff_end_timer.timing-now()) def add_time(self, delta): self.buff_end_timer.add(delta)
class Modifier(object): _static = Static({ 'all_modifiers': ModifierDict(), 'g_condition': None, 'damage_sources': set() }) def __init__(self, name, mtype, order, value, condition=None, get=None): self.mod_name = name self.mod_type = mtype self.mod_order = order self.mod_value = value self.mod_condition = condition self.mod_get = get self._mod_active = 0 self.on() # self._static.all_modifiers.append(self) # self.__active = 1 # @classmethod # def mod(cls, mtype, all_modifiers=None, morder=None): # if not all_modifiers: # all_modifiers = cls._static.all_modifiers # if morder: # return 1 + sum([modifier.get() for modifier in all_modifiers[mtype][morder]]) # m = defaultdict(lambda: 1) # for order, modifiers in all_modifiers[mtype].items(): # m[order] += sum([modifier.get() for modifier in modifiers]) # ret = 1.0 # for i in m: # ret *= m[i] # return ret def get(self): if callable(self.mod_get) and not self.mod_get(): return 0 if self.mod_condition is not None and not self._static.g_condition(self.mod_condition): return 0 return self.mod_value def on(self, modifier=None): if self._mod_active == 1: return self if modifier == None: modifier = self # if modifier.mod_condition: # if not m_condition.on(modifier.mod_condition): # return self # if modifier.mod_condition is not None: # if not self._static.g_condition(modifier.mod_condition): # return self self._static.all_modifiers.append(self) self._mod_active = 1 return self def off(self, modifier=None): if self._mod_active == 0: return self self._mod_active = 0 if modifier == None: modifier = self self._static.all_modifiers.remove(self) return self def __enter__(self): self.on() def __exit__(self, exc_type, exc_val, exc_tb): self.off() def __repr__(self): return '<%s %s %s %s>' % (self.mod_name, self.mod_type, self.mod_order, self.get())
class Buff(object): MAXHP_BUFF_CAP = 0.30 _static = Static({"all_buffs": [], "adv": None}) DB_DURATION = 15 # usual doublebuff effect duration for offensive buffs, note that regen lasts 20s def __init__( self, name="<buff_noname>", value=0, duration=0, mtype="att", morder=None, modifier=None, hidden=False, source=None, ): self.name = name self.__value = value self.duration = duration self.mod_type = mtype self.mod_order = morder or ("chance" if self.mod_type == "crit" else "buff") self.bufftype = "misc" if hidden else "self" self.source = source if self.source is not None and source[0] != "s" and source[0:2] != "ds": self.bufftime = self._ex_bufftime else: self.bufftime = self._bufftime self.buff_end_timer = Timer(self.buff_end_proc) if modifier: self.modifier = modifier self.get = self.modifier.get elif mtype != "effect": self.modifier = Modifier("mod_" + self.name, self.mod_type, self.mod_order, 0) self.modifier.get = self.get else: self.modifier = None self.dmg_test_event = Event("dmg_formula") self.dmg_test_event.dmg_coef = 1 self.dmg_test_event.dname = "test" self.hidden = hidden self.__stored = 0 self.__active = 0 self.buffevent = Event("buff") self.pause_time = -1 self.refresh_time = -1 # self.on() def logwrapper(self, *args): if not self.hidden: log("buff", *args) def _no_bufftime(self): return 1 def _ex_bufftime(self): return 1 + self._static.adv.sub_mod("buff", "ex") def _bufftime(self): return self._static.adv.mod("buff", operator=operator.add) def _debufftime(self): return self._static.adv.mod("debuff", operator=operator.add) def any_bufftime(self): self.bufftime = self._bufftime return self def no_bufftime(self): self.bufftime = self._no_bufftime return self def ex_bufftime(self): self.bufftime = self._ex_bufftime return self def value(self, newvalue=None): if newvalue is not None: self.logwrapper( self.name, f"{self.mod_type}({self.mod_order}): {newvalue:.02f}", "buff value change", ) return self.set(newvalue) else: return self.__value @allow_acl def get(self): if self.__active: return self.__value else: return 0 def set(self, v, d=None): self.__value = v if d != None: self.duration = d return self def stack(self): stack = 0 for i in self._static.all_buffs: if i.name == self.name: if i.__active != 0: stack += 1 return stack def valuestack(self): stack = 0 value = 0 for i in self._static.all_buffs: if i.name == self.name: if i.__active != 0: stack += 1 value += i.__value return value, stack def effect_on(self): value = self.get() if self.mod_type == "defense" and value > 0: db = Event("defchain") db.source = self.source db.on() if self.bufftype == "team": log("buff", "doublebuff", 15 * self.bufftime()) if self.bufftime == self._bufftime: self._static.adv.slots.c.set_need_bufftime() elif self.mod_type == "maxhp": if self._static.adv.sub_mod("maxhp", "buff") < Buff.MAXHP_BUFF_CAP: self.modifier.on() percent = value * 100 log("heal", self.name, self._static.adv.max_hp * value, "team" if self.bufftype == "team" else "self") self._static.adv.add_hp(percent) # FIXME: heal formula 1day twust elif self.mod_type == "regen" and value != 0: self.set_hp_event = Event("set_hp") self.set_hp_event.delta = value self.regen_timer = Timer(self.hp_regen, 3.9, True).on() elif self.mod_type == "heal" and value != 0: self.set_hp_event = Event("heal_make") self.set_hp_event.name = self.name self.set_hp_event.delta = self._static.adv.heal_formula( self.source, value) self.set_hp_event.target = "team" if self.bufftype == "team" else "self" self.regen_timer = Timer(self.hp_regen, 2.9, True).on() else: return self.modifier and self.modifier.on() def effect_off(self): if self.mod_type in ("regen", "heal"): self.regen_timer.off() else: return self.modifier and self.modifier.off() def buff_end_proc(self, e): self.logwrapper( self.name, f"{self.mod_type}({self.mod_order}): {self.value():.02f}", "buff end <timeout>", ) self.__active = 0 if self.__stored: self._static.all_buffs.remove(self) self.__stored = 0 value, stack = self.valuestack() if stack > 0: self.logwrapper( self.name, f"{self.mod_type}({self.mod_order}): {value:.02f}", f"buff stack <{stack}>", ) self.effect_off() def count_team_buff(self): if self.bufftype == "self": return base_mods = [ Modifier("base_cc", "crit", "chance", 0.12), Modifier("base_killer", "killer", "passive", 0.30), ] self.dmg_test_event.modifiers = ModifierDict() for mod in base_mods: self.dmg_test_event.modifiers.append(mod) for b in filter(lambda b: b.get() and b.bufftype == "simulated_def", self._static.all_buffs): self.dmg_test_event.modifiers.append(b.modifier) self.dmg_test_event() no_team_buff_dmg = self.dmg_test_event.dmg placeholders = [] for b in filter( lambda b: b.get() and b.bufftype in ("team", "debuff"), self._static.all_buffs, ): placehold = None if b.modifier.mod_type == "s": placehold = Modifier("placehold_sd", "att", "sd", b.modifier.get() / 2) elif b.modifier.mod_type == "spd": placehold = Modifier("placehold_spd", "att", "spd", b.modifier.get()) elif b.modifier.mod_type.endswith("_killer"): placehold = Modifier("placehold_k", "killer", "passive", b.modifier.get()) if placehold: self.dmg_test_event.modifiers.append(placehold) placeholders.append(placehold) else: self.dmg_test_event.modifiers.append(b.modifier) self.dmg_test_event() team_buff_dmg = self.dmg_test_event.dmg log("buff", "team", team_buff_dmg / no_team_buff_dmg - 1) for mod in chain(base_mods, placeholders): mod.off() def on(self, duration=None): d = max(-1, (duration or self.duration) * self.bufftime()) if d != -1 and self.bufftime == self._bufftime: self._static.adv.slots.c.set_need_bufftime() if self.__active == 0: self.__active = 1 if self.__stored == 0: self._static.all_buffs.append(self) self.__stored = 1 if d >= 0: self.buff_end_timer.on(d) proc_type = "start" else: if d >= 0: if self.buff_end_timer.online: self.buff_end_timer.on(d) else: self.refresh_time = d else: return self proc_type = "refresh" self.logwrapper( self.name, f"{self.mod_type}({self.mod_order}): {self.value():.02f}", f"buff {proc_type} <{d:.02f}s>", ) value, stack = self.valuestack() if stack > 1: log( "buff", self.name, f"{self.mod_type}({self.mod_order}): {value:.02f}", f"buff stack <{stack}>", ) self.effect_on() self.buffevent.buff = self self.buffevent.on() return self def hp_regen(self, t): self.set_hp_event.on() def off(self): if self.__active == 0: return self.logwrapper( self.name, f"{self.mod_type}({self.mod_order}): {self.value():.02f}", f"buff end <turn off>", ) self.__active = 0 self.buff_end_timer.off() self.effect_off() return self @property def adv(self): return self._static.adv @allow_acl def timeleft(self): return -1 if self.duration == -1 else (self.buff_end_timer.timing - now()) def add_time(self, delta): self.buff_end_timer.add(delta) def pause(self): self.pause_time = self.timeleft() if self.pause_time > 0: log("pause", self.name, self.pause_time) self.buff_end_timer.off() def resume(self): self.pause_time = max(self.pause_time, self.refresh_time) if self.pause_time > 0: log("resume", self.name, self.pause_time, now() + self.pause_time) self.buff_end_timer.on(self.pause_time) self.pause_time = -1