Пример #1
0
    def add_aura(self, aura):
        # Note: This order of applying, removing colliding and then returning might be problematic if cases are added to can_apply_aura.
        # At the moment mount behaviour depends on this order
        can_apply = self.can_apply_aura(aura)
        self.remove_colliding_effects(aura)
        if not can_apply:
            return

        aura.initialize_period_timestamps(
        )  # Initialize periodic spell timestamps on application

        AuraEffectHandler.handle_aura_effect_change(aura)
        aura.index = self.get_next_aura_index(aura)
        self.active_auras[aura.index] = aura

        if not aura.passive:
            self.write_aura_to_unit(aura)
            self.write_aura_flag_to_unit(aura)
            self.send_aura_duration(aura)

        # Aura application threat TODO handle threat elsewhere
        if aura.is_harmful() and aura.source_spell.generates_threat():
            self.unit_mgr.attack(aura.caster)

        self.unit_mgr.set_dirty()
Пример #2
0
    def check_aura_procs(self,
                         involved_cast=None,
                         killed_unit=False,
                         damage_info=None,
                         is_melee_swing=False):
        is_receiver = (damage_info and damage_info.target is self.unit_mgr) or \
                      (involved_cast and involved_cast.spell_caster is not self.unit_mgr)

        # Always pass the second unit as the effect target. The handler will choose the target based on the spell.
        if damage_info:
            effect_target = damage_info.attacker if is_receiver else damage_info.target
        elif involved_cast:
            # All targets for the spell could be passed, but this would only matter for ProcFlags.SPELL_CAST
            # SPELL_CAST is only used by one deprecated spell which will have the correct target in initial_target.
            effect_target = involved_cast.spell_caster if is_receiver else involved_cast.initial_target
        else:
            effect_target = self.unit_mgr

        flag_cases = {
            ProcFlags.DEAL_COMBAT_DMG:
            not is_receiver and damage_info
            and damage_info.total_damage > 0,  # -> cast on target.
            ProcFlags.TAKE_COMBAT_DMG:
            is_receiver and damage_info and damage_info.total_damage > 0,
            ProcFlags.KILL:
            killed_unit,
            ProcFlags.HEARTBEAT:
            False,  # Heartbeat effects are handled in their respective places on update - ignore the flag here.
            ProcFlags.DODGE:
            is_receiver and damage_info
            and damage_info.proc_victim & ProcFlags.DODGE,
            ProcFlags.PARRY:
            is_receiver and damage_info
            and damage_info.proc_victim & ProcFlags.PARRY,
            ProcFlags.BLOCK:
            is_receiver and damage_info
            and damage_info.proc_victim & ProcFlags.BLOCK,
            ProcFlags.SWING:
            not is_receiver and is_melee_swing,
            ProcFlags.SPELL_CAST:
            not is_receiver and involved_cast,  # Only used by zzOLDMind Bomb.
            ProcFlags.SPELL_HIT:
            is_receiver and involved_cast,
        }
        for aura in list(self.active_auras.values()):
            flags = aura.source_spell.spell_entry.ProcFlags
            if not flags:
                continue

            for proc_flag, condition in flag_cases.items():
                if proc_flag & flags and condition and aura.proc_charges != 0:  # Proc charges are set to -1 for auras with no charges so check for 0.
                    # Remove charge before trigger to avoid infinite loops with procs.
                    aura.proc_charges -= 1
                    AuraEffectHandler.handle_aura_effect_change(aura,
                                                                effect_target,
                                                                is_proc=True)

                    if aura.proc_charges == 0:
                        self.remove_aura(aura)
Пример #3
0
    def update(self, elapsed):
        if self.has_duration():
            self.duration -= int(elapsed * 1000)

        if not self.target:  # Auras that are only tied to effects - ie. persistent area auras
            return
        if self.is_periodic():
            AuraEffectHandler.handle_aura_effect_change(self)
Пример #4
0
    def update(self, timestamp):
        if self.has_duration():
            self.spell_effect.update_effect_aura(timestamp)

        if self.is_periodic():
            AuraEffectHandler.handle_aura_effect_change(self, self.target)

        if self.source_spell.cast_state != SpellState.SPELL_STATE_ACTIVE:
            self.spell_effect.remove_old_periodic_effect_ticks()
