예제 #1
0
    def update(self, now):
        if now > self.last_tick > 0:
            elapsed = now - self.last_tick

            if self.is_spawned:
                # Logic for Trap GameObjects (type 6).
                if self.gobject_template.type == GameObjectTypes.TYPE_TRAP:
                    self.trap_manager.update(elapsed)

                # Check if this game object should be updated yet or not.
                if self.has_pending_updates():
                    MapManager.update_object(self, check_pending_changes=True)
                    self.reset_fields_older_than(now)

                # SpellManager update.
                self.spell_manager.update(now)
            # Not spawned.
            else:
                self.respawn_timer += elapsed
                if self.respawn_timer >= self.respawn_time:
                    if self.is_summon:
                        self.despawn(destroy=True)
                    else:
                        self.respawn()

        self.last_tick = now
예제 #2
0
    def __init__(self,
                 spell,
                 caster,
                 initial_target,
                 target_mask,
                 source_item=None,
                 triggered=False):
        self.spell_entry = spell
        self.spell_caster = caster
        self.source_item = source_item
        self.initial_target = initial_target
        self.spell_target_mask = target_mask
        self.triggered = triggered

        self.duration_entry = DbcDatabaseManager.spell_duration_get_by_id(
            spell.DurationIndex)
        self.range_entry = DbcDatabaseManager.spell_range_get_by_id(
            spell.RangeIndex)
        self.cast_time_entry = DbcDatabaseManager.spell_cast_time_get_by_id(
            spell.CastingTimeIndex)
        self.cast_end_timestamp = self.get_base_cast_time() / 1000 + time.time(
        )
        self.spell_visual_entry = DbcDatabaseManager.spell_visual_get_by_id(
            spell.SpellVisualID)

        if self.spell_caster.object_type_mask & ObjectTypeFlags.TYPE_UNIT:
            self.caster_effective_level = self.calculate_effective_level(
                self.spell_caster.level)
        else:
            self.caster_effective_level = 0

        # Resolve the weapon required for the spell.
        self.spell_attack_type = -1
        # Item target casts (enchants) have target item info in equipment requirements - ignore.
        if spell.EquippedItemClass == ItemClasses.ITEM_CLASS_WEAPON and not self.initial_target_is_item(
        ):
            self.spell_attack_type = AttackTypes.RANGED_ATTACK if self.is_ranged_weapon_attack(
            ) else AttackTypes.BASE_ATTACK

        self.cast_state = SpellState.SPELL_STATE_PREPARING
        self.spell_impact_timestamps = {}

        if caster.get_type_id() == ObjectTypeIds.ID_PLAYER:
            selection_guid = self.spell_caster.current_selection if self.spell_caster.current_selection else caster.guid
            self.targeted_unit_on_cast_start = MapManager.get_surrounding_unit_by_guid(
                self.spell_caster, selection_guid, include_players=True)

        if self.is_fishing_spell():
            # Locate liquid vector in front of the caster.
            self.initial_target = MapManager.find_liquid_location_in_range(
                self.spell_caster, self.range_entry.RangeMin,
                self.range_entry.RangeMax)
        self.cast_flags = SpellCastFlags.CAST_FLAG_NONE

        # Ammo needs to be resolved on initialization since it's needed for validation and spell cast packets.
        self.used_ranged_attack_item = self.get_ammo_for_cast()
        if self.used_ranged_attack_item:
            self.cast_flags |= SpellCastFlags.CAST_FLAG_HAS_AMMO

        self.load_effects()
예제 #3
0
    def send_cast_start(self, casting_spell):
        data = [
            self.unit_mgr.guid,
            self.unit_mgr.guid,  # TODO Source (1st arg) can also be item
            casting_spell.spell_entry.ID,
            casting_spell.cast_flags,
            casting_spell.get_base_cast_time(),
            casting_spell.spell_target_mask
        ]

        signature = '<2QIHiH'  # source, caster, ID, flags, delay .. (targets, opt. ammo displayID / inventorytype).

        if casting_spell.initial_target and casting_spell.spell_target_mask != SpellTargetMask.SELF:  # Some self-cast spells crash client if target is written.
            target_info = casting_spell.get_initial_target_info(
            )  # ([values], signature)
            data.extend(target_info[0])
            signature += target_info[1]

        if casting_spell.cast_flags & SpellCastFlags.CAST_FLAG_HAS_AMMO:
            signature += '2I'
            data.append(5996)  # TODO ammo display ID
            data.append(
                InventoryTypes.AMMO)  # TODO ammo inventorytype (thrown too)

        data = pack(signature, *data)
        MapManager.send_surrounding(
            PacketWriter.get_packet(OpCode.SMSG_SPELL_START, data),
            self.unit_mgr,
            include_self=self.unit_mgr.get_type() == ObjectTypes.TYPE_PLAYER)
예제 #4
0
 def send_chat_message(world_session, guid, chat_flags, message, chat_type,
                       lang, range_):
     MapManager.send_surrounding_in_range(ChatManager._get_message_packet(
         guid, chat_flags, message, chat_type, lang),
                                          world_session.player_mgr,
                                          range_,
                                          use_ignore=True)
