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
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()
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)
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)
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
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)
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
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
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
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}
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
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
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
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
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)
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))
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.
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
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
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
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
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
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()
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
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)
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
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)
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
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