Пример #5
0
    def remove_aura(self, aura):
        # TODO check if aura can be removed (by player)
        AuraEffectHandler.handle_aura_effect_change(aura, True)
        self.active_auras.pop(aura.index)
        if aura.passive:
            return  # Passive auras aren't written to unit

        self.write_aura_to_unit(aura, clear=True)
        self.write_aura_flag_to_unit(aura, clear=True)
        self.unit_mgr.set_dirty()
Пример #6
0
    def remove_colliding_effects(self, aura):
        # Special case with SpellEffect mounting and mounting by aura
        if aura.spell_effect.aura_type == AuraTypes.SPELL_AURA_MOUNTED and \
                aura.target.unit_flags & UnitFlags.UNIT_MASK_MOUNTED and not \
                self.get_auras_by_type(AuraTypes.SPELL_AURA_MOUNTED):
            AuraEffectHandler.handle_mounted(
                aura, aura.target, remove=True)  # Remove mount effect
            # If a mount aura would be applied but we dismount the unit, don't apply the new mount aura.
            return False

        aura_spell_template = aura.source_spell.spell_entry

        new_aura_name = aura_spell_template.Name_enUS
        new_aura_rank = DbcDatabaseManager.SpellHolder.spell_get_rank_by_spell(
            aura_spell_template)

        aura_effect_index = aura.spell_effect.effect_index
        caster_guid = aura.caster.guid

        for applied_aura in list(self.active_auras.values()):
            applied_spell_entry = applied_aura.source_spell.spell_entry
            applied_aura_name = applied_spell_entry.Name_enUS
            applied_aura_rank = DbcDatabaseManager.SpellHolder.spell_get_rank_by_spell(
                applied_spell_entry)

            # TODO Same effects but different spells (exclusivity groups)?

            # Note: This method ignores the case of a weaker spell being applied, as that is handled in can_apply_aura.
            is_similar_and_weaker = applied_aura.spell_effect.effect_index == aura_effect_index and \
                applied_aura_name == new_aura_name and applied_aura_rank < new_aura_rank

            are_exclusive_by_source = ExtendedSpellData.AuraSourceRestrictions.are_colliding_auras(
                aura.spell_id,
                applied_aura.spell_id)  # Paladin seals, warlock curses

            # Source doesn't matter for unique auras.
            is_unique = applied_aura.source_spell.spell_entry.AttributesEx & SpellAttributesEx.SPELL_ATTR_EX_AURA_UNIQUE or not aura.harmful  # Buffs are unique.
            is_stacking = applied_aura.can_stack
            is_same_but_different_aura_index = aura.spell_id == applied_aura.spell_id and aura.spell_effect.effect_index != applied_aura.spell_effect.effect_index

            casters_are_same = applied_aura.caster.guid == caster_guid
            if is_similar_and_weaker and (is_unique or casters_are_same and not is_stacking) or \
                    are_exclusive_by_source and casters_are_same and not is_same_but_different_aura_index:
                self.remove_aura(applied_aura)
                continue

            if applied_aura.spell_effect.aura_type == AuraTypes.SPELL_AURA_MOD_SHAPESHIFT and \
                    aura.spell_effect.aura_type == AuraTypes.SPELL_AURA_MOD_SHAPESHIFT:
                self.remove_aura(
                    applied_aura)  # Player can only be in one shapeshift form.
                continue

        return True
Пример #7
0
    def update(self, timestamp):
        if self.has_duration():
            self.spell_effect.update_effect_aura(timestamp)

        if self.is_periodic():
            AuraEffectHandler.handle_aura_effect_change(self, self.target)

        # Don't remove periodic ticks for channeled spells.
        # Channeled spells have special handling for applied auras because of targeting reasons.
        # See SpellManager::handle_spell_effect_update for more information.
        if self.source_spell.cast_state != SpellState.SPELL_STATE_ACTIVE:
            self.spell_effect.remove_old_periodic_effect_ticks()
