def __init__(self, guid=0, entry=0, object_type=None, walk_speed=config.Unit.Defaults.walk_speed, running_speed=config.Unit.Defaults.run_speed, swim_speed=config.Unit.Defaults.swim_speed, turn_rate=config.Unit.Player.Defaults.turn_speed, movement_flags=0, unit_flags=0, dynamic_flags=0, native_scale=1, native_display_id=0, faction=0, bounding_radius=config.Unit.Defaults.bounding_radius, location=None, transport_id=0, transport=None, pitch=0, zone=0, map_=0): self.guid = guid self.entry = entry self.walk_speed = walk_speed self.running_speed = running_speed self.swim_speed = swim_speed self.turn_rate = turn_rate self.movement_flags = movement_flags self.unit_flags = unit_flags self.dynamic_flags = dynamic_flags self.native_scale = native_scale self.current_scale = native_scale self.native_display_id = native_display_id # Native display ID self.current_display_id = native_display_id self.faction = faction self.bounding_radius = bounding_radius self.location = Vector() self.transport_id = transport_id self.transport = Vector() self.pitch = pitch self.zone = zone self.map_ = map_ self.object_type_mask = ObjectTypeFlags.TYPE_OBJECT self.update_packet_factory = UpdatePacketFactory() self.initialized = False self.is_spawned = True self.summoner = None self.current_cell = '' self.last_tick = 0 self.movement_spline = None self.object_ai = None
def update_surrounding_on_me(self): players, creatures, gobjects = GridManager.get_surrounding_objects( self, [ ObjectTypes.TYPE_PLAYER, ObjectTypes.TYPE_UNIT, ObjectTypes.TYPE_GAMEOBJECT ]) # At this point, all objects aren't synced unless proven otherwise for guid, object_info in list(self.objects_in_range.items()): self.objects_in_range[guid]['synced'] = False for guid, player in players.items(): if self.guid != guid: if guid not in self.objects_in_range: update_packet = player.generate_proper_update_packet( create=True) self.session.request.sendall(update_packet) self.session.request.sendall( NameQueryHandler.get_query_details(player.player)) self.objects_in_range[guid] = { 'object': player, 'synced': True } for guid, creature in creatures.items(): if creature.is_spawned: if guid not in self.objects_in_range: update_packet = UpdatePacketFactory.compress_if_needed( PacketWriter.get_packet( OpCode.SMSG_UPDATE_OBJECT, creature.get_full_update_packet(is_self=False))) self.session.request.sendall(update_packet) self.session.request.sendall(creature.query_details()) self.objects_in_range[guid] = {'object': creature, 'synced': True} for guid, gobject in gobjects.items(): if guid not in self.objects_in_range: update_packet = UpdatePacketFactory.compress_if_needed( PacketWriter.get_packet( OpCode.SMSG_UPDATE_OBJECT, gobject.get_full_update_packet(is_self=False))) self.session.request.sendall(update_packet) self.session.request.sendall(gobject.query_details()) self.objects_in_range[guid] = {'object': gobject, 'synced': True} for guid, object_info in list(self.objects_in_range.items()): if not object_info['synced']: self.destroy_near_object(guid, skip_check=True)
def generate_proper_update_packet(self, is_self=False, create=False): update_packet = UpdatePacketFactory.compress_if_needed( PacketWriter.get_packet( OpCode.SMSG_UPDATE_OBJECT, self.get_full_update_packet(is_self=is_self) if create else self.get_partial_update_packet())) return update_packet
def generate_partial_packet(self, requester): if not self.initialized: self.initialize_field_values() return UpdatePacketFactory.compress_if_needed( PacketWriter.get_packet(OpCode.SMSG_UPDATE_OBJECT, self.get_partial_update_packet(requester)))
def __init__(self, guid=0, entry=0, object_type=None, walk_speed=config.Unit.Defaults.walk_speed, running_speed=config.Unit.Defaults.run_speed, swim_speed=config.Unit.Defaults.swim_speed, turn_rate=config.Unit.Player.Defaults.turn_speed, movement_flags=0, unit_flags=0, dynamic_flags=0, shapeshift_form=0, display_id=0, scale=1, bounding_radius=config.Unit.Defaults.bounding_radius, location=None, transport_id=0, transport=None, pitch=0, zone=0, map_=0): self.guid = guid self.entry = entry self.walk_speed = walk_speed self.running_speed = running_speed self.swim_speed = swim_speed self.turn_rate = turn_rate self.movement_flags = movement_flags self.unit_flags = unit_flags self.dynamic_flags = dynamic_flags self.shapeshift_form = shapeshift_form self.display_id = display_id self.scale = scale self.bounding_radius = bounding_radius self.location = Vector() self.transport_id = transport_id self.transport = Vector() self.pitch = pitch self.zone = zone self.map_ = map_ self.object_type = [ObjectTypes.TYPE_OBJECT] self.update_packet_factory = UpdatePacketFactory() self.current_grid = '' self.last_tick = 0 self.movement_spline = None
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 send_single_item_update(self, world_session, 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: world_session.request.sendall(update_packet) world_session.request.sendall(item.query_details()) else: GridManager.send_surrounding(update_packet, world_session.player_mgr, include_self=False) GridManager.send_surrounding(item.query_details(), world_session.player_mgr, include_self=False)
def send_update_surrounding(self): update_packet = UpdatePacketFactory.compress_if_needed( PacketWriter.get_packet( OpCode.SMSG_UPDATE_OBJECT, self.get_full_update_packet(is_self=False))) MapManager.send_surrounding(update_packet, self, include_self=False)
def generate_create_packet(self, requester): return UpdatePacketFactory.compress_if_needed( PacketWriter.get_packet(OpCode.SMSG_UPDATE_OBJECT, self.get_object_create_packet(requester)))
class ObjectManager: def __init__(self, guid=0, entry=0, object_type=None, walk_speed=config.Unit.Defaults.walk_speed, running_speed=config.Unit.Defaults.run_speed, swim_speed=config.Unit.Defaults.swim_speed, turn_rate=config.Unit.Player.Defaults.turn_speed, movement_flags=0, unit_flags=0, dynamic_flags=0, native_scale=1, native_display_id=0, faction=0, bounding_radius=config.Unit.Defaults.bounding_radius, location=None, transport_id=0, transport=None, pitch=0, zone=0, map_=0): self.guid = guid self.entry = entry self.walk_speed = walk_speed self.running_speed = running_speed self.swim_speed = swim_speed self.turn_rate = turn_rate self.movement_flags = movement_flags self.unit_flags = unit_flags self.dynamic_flags = dynamic_flags self.native_scale = native_scale self.current_scale = native_scale self.native_display_id = native_display_id # Native display ID self.current_display_id = native_display_id self.faction = faction self.bounding_radius = bounding_radius self.location = Vector() self.transport_id = transport_id self.transport = Vector() self.pitch = pitch self.zone = zone self.map_ = map_ self.object_type_mask = ObjectTypeFlags.TYPE_OBJECT self.update_packet_factory = UpdatePacketFactory() self.initialized = False self.is_spawned = True self.summoner = None self.current_cell = '' self.last_tick = 0 self.movement_spline = None self.object_ai = None def __eq__(self, other): if isinstance(other, self.__class__): return self.guid == other.guid return NotImplemented def __ne__(self, other): return not self == other def has_pending_updates(self): return self.update_packet_factory.has_pending_updates() def generate_create_packet(self, requester): return UpdatePacketFactory.compress_if_needed( PacketWriter.get_packet(OpCode.SMSG_UPDATE_OBJECT, self.get_object_create_packet(requester))) def generate_partial_packet(self, requester): if not self.initialized: self.initialize_field_values() return UpdatePacketFactory.compress_if_needed( PacketWriter.get_packet(OpCode.SMSG_UPDATE_OBJECT, self.get_partial_update_packet(requester))) def get_object_create_packet(self, requester): from game.world.managers.objects.units import UnitManager is_self = requester.guid == self.guid if not self.initialized: self.initialize_field_values() # Base structure. data = self._get_base_structure(UpdateTypes.CREATE_OBJECT) # Object type. data += pack('<B', self.get_type_id()) # Movement fields. data += self._get_movement_fields() # Misc fields. combat_unit = UnitManager.UnitManager(self).combat_target if self.object_type_mask & ObjectTypeFlags.TYPE_UNIT \ else None data += pack( '<3IQ', 1 if is_self else 0, # Flags, 1 - Current player, 0 - Other player 1 if self.get_type_id() == ObjectTypeIds.ID_PLAYER else 0, # AttackCycle 0, # TimerId combat_unit.guid if combat_unit else 0, # Victim GUID ) # Normal update fields. data += self._get_fields_update(True, requester) return data def get_partial_update_packet(self, requester): # Base structure. data = self._get_base_structure(UpdateTypes.PARTIAL) # Normal update fields. data += self._get_fields_update(False, requester) return data def get_movement_update_packet(self): # Base structure. data = self._get_base_structure(UpdateTypes.MOVEMENT) # Movement update fields. data += self._get_movement_fields() return data def get_display_id(self): return self.current_display_id def set_display_id(self, display_id): self.current_display_id = display_id return True def reset_display_id(self): self.set_display_id(self.native_display_id) def set_scale(self, scale): self.current_scale = scale self.set_float(ObjectFields.OBJECT_FIELD_SCALE_X, self.current_scale) def change_speed(self, speed=0): if speed <= 0: speed = config.Unit.Defaults.run_speed elif speed >= 56: speed = 56 # Max speed without glitches if self.running_speed == speed: return False self.running_speed = speed return True def reset_scale(self): self.set_scale(self.native_scale) def reset_fields(self): # Reset updated fields. self.update_packet_factory.reset() def reset_fields_older_than(self, timestamp): # Reset updated fields older than the specified timestamp. return self.update_packet_factory.reset_older_than(timestamp) def _get_base_structure(self, update_type): return pack( '<IBQ', 1, # Number of transactions update_type, self.guid, ) def _get_movement_fields(self): data = pack('<Q9fI', self.transport_id, self.transport.x, self.transport.y, self.transport.z, self.transport.o, self.location.x, self.location.y, self.location.z, self.location.o, self.pitch, self.movement_flags) # TODO: NOT WORKING! # if self.movement_spline: # data += self.movement_spline.to_bytes() data += pack( '<I4f', 0, # Fall Time self.walk_speed, self.running_speed, self.swim_speed, self.turn_rate) return data def _get_fields_update(self, is_create, requester): data = b'' mask = self.update_packet_factory.update_mask.copy() for field_index in range( self.update_packet_factory.update_mask.field_count): # Partial packets only care for fields that had changes. if not is_create and mask[field_index] == 0: continue # Check for encapsulation, turn off the bit if requester has no read access. if not self.update_packet_factory.has_read_rights_for_field( field_index, requester): mask[field_index] = 0 continue # Append field value and turn on bit on mask. data += self.update_packet_factory.update_values_bytes[field_index] mask[field_index] = 1 return pack('<B', self.update_packet_factory.update_mask.block_count ) + mask.tobytes() + data # noinspection PyMethodMayBeStatic def is_aura_field(self, index): return UnitFields.UNIT_FIELD_AURA <= index <= UnitFields.UNIT_FIELD_AURA + 55 def set_int32(self, index, value): if self.update_packet_factory.should_update(index, value, 'i'): self.update_packet_factory.update(index, value, 'i') def get_int32(self, index): return self._get_value_by_type_at('i', index) def set_uint32(self, index, value): if self.update_packet_factory.should_update(index, value, 'I'): self.update_packet_factory.update(index, value, 'I') def get_uint32(self, index): return self._get_value_by_type_at('I', index) def set_int64(self, index, value): if self.update_packet_factory.should_update(index, value, 'q'): self.update_packet_factory.update(index, value, 'q') def get_int64(self, index): return self._get_value_by_type_at('q', index) def set_uint64(self, index, value): if self.update_packet_factory.should_update(index, value, 'Q'): self.update_packet_factory.update(index, value, 'Q') def get_uint64(self, index): return self._get_value_by_type_at('Q', index) def set_float(self, index, value): if self.update_packet_factory.should_update(index, value, 'f'): self.update_packet_factory.update(index, value, 'f') def get_float(self, index): return self._get_value_by_type_at('f', index) def _get_value_by_type_at(self, value_type, index): if not self.update_packet_factory.update_values[index]: return 0 # Return the raw value directly if not 64 bits. if value_type.lower() != 'q': return self.update_packet_factory.update_values[index] # Unpack from two field bytes. value = self.update_packet_factory.update_values_bytes[index] if value_type.lower() == 'q': value += self.update_packet_factory.update_values_bytes[index + 1] return unpack(f'<{value_type}', value)[0] # override def update(self, now): pass # override def initialize_field_values(self): pass # override def on_cell_change(self): pass # override def get_type_id(self): return ObjectTypeIds.ID_OBJECT # override def get_debug_messages(self, requester=None): low_guid = self.guid & ~ObjectManager.extract_high_guid(self.guid) return [ f'Guid: {low_guid}, Entry: {self.entry}, Display ID: {self.current_display_id}', f'X: {self.location.x:.3f}, Y: {self.location.y:.3f}, Z: {self.location.z:.3f}, O: {self.location.o:.3f}', f'Distance: {self.location.distance(requester.location) if requester else 0} yd' ] # override def generate_object_guid(self, low_guid): pass # override def despawn(self, destroy=False): if destroy: MapManager.remove_object(self) else: MapManager.despawn_object(self) # override def respawn(self): pass # override def is_on_water(self): liquid_information = MapManager.get_liquid_information( self.map_, self.location.x, self.location.y, self.location.z) map_z = MapManager.calculate_z_for_object(self)[0] return liquid_information and map_z < liquid_information.height # override def is_under_water(self): liquid_information = MapManager.get_liquid_information( self.map_, self.location.x, self.location.y, self.location.z) return liquid_information and self.location.z + ( self.current_scale * 2) < liquid_information.height # override def is_in_deep_water(self): liquid_information = MapManager.get_liquid_information( self.map_, self.location.x, self.location.y, self.location.z) return liquid_information and liquid_information.liquid_type == LiquidTypes.DEEP def can_attack_target(self, target): if target is self: return False # Player only checks. if target.get_type_id() == ObjectTypeIds.ID_PLAYER: # If player is on a flying path. if target.movement_spline and target.movement_spline.flags == SplineFlags.SPLINEFLAG_FLYING: return False # Creature only checks. elif target.get_type_id() == ObjectTypeIds.ID_UNIT: # If the unit is evading. if target.is_evading: return False # Checks for both players and creatures (all units). if target.object_type_mask & ObjectTypeFlags.TYPE_UNIT: if not target.is_alive: return False return self.is_enemy_to(target) def _allegiance_status_checker(self, target) -> UnitReaction: own_faction = DbcDatabaseManager.FactionTemplateHolder.faction_template_get_by_id( self.faction) target_faction = DbcDatabaseManager.FactionTemplateHolder.faction_template_get_by_id( target.faction) if not own_faction: Logger.error(f'Invalid faction template: {self.faction}.') return UnitReaction.UNIT_REACTION_NEUTRAL if not target_faction: Logger.error(f'Invalid faction template: {target.faction}.') return UnitReaction.UNIT_REACTION_NEUTRAL # TODO: Reputation standing checks first. if target_faction.FactionGroup & own_faction.EnemyGroup != 0: return UnitReaction.UNIT_REACTION_HOSTILE own_enemies = { own_faction.Enemies_1, own_faction.Enemies_2, own_faction.Enemies_3, own_faction.Enemies_4 } if target_faction.Faction > 0 and target_faction.Faction in own_enemies: return UnitReaction.UNIT_REACTION_HOSTILE if target_faction.FactionGroup & own_faction.FriendGroup != 0: return UnitReaction.UNIT_REACTION_FRIENDLY own_friends = { own_faction.Friend_1, own_faction.Friend_2, own_faction.Friend_3, own_faction.Friend_4 } if target_faction.Faction > 0 and target_faction.Faction in own_friends: return UnitReaction.UNIT_REACTION_FRIENDLY if target_faction.FriendGroup & own_faction.FactionGroup != 0: return UnitReaction.UNIT_REACTION_FRIENDLY other_friends = { target_faction.Friend_1, target_faction.Friend_2, target_faction.Friend_3, target_faction.Friend_4 } if own_faction.Faction > 0 and own_faction.Faction in other_friends: return UnitReaction.UNIT_REACTION_FRIENDLY return UnitReaction.UNIT_REACTION_NEUTRAL def is_friendly_to(self, target): return self._allegiance_status_checker( target) >= UnitReaction.UNIT_REACTION_NEUTRAL def is_enemy_to(self, target): return self._allegiance_status_checker( target) < UnitReaction.UNIT_REACTION_NEUTRAL def get_destroy_packet(self): data = pack('<Q', self.guid) return PacketWriter.get_packet(OpCode.SMSG_DESTROY_OBJECT, data) @staticmethod def extract_high_guid(guid): return HighGuid(guid & (0xFFFF << 48))
class ObjectManager(object): def __init__(self, guid=0, entry=0, object_type=None, walk_speed=2.5, running_speed=7.0, swim_speed=4.72222223, turn_rate=pi, movement_flags=0, unit_flags=0, dynamic_flags=0, shapeshift_form=0, display_id=0, scale=1, bounding_radius=config.Unit.Defaults.bounding_radius, location=None, transport_id=0, transport=None, pitch=0, zone=0, map_=0): self.guid = guid self.entry = entry self.walk_speed = walk_speed self.running_speed = running_speed self.swim_speed = swim_speed self.turn_rate = turn_rate self.movement_flags = movement_flags self.unit_flags = unit_flags self.dynamic_flags = dynamic_flags self.shapeshift_form = shapeshift_form self.display_id = display_id self.scale = scale self.bounding_radius = bounding_radius self.location = Vector() self.transport_id = transport_id self.transport = Vector() self.pitch = pitch self.zone = zone self.map_ = map_ self.object_type = [ObjectTypes.TYPE_OBJECT] self.update_packet_factory = UpdatePacketFactory() self.current_grid = '' self.last_tick = 0 def get_object_type_value(self): type_value = 0 for type_ in self.object_type: type_value |= type_ return type_value def get_object_create_packet(self, is_self=True): from game.world.managers.objects import UnitManager # Base structure data = self._get_base_structure(UpdateTypes.CREATE_OBJECT) # Object type data += pack('<B', self.get_type_id()) # Movement fields data += self._get_movement_fields() # Misc fields combat_unit = UnitManager.UnitManager(self).combat_target if ObjectTypes.TYPE_UNIT in self.object_type else None data += pack( '<3IQ', 1 if is_self else 0, # Flags, 1 - Current player, 0 - Other player 1 if self.get_type_id() == ObjectTypeIds.ID_PLAYER else 0, # AttackCycle 0, # TimerId combat_unit.guid if combat_unit else 0, # Victim GUID ) # Normal update fields data += self._get_fields_update() return data def get_partial_update_packet(self): # Base structure data = self._get_base_structure(UpdateTypes.PARTIAL) # Normal update fields data += self._get_fields_update() return data def get_movement_update_packet(self): # Base structure data = self._get_base_structure(UpdateTypes.MOVEMENT) # Normal update fields data += self._get_movement_fields() return data def reset_fields(self): # Reset updated fields self.update_packet_factory.reset() def _get_base_structure(self, update_type): return pack( '<IBQ', 1, # Number of transactions update_type, self.guid, ) def _get_movement_fields(self): return pack( '<QfffffffffIIffff', self.transport_id, self.transport.x, self.transport.y, self.transport.z, self.transport.o, self.location.x, self.location.y, self.location.z, self.location.o, self.pitch, self.movement_flags, 0, # Fall Time? self.walk_speed, self.running_speed, self.swim_speed, self.turn_rate ) def _get_fields_update(self): data = pack('<B', self.update_packet_factory.update_mask.block_count) data += self.update_packet_factory.update_mask.to_bytes() for i in range(0, self.update_packet_factory.update_mask.field_count): if self.update_packet_factory.update_mask.is_set(i): data += self.update_packet_factory.update_values[i] return data def set_int32(self, index, value): self.update_packet_factory.update(index, value, 'i') def set_uint32(self, index, value): self.update_packet_factory.update(index, value, 'I') def set_int64(self, index, value): self.update_packet_factory.update(index, value, 'q') def set_uint64(self, index, value): self.update_packet_factory.update(index, value, 'Q') def set_float(self, index, value): self.update_packet_factory.update(index, value, 'f') # override def update(self): pass # override def get_full_update_packet(self, is_self=True): pass # override def get_type(self): return ObjectTypes.TYPE_OBJECT # override def get_type_id(self): return ObjectTypeIds.ID_OBJECT def get_destroy_packet(self): data = pack('<Q', self.guid) return PacketWriter.get_packet(OpCode.SMSG_DESTROY_OBJECT, data)
class ObjectManager(object): def __init__(self, guid=0, entry=0, object_type=None, walk_speed=config.Unit.Defaults.walk_speed, running_speed=config.Unit.Defaults.run_speed, swim_speed=config.Unit.Defaults.swim_speed, turn_rate=config.Unit.Player.Defaults.turn_speed, movement_flags=0, unit_flags=0, dynamic_flags=0, native_scale=1, native_display_id=0, faction=0, bounding_radius=config.Unit.Defaults.bounding_radius, location=None, transport_id=0, transport=None, pitch=0, zone=0, map_=0): self.guid = guid self.entry = entry self.walk_speed = walk_speed self.running_speed = running_speed self.swim_speed = swim_speed self.turn_rate = turn_rate self.movement_flags = movement_flags self.unit_flags = unit_flags self.dynamic_flags = dynamic_flags self.native_scale = native_scale self.current_scale = native_scale self.native_display_id = native_display_id # Native display ID self.current_display_id = native_display_id self.faction = faction self.bounding_radius = bounding_radius self.location = Vector() self.transport_id = transport_id self.transport = Vector() self.pitch = pitch self.zone = zone self.map_ = map_ self.object_type_mask = ObjectTypeFlags.TYPE_OBJECT self.update_packet_factory = UpdatePacketFactory() self.is_spawned = True self.is_summon = False self.current_cell = '' self.last_tick = 0 self.movement_spline = None def __eq__(self, other): if isinstance(other, self.__class__): return self.guid == other.guid return NotImplemented def __ne__(self, other): return not self == other def has_pending_updates(self): return self.update_packet_factory.has_pending_updates() def generate_create_packet(self, requester): return UpdatePacketFactory.compress_if_needed(PacketWriter.get_packet( OpCode.SMSG_UPDATE_OBJECT, self.get_full_update_packet(requester))) def generate_partial_packet(self, requester): return UpdatePacketFactory.compress_if_needed(PacketWriter.get_packet( OpCode.SMSG_UPDATE_OBJECT, self.get_partial_update_packet(requester))) def get_object_create_packet(self, requester): from game.world.managers.objects.units import UnitManager is_self = requester.guid == self.guid # Base structure. data = self._get_base_structure(UpdateTypes.CREATE_OBJECT) # Object type. data += pack('<B', self.get_type_id()) # Movement fields. data += self._get_movement_fields() # Misc fields. combat_unit = UnitManager.UnitManager(self).combat_target if self.object_type_mask & ObjectTypeFlags.TYPE_UNIT \ else None data += pack( '<3IQ', 1 if is_self else 0, # Flags, 1 - Current player, 0 - Other player 1 if self.get_type_id() == ObjectTypeIds.ID_PLAYER else 0, # AttackCycle 0, # TimerId combat_unit.guid if combat_unit else 0, # Victim GUID ) # Normal update fields. data += self._get_fields_update(requester) return data def get_partial_update_packet(self, requester): # Base structure. data = self._get_base_structure(UpdateTypes.PARTIAL) # Normal update fields. data += self._get_fields_update(requester) return data def get_movement_update_packet(self): # Base structure. data = self._get_base_structure(UpdateTypes.MOVEMENT) # Movement update fields. data += self._get_movement_fields() return data def get_display_id(self): return self.current_display_id def set_display_id(self, display_id): self.current_display_id = display_id return True def reset_display_id(self): self.set_display_id(self.native_display_id) def set_scale(self, scale): self.current_scale = scale self.set_float(ObjectFields.OBJECT_FIELD_SCALE_X, self.current_scale) def change_speed(self, speed=0): if speed <= 0: speed = config.Unit.Defaults.run_speed elif speed >= 56: speed = 56 # Max speed without glitches if self.running_speed == speed: return False self.running_speed = speed return True def reset_scale(self): self.set_scale(self.native_scale) def reset_fields(self): # Reset updated fields self.update_packet_factory.reset() def reset_fields_older_than(self, timestamp): # Reset updated fields older than the specified timestamp return self.update_packet_factory.reset_older_than(timestamp) def _get_base_structure(self, update_type): return pack( '<IBQ', 1, # Number of transactions update_type, self.guid, ) def _get_movement_fields(self): data = pack( '<Q9fI', self.transport_id, self.transport.x, self.transport.y, self.transport.z, self.transport.o, self.location.x, self.location.y, self.location.z, self.location.o, self.pitch, self.movement_flags ) # TODO: NOT WORKING! # if self.movement_spline: # data += self.movement_spline.to_bytes() data += pack( '<I4f', 0, # Fall Time self.walk_speed, self.running_speed, self.swim_speed, self.turn_rate ) return data def _get_fields_update(self, requester): data = pack('<B', self.update_packet_factory.update_mask.block_count) data += self.update_packet_factory.update_mask.to_bytes() for i in range(0, self.update_packet_factory.update_mask.field_count): if self.update_packet_factory.update_mask.is_set(i): data += self.update_packet_factory.update_values[i] return data def set_int32(self, index, value): self.update_packet_factory.update(index, value, 'i') def get_int32(self, index): return unpack('<i', self.update_packet_factory.update_values[index])[0] def set_uint32(self, index, value): self.update_packet_factory.update(index, value, 'I') def get_uint32(self, index): return unpack('<I', self.update_packet_factory.update_values[index])[0] def set_int64(self, index, value): self.update_packet_factory.update(index, value, 'q') def get_int64(self, index): return unpack('<q', self.update_packet_factory.update_values[index] + self.update_packet_factory.update_values[index + 1])[0] def set_uint64(self, index, value): self.update_packet_factory.update(index, value, 'Q') def get_uint64(self, index): return unpack('<Q', self.update_packet_factory.update_values[index] + self.update_packet_factory.update_values[index + 1])[0] def set_float(self, index, value): self.update_packet_factory.update(index, value, 'f') def get_float(self, index): return unpack('<f', self.update_packet_factory.update_values[index])[0] # override def update(self, now): pass # override def get_full_update_packet(self, requester): pass # override def on_cell_change(self): pass # override def get_type_id(self): return ObjectTypeIds.ID_OBJECT # override def get_debug_messages(self): low_guid = self.guid & ~ObjectManager.extract_high_guid(self.guid) return [ f'Guid: {low_guid}, Entry: {self.entry}, Display ID: {self.current_display_id}', f'X: {self.location.x}, Y: {self.location.y}, Z: {self.location.z}, O: {self.location.o}' ] # override def generate_object_guid(self, low_guid): pass # override def despawn(self, destroy=False): if destroy: MapManager.remove_object(self) else: MapManager.despawn_object(self) # override def respawn(self): pass # override def is_on_water(self): liquid_information = MapManager.get_liquid_information(self.map_, self.location.x, self.location.y, self.location.z) map_z = MapManager.calculate_z_for_object(self)[0] return liquid_information and map_z < liquid_information.height # override def is_under_water(self): liquid_information = MapManager.get_liquid_information(self.map_, self.location.x, self.location.y, self.location.z) return liquid_information and self.location.z + (self.current_scale * 2) < liquid_information.height # override def is_in_deep_water(self): liquid_information = MapManager.get_liquid_information(self.map_, self.location.x, self.location.y, self.location.z) return liquid_information and liquid_information.liquid_type == LiquidTypes.DEEP # override def can_attack_target(self, target): if target is self: return False # Player only checks. if target.get_type_id() == ObjectTypeIds.ID_PLAYER: # If player is on a flying path. if target.movement_spline and target.movement_spline.flags == SplineFlags.SPLINEFLAG_FLYING: return False # Creature only checks. elif target.get_type_id() == ObjectTypeIds.ID_UNIT: # If the unit is evading. if target.is_evading: return False # Checks for both players and creatures (all units). if target.object_type_mask & ObjectTypeFlags.TYPE_UNIT: if not target.is_alive: return False return self.is_enemy_to(target) def _allegiance_status_checker(self, target, check_friendly=True): own_faction = DbcDatabaseManager.FactionTemplateHolder.faction_template_get_by_id(self.faction) target_faction = DbcDatabaseManager.FactionTemplateHolder.faction_template_get_by_id(target.faction) if not own_faction: Logger.error(f'Invalid faction template: {self.faction}.') return not check_friendly if not target_faction: Logger.error(f'Invalid faction template: {target.faction}.') return not check_friendly own_enemies = [own_faction.Enemies_1, own_faction.Enemies_2, own_faction.Enemies_3, own_faction.Enemies_4] own_friends = [own_faction.Friend_1, own_faction.Friend_2, own_faction.Friend_3, own_faction.Friend_4] if target_faction.Faction > 0: for enemy in own_enemies: if enemy == target_faction.Faction: return not check_friendly for friend in own_friends: if friend == target_faction.Faction: return check_friendly if check_friendly: return ((own_faction.FriendGroup & target_faction.FactionGroup) or (own_faction.FactionGroup & target_faction.FriendGroup)) != 0 else: return ((own_faction.EnemyGroup & target_faction.FactionGroup) or (own_faction.FactionGroup & target_faction.EnemyGroup)) != 0 def is_friendly_to(self, target): return self._allegiance_status_checker(target, True) def is_enemy_to(self, target): return self._allegiance_status_checker(target, False) def get_destroy_packet(self): data = pack('<Q', self.guid) return PacketWriter.get_packet(OpCode.SMSG_DESTROY_OBJECT, data) @staticmethod def extract_high_guid(guid): return HighGuid(guid & (0xFFFF << 48))
class ObjectManager(object): def __init__(self, guid=0, entry=0, object_type=None, walk_speed=config.Unit.Defaults.walk_speed, running_speed=config.Unit.Defaults.run_speed, swim_speed=config.Unit.Defaults.swim_speed, turn_rate=config.Unit.Player.Defaults.turn_speed, movement_flags=0, unit_flags=0, dynamic_flags=0, native_scale=1, native_display_id=0, faction=0, bounding_radius=config.Unit.Defaults.bounding_radius, location=None, transport_id=0, transport=None, pitch=0, zone=0, map_=0): self.guid = guid self.entry = entry self.walk_speed = walk_speed self.running_speed = running_speed self.swim_speed = swim_speed self.turn_rate = turn_rate self.movement_flags = movement_flags self.unit_flags = unit_flags self.dynamic_flags = dynamic_flags self.native_scale = native_scale self.current_scale = native_scale self.native_display_id = native_display_id # Native display ID self.current_display_id = native_display_id self.faction = faction self.bounding_radius = bounding_radius self.location = Vector() self.transport_id = transport_id self.transport = Vector() self.pitch = pitch self.zone = zone self.map_ = map_ self.object_type = [ObjectTypes.TYPE_OBJECT] self.update_packet_factory = UpdatePacketFactory() self.dirty = False self.current_cell = '' self.last_tick = 0 self.movement_spline = None def get_object_type_value(self): type_value = 0 for type_ in self.object_type: type_value |= type_ return type_value def get_object_create_packet(self, is_self=True): from game.world.managers.objects import UnitManager # Base structure data = self._get_base_structure(UpdateTypes.CREATE_OBJECT) # Object type data += pack('<B', self.get_type_id()) # Movement fields data += self._get_movement_fields() # Misc fields combat_unit = UnitManager.UnitManager( self ).combat_target if ObjectTypes.TYPE_UNIT in self.object_type else None data += pack( '<3IQ', 1 if is_self else 0, # Flags, 1 - Current player, 0 - Other player 1 if self.get_type_id() == ObjectTypeIds.ID_PLAYER else 0, # AttackCycle 0, # TimerId combat_unit.guid if combat_unit else 0, # Victim GUID ) # Normal update fields data += self._get_fields_update() return data def get_partial_update_packet(self): # Base structure data = self._get_base_structure(UpdateTypes.PARTIAL) # Normal update fields data += self._get_fields_update() return data def get_movement_update_packet(self): # Base structure data = self._get_base_structure(UpdateTypes.MOVEMENT) # Normal update fields data += self._get_movement_fields() return data def set_dirty(self, is_dirty=True): self.dirty = is_dirty def get_display_id(self): return self.current_display_id def set_display_id(self, display_id): self.current_display_id = display_id def reset_display_id(self): self.set_display_id(self.native_display_id) def set_scale(self, scale): self.current_scale = scale self.set_float(ObjectFields.OBJECT_FIELD_SCALE_X, self.current_scale) def reset_scale(self): self.set_scale(self.native_scale) def reset_fields(self): # Reset updated fields self.update_packet_factory.reset() def _get_base_structure(self, update_type): return pack( '<IBQ', 1, # Number of transactions update_type, self.guid, ) def _get_movement_fields(self): data = pack('<Q9fI', self.transport_id, self.transport.x, self.transport.y, self.transport.z, self.transport.o, self.location.x, self.location.y, self.location.z, self.location.o, self.pitch, self.movement_flags) # TODO: NOT WORKING! # if self.movement_spline: # data += self.movement_spline.to_bytes() data += pack( '<I4f', 0, # Fall Time self.walk_speed, self.running_speed, self.swim_speed, self.turn_rate) return data def _get_fields_update(self): data = pack('<B', self.update_packet_factory.update_mask.block_count) data += self.update_packet_factory.update_mask.to_bytes() for i in range(0, self.update_packet_factory.update_mask.field_count): if self.update_packet_factory.update_mask.is_set(i): data += self.update_packet_factory.update_values[i] return data def set_int32(self, index, value): self.update_packet_factory.update(index, value, 'i') def get_int32(self, index): return unpack('<i', self.update_packet_factory.update_values[index])[0] def set_uint32(self, index, value): self.update_packet_factory.update(index, value, 'I') def get_uint32(self, index): return unpack('<I', self.update_packet_factory.update_values[index])[0] def set_int64(self, index, value): self.update_packet_factory.update(index, value, 'q') def get_int64(self, index): return unpack( '<q', self.update_packet_factory.update_values[index] + self.update_packet_factory.update_values[index + 1])[0] def set_uint64(self, index, value): self.update_packet_factory.update(index, value, 'Q') def get_uint64(self, index): return unpack( '<Q', self.update_packet_factory.update_values[index] + self.update_packet_factory.update_values[index + 1])[0] def set_float(self, index, value): self.update_packet_factory.update(index, value, 'f') def get_float(self, index): return unpack('<f', self.update_packet_factory.update_values[index])[0] # override def update(self): pass # override def get_full_update_packet(self, is_self=True): pass # override def on_cell_change(self): pass # override def get_type(self): return ObjectTypes.TYPE_OBJECT # override def get_type_id(self): return ObjectTypeIds.ID_OBJECT # override def get_debug_messages(self): if self.get_type() == ObjectTypes.TYPE_UNIT: guid = self.guid & ~HighGuid.HIGHGUID_UNIT elif self.get_type() == ObjectTypes.TYPE_PLAYER: guid = self.guid & ~HighGuid.HIGHGUID_PLAYER elif self.get_type() == ObjectTypes.TYPE_GAMEOBJECT: guid = self.guid & ~HighGuid.HIGHGUID_GAMEOBJECT else: guid = self.guid return [ f'Guid: {guid}, Entry: {self.entry}, Display ID: {self.current_display_id}', f'X: {self.location.x}, Y: {self.location.y}, Z: {self.location.z}, O: {self.location.o}' ] def get_destroy_packet(self): data = pack('<Q', self.guid) return PacketWriter.get_packet(OpCode.SMSG_DESTROY_OBJECT, data)
class ObjectManager(object): def __init__(self, guid=0, entry=0, object_type=None, walk_speed=config.Unit.Defaults.walk_speed, running_speed=config.Unit.Defaults.run_speed, swim_speed=config.Unit.Defaults.swim_speed, turn_rate=config.Unit.Player.Defaults.turn_speed, movement_flags=0, unit_flags=0, dynamic_flags=0, native_scale=1, native_display_id=0, faction=0, bounding_radius=config.Unit.Defaults.bounding_radius, location=None, transport_id=0, transport=None, pitch=0, zone=0, map_=0): self.guid = guid self.entry = entry self.walk_speed = walk_speed self.running_speed = running_speed self.swim_speed = swim_speed self.turn_rate = turn_rate self.movement_flags = movement_flags self.unit_flags = unit_flags self.dynamic_flags = dynamic_flags self.native_scale = native_scale self.current_scale = native_scale self.native_display_id = native_display_id # Native display ID self.current_display_id = native_display_id self.faction = faction self.bounding_radius = bounding_radius self.location = Vector() self.transport_id = transport_id self.transport = Vector() self.pitch = pitch self.zone = zone self.map_ = map_ self.object_type = [ObjectTypes.TYPE_OBJECT] self.update_packet_factory = UpdatePacketFactory() self.is_spawned = True self.is_summon = False self.dirty = False self.current_cell = '' self.last_tick = 0 self.movement_spline = None def __eq__(self, other): if isinstance(other, self.__class__): return self.guid == other.guid return NotImplemented def __ne__(self, other): return not self == other def get_object_type_value(self): type_value = 0 for type_ in self.object_type: type_value |= type_ return type_value def generate_proper_update_packet(self, is_self=False, create=False): update_packet = UpdatePacketFactory.compress_if_needed( PacketWriter.get_packet( OpCode.SMSG_UPDATE_OBJECT, self.get_full_update_packet(is_self=is_self) if create else self.get_partial_update_packet())) return update_packet def send_create_packet_surroundings(self, **kwargs): update_packet = self.generate_proper_update_packet(False, True) MapManager.send_surrounding(update_packet, self, include_self=False) def get_object_create_packet(self, is_self=True): from game.world.managers.objects.units import UnitManager # Base structure. data = self._get_base_structure(UpdateTypes.CREATE_OBJECT) # Object type. data += pack('<B', self.get_type_id()) # Movement fields. data += self._get_movement_fields() # Misc fields. combat_unit = UnitManager.UnitManager( self ).combat_target if ObjectTypes.TYPE_UNIT in self.object_type else None data += pack( '<3IQ', 1 if is_self else 0, # Flags, 1 - Current player, 0 - Other player 1 if self.get_type_id() == ObjectTypeIds.ID_PLAYER else 0, # AttackCycle 0, # TimerId combat_unit.guid if combat_unit else 0, # Victim GUID ) # Normal update fields. data += self._get_fields_update() return data def get_partial_update_packet(self): # Base structure. data = self._get_base_structure(UpdateTypes.PARTIAL) # Normal update fields. data += self._get_fields_update() return data def get_movement_update_packet(self): # Base structure. data = self._get_base_structure(UpdateTypes.MOVEMENT) # Movement update fields. data += self._get_movement_fields() return data def set_dirty(self, is_dirty=True): self.dirty = is_dirty def get_display_id(self): return self.current_display_id def set_display_id(self, display_id): self.current_display_id = display_id return True def reset_display_id(self): self.set_display_id(self.native_display_id) def set_scale(self, scale): self.current_scale = scale self.set_float(ObjectFields.OBJECT_FIELD_SCALE_X, self.current_scale) def change_speed(self, speed=0): if speed <= 0: speed = config.Unit.Defaults.run_speed elif speed >= 56: speed = 56 # Max speed without glitches if self.running_speed == speed: return False self.running_speed = speed return True def reset_scale(self): self.set_scale(self.native_scale) def reset_fields(self): # Reset updated fields self.update_packet_factory.reset() def reset_fields_older_than(self, timestamp): # Reset updated fields older than the specified timestamp return self.update_packet_factory.reset_older_than(timestamp) def _get_base_structure(self, update_type): return pack( '<IBQ', 1, # Number of transactions update_type, self.guid, ) def _get_movement_fields(self): data = pack('<Q9fI', self.transport_id, self.transport.x, self.transport.y, self.transport.z, self.transport.o, self.location.x, self.location.y, self.location.z, self.location.o, self.pitch, self.movement_flags) # TODO: NOT WORKING! # if self.movement_spline: # data += self.movement_spline.to_bytes() data += pack( '<I4f', 0, # Fall Time self.walk_speed, self.running_speed, self.swim_speed, self.turn_rate) return data def _get_fields_update(self): data = pack('<B', self.update_packet_factory.update_mask.block_count) data += self.update_packet_factory.update_mask.to_bytes() for i in range(0, self.update_packet_factory.update_mask.field_count): if self.update_packet_factory.update_mask.is_set(i): data += self.update_packet_factory.update_values[i] return data def set_int32(self, index, value): self.update_packet_factory.update(index, value, 'i') def get_int32(self, index): return unpack('<i', self.update_packet_factory.update_values[index])[0] def set_uint32(self, index, value): self.update_packet_factory.update(index, value, 'I') def get_uint32(self, index): return unpack('<I', self.update_packet_factory.update_values[index])[0] def set_int64(self, index, value): self.update_packet_factory.update(index, value, 'q') def get_int64(self, index): return unpack( '<q', self.update_packet_factory.update_values[index] + self.update_packet_factory.update_values[index + 1])[0] def set_uint64(self, index, value): self.update_packet_factory.update(index, value, 'Q') def get_uint64(self, index): return unpack( '<Q', self.update_packet_factory.update_values[index] + self.update_packet_factory.update_values[index + 1])[0] def set_float(self, index, value): self.update_packet_factory.update(index, value, 'f') def get_float(self, index): return unpack('<f', self.update_packet_factory.update_values[index])[0] # override def update(self): pass # override def get_full_update_packet(self, is_self=True): pass # override def on_cell_change(self): pass # override def get_type(self): return ObjectTypes.TYPE_OBJECT # override def get_type_id(self): return ObjectTypeIds.ID_OBJECT # override def get_debug_messages(self): low_guid = self.guid & ~ObjectManager.extract_high_guid(self.guid) return [ f'Guid: {low_guid}, Entry: {self.entry}, Display ID: {self.current_display_id}', f'X: {self.location.x}, Y: {self.location.y}, Z: {self.location.z}, O: {self.location.o}' ] # override def generate_object_guid(self, low_guid): pass # override def despawn(self): MapManager.despawn_object(self) # override def respawn(self): pass # override def is_on_water(self): liquid_information = MapManager.get_liquid_information( self.map_, self.location.x, self.location.y, self.location.z) map_z = MapManager.calculate_z_for_object(self) return liquid_information and map_z < liquid_information.height # override def is_under_water(self): liquid_information = MapManager.get_liquid_information( self.map_, self.location.x, self.location.y, self.location.z) return liquid_information and self.location.z + ( self.current_scale * 2) < liquid_information.height # override def is_in_deep_water(self): liquid_information = MapManager.get_liquid_information( self.map_, self.location.x, self.location.y, self.location.z) return liquid_information and liquid_information.liquid_type == LiquidTypes.DEEP def get_destroy_packet(self): data = pack('<Q', self.guid) return PacketWriter.get_packet(OpCode.SMSG_DESTROY_OBJECT, data) @staticmethod def extract_high_guid(guid): return HighGuid(guid & (0xFFFF << 48))
def get_single_item_update_packet(self, item, requester): update_packet = UpdatePacketFactory.compress_if_needed( PacketWriter.get_packet(OpCode.SMSG_UPDATE_OBJECT, item.get_full_update_packet(requester))) return update_packet