예제 #5
0
    def handle(world_session, socket, reader):
        if len(reader.data
               ) >= 8:  # Avoid handling empty quest giver hello packet.
            guid = unpack('<Q', reader.data[:8])[0]
            high_guid = ObjectManager.extract_high_guid(guid)

            quest_giver = None
            if high_guid == HighGuid.HIGHGUID_UNIT:
                quest_giver = MapManager.get_surrounding_unit_by_guid(
                    world_session.player_mgr, guid)
            elif high_guid == HighGuid.HIGHGUID_GAMEOBJECT:
                quest_giver = MapManager.get_surrounding_gameobject_by_guid(
                    world_session.player_mgr, guid)

            if not quest_giver:
                Logger.error(
                    f'Error in CMSG_QUESTGIVER_HELLO, could not find quest giver with guid of: {guid}'
                )
                return 0
            if world_session.player_mgr.is_enemy_to(quest_giver):
                return 0

            # TODO: Stop the npc if it's moving
            # TODO: Remove feign death from player (if it even exists in 0.5.3)
            # TODO: If the gossip menu is already open, do nothing
            if quest_giver.is_within_interactable_distance(
                    world_session.player_mgr):
                world_session.player_mgr.quest_manager.handle_quest_giver_hello(
                    quest_giver, guid)

        return 0
예제 #6
0
    def send_spell_go(self, casting_spell):
        data = [
            self.unit_mgr.guid, self.unit_mgr.guid,
            casting_spell.spell_entry.ID, casting_spell.cast_flags
        ]

        signature = '<2QIH'  # source, caster, ID, flags .. (targets, ammo info).

        # Prepare target data
        results_by_type = {
            SpellMissReason.MISS_REASON_NONE: []
        }  # Hits need to be written first.

        # Only include the primary effect targets.
        targets = casting_spell.effects[
            0].targets.get_resolved_effect_targets_by_type(ObjectManager)
        for target in targets:
            miss_info = casting_spell.object_target_results[target.guid]
            new_targets = results_by_type.get(miss_info.result, [])
            new_targets.append(target.guid)
            results_by_type[
                miss_info.
                result] = new_targets  # Sort targets by hit type for filling packet fields.

        hit_count = len(results_by_type[SpellMissReason.MISS_REASON_NONE])
        miss_count = len(
            targets) - hit_count  # Subtract hits from all targets.
        # Write targets, hits first
        for result, guids in results_by_type.items():
            if result == SpellMissReason.MISS_REASON_NONE:  # Hit count is written separately.
                signature += 'B'
                data.append(hit_count)

            if result != SpellMissReason.MISS_REASON_NONE:  # Write reason for miss.
                signature += 'B'
                data.append(result)

            if len(guids) > 0:  # Write targets if there are any
                signature += f'{len(guids)}Q'
            for target_guid in guids:
                data.append(target_guid)

            if result == SpellMissReason.MISS_REASON_NONE:  # Write miss count at the end of hits since it needs to be written even if none happen.
                signature += 'B'
                data.append(miss_count)

        signature += 'H'  # SpellTargetMask
        data.append(casting_spell.spell_target_mask)

        if casting_spell.spell_target_mask != SpellTargetMask.SELF:  # Write target info - same as cast start.
            target_info = casting_spell.get_initial_target_info(
            )  # ([values], signature)
            data.extend(target_info[0])
            signature += target_info[1]

        packed = pack(signature, *data)
        MapManager.send_surrounding(
            PacketWriter.get_packet(OpCode.SMSG_SPELL_GO, packed),
            self.unit_mgr,
            include_self=self.unit_mgr.get_type() == ObjectTypes.TYPE_PLAYER)
예제 #7
0
    def update(self, now):
        if now > self.last_tick > 0:
            elapsed = now - self.last_tick

            if self.is_alive and self.is_spawned:
                # Regeneration.
                self.regenerate(elapsed)
                # Spell/aura updates
                self.spell_manager.update(now)
                self.aura_manager.update(now)
                # Movement Updates
                self.movement_manager.update_pending_waypoints(elapsed)
                # Random Movement
                self._perform_random_movement(now)
                # Combat movement
                self._perform_combat_movement()
                # Attack update
                if self.combat_target and self.is_within_interactable_distance(self.combat_target):
                    self.attack_update(elapsed)
            # Dead
            elif not self.is_alive:
                self.respawn_timer += elapsed
                if self.respawn_timer >= self.respawn_time:
                    self.respawn()
                # Destroy body when creature is about to respawn.
                elif self.is_spawned and self.respawn_timer >= self.respawn_time * 0.8:
                    self.despawn(destroy=self.is_summon)

            # Check if this creature object should be updated yet or not.
            if self.has_pending_updates():
                MapManager.update_object(self, check_pending_changes=True)
                self.reset_fields_older_than(now)

        self.last_tick = now