Пример #8
0
    def add_aura(self, aura):
        can_apply = self.can_apply_aura(
            aura) and self.remove_colliding_effects(aura)
        if not can_apply:
            return

        # Application threat and negative aura application interrupts.
        if aura.harmful:
            if aura.caster.object_type_mask & ObjectTypeFlags.TYPE_UNIT and \
                    aura.source_spell.generates_threat():
                # TODO Replace once threat is handled properly.
                self.unit_mgr.attack(aura.caster)

            self.check_aura_interrupts(negative_aura_applied=True)

        applied_similar_auras = self.get_similar_applied_auras(
            aura, accept_all_ranks=False, accept_all_sources=False)
        is_refresh = len(applied_similar_auras) > 0
        if is_refresh > 0:
            # Only one similar aura from the same source can be applied.
            # Lower ranks are removed by remove_colliding_effects.
            similar_aura = applied_similar_auras[0]

            if aura.can_stack and similar_aura.applied_stacks < similar_aura.max_stacks:
                similar_aura.applied_stacks += 1  # Add a stack if the aura isn't at max already

            similar_aura.spell_effect.start_aura_duration(
                overwrite=True)  # Refresh duration

            # Note that this aura will not be actually applied.
            # Index and stacks are copied for sending information and updating effect points.
            aura.applied_stacks = similar_aura.applied_stacks
            aura.index = similar_aura.index
        else:
            aura.index = self.get_next_aura_index(aura)
            self.active_auras[aura.index] = aura

        # Handle effects after possible stack increase/refresh to update stats properly.
        AuraEffectHandler.handle_aura_effect_change(aura, aura.target)

        # TODO Some aura applications appear twice in the combat log.
        # For example, the proc effect from frost armor appears twice.
        # Gouge seems to appear twice against players (tested in duels), but not against NPCs.
        if not aura.passive:
            if not is_refresh:
                self.write_aura_to_unit(aura)
                self.write_aura_flag_to_unit(aura)
            self.send_aura_duration(aura)
Пример #9
0
    def remove_aura(self, aura, canceled=False):
        AuraEffectHandler.handle_aura_effect_change(aura,
                                                    aura.target,
                                                    remove=True)
        if not self.active_auras.pop(aura.index, None):
            return
        # Some area effect auras (paladin auras, tranq etc.) are tied to spell effects. Cancel cast on aura cancel, canceling the auras as well.
        self.unit_mgr.spell_manager.remove_cast(aura.source_spell,
                                                interrupted=canceled)

        # Some spells start cooldown on aura remove, handle that case here.
        if aura.source_spell.trigger_cooldown_on_aura_remove():
            self.unit_mgr.spell_manager.set_on_cooldown(
                aura.source_spell, start_locked_cooldown=True)

        self.write_aura_to_unit(aura, clear=True)
Пример #10
0
    def remove_colliding_effects(self, aura):
        # Special case with SpellEffect mounting and mounting by aura
        if aura.spell_effect.aura_type == AuraTypes.SPELL_AURA_MOUNTED and \
                aura.target.unit_flags & UnitFlags.UNIT_MASK_MOUNTED and not \
                self.get_auras_by_type(AuraTypes.SPELL_AURA_MOUNTED):
            AuraEffectHandler.handle_mounted(aura, True)  # Remove mount effect

        aura_spell_template = aura.source_spell.spell_entry
        aura_effect_index = aura.spell_effect.effect_index
        caster_guid = aura.caster.guid

        for applied_aura in list(self.active_auras.values()):
            if applied_aura.caster.guid != caster_guid or \
                    applied_aura.spell_effect.effect_index != aura_effect_index or \
                    applied_aura.source_spell.spell_entry != aura_spell_template:
                continue
            self.remove_aura(
                applied_aura)  # Remove identical auras the caster has applied
