def modify_emblem(self, player_mgr, emblem_style, emblem_color, border_style, border_color, background_color): self.guild.emblem_style = emblem_style self.guild.emblem_color = emblem_color self.guild.border_style = border_style self.guild.border_color = border_color self.guild.background_color = background_color self.update_db_guild() GuildManager.send_emblem_result( player_mgr, GuildEmblemResult.ERR_GUILDEMBLEM_SUCCESS) query_packet = self.build_guild_query() MapManager.send_surrounding(query_packet, player_mgr, include_self=True)
def receive_damage(self, amount, source=None): is_player = self.get_type() == ObjectTypes.TYPE_PLAYER new_health = self.health - amount if new_health <= 0: self.die(killer=source) else: self.set_health(new_health) # If unit is a creature and it's being attacked by another unit, automatically set combat target. if not self.combat_target and not is_player and source and source.get_type( ) != ObjectTypes.TYPE_GAMEOBJECT: self.attack(source) update_packet = self.generate_proper_update_packet(is_self=is_player) MapManager.send_surrounding(update_packet, self, include_self=is_player)
def respawn(self): super().respawn() self.set_health(self.max_health) self.set_mana(self.max_power_1) self.loot_manager.clear() self.set_lootable(False) if self.killed_by and self.killed_by.group_manager: self.killed_by.group_manager.clear_looters_for_victim(self) self.killed_by = None self.is_spawned = True self.respawn_timer = 0 self.respawn_time = randint(self.creature_instance.spawntimesecsmin, self.creature_instance.spawntimesecsmax) MapManager.send_surrounding(self.generate_proper_update_packet(create=True), self, include_self=False)
def end_duel(self, duel_winner_flag, duel_complete_flag, winner): if not self.arbiter or self.duel_state == DuelState.DUEL_STATE_FINISHED or not self.players: return if self.duel_state == DuelState.DUEL_STATE_STARTED: duel_complete_flag = DuelComplete.DUEL_FINISHED # Set this first to prevent next tick to trigger. self.duel_state = DuelState.DUEL_STATE_FINISHED if duel_winner_flag == DuelWinner.DUEL_WINNER_KNOCKOUT: # TODO: Should trigger EMOTE BEG on loser? # TODO: Should root loser for 3 secs? pass # Send either the duel ended by natural means or if it was canceled/interrupted packet = PacketWriter.get_packet(OpCode.SMSG_DUEL_COMPLETE, pack('<B', duel_complete_flag)) MapManager.send_surrounding(packet, self.arbiter) # Was not interrupted, broadcast duel result. if duel_complete_flag == DuelComplete.DUEL_FINISHED: winner_name_bytes = PacketWriter.string_to_bytes(winner.player.name) loser_name_bytes = PacketWriter.string_to_bytes(self.players[winner.guid].target.player.name) data = pack(f'<B{len(winner_name_bytes)}s{len(loser_name_bytes)}s', duel_winner_flag, winner_name_bytes, loser_name_bytes) packet = PacketWriter.get_packet(OpCode.SMSG_DUEL_WINNER, data) MapManager.send_surrounding(packet, self.arbiter) packet = PacketWriter.get_packet(OpCode.SMSG_CANCEL_COMBAT) for entry in self.players.values(): entry.player.enqueue_packet(packet) entry.player.leave_combat() self.build_update(entry.player) entry.player.spell_manager.remove_unit_from_all_cast_targets(entry.target.guid) entry.player.aura_manager.remove_harmful_auras_by_caster(entry.target.guid) # Clean up arbiter go and cleanup. MapManager.remove_object(self.arbiter) # Finally, flush this DualManager instance. self.flush()
def update(self): now = time.time() if now > self.last_tick > 0: elapsed = now - self.last_tick if self.is_alive: # Spell/aura updates self.spell_manager.update(now) self.aura_manager.update(elapsed) # Movement Updates self.movement_manager.update_pending_waypoints(elapsed) # Random Movement self._perform_random_movement(now) # Combat movement self._perform_combat_movement(now) # Attack update if self.combat_target and self.is_within_interactable_distance( self.combat_target): self.attack_update(elapsed) # Dead else: 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.is_spawned = False MapManager.send_surrounding(self.get_destroy_packet(), self, include_self=False) self.last_tick = now if self.dirty: MapManager.send_surrounding( self.generate_proper_update_packet(create=False), self, include_self=False) MapManager.update_object(self) self.reset_fields() self.set_dirty(is_dirty=False)
def update(self): now = time.time() if now > self.last_tick > 0: elapsed = now - self.last_tick if self.is_spawned: # Check "dirtiness" to determine if this game 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) # Not spawned. else: self.respawn_timer += elapsed if self.respawn_timer >= self.respawn_time and not self.is_summon: self.respawn() self.last_tick = now
def send_spell_cast_debug_info(self, damage_info, miss_reason, spell_id, healing=False, is_periodic=False): flags = SpellHitFlags.HIT_FLAG_HEALED if healing else SpellHitFlags.HIT_FLAG_DAMAGE if is_periodic: # Periodic damage/healing does not show in combat log - only on character frame. flags |= SpellHitFlags.HIT_FLAG_PERIODIC if miss_reason != SpellMissReason.MISS_REASON_NONE: combat_log_data = pack('<i2Q2i', flags, damage_info.attacker.guid, damage_info.target.guid, spell_id, miss_reason) combat_log_opcode = OpCode.SMSG_ATTACKERSTATEUPDATEDEBUGINFOSPELLMISS else: combat_log_data = pack( '<I2Q2If3I', flags, damage_info.attacker.guid, damage_info.target.guid, spell_id, damage_info.total_damage, damage_info.damage, damage_info.damage_school_mask, damage_info.damage, damage_info.absorb) combat_log_opcode = OpCode.SMSG_ATTACKERSTATEUPDATEDEBUGINFOSPELL MapManager.send_surrounding( PacketWriter.get_packet(combat_log_opcode, combat_log_data), self, include_self=self.get_type() == ObjectTypes.TYPE_PLAYER) if not healing: damage_data = pack('<Q2i2IQ', damage_info.target.guid, damage_info.total_damage, damage_info.damage, miss_reason, spell_id, damage_info.attacker.guid) MapManager.send_surrounding( PacketWriter.get_packet(OpCode.SMSG_DAMAGE_DONE, damage_data), self, include_self=self.get_type() == ObjectTypes.TYPE_PLAYER)
def send_attack_state_update(self, damage_info): data = pack( '<I2QIBIf7I', damage_info.hit_info, damage_info.attacker.guid, damage_info.target.guid, damage_info.total_damage, 1, # Sub damage count damage_info.damage_school_mask, damage_info.total_damage, damage_info.damage, damage_info.absorb, damage_info.target_state, damage_info.resist, 0, 0, damage_info.blocked_amount) MapManager.send_surrounding( PacketWriter.get_packet(OpCode.SMSG_ATTACKERSTATEUPDATE, data), self, include_self=self.get_type() == ObjectTypes.TYPE_PLAYER) # Damage effects self.deal_damage(damage_info.target, damage_info.total_damage)
def handle_partial_interrupt(self): if not self.spell_entry.InterruptFlags & SpellInterruptFlags.SPELL_INTERRUPT_FLAG_PARTIAL: return # TODO Did pushback resistance exist? curr_time = time.time() remaining_cast_before_pushback = self.cast_end_timestamp - curr_time if self.is_channeled() and self.cast_state == SpellState.SPELL_STATE_ACTIVE: channel_length = self.duration_entry.Duration/1000 # /1000 for seconds. final_opcode = OpCode.MSG_CHANNEL_UPDATE pushback_length_sec = min(remaining_cast_before_pushback, channel_length * 0.25) for effect in self.get_effects(): if remaining_cast_before_pushback <= pushback_length_sec: # Applied aura duration is not timestamp based so it's stored in milliseconds. # To avoid rounding issues, set to zero instead of subtracting if pushback leads to channel stop. effect.applied_aura_duration = 0 else: effect.applied_aura_duration -= pushback_length_sec * 1000 # aura duration is stored as millis. effect.remove_old_periodic_effect_ticks() self.cast_end_timestamp -= pushback_length_sec data = pack('<I', int((remaining_cast_before_pushback - pushback_length_sec)*1000)) # *1000 for millis. elif self.cast_state == SpellState.SPELL_STATE_CASTING: final_opcode = OpCode.SMSG_SPELL_DELAYED cast_progress_seconds = self.get_base_cast_time()/1000 - remaining_cast_before_pushback # base cast time in seconds. pushback_length_sec = min(cast_progress_seconds, 0.5) # Push back 0.5s or to beginning of cast. self.cast_end_timestamp += pushback_length_sec data = pack('<QI', self.spell_caster.guid, int(pushback_length_sec * 1000)) # *1000 for millis. else: return MapManager.send_surrounding(PacketWriter.get_packet(final_opcode, data), self.spell_caster, include_self=self.spell_caster.get_type_id() == ObjectTypeIds.ID_PLAYER)
def send_attack_start(self, victim_guid): data = pack('<2Q', self.guid, victim_guid) MapManager.send_surrounding( PacketWriter.get_packet(OpCode.SMSG_ATTACKSTART, data), self)
def handle_movement_status(world_session, socket, reader: PacketReader) -> int: # Avoid handling malformed movement packets, or handling them while no player or player teleporting. if world_session.player_mgr and len(reader.data) >= 48: try: transport_guid, transport_x, transport_y, transport_z, transport_o, x, y, z, o, pitch, flags = \ unpack('<Q9fI', reader.data[:48]) # Hacky way to prevent random teleports when colliding with elevators # Also acts as a rudimentary teleport cheat detection if not world_session.player_mgr.pending_taxi_destination and world_session.player_mgr.location.distance( x=x, y=y, z=z) > 64: Logger.anticheat( f'Preventing coordinate desync from player {world_session.player_mgr.player.name} ({world_session.player_mgr.guid}).' ) world_session.player_mgr.teleport( world_session.player_mgr.map_, world_session.player_mgr.location) return 0 # Send movement info to SpellManager until movement handling is merged to update system if flags & 0xF != 0: # MoveFlags.MOVEFLAG_MOVE_MASK | MoveFlags.MOVEFLAG_STRAFE_MASK world_session.player_mgr.spell_manager.flag_as_moved() world_session.player_mgr.transport_id = transport_guid world_session.player_mgr.transport.x = transport_x world_session.player_mgr.transport.y = transport_y world_session.player_mgr.transport.z = transport_z world_session.player_mgr.transport.o = transport_o world_session.player_mgr.location.x = x world_session.player_mgr.location.y = y world_session.player_mgr.location.z = z world_session.player_mgr.location.o = o world_session.player_mgr.pitch = pitch world_session.player_mgr.movement_flags = flags if flags & MoveFlags.MOVEFLAG_SPLINE_MOVER: world_session.player_mgr.movement_spline = MovementManager.MovementSpline.from_bytes( reader.data[48:]) movement_data = pack(f'<Q{len(reader.data)}s', world_session.player_mgr.guid, reader.data) MapManager.send_surrounding(PacketWriter.get_packet( OpCode(reader.opcode), movement_data), world_session.player_mgr, include_self=False) MapManager.update_object(world_session.player_mgr) world_session.player_mgr.sync_player() # Get up if you jump while not standing if reader.opcode == OpCode.MSG_MOVE_JUMP and \ world_session.player_mgr.stand_state != StandState.UNIT_DEAD and \ world_session.player_mgr.stand_state != StandState.UNIT_STANDING: world_session.player_mgr.stand_state = StandState.UNIT_STANDING world_session.player_mgr.set_dirty() except (AttributeError, error): Logger.error( f'Error while handling {OpCode(reader.opcode).name}, skipping. Data: {reader.data}' ) return 0
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 send_custom_animation(self, animation): data = pack('<QI', self.guid, animation) packet = PacketWriter.get_packet(OpCode.SMSG_GAMEOBJECT_CUSTOM_ANIM, data) MapManager.send_surrounding(packet, self, include_self=False)
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 handle_movement_status(world_session, socket, reader: PacketReader) -> int: # Avoid handling malformed movement packets, or handling them while no player or player teleporting. if world_session.player_mgr and len(reader.data) >= 48: try: transport_guid, transport_x, transport_y, transport_z, transport_o, x, y, z, o, pitch, flags = \ unpack('<Q9fI', reader.data[:48]) # Hacky way to prevent random teleports when colliding with elevators # Also acts as a rudimentary teleport cheat detection if not world_session.player_mgr.pending_taxi_destination and \ world_session.player_mgr.location.distance(x=x, y=y, z=z) > 64: Logger.anticheat( f'Preventing coordinate desync from player {world_session.player_mgr.player.name} ' f'({world_session.player_mgr.guid}).') world_session.player_mgr.teleport( world_session.player_mgr.map_, world_session.player_mgr.location, is_instant=True) return 0 jumped = reader.opcode == OpCode.MSG_MOVE_JUMP # Movement and jump actions. if flags & (MoveFlags.MOVEFLAG_MOVE_MASK | MoveFlags.MOVEFLAG_STRAFE_MASK) or jumped: # Don't mark player as moved if only jumping. if not jumped: world_session.player_mgr.set_has_moved(True) # Cancel looting if moved. if world_session.player_mgr.loot_selection: world_session.player_mgr.send_loot_release( world_session.player_mgr.loot_selection) world_session.player_mgr.spell_manager.check_spell_interrupts( moved=True) world_session.player_mgr.aura_manager.check_aura_interrupts( moved=True) # Turn actions. if flags & MoveFlags.MOVEFLAG_TURN_MASK: world_session.player_mgr.spell_manager.check_spell_interrupts( turned=True) world_session.player_mgr.transport_id = transport_guid world_session.player_mgr.transport.x = transport_x world_session.player_mgr.transport.y = transport_y world_session.player_mgr.transport.z = transport_z world_session.player_mgr.transport.o = transport_o world_session.player_mgr.location.x = x world_session.player_mgr.location.y = y world_session.player_mgr.location.z = z world_session.player_mgr.location.o = o world_session.player_mgr.pitch = pitch world_session.player_mgr.movement_flags = flags if flags & MoveFlags.MOVEFLAG_SPLINE_MOVER: world_session.player_mgr.movement_spline = MovementManager.MovementSpline.from_bytes( reader.data[48:]) # Broadcast player movement to surroundings. movement_data = pack(f'<Q{len(reader.data)}s', world_session.player_mgr.guid, reader.data) movement_packet = PacketWriter.get_packet( OpCode(reader.opcode), movement_data) MapManager.send_surrounding(movement_packet, world_session.player_mgr, include_self=False) # Get up if you jump while not standing. if jumped and world_session.player_mgr.stand_state != StandState.UNIT_DEAD and \ world_session.player_mgr.stand_state != StandState.UNIT_STANDING: world_session.player_mgr.set_stand_state( StandState.UNIT_STANDING) except (AttributeError, error): Logger.error( f'Error while handling {OpCode(reader.opcode).name}, skipping. Data: {reader.data}' ) return 0