예제 #8
0
    def update(self, elapsed):
        if not self.is_ready():
            self.remaining_cooldown = max(0, self.remaining_cooldown - elapsed)
            return

        # If the trap should be triggered by creatures, search for them along with players.
        if self.spell_id in TrapManager.TRIGGERED_BY_CREATURES:
            surrounding_creatures, surrounding_players = MapManager.get_surrounding_units_by_location(
                self.trap_object.location,
                self.trap_object.map_,
                self.radius,
                include_players=True)
            surrounding_units = surrounding_creatures | surrounding_players
        else:
            # This trap can only be triggered by players.
            surrounding_units = MapManager.get_surrounding_players_by_location(
                self.trap_object.location, self.trap_object.map_, self.radius)

        for unit in surrounding_units.values():
            # Keep looping until we find a valid unit.
            if not self.trap_object.can_attack_target(unit):
                continue

            # Valid target found, trigger the trap. In case charges = 1, despawn the trap.
            if self.trigger(unit) and self.charges == 1:
                self.trap_object.set_active()
                self.trap_object.despawn()
            break
예제 #9
0
    def handle(world_session, socket, reader):
        if len(reader.data
               ) >= 8:  # Avoid handling empty debug AI state packet.
            guid = unpack('<Q', reader.data[:8])[0]
            world_object = MapManager.get_surrounding_unit_by_guid(
                world_session.player_mgr, guid, include_players=True)

            # If no Unit or Player, try to get a Gameobject.
            if not world_object:
                world_object = MapManager.get_surrounding_gameobject_by_guid(
                    world_session.player_mgr, guid)

            # Still no object with that Guid? Return.
            if not world_object:
                return 0

            messages = world_object.get_debug_messages()
            data = pack('<QI', guid, len(messages))

            for message in messages:
                message_bytes = PacketWriter.string_to_bytes(
                    message[:127])  # Max length is 128 (127 + null byte).
                data += pack(f'<{len(message_bytes)}s', message_bytes)

            world_session.enqueue_packet(
                PacketWriter.get_packet(OpCode.SMSG_DEBUG_AISTATE, data))

        return 0
예제 #10
0
    def is_quest_complete(self, quest_giver_guid):
        quest_giver = None
        high_guid = ObjectManager.extract_high_guid(quest_giver_guid)

        if high_guid == HighGuid.HIGHGUID_GAMEOBJECT:
            quest_giver = MapManager.get_surrounding_gameobject_by_guid(self.owner, quest_giver_guid)
        elif high_guid == HighGuid.HIGHGUID_UNIT:
            quest_giver = MapManager.get_surrounding_unit_by_guid(self.owner, quest_giver_guid)

        if not quest_giver:
            return False

        if QuestHelpers.is_instant_complete_quest(self.quest):
            return True

        if self.db_state.state != QuestState.QUEST_REWARD:
            return False

        if quest_giver.get_type_id() == ObjectTypeIds.ID_UNIT and quest_giver.get_type_id() != ObjectTypeIds.ID_PLAYER:
            involved_relations_list = WorldDatabaseManager.QuestRelationHolder.creature_quest_finisher_get_by_entry(quest_giver.entry)
        elif quest_giver.get_type_id() == ObjectTypeIds.ID_GAMEOBJECT:
            involved_relations_list = WorldDatabaseManager.QuestRelationHolder.gameobject_quest_finisher_get_by_entry(quest_giver.entry)
        else:
            return False

        # Return if this quest is finished by this quest giver.
        return self.quest.entry in {quest_entry[1] for quest_entry in involved_relations_list}
예제 #11
0
    def handle(world_session, socket, reader):
        if len(
                reader.data
        ) >= 12:  # Avoid handling empty quest giver request reward packet.
            guid, quest_id = unpack('<QI', reader.data[:12])
            high_guid = ObjectManager.extract_high_guid(guid)
            is_item = False

            quest_giver = None
            if high_guid == HighGuid.HIGHGUID_UNIT:
                quest_giver = MapManager.get_surrounding_unit_by_guid(
                    world_session.player_mgr, guid)
            elif high_guid == HighGuid.HIGHGUID_GAMEOBJECT:
                quest_giver = MapManager.get_surrounding_gameobject_by_guid(
                    world_session.player_mgr, guid)
            elif high_guid == HighGuid.HIGHGUID_ITEM:
                is_item = True
                quest_giver = world_session.player_mgr.inventory.get_item_by_guid(
                    guid)

            if not quest_giver:
                Logger.error(
                    f'Error in CMSG_QUESTGIVER_REQUEST_REWARD, could not find quest giver with guid: {guid}'
                )
                return 0

            if not is_item and world_session.player_mgr.is_enemy_to(
                    quest_giver):
                return 0

            world_session.player_mgr.quest_manager.handle_request_reward(
                guid, quest_id)
        return 0