Пример #11
0
    def add_aura(self, aura):
        can_apply = self.can_apply_aura(
            aura) and self.remove_colliding_effects(aura)
        if not can_apply:
            return

        # Application threat and negative aura application interrupts.
        if aura.harmful:
            # Add threat for non-player targets against unit casters.
            if aura.caster.object_type_mask & ObjectTypeFlags.TYPE_UNIT and \
                    self.unit_mgr.get_type_id() == ObjectTypeIds.ID_UNIT and aura.source_spell.generates_threat():
                # TODO: Threat calculation.
                self.unit_mgr.threat_manager.add_threat(aura.caster, 10)

            self.check_aura_interrupts(negative_aura_applied=True)

        applied_similar_auras = self.get_similar_applied_auras(
            aura, accept_all_ranks=False, accept_all_sources=False)
        is_refresh = len(applied_similar_auras) > 0
        if is_refresh > 0:
            # Only one similar aura from the same source can be applied.
            # Lower ranks are removed by remove_colliding_effects.
            similar_aura = applied_similar_auras[0]

            if aura.can_stack and similar_aura.applied_stacks < similar_aura.max_stacks:
                similar_aura.applied_stacks += 1  # Add a stack if the aura isn't at max already

            similar_aura.spell_effect.start_aura_duration(
                overwrite=True)  # Refresh duration

            # Note that this aura will not be actually applied.
            # Index and stacks are copied for sending information and updating effect points.
            aura.applied_stacks = similar_aura.applied_stacks
            aura.index = similar_aura.index
        else:
            aura.index = self.get_next_aura_index(aura)
            self.active_auras[aura.index] = aura

        # Handle effects after possible stack increase/refresh to update stats properly.
        AuraEffectHandler.handle_aura_effect_change(aura, aura.target)

        self.write_aura_to_unit(aura, is_refresh=is_refresh)
Пример #12
0
    def remove_colliding_effects(self, aura):
        # Special case with SpellEffect mounting and mounting by aura
        if aura.spell_effect.aura_type == AuraTypes.SPELL_AURA_MOUNTED and \
                aura.target.unit_flags & UnitFlags.UNIT_MASK_MOUNTED and not \
                self.get_auras_by_type(AuraTypes.SPELL_AURA_MOUNTED):
            AuraEffectHandler.handle_mounted(
                aura, aura.target, remove=True)  # Remove mount effect

        aura_spell_template = aura.source_spell.spell_entry
        aura_effect_index = aura.spell_effect.effect_index
        caster_guid = aura.caster.guid

        for applied_aura in list(self.active_auras.values()):
            # TODO is_similar does not match different spell ranks
            is_similar = applied_aura.source_spell.spell_entry == aura_spell_template and \
                         applied_aura.spell_effect.effect_index == aura_effect_index  # Spell and effect are the same

            are_exclusive_by_source = ExtendedSpellData.AuraSourceRestrictions.are_colliding_auras(
                aura.spell_id,
                applied_aura.spell_id)  # Paladin seals, warlock curses

            # Source doesn't matter for unique auras
            is_unique = applied_aura.source_spell.spell_entry.AttributesEx & SpellAttributesEx.SPELL_ATTR_EX_AURA_UNIQUE or not aura.harmful  # Buffs are unique.
            is_stacking = applied_aura.can_stack

            casters_are_same = applied_aura.caster.guid == caster_guid
            if is_similar and (is_unique or casters_are_same and not is_stacking) or \
                    are_exclusive_by_source and casters_are_same:
                self.remove_aura(applied_aura)
                continue

            if applied_aura.spell_effect.aura_type == AuraTypes.SPELL_AURA_MOD_SHAPESHIFT and \
                    aura.spell_effect.aura_type == AuraTypes.SPELL_AURA_MOD_SHAPESHIFT:
                self.remove_aura(
                    applied_aura)  # Player can only be in one shapeshift form
                continue
Пример #13
0
                aura.spell_id)) > 0:
            if existing_auras[0].applied_stacks < existing_auras[0].max_stacks:
                existing_auras[
                    0].applied_stacks += 1  # Add a stack if the aura isn't at max already
            existing_auras[0].spell_effect.start_aura_duration(
                overwrite=True)  # Refresh duration

            # Note that this aura will not be actually applied. Index and stacks are copied for sending information and updating effect points.
            aura.applied_stacks = existing_auras[0].applied_stacks
            aura.index = existing_auras[0].index
        else:
            aura.index = self.get_next_aura_index(aura)
            self.active_auras[aura.index] = aura

        # Handle effects after possible stack increase to update stats properly
        AuraEffectHandler.handle_aura_effect_change(aura, aura.target)

        if not aura.passive:
            self.write_aura_to_unit(aura)
            self.write_aura_flag_to_unit(aura)
            self.send_aura_duration(aura)

        # Aura application threat TODO handle threat elsewhere
        if aura.harmful:
            if aura.source_spell.generates_threat():
                self.unit_mgr.attack(aura.caster)
            self.check_aura_interrupts(negative_aura_applied=True)

        self.unit_mgr.set_dirty()

    has_moved = False  # Set from SpellManager - TODO pass movement info from unit update instead