예제 #12
0
    def update(self, now):
        if now > self.last_tick > 0:
            elapsed = now - self.last_tick

            if self.is_spawned and self.initialized:
                # Logic for Trap GameObjects (type 6).
                if self.has_observers() and self.gobject_template.type == GameObjectTypes.TYPE_TRAP:
                    self.trap_manager.update(elapsed)
                # Logic for Fishing node.
                if self.has_observers() and self.gobject_template.type == GameObjectTypes.TYPE_FISHINGNODE:
                    self.fishing_node_manager.update(elapsed)

                # SpellManager update.
                self.spell_manager.update(now)

                # Check if this game object should be updated yet or not.
                if self.has_pending_updates():
                    MapManager.update_object(self, has_changes=True)
                    self.reset_fields_older_than(now)
            # Not spawned but initialized.
            elif self.initialized:
                self.respawn_timer += elapsed
                if self.respawn_timer >= self.respawn_time:
                    if self.summoner:
                        self.despawn(destroy=True)
                    else:
                        self.respawn()

        self.last_tick = now
예제 #13
0
    def handle(world_session, socket, reader):
        if len(reader.data
               ) >= 8:  # Avoid handling empty quest giver status packet.
            guid = unpack('<Q', reader.data[:8])[0]
            high_guid = ObjectManager.extract_high_guid(guid)

            quest_giver = None
            if high_guid == HighGuid.HIGHGUID_UNIT:
                quest_giver = MapManager.get_surrounding_unit_by_guid(
                    world_session.player_mgr, guid)
            elif high_guid == HighGuid.HIGHGUID_GAMEOBJECT:
                quest_giver = MapManager.get_surrounding_gameobject_by_guid(
                    world_session.player_mgr, guid)

            if not quest_giver:
                Logger.error(
                    f'Error in CMSG_QUESTGIVER_STATUS_QUERY, could not find quest giver with guid of: {guid}'
                )
                return 0

            # Only units are able to provide quest status.
            if world_session.player_mgr and quest_giver.get_type_id(
            ) == ObjectTypeIds.ID_UNIT:
                quest_giver_status = world_session.player_mgr.quest_manager.get_dialog_status(
                    quest_giver)
                world_session.player_mgr.quest_manager.send_quest_giver_status(
                    guid, quest_giver_status)

        return 0
예제 #14
0
    def handle(world_session, socket, reader):
        if len(
                reader.data
        ) >= 12:  # Avoid handling empty quest giver complete quest packet.
            guid, quest_id = unpack('<QI', reader.data[:12])
            high_guid = ObjectManager.extract_high_guid(guid)

            quest_giver = None
            if high_guid == HighGuid.HIGHGUID_UNIT:
                quest_giver = MapManager.get_surrounding_unit_by_guid(
                    world_session.player_mgr, guid)
            elif high_guid == HighGuid.HIGHGUID_GAMEOBJECT:
                quest_giver = MapManager.get_surrounding_gameobject_by_guid(
                    world_session.player_mgr, guid)

            if not quest_giver:
                Logger.error(
                    f'Error in CMSG_QUESTGIVER_COMPLETE_QUEST, could not find quest giver with guid of: {guid}'
                )
                return 0
            if world_session.player_mgr.is_enemy_to(quest_giver):
                return 0

            world_session.player_mgr.quest_manager.handle_complete_quest(
                quest_id, guid)
        return 0
예제 #15
0
 def send_attack_stop(self, victim_guid):
     # Last uint32 is "deceased"; can be either 1 (self is dead), or 0, (self is alive).
     # Forces the unit to face the corpse and disables clientside
     # turning (UnitFlags.DisableMovement) CGUnit_C::OnAttackStop
     data = pack('<2QI', self.guid, victim_guid, 0 if self.is_alive else 1)
     MapManager.send_surrounding(
         PacketWriter.get_packet(OpCode.SMSG_ATTACKSTOP, data), self)
예제 #16
0
    def handle_channel_end(self, casting_spell):
        if not casting_spell.is_channeled():
            return

        if self.unit_mgr.get_type() != ObjectTypes.TYPE_PLAYER:
            return

        if self.unit_mgr.channel_object:
            channel_object = MapManager.get_surrounding_gameobject_by_guid(
                self.unit_mgr, self.unit_mgr.channel_object)
            if channel_object and \
                    channel_object.gobject_template.type == GameObjectTypes.TYPE_RITUAL and \
                    channel_object.ritual_caster is self.unit_mgr:
                for player in channel_object.ritual_participants:
                    ritual_channel_spell_id = channel_object.gobject_template.data2
                    player.spell_manager.remove_cast_by_id(
                        ritual_channel_spell_id)
                MapManager.remove_object(channel_object)

        self.unit_mgr.set_channel_object(0)
        self.unit_mgr.set_channel_spell(0)
        self.unit_mgr.set_dirty()

        if self.unit_mgr.get_type() != ObjectTypes.TYPE_PLAYER:
            return

        data = pack('<I', 0)
        self.unit_mgr.enqueue_packet(
            PacketWriter.get_packet(OpCode.MSG_CHANNEL_UPDATE, data))
예제 #17
0
    def get_target_info(world_session, target_mask, target_bytes):
        caster = world_session.player_mgr

        if target_mask & SpellTargetMask.CAN_TARGET_TERRAIN != 0 and len(
                target_bytes) == 12:
            target_info = Vector.from_bytes(
                target_bytes)  # Terrain, target is vector.
        elif len(target_bytes) == 8:
            target_info = unpack('<Q',
                                 target_bytes)[0]  # Some object (read guid).
        else:
            target_info = caster  # Self

        if target_mask & SpellTargetMask.CAN_TARGET_TERRAIN:
            return target_info
        if target_mask & SpellTargetMask.UNIT and target_info != caster:
            return MapManager.get_surrounding_unit_by_guid(
                caster, target_info, include_players=True)
        if target_mask & SpellTargetMask.ITEM_TARGET_MASK and not target_mask & SpellTargetMask.TRADE_ITEM:
            return caster.inventory.get_item_info_by_guid(target_info)[
                3]  # (container_slot, container, slot, item).
        if target_mask & SpellTargetMask.CAN_TARGET_OBJECTS:  # Can also include items, so we check for that first.
            return MapManager.get_surrounding_gameobject_by_guid(
                caster, target_info)
        if target_mask & SpellTargetMask.ITEM_TARGET_MASK and target_mask & SpellTargetMask.TRADE_ITEM:
            if caster.trade_data and caster.trade_data.other_player and caster.trade_data.other_player.trade_data:
                return caster.trade_data.other_player.trade_data.get_item_by_slot(
                    target_info)

        return caster  # Assume self cast for now. Invalid target will be resolved later.
예제 #18
0
    def handle(world_session, socket, reader):
        if len(reader.data) >= 8:  # Avoid handling empty quest giver status packet.
            quest_giver_guid = unpack('<Q', reader.data[:8])[0]
            quest_giver = None
            # NPC
            if quest_giver_guid & HighGuid.HIGHGUID_UNIT:
                quest_giver = MapManager.get_surrounding_unit_by_guid(world_session.player_mgr, quest_giver_guid)
            # Gameobject
            elif quest_giver_guid & HighGuid.HIGHGUID_GAMEOBJECT:
                quest_giver = MapManager.get_surrounding_gameobject_by_guid(world_session.player_mgr, quest_giver_guid)
            if not quest_giver:
                return 0

            quest_giver_status = QuestGiverStatus.QUEST_GIVER_NONE
            if world_session.player_mgr:
                if quest_giver.get_type() == ObjectTypes.TYPE_UNIT:
                    quest_giver_status = world_session.player_mgr.quest_manager.get_dialog_status(quest_giver)
                elif quest_giver.get_type() == ObjectTypes.TYPE_GAMEOBJECT:
                    # TODO: Proper handling for game object
                    quest_giver_status = QuestGiverStatus.QUEST_GIVER_NONE
                else:
                    Logger.error(f'Error in CMSG_QUESTGIVER_STATUS_QUERY, quest giver was an unexpected type of: {quest_giver.object_type}')

                world_session.player_mgr.quest_manager.send_quest_giver_status(quest_giver_guid, quest_giver_status)

        return 0
예제 #19
0
    def handle(world_session, socket, reader: PacketReader) -> int:
        if len(reader.data
               ) >= 8:  # Avoid handling empty debug AI state packet.
            guid = unpack('<Q', reader.data[:8])[0]

            high_guid: HighGuid = ObjectManager.extract_high_guid(guid)
            if high_guid == HighGuid.HIGHGUID_UNIT or high_guid == HighGuid.HIGHGUID_PLAYER:
                world_object = MapManager.get_surrounding_unit_by_guid(
                    world_session.player_mgr, guid, include_players=True)
            else:
                world_object = MapManager.get_surrounding_gameobject_by_guid(
                    world_session.player_mgr, guid)

            # No object with that Guid? Return.
            if not world_object:
                return 0

            messages: list[str] = world_object.get_debug_messages()
            data = pack('<QI', guid, len(messages))

            for message in messages:
                message_bytes = PacketWriter.string_to_bytes(
                    message[:127])  # Max length is 128 (127 + null byte).
                data += pack(f'<{len(message_bytes)}s', message_bytes)

            world_session.enqueue_packet(
                PacketWriter.get_packet(OpCode.SMSG_DEBUG_AISTATE, data))

        return 0
예제 #20
0
    def update(self):
        now = time.time()
        if now > self.last_tick > 0:
            elapsed = now - self.last_tick

            if self.is_alive and self.is_spawned:
                # Spell/aura updates
                self.spell_manager.update(now, elapsed)
                self.aura_manager.update(now)
                # Movement Updates
                self.movement_manager.update_pending_waypoints(elapsed)
                # Random Movement
                self._perform_random_movement(now)
                # Combat movement
                self._perform_combat_movement()
                # Attack update
                if self.combat_target and self.is_within_interactable_distance(self.combat_target):
                    self.attack_update(elapsed)
            # Dead
            elif not self.is_alive:
                self.respawn_timer += elapsed
                if self.respawn_timer >= self.respawn_time and not self.is_summon:
                    self.respawn()
                # Destroy body when creature is about to respawn
                elif self.is_spawned and self.respawn_timer >= self.respawn_time * 0.8:
                    self.despawn()

            # Check "dirtiness" to determine if this creature object should be updated yet or not.
            if self.dirty:
                MapManager.send_surrounding(self.generate_proper_update_packet(create=False), self, include_self=False)
                MapManager.update_object(self)
                if self.reset_fields_older_than(now):
                    self.set_dirty(is_dirty=False)

        self.last_tick = now
예제 #21
0
    def handle(world_session, socket, reader):
        if len(reader.data) >= 6:  # Avoid handling empty cast spell packet.
            spell_id, target_mask = unpack('<IH', reader.data[:6])

            caster = world_session.player_mgr

            if target_mask & SpellTargetMask.CAN_TARGET_TERRAIN != 0 and len(
                    reader.data) >= 18:
                target_info = Vector.from_bytes(
                    reader.data[-12:])  # Terrain, target is vector
            elif len(reader.data) == 14:
                target_info = unpack(
                    '<Q', reader.data[-8:])[0]  # some object (read guid)
            else:
                target_info = caster  # Self

            if target_mask & SpellTargetMask.CAN_TARGET_TERRAIN:
                spell_target = target_info
            elif target_mask & SpellTargetMask.UNIT and target_info != caster:
                spell_target = MapManager.get_surrounding_unit_by_guid(
                    caster, target_info, include_players=True)
            elif target_mask & SpellTargetMask.ITEM_TARGET_MASK:
                spell_target = caster.inventory.get_item_info_by_guid(
                    target_info)[3]  # (container_slot, container, slot, item)
            elif target_mask & SpellTargetMask.CAN_TARGET_OBJECTS:  # Can also include items so we check for that first
                spell_target = MapManager.get_surrounding_gameobject_by_guid(
                    caster, target_info)
            else:
                spell_target = caster  # Assume self cast for now. Invalid target will be resolved later

            world_session.player_mgr.spell_manager.handle_cast_attempt(
                spell_id, world_session.player_mgr, spell_target, target_mask)
        return 0
예제 #22
0
    def load_data():
        # Map tiles
        MapManager.initialize_maps()
        MapManager.initialize_area_tables()

        # Gameobject spawns
        if config.Server.Settings.load_gameobjects:
            WorldLoader.load_gameobjects()
            WorldLoader.load_gameobject_loot_templates()
        else:
            Logger.info('Skipped game object loading.')

        # Creature spawns
        if config.Server.Settings.load_creatures:
            WorldLoader.load_creature_loot_templates()
            WorldLoader.load_creatures()
            WorldLoader.load_creature_quests()
            WorldLoader.load_creature_involved_quests()
        else:
            Logger.info('Skipped creature loading.')

        WorldLoader.load_item_templates()
        WorldLoader.load_quests()
        WorldLoader.load_spells()
        WorldLoader.load_skills()
        WorldLoader.load_skill_line_abilities()
        WorldLoader.load_char_base_infos()
        WorldLoader.load_taxi_nodes()
        WorldLoader.load_taxi_path_nodes()
        WorldLoader.load_factions()
        WorldLoader.load_faction_templates()

        # Character related data
        WorldLoader.load_groups()
        WorldLoader.load_guilds()
    def handle(world_session, socket, reader):
        if len(
                reader.data
        ) >= 12:  # Avoid handling empty quest giver accept quest packet.
            guid, quest_id = unpack('<QI', reader.data[:12])
            high_guid = ObjectManager.extract_high_guid(guid)
            is_item = False

            quest_giver = None
            if high_guid == HighGuid.HIGHGUID_UNIT:
                quest_giver = MapManager.get_surrounding_unit_by_guid(
                    world_session.player_mgr, guid)
            elif high_guid == HighGuid.HIGHGUID_GAMEOBJECT:
                quest_giver = MapManager.get_surrounding_gameobject_by_guid(
                    world_session.player_mgr, guid)
            elif high_guid == HighGuid.HIGHGUID_ITEM:
                is_item = True
                quest_giver = world_session.player_mgr.inventory.get_item_by_guid(
                    guid)

            if not quest_giver:
                Logger.error(
                    f'Error in CMSG_QUESTGIVER_ACCEPT_QUEST, could not find quest giver with guid of: {guid}'
                )
                return 0
            elif not is_item and world_session.player_mgr.is_enemy_to(
                    quest_giver):
                return 0
            elif world_session.player_mgr.quest_manager.is_quest_log_full():
                world_session.enqueue_packet(
                    PacketWriter.get_packet(OpCode.SMSG_QUESTLOG_FULL))
            else:
                world_session.player_mgr.quest_manager.handle_accept_quest(
                    quest_id, guid, shared=False)
        return 0
예제 #24
0
    def load_data():
        # Map tiles
        MapManager.initialize_maps()
        MapManager.initialize_area_tables()

        # Below order matters.

        # Loot related, even if not loading creatures or gameobjects, loot might be referenced.
        WorldLoader.load_gameobject_loot_templates()
        WorldLoader.load_fishing_loot_templates()
        WorldLoader.load_creature_loot_templates()
        WorldLoader.load_item_templates()
        WorldLoader.load_reference_loot_templates()
        WorldLoader.load_pickpocketing_loot_templates()
        WorldLoader.load_item_loot_templates()

        # Spells.
        WorldLoader.load_spells()
        WorldLoader.load_creature_spells()

        # Gameobject spawns
        if config.Server.Settings.load_gameobjects:
            WorldLoader.load_gameobject_quest_starters()
            WorldLoader.load_gameobject_quest_finishers()
            WorldLoader.load_gameobjects()
        else:
            Logger.info('Skipped game object loading.')

        # Creature spawns
        if config.Server.Settings.load_creatures:
            WorldLoader.load_creature_equip_templates()
            WorldLoader.load_creatures()
            WorldLoader.load_creature_on_kill_reputation()
            WorldLoader.load_creature_quest_starters()
            WorldLoader.load_creature_quest_finishers()
            WorldLoader.load_creature_display_info()
            WorldLoader.load_creature_model_info()
            WorldLoader.load_creature_families()
            WorldLoader.load_npc_gossip()
            WorldLoader.load_npc_text()
        else:
            Logger.info('Skipped creature loading.')

        WorldLoader.load_area_trigger_quest_relations()
        WorldLoader.load_quests()
        WorldLoader.load_spell_chains()
        WorldLoader.load_trainer_spells()
        WorldLoader.load_skills()
        WorldLoader.load_skill_line_abilities()
        WorldLoader.load_char_base_infos()
        WorldLoader.load_taxi_nodes()
        WorldLoader.load_taxi_path_nodes()
        WorldLoader.load_factions()
        WorldLoader.load_faction_templates()
        WorldLoader.load_locks()

        # Character related data
        WorldLoader.load_groups()
        WorldLoader.load_guilds()
예제 #25
0
    def handle(world_session, socket, reader):
        if world_session.player_mgr.is_alive and len(reader.data) >= 12:
            emote_text_id, guid = unpack('<IQ', reader.data)
            emote = DbcDatabaseManager.emote_text_get_by_id(emote_text_id)

            if emote:
                data = pack('<QI', world_session.player_mgr.guid,
                            emote_text_id)
                target = MapManager.get_surrounding_unit_by_guid(
                    world_session.player_mgr, guid, include_players=True)

                if not target:
                    data += pack('<B', 0)
                elif target.get_type() == ObjectTypes.TYPE_PLAYER:
                    player_name_bytes = PacketWriter.string_to_bytes(
                        target.player.name)
                    data += pack(f'<{len(player_name_bytes)}s',
                                 player_name_bytes)
                elif target.get_type(
                ) == ObjectTypes.TYPE_UNIT and target.creature_template:
                    unit_name_bytes = PacketWriter.string_to_bytes(
                        target.creature_template.name)
                    data += pack(f'<{len(unit_name_bytes)}s', unit_name_bytes)
                else:
                    data += pack('<B', 0)

                MapManager.send_surrounding_in_range(
                    PacketWriter.get_packet(OpCode.SMSG_TEXT_EMOTE,
                                            data), world_session.player_mgr,
                    config.World.Chat.ChatRange.emote_range)

                # Perform visual emote action if needed

                emote_id = emote.EmoteID
                state = StandState.UNIT_STANDING
                needs_broadcast = True

                if emote_text_id == Emotes.SIT:
                    if not world_session.player_mgr.is_sitting:
                        state = StandState.UNIT_SITTING
                        world_session.player_mgr.set_stand_state(state)
                elif emote_text_id == Emotes.STAND:
                    world_session.player_mgr.set_stand_state(state)
                elif emote_text_id == Emotes.SLEEP:
                    if world_session.player_mgr.stand_state != StandState.UNIT_SLEEPING:
                        state = StandState.UNIT_SLEEPING
                    world_session.player_mgr.set_stand_state(state)
                elif emote_text_id == Emotes.KNEEL:
                    if world_session.player_mgr.stand_state != StandState.UNIT_KNEEL:
                        state = StandState.UNIT_KNEEL
                    world_session.player_mgr.set_stand_state(state)
                else:
                    needs_broadcast = False
                    world_session.player_mgr.play_emote(emote_id)

                if needs_broadcast:
                    world_session.player_mgr.set_dirty()

        return 0
예제 #26
0
    def respawn(self):
        # Set properties before making it visible.
        self.state = GameObjectStates.GO_STATE_READY
        self.respawn_timer = 0
        self.respawn_time = randint(self.gobject_instance.spawn_spawntimemin,
                                    self.gobject_instance.spawn_spawntimemin)

        MapManager.respawn_object(self)
예제 #27
0
    def update(self, now):
        if now > self.last_tick > 0:
            elapsed = now - self.last_tick

            if self.is_alive and self.is_spawned and self.initialized:
                # Time to live.
                if self.time_to_live_timer > 0:
                    self.time_to_live_timer -= elapsed
                # Regeneration.
                self.regenerate(elapsed)
                # Spell/Aura Update.
                self.spell_manager.update(now)
                self.aura_manager.update(now)
                # Movement Updates.
                self.movement_manager.update_pending_waypoints(elapsed)
                if self.has_moved:
                    self._on_relocation()
                    self.set_has_moved(False)
                # Random Movement, if visible to players.
                if self.has_observers():
                    self._perform_random_movement(now)
                # Combat Movement.
                self._perform_combat_movement()
                # AI.
                if self.object_ai:
                    self.object_ai.update_ai(elapsed)
                # Attack Update.
                if self.combat_target and self.is_within_interactable_distance(
                        self.combat_target):
                    self.attack_update(elapsed)
                # Not in combat, check if threat manager can resolve a target.
                elif self.threat_manager:
                    target = self.threat_manager.resolve_target()
                    if target:
                        is_melee = self.is_within_interactable_distance(target)
                        self.attack(target, is_melee=is_melee)

            # Dead
            elif not self.is_alive and self.initialized:
                self.respawn_timer += elapsed
                if self.respawn_timer >= self.respawn_time:
                    self.respawn()
                # Destroy body when creature is about to respawn.
                elif self.is_spawned and self.respawn_timer >= self.respawn_time * 0.8:
                    if self.summoner:
                        self.despawn(destroy=True)
                    else:
                        self.despawn()

            # Time to live expired, destroy.
            if self.time_to_live_timer < -1:
                self.despawn(destroy=True)
            # Check if this creature object should be updated yet or not.
            elif self.has_pending_updates():
                MapManager.update_object(self, has_changes=True)
                self.reset_fields_older_than(now)

        self.last_tick = now
예제 #28
0
 def send_single_item_update(self, item, is_self):
     update_packet = UpdatePacketFactory.compress_if_needed(PacketWriter.get_packet(
         OpCode.SMSG_UPDATE_OBJECT, item.get_full_update_packet(is_self=False)))
     if is_self:
         self.owner.enqueue_packet(update_packet)
         self.owner.enqueue_packet(item.query_details())
     else:
         MapManager.send_surrounding(update_packet, self.owner, include_self=False)
         MapManager.send_surrounding(item.query_details(), self.owner, include_self=False)
예제 #29
0
    def handle(world_session, socket, reader: PacketReader) -> int:
        # TODO Not working, wrong packet data, or animation not implemented client side?
        player_guid = unpack('<Q', reader.data[:8])[0]
        data = pack('<Q', player_guid)
        mount_anim_packet = PacketWriter.get_packet(
            OpCode.SMSG_MOUNTSPECIAL_ANIM, data)
        MapManager.send_surrounding(mount_anim_packet,
                                    world_session.player_mgr)

        return 0
예제 #30
0
    def send_move_to(self, waypoints, speed, spline_flag):
        self.reset()
        self.speed = speed

        start_time = int(WorldManager.get_seconds_since_startup() * 1000)

        location_bytes = self.unit.location.to_bytes(include_orientation=False)
        data = pack(f'<Q{len(location_bytes)}sIBI', self.unit.guid,
                    location_bytes, start_time, 0, spline_flag)

        waypoints_data = b''
        waypoints_length = len(waypoints)
        last_waypoint = self.unit.location
        total_distance = 0
        total_time = 0
        current_id = 0
        for waypoint in waypoints:
            waypoints_data += waypoint.to_bytes(include_orientation=False)
            current_distance = last_waypoint.distance(waypoint)
            current_time = current_distance / speed
            total_distance += current_distance
            total_time += current_time

            self.pending_waypoints.append(
                PendingWaypoint(current_id, total_time, waypoint))
            last_waypoint = waypoint
            current_id += 1

        data += pack(f'<2I{len(waypoints_data)}s', int(total_time * 1000),
                     waypoints_length, waypoints_data)

        MapManager.send_surrounding(PacketWriter.get_packet(
            OpCode.SMSG_MONSTER_MOVE, data),
                                    self.unit,
                                    include_self=self.is_player)

        # Player shouldn't instantly dismount after reaching the taxi destination
        if self.is_player and spline_flag == SplineFlags.SPLINEFLAG_FLYING:
            self.total_waypoint_time = total_time + 1.0  # Add 1 extra second
        else:
            self.total_waypoint_time = total_time

        # Generate the spline
        spline = MovementSpline()
        spline.flags = spline_flag
        spline.spot = self.unit.location
        spline.guid = self.unit.guid
        spline.facing = self.unit.location.o
        spline.elapsed = 0
        spline.total_time = int(self.total_waypoint_time * 1000)
        spline.points = waypoints
        self.unit.movement_spline = spline

        self.last_position = self.unit.location
        self.should_update_waypoints = True