2024-07-22 08:52:28 -04:00
/*
EQ2Emulator : Everquest II Server Emulator
Copyright ( C ) 2007 EQ2EMulator Development Team ( http : //www.eq2emulator.net)
This file is part of EQ2Emulator .
EQ2Emulator is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
EQ2Emulator is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with EQ2Emulator . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "Spawn.h"
# include <stdio.h>
# include "../common/timer.h"
# include <time.h>
# include <math.h>
# include "Entity.h"
# include "Widget.h"
# include "Sign.h"
# include "../common/MiscFunctions.h"
# include "../common/Log.h"
# include "Rules/Rules.h"
# include "World.h"
# include "LuaInterface.h"
# include "Bots/Bot.h"
# include "Zone/raycast_mesh.h"
# include "RaceTypes/RaceTypes.h"
# include "VisualStates.h"
extern ConfigReader configReader ;
extern RuleManager rule_manager ;
extern World world ;
extern ZoneList zone_list ;
extern MasterRaceTypeList race_types_list ;
extern LuaInterface * lua_interface ;
extern VisualStates visual_states ;
Spawn : : Spawn ( ) {
loot_coins = 0 ;
trap_triggered = false ;
trap_state = 0 ;
chest_drop_time = 0 ;
trap_opened_time = 0 ;
group_id = 0 ;
size_offset = 0 ;
merchant_id = 0 ;
merchant_type = 0 ;
merchant_min_level = 0 ;
merchant_max_level = 0 ;
memset ( & appearance , 0 , sizeof ( AppearanceData ) ) ;
memset ( & basic_info , 0 , sizeof ( BasicInfoStruct ) ) ;
appearance . pos . state = 0x4080 ;
appearance . difficulty = 6 ;
size = 32 ;
appearance . pos . collision_radius = 32 ;
id = Spawn : : NextID ( ) ;
oversized_packet = 0xFF ;
zone = 0 ;
spawn_location_id = 0 ;
spawn_entry_id = 0 ;
spawn_location_spawns_id = 0 ;
respawn = 0 ;
expire_time = 0 ;
expire_offset = 0 ;
x_offset = 0 ;
y_offset = 0 ;
z_offset = 0 ;
database_id = 0 ;
packet_num = 1 ;
changed = false ;
vis_changed = false ;
position_changed = false ;
send_spawn_changes = true ;
info_changed = false ;
appearance . pos . Speed1 = 0 ;
last_attacker = 0 ;
faction_id = 0 ;
running_to = 0 ;
tmp_visual_state = - 1 ;
tmp_action_state = - 1 ;
transporter_id = 0 ;
invulnerable = false ;
spawn_group_list = 0 ;
MSpawnGroup = 0 ;
movement_locations = 0 ;
target = 0 ;
primary_command_list_id = 0 ;
secondary_command_list_id = 0 ;
is_pet = false ;
m_followTarget = 0 ;
following = false ;
req_quests_continued_access = false ;
req_quests_override = 0 ;
req_quests_private = false ;
m_illusionModel = 0 ;
Cell_Info . CurrentCell = nullptr ;
Cell_Info . CellListIndex = - 1 ;
m_addedToWorldTimestamp = 0 ;
m_spawnAnim = 0 ;
m_spawnAnimLeeway = 0 ;
m_Update . SetName ( " Spawn::m_Update " ) ;
m_requiredHistory . SetName ( " Spawn::m_requiredHistory " ) ;
m_requiredQuests . SetName ( " Spawn::m_requiredQuests " ) ;
last_heading_angle = 0.0 ;
last_grid_update = 0 ;
last_location_update = 0.0 ;
last_movement_update = Timer : : GetCurrentTime2 ( ) ;
forceMapCheck = false ;
m_followDistance = 0 ;
MCommandMutex . SetName ( " Entity::MCommandMutex " ) ;
has_spawn_proximities = false ;
pickup_item_id = 0 ;
pickup_unique_item_id = 0 ;
disable_sounds = false ;
has_quests_required = false ;
has_history_required = false ;
is_flying_creature = false ;
is_water_creature = false ;
region_map = nullptr ;
current_map = nullptr ;
RegionMutex . SetName ( " Spawn::RegionMutex " ) ;
pause_timer . Disable ( ) ;
m_SpawnMutex . SetName ( " Spawn::SpawnMutex " ) ;
appearance_equipment_list . SetAppearanceType ( 1 ) ;
is_transport_spawn = false ;
rail_id = 0 ;
is_omitted_by_db_flag = false ;
loot_tier = 0 ;
loot_drop_type = 0 ;
deleted_spawn = false ;
is_collector = false ;
trigger_widget_id = 0 ;
scared_by_strong_players = false ;
is_alive = true ;
SetLockedNoLoot ( ENCOUNTER_STATE_AVAILABLE ) ;
loot_method = GroupLootMethod : : METHOD_FFA ;
loot_rarity = 0 ;
loot_group_id = 0 ;
looter_spawn_id = 0 ;
is_loot_complete = false ;
is_loot_dispensed = false ;
reset_movement = false ;
2024-12-19 13:02:18 -05:00
respawn_offset_low = 0 ;
respawn_offset_high = 0 ;
2024-12-23 08:46:11 -05:00
duplicated_spawn = true ;
2024-07-22 08:52:28 -04:00
ResetKnockedBack ( ) ;
}
Spawn : : ~ Spawn ( ) {
is_running = false ;
vector < Item * > : : iterator itr ;
for ( itr = loot_items . begin ( ) ; itr ! = loot_items . end ( ) ; itr + + )
safe_delete ( * itr ) ;
loot_items . clear ( ) ;
RemovePrimaryCommands ( ) ;
for ( int32 i = 0 ; i < secondary_command_list . size ( ) ; i + + ) {
safe_delete ( secondary_command_list [ i ] ) ;
}
secondary_command_list . clear ( ) ;
RemoveSpawnFromGroup ( ) ;
MMovementLocations . lock ( ) ;
if ( movement_locations ) {
while ( movement_locations - > size ( ) ) {
safe_delete ( movement_locations - > front ( ) ) ;
movement_locations - > pop_front ( ) ;
}
safe_delete ( movement_locations ) ;
}
MMovementLocations . unlock ( ) ;
MMovementLoop . lock ( ) ;
for ( int32 i = 0 ; i < movement_loop . size ( ) ; i + + )
safe_delete ( movement_loop . at ( i ) ) ;
movement_loop . clear ( ) ;
MMovementLoop . unlock ( ) ;
m_requiredHistory . writelock ( __FUNCTION__ , __LINE__ ) ;
map < int32 , LUAHistory * > : : iterator lua_itr ;
for ( lua_itr = required_history . begin ( ) ; lua_itr ! = required_history . end ( ) ; lua_itr + + ) {
safe_delete ( lua_itr - > second ) ;
}
required_history . clear ( ) ;
m_requiredHistory . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
m_requiredQuests . writelock ( __FUNCTION__ , __LINE__ ) ;
map < int32 , vector < int16 > * > : : iterator rq_itr ;
for ( rq_itr = required_quests . begin ( ) ; rq_itr ! = required_quests . end ( ) ; rq_itr + + ) {
safe_delete ( rq_itr - > second ) ;
}
required_quests . clear ( ) ;
m_requiredQuests . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
// just in case to make sure data is destroyed
RemoveSpawnProximities ( ) ;
Regions . clear ( ) ;
}
void Spawn : : RemovePrimaryCommands ( )
{
for ( int32 i = 0 ; i < primary_command_list . size ( ) ; i + + ) {
safe_delete ( primary_command_list [ i ] ) ;
}
primary_command_list . clear ( ) ;
}
void Spawn : : InitializeHeaderPacketData ( Player * player , PacketStruct * header , int16 index ) {
header - > setDataByName ( " index " , index ) ;
if ( GetSpawnAnim ( ) > 0 & & Timer : : GetCurrentTime2 ( ) < ( GetAddedToWorldTimestamp ( ) + GetSpawnAnimLeeway ( ) ) ) {
if ( header - > GetVersion ( ) > = 57080 )
header - > setDataByName ( " spawn_anim " , GetSpawnAnim ( ) ) ;
else
header - > setDataByName ( " spawn_anim " , ( int16 ) GetSpawnAnim ( ) ) ;
}
else {
if ( header - > GetVersion ( ) > = 57080 )
header - > setDataByName ( " spawn_anim " , 0xFFFFFFFF ) ;
else
header - > setDataByName ( " spawn_anim " , 0xFFFF ) ;
}
if ( primary_command_list . size ( ) > 0 ) {
if ( primary_command_list . size ( ) > 1 ) {
header - > setArrayLengthByName ( " command_list " , primary_command_list . size ( ) ) ;
for ( int32 i = 0 ; i < primary_command_list . size ( ) ; i + + ) {
header - > setArrayDataByName ( " command_list_name " , primary_command_list [ i ] - > name . c_str ( ) , i ) ;
header - > setArrayDataByName ( " command_list_max_distance " , primary_command_list [ i ] - > distance , i ) ;
header - > setArrayDataByName ( " command_list_error " , primary_command_list [ i ] - > error_text . c_str ( ) , i ) ;
header - > setArrayDataByName ( " command_list_command " , primary_command_list [ i ] - > command . c_str ( ) , i ) ;
}
}
if ( header - > GetVersion ( ) < = 561 ) {
header - > setMediumStringByName ( " default_command " , primary_command_list [ 0 ] - > name . c_str ( ) ) ;
}
else
header - > setMediumStringByName ( " default_command " , primary_command_list [ 0 ] - > command . c_str ( ) ) ;
header - > setDataByName ( " max_distance " , primary_command_list [ 0 ] - > distance ) ;
}
if ( spawn_group_list & & MSpawnGroup ) {
MSpawnGroup - > readlock ( __FUNCTION__ , __LINE__ ) ;
header - > setArrayLengthByName ( " group_size " , spawn_group_list - > size ( ) ) ;
vector < Spawn * > : : iterator itr ;
int i = 0 ;
for ( itr = spawn_group_list - > begin ( ) ; itr ! = spawn_group_list - > end ( ) ; itr + + , i + + ) {
int32 idx = 0 ;
idx = player - > GetIDWithPlayerSpawn ( ( * itr ) ) ;
header - > setArrayDataByName ( " group_spawn_id " , idx , i ) ;
}
MSpawnGroup - > releasereadlock ( __FUNCTION__ , __LINE__ ) ;
}
header - > setDataByName ( " spawn_id " , player - > GetIDWithPlayerSpawn ( this ) ) ;
header - > setDataByName ( " crc " , 1 ) ;
header - > setDataByName ( " time_stamp " , Timer : : GetCurrentTime2 ( ) ) ;
}
void Spawn : : InitializeVisPacketData ( Player * player , PacketStruct * vis_packet ) {
int16 version = vis_packet - > GetVersion ( ) ;
//why?
/*if (IsPlayer()) {
appearance . pos . grid_id = 0xFFFFFFFF ;
} */
int8 tag_icon = 0 ;
int32 tmp_id = 0 ;
if ( faction_id & & ( tag_icon = player - > MatchGMVisualFilter ( GMTagFilterType : : GMFILTERTYPE_FACTION , faction_id , " " , true ) ) > 0 ) ;
else if ( IsGroundSpawn ( ) & & ( tag_icon = player - > MatchGMVisualFilter ( GMTagFilterType : : GMFILTERTYPE_GROUNDSPAWN , 1 , " " , true ) ) > 0 ) ;
else if ( ( this - > GetSpawnGroupID ( ) & & ( tag_icon = player - > MatchGMVisualFilter ( GMTagFilterType : : GMFILTERTYPE_SPAWNGROUP , 1 , " " , true ) ) > 0 ) | |
( ! this - > GetSpawnGroupID ( ) & & ( tag_icon = player - > MatchGMVisualFilter ( GMTagFilterType : : GMFILTERTYPE_SPAWNGROUP , 0 , " " , true ) ) > 0 ) ) ;
else if ( ( this - > GetRace ( ) & & ( tag_icon = player - > MatchGMVisualFilter ( GMTagFilterType : : GMFILTERTYPE_RACE , GetRace ( ) , " " , true ) ) > 0 ) ) ;
else if ( ( ( tmp_id = race_types_list . GetRaceType ( GetModelType ( ) ) > 0 ) & & ( tag_icon = player - > MatchGMVisualFilter ( GMTagFilterType : : GMFILTERTYPE_RACE , tmp_id , " " , true ) ) > 0 ) ) ;
else if ( ( ( tmp_id = race_types_list . GetRaceBaseType ( GetModelType ( ) ) > 0 ) & & ( tag_icon = player - > MatchGMVisualFilter ( GMTagFilterType : : GMFILTERTYPE_RACE , tmp_id , " " , true ) ) > 0 ) ) ;
else if ( IsEntity ( ) & & ( tag_icon = ( ( Entity * ) this ) - > GetInfoStruct ( ) - > get_tag1 ( ) ) > 0 ) ;
vis_packet - > setDataByName ( " tag1 " , tag_icon ) ;
if ( IsPlayer ( ) )
vis_packet - > setDataByName ( " player " , 1 ) ;
if ( version < = 561 ) {
vis_packet - > setDataByName ( " targetable " , appearance . targetable ) ;
vis_packet - > setDataByName ( " show_name " , appearance . display_name ) ;
vis_packet - > setDataByName ( " attackable " , appearance . attackable ) ;
if ( appearance . attackable = = 1 )
vis_packet - > setDataByName ( " attackable_icon " , 1 ) ;
if ( IsPlayer ( ) ) {
if ( ( ( Player * ) this ) - > IsGroupMember ( player ) )
vis_packet - > setDataByName ( " group_member " , 1 ) ;
}
}
if ( appearance . targetable = = 1 | | appearance . show_level = = 1 | | appearance . display_name = = 1 ) {
if ( ! IsGroundSpawn ( ) ) {
int8 arrow_color = ARROW_COLOR_WHITE ;
sint8 npc_con = player - > GetFactions ( ) - > GetCon ( faction_id ) ;
if ( IsPlayer ( ) & & ! ( ( Player * ) this ) - > CanSeeInvis ( player ) )
npc_con = 0 ;
else if ( ! IsPlayer ( ) & & IsEntity ( ) & & ! ( ( Entity * ) this ) - > CanSeeInvis ( player ) )
npc_con = 0 ;
if ( appearance . attackable = = 1 )
arrow_color = player - > GetArrowColor ( GetLevel ( ) ) ;
if ( version < = 373 ) {
if ( GetMerchantID ( ) > 0 )
arrow_color + = 7 ;
else {
if ( primary_command_list . size ( ) > 0 ) {
int16 len = strlen ( primary_command_list [ 0 ] - > command . c_str ( ) ) ;
if ( len > = 4 & & strncmp ( primary_command_list [ 0 ] - > command . c_str ( ) , " bank " , 4 ) = = 0 )
arrow_color + = 14 ;
else if ( len > = 4 & & strncmp ( primary_command_list [ 0 ] - > command . c_str ( ) , " hail " , 4 ) = = 0 )
arrow_color + = 21 ;
else if ( len > = 6 & & strncmp ( primary_command_list [ 0 ] - > command . c_str ( ) , " attack " , 6 ) = = 0 ) {
if ( arrow_color > 5 )
arrow_color = 34 ;
else
arrow_color + = 29 ;
}
}
}
}
if ( IsNPC ( ) & & ( ( ( Entity * ) this ) - > GetLockedNoLoot ( ) = = ENCOUNTER_STATE_BROKEN | |
( ( ( Entity * ) this ) - > GetLockedNoLoot ( ) = = ENCOUNTER_STATE_OVERMATCHED ) | |
( ( Entity * ) this ) - > GetLockedNoLoot ( ) = = ENCOUNTER_STATE_LOCKED & & ! ( ( NPC * ) this ) - > Brain ( ) - > IsEntityInEncounter ( player - > GetID ( ) ) ) ) {
vis_packet - > setDataByName ( " arrow_color " , ARROW_COLOR_GRAY ) ;
}
else {
vis_packet - > setDataByName ( " arrow_color " , arrow_color ) ;
}
if ( appearance . attackable = = 0 | | IsPlayer ( ) | | IsBot ( ) | | ( IsEntity ( ) & & ( ( Entity * ) this ) - > GetOwner ( ) & &
( ( ( Entity * ) this ) - > GetOwner ( ) - > IsPlayer ( ) | | ( ( Entity * ) this ) - > GetOwner ( ) - > IsBot ( ) ) ) ) {
vis_packet - > setDataByName ( " locked_no_loot " , 1 ) ;
}
else {
vis_packet - > setDataByName ( " locked_no_loot " , appearance . locked_no_loot ) ;
}
if ( player - > GetArrowColor ( GetLevel ( ) ) = = ARROW_COLOR_GRAY )
if ( npc_con = = - 4 )
npc_con = - 3 ;
vis_packet - > setDataByName ( " npc_con " , npc_con ) ;
if ( appearance . attackable = = 1 & & IsNPC ( ) & & ( player - > GetFactions ( ) - > GetCon ( faction_id ) < = - 4 | | ( ( NPC * ) this ) - > Brain ( ) - > GetHate ( player ) > 1 ) ) {
vis_packet - > setDataByName ( " npc_hate " , ( ( NPC * ) this ) - > Brain ( ) - > GetHatePercentage ( player ) ) ;
vis_packet - > setDataByName ( " show_difficulty_arrows " , 1 ) ;
}
int8 quest_flag = player - > CheckQuestFlag ( this ) ;
if ( version < 1188 & & quest_flag > = 16 )
quest_flag = 1 ;
vis_packet - > setDataByName ( " quest_flag " , quest_flag ) ;
if ( player - > HasQuestUpdateRequirement ( this ) ) {
vis_packet - > setDataByName ( " name_quest_icon " , 1 ) ;
}
}
}
int8 vis_flags = 0 ;
if ( MeetsSpawnAccessRequirements ( player ) ) {
if ( appearance . attackable = = 1 )
vis_flags + = 64 ; //attackable icon
if ( appearance . show_level = = 1 )
vis_flags + = 32 ;
if ( appearance . display_name = = 1 )
vis_flags + = 16 ;
if ( IsPlayer ( ) | | appearance . targetable = = 1 )
vis_flags + = 4 ;
if ( appearance . show_command_icon = = 1 )
vis_flags + = 2 ;
if ( this = = player ) {
//if (version <= 283) {
// vis_flags = 1;
//}
//else
vis_flags + = 1 ;
}
}
else if ( req_quests_override > 0 )
{
//Check to see if there's an override value set
vis_flags = req_quests_override & 0xFF ;
}
if ( player - > HasGMVision ( ) )
{
if ( ( vis_flags & 16 ) = = 0 & & appearance . display_name = = 0 )
vis_flags + = 16 ;
if ( ( vis_flags & 4 ) = = 0 )
vis_flags + = 4 ;
}
2024-12-12 21:06:51 -05:00
if ( version < 561 & & ( vis_flags > 1 | | appearance . display_hand_icon > 0 ) & & MeetsSpawnAccessRequirements ( player ) ) //interactable
2024-07-22 08:52:28 -04:00
vis_flags = 1 ;
2024-12-12 21:06:51 -05:00
else if ( version > = 561 & & ( vis_flags > 1 | | appearance . display_hand_icon > 0 ) & & MeetsSpawnAccessRequirements ( player ) )
2024-12-07 11:21:44 -05:00
vis_flags = vis_flags ;
2024-12-01 07:42:16 -05:00
else if ( ( req_quests_override & 256 ) > 0 ) {
if ( vis_flags > 1 )
vis_flags = 1 ;
}
2024-12-07 11:21:44 -05:00
else if ( ! player - > HasGMVision ( ) ) {
vis_flags = 0 ;
}
2024-07-22 08:52:28 -04:00
if ( MeetsSpawnAccessRequirements ( player ) ) {
vis_packet - > setDataByName ( " hand_flag " , appearance . display_hand_icon ) ;
}
2024-12-01 07:42:16 -05:00
else if ( ( req_quests_override & 256 ) > 0 ) {
2024-07-22 08:52:28 -04:00
vis_packet - > setDataByName ( " hand_flag " , 1 ) ;
}
2024-12-01 07:42:16 -05:00
vis_packet - > setDataByName ( " vis_flags " , vis_flags ) ;
2024-07-22 08:52:28 -04:00
if ( ( version = = 546 | | version = = 561 ) & & GetMerchantID ( ) > 0 ) {
vis_packet - > setDataByName ( " guild " , " <Merchant> " ) ;
}
}
void Spawn : : InitializeFooterPacketData ( Player * player , PacketStruct * footer ) {
if ( IsWidget ( ) ) {
Widget * widget = ( Widget * ) this ;
if ( widget - > GetMultiFloorLift ( ) ) {
footer - > setDataByName ( " widget_x " , widget - > GetX ( ) ) ;
footer - > setDataByName ( " widget_y " , widget - > GetY ( ) ) ;
footer - > setDataByName ( " widget_z " , widget - > GetZ ( ) ) ;
}
else {
footer - > setDataByName ( " widget_x " , widget - > GetWidgetX ( ) ) ;
footer - > setDataByName ( " widget_y " , widget - > GetWidgetY ( ) ) ;
footer - > setDataByName ( " widget_z " , widget - > GetWidgetZ ( ) ) ;
}
footer - > setDataByName ( " widget_id " , widget - > GetWidgetID ( ) ) ;
}
else if ( IsSign ( ) ) {
Sign * sign = ( Sign * ) this ;
footer - > setDataByName ( " widget_id " , sign - > GetWidgetID ( ) ) ;
footer - > setDataByName ( " widget_x " , sign - > GetWidgetX ( ) ) ;
footer - > setDataByName ( " widget_y " , sign - > GetWidgetY ( ) ) ;
footer - > setDataByName ( " widget_z " , sign - > GetWidgetZ ( ) ) ;
int8 showSignText = 1 ;
2024-12-01 13:37:35 -05:00
if ( ( HasQuestsRequired ( ) | | HasHistoryRequired ( ) ) & & ! MeetsSpawnAccessRequirements ( player ) & & ( req_quests_override & 512 ) > 0 ) {
2024-07-22 08:52:28 -04:00
showSignText = 0 ;
}
if ( sign - > GetSignTitle ( ) )
footer - > setMediumStringByName ( " title " , sign - > GetSignTitle ( ) ) ;
if ( sign - > GetSignDescription ( ) )
footer - > setMediumStringByName ( " description " , sign - > GetSignDescription ( ) ) ;
footer - > setDataByName ( " sign_distance " , sign - > GetSignDistance ( ) ) ;
footer - > setDataByName ( " show " , showSignText ) ;
// in live we see that the language is set when the player does not have it, otherwise its left as 00's.
if ( ! player - > HasLanguage ( sign - > GetLanguage ( ) ) ) {
footer - > setDataByName ( " language " , sign - > GetLanguage ( ) ) ;
}
}
if ( IsPlayer ( ) )
footer - > setDataByName ( " is_player " , 1 ) ;
if ( strlen ( appearance . name ) < 1 )
strncpy ( appearance . name , to_string ( GetID ( ) ) . c_str ( ) , 128 ) ;
2024-12-12 15:38:27 -05:00
if ( footer - > GetVersion ( ) > 561 ) {
footer - > setMediumStringByName ( " name " , appearance . name ) ;
footer - > setMediumStringByName ( " guild " , appearance . sub_title ) ;
footer - > setMediumStringByName ( " prefix " , appearance . prefix_title ) ;
footer - > setMediumStringByName ( " suffix " , appearance . suffix_title ) ;
footer - > setMediumStringByName ( " last_name " , appearance . last_name ) ;
if ( appearance . attackable = = 0 & & GetLevel ( ) > 0 )
footer - > setDataByName ( " spawn_type " , 1 ) ;
else if ( appearance . attackable = = 0 )
footer - > setDataByName ( " spawn_type " , 6 ) ;
else
footer - > setDataByName ( " spawn_type " , 3 ) ;
}
else {
footer - > setDataByName ( " guild " , appearance . sub_title ) ;
footer - > setDataByName ( " prefix " , appearance . prefix_title ) ;
footer - > setDataByName ( " suffix " , appearance . suffix_title ) ;
footer - > setDataByName ( " last_name " , appearance . last_name ) ;
}
2024-07-22 08:52:28 -04:00
}
EQ2Packet * Spawn : : spawn_serialize ( Player * player , int16 version , int16 offset , int32 value , int16 offset2 , int16 offset3 , int16 offset4 , int32 value2 ) {
// If spawn is NPC AND is pet && owner is a player && owner is the player passed to this function && player's char sheet pet id is 0
m_Update . writelock ( __FUNCTION__ , __LINE__ ) ;
int16 index = 0 ;
if ( ( index = player - > GetIndexForSpawn ( this ) ) > 0 ) {
player - > SetSpawnMapIndex ( this , index ) ;
}
else {
index = player - > SetSpawnMapAndIndex ( this ) ;
}
// Jabantiz - [Bug] Client Crash on Revive
if ( player - > GetIDWithPlayerSpawn ( this ) = = 0 ) {
player - > SetSpawnMap ( this ) ;
}
PacketStruct * header = player - > GetSpawnHeaderStruct ( ) ;
2025-03-04 21:58:50 -05:00
if ( ! header ) {
LogWrite ( SPAWN__ERROR , 0 , " Spawn " , " Spawn::spawn_serialize: GetSpawnHeaderStruct returned null! " ) ;
player - > GetZone ( ) - > SetSpawnStructs ( player - > GetClient ( ) ) ;
header = player - > GetSpawnHeaderStruct ( ) ;
if ( ! header ) {
LogWrite ( SPAWN__ERROR , 0 , " Spawn " , " Spawn::spawn_serialize: GetSpawnHeaderStruct still null likely to crash! " ) ;
return nullptr ;
}
}
2024-07-22 08:52:28 -04:00
header - > ResetData ( ) ;
InitializeHeaderPacketData ( player , header , index ) ;
PacketStruct * footer = 0 ;
if ( IsWidget ( ) )
footer = player - > GetWidgetFooterStruct ( ) ;
else if ( IsSign ( ) )
footer = player - > GetSignFooterStruct ( ) ;
else if ( version > 561 )
footer = player - > GetSpawnFooterStruct ( ) ;
if ( footer ) {
footer - > ResetData ( ) ;
InitializeFooterPacketData ( player , footer ) ;
}
PacketStruct * vis_struct = player - > GetSpawnVisStruct ( ) ;
PacketStruct * info_struct = player - > GetSpawnInfoStruct ( ) ;
PacketStruct * pos_struct = player - > GetSpawnPosStruct ( ) ;
player - > info_mutex . writelock ( __FUNCTION__ , __LINE__ ) ;
player - > vis_mutex . writelock ( __FUNCTION__ , __LINE__ ) ;
player - > pos_mutex . writelock ( __FUNCTION__ , __LINE__ ) ;
info_struct - > ResetData ( ) ;
InitializeInfoPacketData ( player , info_struct ) ;
vis_struct - > ResetData ( ) ;
InitializeVisPacketData ( player , vis_struct ) ;
pos_struct - > ResetData ( ) ;
InitializePosPacketData ( player , pos_struct ) ;
2024-12-12 15:38:27 -05:00
2024-07-22 08:52:28 -04:00
if ( version < = 283 ) {
if ( offset = = 777 ) {
info_struct - > setDataByName ( " name " , " This is a really long name \n " ) ;
info_struct - > setDataByName ( " last_name " , " This is a really long LAST name \n " ) ;
info_struct - > setDataByName ( " name_suffix " , " This is a really long SUFFIX \n " ) ;
info_struct - > setDataByName ( " name_prefix " , " This is a really long PREFIX \n " ) ;
info_struct - > setDataByName ( " unknown " , " This is a really long UNKNOWN \n " ) ;
info_struct - > setDataByName ( " second_suffix " , " This is a really long 2nd SUFFIX \n " ) ;
}
//info_struct->setDataByName("unknown2", 3, 0); // level
//info_struct->setDataByName("unknown2", 1, 1); //unknown, two down arrows
//info_struct->setDataByName("unknown2", 1, 2); //unknown
//info_struct->setDataByName("unknown2", 1, 3); //unknown
//info_struct->setDataByName("unknown2", 1, 4); //solo fight
//info_struct->setDataByName("unknown2", 1, 5); //unknown
//info_struct->setDataByName("unknown2", 1, 6); //unknown
//info_struct->setDataByName("unknown2", 1, 7); //merchant
//info_struct->setDataByName("unknown2", 1, 8); //unknown
//info_struct->setDataByName("unknown2", 1, 9); //unknown
//info_struct->setDataByName("unknown2", 1, 10);
//112: 00 00 00 01 02 03 04 05 - 06 07 08 09 0A 00 00 00 | ................ merchant, x4
//112: 00 00 00 01 02 03 04 05 - 00 00 00 00 00 00 00 00 | ................ x4, epic, indifferent
//info_struct->setDataByName("body_size", 42);
//for(int i=0;i<8;i++)
// info_struct->setDataByName("persistent_spell_visuals", 329, i);
//info_struct->setDataByName("persistent_spell_levels", 20);
}
string * vis_data = vis_struct - > serializeString ( ) ;
string * pos_data = pos_struct - > serializeString ( ) ;
string * info_data = info_struct - > serializeString ( ) ;
int16 part2_size = pos_data - > length ( ) + vis_data - > length ( ) + info_data - > length ( ) ;
uchar * part2 = new uchar [ part2_size ] ;
player - > AddSpawnPosPacketForXOR ( id , ( uchar * ) pos_data - > c_str ( ) , pos_data - > length ( ) ) ;
player - > AddSpawnVisPacketForXOR ( id , ( uchar * ) vis_data - > c_str ( ) , vis_data - > length ( ) ) ;
player - > AddSpawnInfoPacketForXOR ( id , ( uchar * ) info_data - > c_str ( ) , info_data - > length ( ) ) ;
int32 vislength = vis_data - > length ( ) ;
int32 poslength = pos_data - > length ( ) ;
int32 infolength = info_data - > length ( ) ;
uchar * ptr = part2 ;
memcpy ( ptr , pos_data - > c_str ( ) , pos_data - > length ( ) ) ;
ptr + = pos_data - > length ( ) ;
memcpy ( ptr , vis_data - > c_str ( ) , vis_data - > length ( ) ) ;
ptr + = vis_data - > length ( ) ;
memcpy ( ptr , info_data - > c_str ( ) , info_data - > length ( ) ) ;
player - > pos_mutex . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
player - > info_mutex . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
player - > vis_mutex . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
string * part1 = header - > serializeString ( ) ;
string * part3 = 0 ;
if ( footer )
part3 = footer - > serializeString ( ) ;
//uchar blah7[] = {0x01,0x01,0x00,0x00,0x01,0x01,0x00,0x01,0x01,0x00 };
//uchar blah7[] = { 0x03,0x01,0x00,0x01,0x01,0x01,0x00,0x01,0x01,0x00 }; base
//uchar blah7[] = { 0x03,0x00,0x00,0x01,0x01,0x01,0x00,0x01,0x01,0x00 }; //no change
//uchar blah7[] = { 0x03,0x00,0x00,0x00,0x01,0x01,0x00,0x01,0x01,0x00 }; //blue instead of green
//uchar blah7[] = { 0x03,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x01,0x00 }; //no change
//uchar blah7[] = { 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x00 }; //not selectable
//uchar blah7[] = { 0x03,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x01,0x00 }; //no change
//uchar blah7[] = { 0x03,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00 }; //no longer have the two down arrows
//uchar blah7[] = { 0x01,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00 }; //arrow color green instead of white/gray
//memcpy(part2 + 77, blah7, sizeof(blah7));
//DumpPacket(part2, 885);
if ( offset > 0 ) {
if ( offset4 > 0 & & offset4 > = offset3 ) {
uchar * ptr2 = ( uchar * ) part2 ;
ptr2 + = offset3 ;
while ( offset4 > = offset3 ) {
int8 jumpsize = 1 ;
if ( value2 > 0xFFFF ) {
memcpy ( ptr2 , ( uchar * ) & value2 , 4 ) ;
jumpsize = 4 ;
}
else if ( value2 > 0xFF ) {
memcpy ( ptr2 , ( uchar * ) & value2 , 2 ) ;
jumpsize = 2 ;
}
else {
memcpy ( ptr2 , ( uchar * ) & value2 , 1 ) ;
jumpsize = 1 ;
}
ptr2 + = jumpsize ;
offset4 - = jumpsize ;
}
}
if ( offset2 > 0 & & offset2 > = offset ) {
uchar * ptr2 = ( uchar * ) part2 ;
ptr2 + = offset ;
while ( offset2 > = offset ) {
int8 jumpsize = 1 ;
if ( value > 0xFFFF ) {
memcpy ( ptr2 , ( uchar * ) & value , 4 ) ;
jumpsize = 4 ;
}
else if ( value > 0xFF ) {
memcpy ( ptr2 , ( uchar * ) & value , 2 ) ;
jumpsize = 2 ;
}
else
memcpy ( ptr2 , ( uchar * ) & value , 1 ) ;
ptr2 + = jumpsize ;
offset2 - = jumpsize ;
}
}
else {
uchar * ptr2 = ( uchar * ) part2 ;
ptr2 + = offset ;
if ( value > 0xFFFF )
memcpy ( ptr2 , ( uchar * ) & value , 4 ) ;
else if ( value > 0xFF )
memcpy ( ptr2 , ( uchar * ) & value , 2 ) ;
else
memcpy ( ptr2 , ( uchar * ) & value , 1 ) ;
}
cout < < " setting offset: " < < offset < < " to: " < < value < < endl ;
}
//if (offset > 0)
// DumpPacket(part2, part2_size);
uchar tmp [ 4000 ] ;
bool reverse = ( version > 373 ) ;
part2_size = Pack ( tmp , part2 , part2_size , 4000 , version , reverse ) ;
int32 total_size = part1 - > length ( ) + part2_size + 3 ;
if ( part3 )
total_size + = part3 - > length ( ) ;
int32 final_packet_size = total_size + 1 ;
if ( version > 373 )
final_packet_size + = 3 ;
else {
if ( final_packet_size > = 255 ) {
final_packet_size + = 2 ;
}
}
uchar * final_packet = new uchar [ final_packet_size ] ;
ptr = final_packet ;
if ( version < = 373 ) {
if ( ( final_packet_size - total_size ) > 1 ) {
memcpy ( ptr , & oversized_packet , sizeof ( oversized_packet ) ) ;
ptr + = sizeof ( oversized_packet ) ;
memcpy ( ptr , & total_size , 2 ) ;
ptr + = 2 ;
}
else {
memcpy ( ptr , & total_size , 1 ) ;
ptr + = 1 ;
}
}
else {
memcpy ( ptr , & total_size , sizeof ( total_size ) ) ;
ptr + = sizeof ( total_size ) ;
}
memcpy ( ptr , & oversized_packet , sizeof ( oversized_packet ) ) ;
ptr + = sizeof ( oversized_packet ) ;
int16 opcode = 0 ;
if ( EQOpcodeManager . count ( GetOpcodeVersion ( version ) ) ! = 0 ) {
if ( IsWidget ( ) )
opcode = EQOpcodeManager [ GetOpcodeVersion ( version ) ] - > EmuToEQ ( OP_EqCreateWidgetCmd ) ;
else if ( IsSign ( ) )
opcode = EQOpcodeManager [ GetOpcodeVersion ( version ) ] - > EmuToEQ ( OP_EqCreateSignWidgetCmd ) ;
else
opcode = EQOpcodeManager [ GetOpcodeVersion ( version ) ] - > EmuToEQ ( OP_EqCreateGhostCmd ) ;
}
memcpy ( ptr , & opcode , sizeof ( opcode ) ) ;
ptr + = sizeof ( opcode ) ;
memcpy ( ptr , part1 - > c_str ( ) , part1 - > length ( ) ) ;
ptr + = part1 - > length ( ) ;
memcpy ( ptr , tmp , part2_size ) ;
ptr + = part2_size ;
if ( part3 )
memcpy ( ptr , part3 - > c_str ( ) , part3 - > length ( ) ) ;
delete [ ] part2 ;
//printf("SpawnPacket %s (id: %u, index: %u) to %s: p1: %i, p2: %i, p3: %i, ts: %i. poslength: %u, infolength: %u, vislength: %u\n", GetName(), GetID(), index, player->GetName(), part1->length(), part2_size, (part3 != nullptr) ? part3->length() : -1, total_size, poslength, infolength, vislength);
EQ2Packet * ret = new EQ2Packet ( OP_ClientCmdMsg , final_packet , final_packet_size ) ;
delete [ ] final_packet ;
m_Update . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
return ret ;
}
uchar * Spawn : : spawn_info_changes ( Player * player , int16 version , int16 * info_packet_size ) {
int16 index = player - > GetIndexForSpawn ( this ) ;
PacketStruct * packet = player - > GetSpawnInfoStruct ( ) ;
player - > info_mutex . writelock ( __FUNCTION__ , __LINE__ ) ;
packet - > ResetData ( ) ;
InitializeInfoPacketData ( player , packet ) ;
string * data = packet - > serializeString ( ) ;
int32 size = data - > length ( ) ;
uchar * xor_info_packet = player - > GetTempInfoPacketForXOR ( ) ;
if ( ! xor_info_packet | | size ! = player - > GetTempInfoXorSize ( ) )
{
LogWrite ( ZONE__DEBUG , 0 , " Zone " , " InstantiateInfoPacket: %i, %i " , size , player - > GetTempInfoXorSize ( ) ) ;
safe_delete ( xor_info_packet ) ;
xor_info_packet = player - > SetTempInfoPacketForXOR ( size ) ;
}
uchar * orig_packet = player - > GetSpawnInfoPacketForXOR ( id ) ;
if ( orig_packet ) {
memcpy ( xor_info_packet , ( uchar * ) data - > c_str ( ) , size ) ;
Encode ( xor_info_packet , orig_packet , size ) ;
}
bool changed = false ;
for ( int i = 0 ; i < size ; + + i ) {
if ( xor_info_packet [ i ] ) {
changed = true ;
break ;
}
}
if ( ! changed ) {
player - > info_mutex . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
return nullptr ;
}
uchar * tmp = new uchar [ size + 1000 ] ;
size = Pack ( tmp , xor_info_packet , size , size + 1000 , version ) ;
player - > info_mutex . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
int32 orig_size = size ;
size - = sizeof ( int32 ) ;
size + = CheckOverLoadSize ( index ) ;
* info_packet_size = size + CheckOverLoadSize ( size ) ;
uchar * tmp2 = new uchar [ * info_packet_size ] ;
uchar * ptr = tmp2 ;
ptr + = DoOverLoad ( size , ptr ) ;
ptr + = DoOverLoad ( index , ptr ) ;
memcpy ( ptr , tmp + sizeof ( int32 ) , orig_size - sizeof ( int32 ) ) ;
delete [ ] tmp ;
return tmp2 ;
}
uchar * Spawn : : spawn_vis_changes ( Player * player , int16 version , int16 * vis_packet_size ) {
PacketStruct * vis_struct = player - > GetSpawnVisStruct ( ) ;
int16 index = player - > GetIndexForSpawn ( this ) ;
player - > vis_mutex . writelock ( __FUNCTION__ , __LINE__ ) ;
uchar * orig_packet = player - > GetSpawnVisPacketForXOR ( id ) ;
vis_struct - > ResetData ( ) ;
InitializeVisPacketData ( player , vis_struct ) ;
string * data = vis_struct - > serializeString ( ) ;
int32 size = data - > length ( ) ;
uchar * xor_vis_packet = player - > GetTempVisPacketForXOR ( ) ;
if ( ! xor_vis_packet | | size ! = player - > GetTempVisXorSize ( ) )
{
LogWrite ( ZONE__DEBUG , 0 , " Zone " , " InstantiateVisPacket: %i, %i " , size , player - > GetTempVisXorSize ( ) ) ;
safe_delete ( xor_vis_packet ) ;
xor_vis_packet = player - > SetTempVisPacketForXOR ( size ) ;
}
if ( orig_packet ) {
memcpy ( xor_vis_packet , ( uchar * ) data - > c_str ( ) , size ) ;
Encode ( xor_vis_packet , orig_packet , size ) ;
}
bool changed = false ;
for ( int i = 0 ; i < size ; + + i ) {
if ( xor_vis_packet [ i ] ) {
changed = true ;
break ;
}
}
if ( ! changed ) {
player - > vis_mutex . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
return nullptr ;
}
uchar * tmp = new uchar [ size + 1000 ] ;
size = Pack ( tmp , xor_vis_packet , size , size + 1000 , version ) ;
player - > vis_mutex . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
int32 orig_size = size ;
size - = sizeof ( int32 ) ;
size + = CheckOverLoadSize ( index ) ;
* vis_packet_size = size + CheckOverLoadSize ( size ) ;
uchar * tmp2 = new uchar [ * vis_packet_size ] ;
uchar * ptr = tmp2 ;
ptr + = DoOverLoad ( size , ptr ) ;
ptr + = DoOverLoad ( index , ptr ) ;
memcpy ( ptr , tmp + sizeof ( int32 ) , orig_size - sizeof ( int32 ) ) ;
delete [ ] tmp ;
return tmp2 ;
}
uchar * Spawn : : spawn_pos_changes ( Player * player , int16 version , int16 * pos_packet_size , bool override_ ) {
int16 index = player - > GetIndexForSpawn ( this ) ;
PacketStruct * packet = player - > GetSpawnPosStruct ( ) ;
player - > pos_mutex . writelock ( __FUNCTION__ , __LINE__ ) ;
uchar * orig_packet = player - > GetSpawnPosPacketForXOR ( id ) ;
packet - > ResetData ( ) ;
2025-04-26 09:22:58 -04:00
InitializePosPacketData ( player , packet ) ;
2024-07-22 08:52:28 -04:00
string * data = packet - > serializeString ( ) ;
int32 size = data - > length ( ) ;
uchar * xor_pos_packet = player - > GetTempPosPacketForXOR ( ) ;
if ( ! xor_pos_packet | | size ! = player - > GetTempPosXorSize ( ) )
{
LogWrite ( ZONE__DEBUG , 0 , " Zone " , " InstantiatePosPacket: %i, %i " , size , player - > GetTempPosXorSize ( ) ) ;
safe_delete ( xor_pos_packet ) ;
xor_pos_packet = player - > SetTempPosPacketForXOR ( size ) ;
}
if ( orig_packet ) {
memcpy ( xor_pos_packet , ( uchar * ) data - > c_str ( ) , size ) ;
Encode ( xor_pos_packet , orig_packet , size ) ;
}
bool changed = false ;
for ( int i = 0 ; i < size ; + + i ) {
if ( xor_pos_packet [ i ] ) {
changed = true ;
break ;
}
}
if ( ! changed & & ! override_ ) {
player - > pos_mutex . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
return nullptr ;
}
int16 newSize = size + 1000 ;
uchar * tmp = new uchar [ newSize ] ;
size = Pack ( tmp , xor_pos_packet , size , newSize , version ) ;
player - > pos_mutex . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
int32 orig_size = size ;
// Needed for CoE+ clients
if ( version > = 1188 )
size + = 1 ;
if ( IsPlayer ( ) & & version > 561 )
size + = 4 ;
size - = sizeof ( int32 ) ;
size + = CheckOverLoadSize ( index ) ;
* pos_packet_size = size + CheckOverLoadSize ( size ) ;
uchar * tmp2 = new uchar [ * pos_packet_size ] ;
uchar * ptr = tmp2 ;
ptr + = DoOverLoad ( size , ptr ) ;
ptr + = DoOverLoad ( index , ptr ) ;
// extra byte in coe+ clients, 0 for NPC's 1 for Players
int8 x = 0 ;
if ( IsPlayer ( ) & & version > 561 ) {
if ( version > = 1188 ) {
// set x to 1 and add it to the packet
x = 1 ;
memcpy ( ptr , & x , sizeof ( int8 ) ) ;
ptr + = sizeof ( int8 ) ;
}
int32 now = Timer : : GetCurrentTime2 ( ) ;
memcpy ( ptr , & now , sizeof ( int32 ) ) ;
ptr + = sizeof ( int32 ) ;
}
else if ( version > = 1188 ) {
// add x to packet
memcpy ( ptr , & x , sizeof ( int8 ) ) ;
ptr + = sizeof ( int8 ) ;
}
memcpy ( ptr , tmp + sizeof ( int32 ) , orig_size - sizeof ( int32 ) ) ;
delete [ ] tmp ;
return tmp2 ;
}
EQ2Packet * Spawn : : player_position_update_packet ( Player * player , int16 version , bool override_ ) {
if ( ! player | | player - > IsPlayer ( ) = = false ) {
LogWrite ( SPAWN__ERROR , 0 , " Spawn " , " Error: Called player_position_update_packet without player! " ) ;
return 0 ;
}
else if ( IsPlayer ( ) = = false ) {
LogWrite ( SPAWN__ERROR , 0 , " Spawn " , " Error: Called player_position_update_packet from spawn! " ) ;
return 0 ;
}
static const int8 info_size = 1 ;
static const int8 vis_size = 1 ;
int16 pos_packet_size = 0 ;
m_Update . writelock ( __FUNCTION__ , __LINE__ ) ;
uchar * pos_changes = spawn_pos_changes ( player , version , & pos_packet_size , override_ ) ;
if ( pos_changes = = NULL )
{
m_Update . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
return NULL ;
}
int32 size = info_size + pos_packet_size + vis_size + 8 ;
if ( version > = 374 )
size + = 3 ;
else if ( version < = 373 & & size > = 255 ) { //1 byte to 3 for overloaded val
size + = 2 ;
}
static const int8 oversized = 255 ;
int16 opcode_val = 0 ;
if ( EQOpcodeManager . count ( GetOpcodeVersion ( version ) ) ! = 0 ) {
opcode_val = EQOpcodeManager [ GetOpcodeVersion ( version ) ] - > EmuToEQ ( OP_EqUpdateGhostCmd ) ;
}
uchar * tmp = new uchar [ size ] ;
memset ( tmp , 0 , size ) ;
uchar * ptr = tmp ;
if ( version > = 374 ) {
size - = 4 ;
memcpy ( ptr , & size , sizeof ( int32 ) ) ;
size + = 4 ;
ptr + = sizeof ( int32 ) ;
}
else {
if ( size > = 255 ) {
memcpy ( ptr , & oversized , sizeof ( int8 ) ) ;
ptr + = sizeof ( int8 ) ;
size - = 3 ;
memcpy ( ptr , & size , sizeof ( int16 ) ) ;
size + = 3 ;
ptr + = 3 ;
}
else {
size - = 1 ;
memcpy ( ptr , & size , sizeof ( int8 ) ) ;
ptr + = sizeof ( int8 ) ;
size + = 1 ;
ptr + = 1 ;
}
}
memcpy ( ptr , & oversized , sizeof ( int8 ) ) ;
ptr + = sizeof ( int8 ) ;
memcpy ( ptr , & opcode_val , sizeof ( int16 ) ) ;
ptr + = sizeof ( int16 ) ;
if ( version < = 373 ) {
int32 timestamp = Timer : : GetCurrentTime2 ( ) ;
memcpy ( ptr , & timestamp , sizeof ( int32 ) ) ;
}
ptr + = sizeof ( int32 ) ;
ptr + = info_size ;
memcpy ( ptr , pos_changes , pos_packet_size ) ;
delete [ ] pos_changes ;
m_Update . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
EQ2Packet * ret_packet = new EQ2Packet ( OP_ClientCmdMsg , tmp , size ) ;
// DumpPacket(ret_packet);
delete [ ] tmp ;
return ret_packet ;
}
EQ2Packet * Spawn : : spawn_update_packet ( Player * player , int16 version , bool override_changes , bool override_vis_changes ) {
if ( ! player | | player - > IsPlayer ( ) = = false ) {
LogWrite ( SPAWN__ERROR , 0 , " Spawn " , " Error: Called spawn_update_packet without player! " ) ;
return 0 ;
}
else if ( ( IsPlayer ( ) & & info_changed = = false & & vis_changed = = false ) | | ( info_changed = = false & & vis_changed = = false & & position_changed = = false ) ) {
if ( ! override_changes & & ! override_vis_changes )
return 0 ;
}
uchar * info_changes = 0 ;
uchar * pos_changes = 0 ;
uchar * vis_changes = 0 ;
static const int8 oversized = 255 ;
int16 opcode_val = 0 ;
if ( EQOpcodeManager . count ( GetOpcodeVersion ( version ) ) ! = 0 ) {
opcode_val = EQOpcodeManager [ GetOpcodeVersion ( version ) ] - > EmuToEQ ( OP_EqUpdateGhostCmd ) ;
}
//We need to lock these variables up to make this thread safe
m_Update . writelock ( __FUNCTION__ , __LINE__ ) ;
//These variables are set in the spawn_info_changes, pos and vis changes functions
int16 info_packet_size = 1 ;
int16 pos_packet_size = 1 ;
int16 vis_packet_size = 1 ;
if ( info_changed | | override_changes )
info_changes = spawn_info_changes ( player , version , & info_packet_size ) ;
if ( ( position_changed | | override_changes ) & & IsPlayer ( ) = = false )
pos_changes = spawn_pos_changes ( player , version , & pos_packet_size ) ;
if ( vis_changed | | override_changes | | override_vis_changes )
vis_changes = spawn_vis_changes ( player , version , & vis_packet_size ) ;
int32 size = info_packet_size + pos_packet_size + vis_packet_size + 8 ;
if ( version > = 374 )
size + = 3 ;
else if ( version < = 373 & & size > = 255 ) { //1 byte to 3 for overloaded val
size + = 2 ;
}
uchar * tmp = new uchar [ size ] ;
memset ( tmp , 0 , size ) ;
uchar * ptr = tmp ;
if ( version > = 374 ) {
size - = 4 ;
memcpy ( ptr , & size , sizeof ( int32 ) ) ;
size + = 4 ;
ptr + = sizeof ( int32 ) ;
}
else {
if ( size > = 255 ) {
memcpy ( ptr , & oversized , sizeof ( int8 ) ) ;
ptr + = sizeof ( int8 ) ;
size - = 3 ;
memcpy ( ptr , & size , sizeof ( int16 ) ) ;
size + = 3 ;
ptr + = 3 ;
}
else {
size - = 1 ;
memcpy ( ptr , & size , sizeof ( int8 ) ) ;
ptr + = sizeof ( int8 ) ;
size + = 1 ;
}
}
memcpy ( ptr , & oversized , sizeof ( int8 ) ) ;
ptr + = sizeof ( int8 ) ;
memcpy ( ptr , & opcode_val , sizeof ( int16 ) ) ;
ptr + = sizeof ( int16 ) ;
if ( IsPlayer ( ) = = false | | version < = 546 ) { //this isnt sent for player updates, it is sent on position update
//int32 time = Timer::GetCurrentTime2();
packet_num = Timer : : GetCurrentTime2 ( ) ;
memcpy ( ptr , & packet_num , sizeof ( int32 ) ) ;
}
ptr + = sizeof ( int32 ) ;
if ( info_changes )
memcpy ( ptr , info_changes , info_packet_size ) ;
ptr + = info_packet_size ;
if ( pos_changes )
memcpy ( ptr , pos_changes , pos_packet_size ) ;
ptr + = pos_packet_size ;
if ( vis_changes )
memcpy ( ptr , vis_changes , vis_packet_size ) ;
EQ2Packet * ret_packet = 0 ;
if ( info_packet_size + pos_packet_size + vis_packet_size > 0 )
ret_packet = new EQ2Packet ( OP_ClientCmdMsg , tmp , size ) ;
delete [ ] tmp ;
safe_delete_array ( info_changes ) ;
safe_delete_array ( vis_changes ) ;
safe_delete_array ( pos_changes ) ;
m_Update . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
return ret_packet ;
}
uchar * Spawn : : spawn_info_changes_ex ( Player * player , int16 version , int16 * info_packet_size ) {
int16 index = player - > GetIndexForSpawn ( this ) ;
PacketStruct * packet = player - > GetSpawnInfoStruct ( ) ;
player - > info_mutex . writelock ( __FUNCTION__ , __LINE__ ) ;
packet - > ResetData ( ) ;
InitializeInfoPacketData ( player , packet ) ;
string * data = packet - > serializeString ( ) ;
int32 size = data - > length ( ) ;
uchar * xor_info_packet = player - > GetTempInfoPacketForXOR ( ) ;
if ( ! xor_info_packet | | size ! = player - > GetTempInfoXorSize ( ) ) {
LogWrite ( ZONE__DEBUG , 0 , " Zone " , " InstantiateInfoExPacket: %i, %i " , size , player - > GetTempInfoXorSize ( ) ) ;
safe_delete ( xor_info_packet ) ;
xor_info_packet = player - > SetTempInfoPacketForXOR ( size ) ;
}
uchar * orig_packet = player - > GetSpawnInfoPacketForXOR ( id ) ;
if ( orig_packet ) {
//if (!IsPlayer() && this->EngagedInCombat())
//packet->PrintPacket();
memcpy ( xor_info_packet , ( uchar * ) data - > c_str ( ) , size ) ;
Encode ( xor_info_packet , orig_packet , size ) ;
}
bool changed = false ;
for ( int i = 0 ; i < size ; + + i ) {
if ( xor_info_packet [ i ] ) {
changed = true ;
break ;
}
}
if ( ! changed ) {
player - > info_mutex . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
return nullptr ;
}
uchar * tmp = new uchar [ size + 1000 ] ;
size = Pack ( tmp , xor_info_packet , size , size + 1000 , version ) ;
player - > info_mutex . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
int32 orig_size = size ;
size - = sizeof ( int32 ) ;
size + = CheckOverLoadSize ( index ) ;
* info_packet_size = size ;
uchar * tmp2 = new uchar [ size ] ;
uchar * ptr = tmp2 ;
ptr + = DoOverLoad ( index , ptr ) ;
memcpy ( ptr , tmp + sizeof ( int32 ) , orig_size - sizeof ( int32 ) ) ;
delete [ ] tmp ;
return move ( tmp2 ) ;
}
uchar * Spawn : : spawn_vis_changes_ex ( Player * player , int16 version , int16 * vis_packet_size ) {
PacketStruct * vis_struct = player - > GetSpawnVisStruct ( ) ;
int16 index = player - > GetIndexForSpawn ( this ) ;
player - > vis_mutex . writelock ( __FUNCTION__ , __LINE__ ) ;
uchar * orig_packet = player - > GetSpawnVisPacketForXOR ( id ) ;
vis_struct - > ResetData ( ) ;
InitializeVisPacketData ( player , vis_struct ) ;
string * data = vis_struct - > serializeString ( ) ;
int32 size = data - > length ( ) ;
uchar * xor_vis_packet = player - > GetTempVisPacketForXOR ( ) ;
if ( ! xor_vis_packet | | size ! = player - > GetTempVisXorSize ( ) ) {
LogWrite ( ZONE__DEBUG , 0 , " Zone " , " InstantiateVisExPacket: %i, %i " , size , player - > GetTempVisXorSize ( ) ) ;
safe_delete ( xor_vis_packet ) ;
xor_vis_packet = player - > SetTempVisPacketForXOR ( size ) ;
}
if ( orig_packet ) {
//if (!IsPlayer() && this->EngagedInCombat())
// vis_struct->PrintPacket();
memcpy ( xor_vis_packet , ( uchar * ) data - > c_str ( ) , size ) ;
Encode ( xor_vis_packet , orig_packet , size ) ;
}
bool changed = false ;
for ( int i = 0 ; i < size ; + + i ) {
if ( xor_vis_packet [ i ] ) {
changed = true ;
break ;
}
}
if ( ! changed ) {
player - > vis_mutex . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
return nullptr ;
}
uchar * tmp = new uchar [ size + 1000 ] ;
size = Pack ( tmp , xor_vis_packet , size , size + 1000 , version ) ;
player - > vis_mutex . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
int32 orig_size = size ;
size - = sizeof ( int32 ) ;
size + = CheckOverLoadSize ( index ) ;
* vis_packet_size = size ;
uchar * tmp2 = new uchar [ size ] ;
uchar * ptr = tmp2 ;
ptr + = DoOverLoad ( index , ptr ) ;
memcpy ( ptr , tmp + sizeof ( int32 ) , orig_size - sizeof ( int32 ) ) ;
delete [ ] tmp ;
return move ( tmp2 ) ;
}
uchar * Spawn : : spawn_pos_changes_ex ( Player * player , int16 version , int16 * pos_packet_size ) {
int16 index = player - > GetIndexForSpawn ( this ) ;
PacketStruct * packet = player - > GetSpawnPosStruct ( ) ;
player - > pos_mutex . writelock ( __FUNCTION__ , __LINE__ ) ;
uchar * orig_packet = player - > GetSpawnPosPacketForXOR ( id ) ;
packet - > ResetData ( ) ;
InitializePosPacketData ( player , packet ) ;
string * data = packet - > serializeString ( ) ;
int32 size = data - > length ( ) ;
uchar * xor_pos_packet = player - > GetTempPosPacketForXOR ( ) ;
if ( ! xor_pos_packet | | size ! = player - > GetTempPosXorSize ( ) ) {
LogWrite ( ZONE__DEBUG , 0 , " Zone " , " InstantiatePosExPacket: %i, %i " , size , player - > GetTempPosXorSize ( ) ) ;
safe_delete ( xor_pos_packet ) ;
xor_pos_packet = player - > SetTempPosPacketForXOR ( size ) ;
}
if ( orig_packet ) {
//if (!IsPlayer() && this->EngagedInCombat())
// packet->PrintPacket();
memcpy ( xor_pos_packet , ( uchar * ) data - > c_str ( ) , size ) ;
Encode ( xor_pos_packet , orig_packet , size ) ;
}
bool changed = false ;
for ( int i = 0 ; i < size ; + + i ) {
if ( xor_pos_packet [ i ] ) {
changed = true ;
break ;
}
}
if ( ! changed & & version > 561 ) {
player - > pos_mutex . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
return nullptr ;
}
int16 newSize = size + 1000 ;
uchar * tmp = new uchar [ newSize ] ;
size = Pack ( tmp , xor_pos_packet , size , newSize , version ) ;
player - > pos_mutex . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
int32 orig_size = size ;
if ( version > = 1188 ) {
size + = 1 ;
}
if ( IsPlayer ( ) & & version > 561 ) {
size + = 4 ;
}
size - = sizeof ( int32 ) ;
size + = CheckOverLoadSize ( index ) ;
* pos_packet_size = size ;
uchar * tmp2 = new uchar [ size ] ;
uchar * ptr = tmp2 ;
ptr + = DoOverLoad ( index , ptr ) ;
// extra byte in coe+ clients, 0 for NPC's 1 for Players
int8 x = 0 ;
if ( version > 561 ) {
if ( IsPlayer ( ) ) {
if ( version > = 1188 ) {
x = 1 ;
memcpy ( ptr , & x , sizeof ( int8 ) ) ;
ptr + = sizeof ( int8 ) ;
}
int32 now = Timer : : GetCurrentTime2 ( ) ;
memcpy ( ptr , & now , sizeof ( int32 ) ) ;
ptr + = sizeof ( int32 ) ;
}
else if ( version > = 1188 ) {
memcpy ( ptr , & x , sizeof ( int8 ) ) ;
ptr + = sizeof ( int8 ) ;
}
}
memcpy ( ptr , tmp + sizeof ( int32 ) , orig_size - sizeof ( int32 ) ) ;
delete [ ] tmp ;
return move ( tmp2 ) ;
}
EQ2Packet * Spawn : : serialize ( Player * player , int16 version ) {
return 0 ;
}
Spawn * Spawn : : GetTarget ( ) {
Spawn * ret = 0 ;
Project Nebulark Part 1, So much to list, this is a rough summary
- Raid support and cross peer support for Isle of Refuge, DoF, KoS and AoM clients.
- Zone Persistence added to non-instanced zones.
- Commands: /whogroup, /whoraid, /raidinvite, /raid_looter, /kickfromgroup, /kickfromraid, /leaveraid, /split, /raidsay (rsay) added.
- Cross peer zone and instance support
- Cross tell support (along with ignore)
- Cross ooc support
- Cross group support (can chat, leave group, disband cross peers, update group options)
- Cross who all support
- houses/instances fixed no more cross objects/spawns/etc from other houses
- houses now display characters name with the house zone description
- 1000's of house items now properly work with wall/ceiling
- debug messages removed from housing placement
- Encounters locked to raid instead of group
- group options restricted to raid leader
- reload rules for following are peer wide:
COMMAND_RELOADSTRUCTS
COMMAND_RELOAD_QUESTS
COMMAND_RELOAD_SPELLS
COMMAND_RELOAD_ZONESCRIPTS
COMMAND_RELOAD_FACTIONS
COMMAND_RELOAD_MAIL
COMMAND_RELOAD_GUILDS
COMMAND_RELOAD_RULES
COMMAND_RELOAD_STARTABILITIES
COMMAND_RELOAD_VOICEOVERS
COMMAND_RELOADSPAWNSCRIPTS
COMMAND_RELOADREGIONSCRIPTS
COMMAND_RELOADLUASYSTEM
- special/static zones (always_loaded) are now defined by a peer_priority unsigned short (smallint(5)) in zones table. peer_priority = server_config world.peerpriority will spawn on that exe instance, if it is not available it is distributed to all peers. Using the value of 0 (assuming no peer has priority of 0) or 65535 will result in peer distribution of zones.
server_config.json "WorldServer" block updated with the following (web peer port information), priority must be unique for EACH peer:
"peeraddress": "10.1.1.2",
"peerport": "9102",
"peerpriority": "1",
New Command Line Run Arguments for World Exe to override server_config.json values
Allowed options:
--worldaddress arg World address
--internalworldaddress arg Internal world address
--worldport arg (=0) Web world port
--webworldaddress arg Web world address
--webworldport arg (=0) Web world port
--peerpriority arg (=0) Peer priority
- fixed Isle of Refuge client group struct (raids added also)
- new log category Peering
- new LUA Functions AddRespawn(Zone, LocationID, RespawnTime) and CreatePersistedRespawn(LocationID, SpawnType, RespawnTime, ZoneID)
2024-11-18 11:13:04 -05:00
if ( ! GetZone ( ) )
return 0 ;
2024-07-22 08:52:28 -04:00
// only attempt to get a spawn if we had a target stored
if ( target ! = 0 )
{
ret = GetZone ( ) - > GetSpawnByID ( target ) ;
if ( ! ret )
target = 0 ;
}
return ret ;
}
void Spawn : : SetTarget ( Spawn * spawn ) {
SetInfo ( & target , spawn ? spawn - > GetID ( ) : 0 ) ;
}
Spawn * Spawn : : GetLastAttacker ( ) {
Spawn * ret = 0 ;
ret = GetZone ( ) - > GetSpawnByID ( last_attacker ) ;
if ( ! ret )
last_attacker = 0 ;
return ret ;
}
void Spawn : : SetLastAttacker ( Spawn * spawn ) {
last_attacker = spawn - > GetID ( ) ;
}
void Spawn : : SetInvulnerable ( bool val ) {
invulnerable = val ;
}
bool Spawn : : GetInvulnerable ( ) {
return invulnerable ;
}
bool Spawn : : TakeDamage ( int32 damage ) {
if ( invulnerable )
return false ;
if ( IsEntity ( ) ) {
if ( ( ( Entity * ) this ) - > IsMezzed ( ) )
( ( Entity * ) this ) - > RemoveAllMezSpells ( ) ;
if ( damage = = 0 )
return true ;
}
int32 hp = GetHP ( ) ;
if ( damage > = hp ) {
SetHP ( 0 ) ;
if ( IsPlayer ( ) ) {
( ( Player * ) this ) - > InCombat ( false ) ;
( ( Player * ) this ) - > SetRangeAttack ( false ) ;
GetZone ( ) - > TriggerCharSheetTimer ( ) ; // force char sheet updates now
}
}
else {
SetHP ( hp - damage ) ;
// if player flag the char sheet as changed so the ui updates properly
if ( IsPlayer ( ) )
( ( Player * ) this ) - > SetCharSheetChanged ( true ) ;
}
return true ;
}
ZoneServer * Spawn : : GetZone ( ) {
return zone ;
}
2024-10-20 09:35:41 -04:00
int32 Spawn : : GetZoneID ( ) {
return zone ? zone - > GetZoneID ( ) : 0 ;
}
2024-07-22 08:52:28 -04:00
void Spawn : : SetZone ( ZoneServer * in_zone , int32 version ) {
zone = in_zone ;
if ( in_zone )
{
region_map = world . GetRegionMap ( std : : string ( in_zone - > GetZoneFile ( ) ) , version ) ;
current_map = world . GetMap ( std : : string ( in_zone - > GetZoneFile ( ) ) , version ) ;
}
else
{
region_map = nullptr ;
current_map = nullptr ;
}
}
/*** HIT POINT ***/
void Spawn : : SetHP ( sint32 new_val , bool setUpdateFlags ) {
2024-11-22 07:34:53 -05:00
if ( new_val < 0 )
new_val = 0 ;
2024-07-22 08:52:28 -04:00
if ( new_val = = 0 ) {
ClearRunningLocations ( ) ;
CalculateRunningLocation ( true ) ;
if ( IsEntity ( ) ) {
is_alive = false ;
}
}
if ( IsNPC ( ) & & new_val > 0 & & ! is_alive ) {
is_alive = true ;
}
else if ( IsPlayer ( ) & & new_val > 0 & & ! is_alive ) {
LogWrite ( SPAWN__ERROR , 0 , " Spawn " , " Cannot change player HP > 0 while dead (%s)! Player must revive. " , GetName ( ) ) ;
return ;
}
if ( new_val > basic_info . max_hp )
SetInfo ( & basic_info . max_hp , new_val , setUpdateFlags ) ;
SetInfo ( & basic_info . cur_hp , new_val , setUpdateFlags ) ;
if ( /*IsPlayer() &&*/ GetZone ( ) & & basic_info . cur_hp > 0 & & basic_info . cur_hp < basic_info . max_hp )
GetZone ( ) - > AddDamagedSpawn ( this ) ;
SendGroupUpdate ( ) ;
if ( IsPlayer ( ) & & new_val = = 0 ) // fixes on death not showing hp update for players
( ( Player * ) this ) - > SetCharSheetChanged ( true ) ;
if ( IsNPC ( ) & & ( ( NPC * ) this ) - > IsPet ( ) & & ( ( NPC * ) this ) - > GetOwner ( ) & & ( ( NPC * ) this ) - > GetOwner ( ) - > IsPlayer ( ) ) {
Player * player = ( Player * ) ( ( NPC * ) this ) - > GetOwner ( ) ;
if ( player - > GetPet ( ) & & player - > GetCharmedPet ( ) ) {
if ( this = = player - > GetPet ( ) ) {
player - > GetInfoStruct ( ) - > set_pet_health_pct ( ( float ) basic_info . cur_hp / ( float ) basic_info . max_hp ) ;
player - > SetCharSheetChanged ( true ) ;
}
}
else {
player - > GetInfoStruct ( ) - > set_pet_health_pct ( ( float ) basic_info . cur_hp / ( float ) basic_info . max_hp ) ;
player - > SetCharSheetChanged ( true ) ;
}
}
}
void Spawn : : SetTotalHP ( sint32 new_val ) {
if ( basic_info . hp_base = = 0 ) {
SetTotalHPBase ( new_val ) ;
SetTotalHPBaseInstance ( new_val ) ;
}
SetInfo ( & basic_info . max_hp , new_val ) ;
if ( GetZone ( ) & & basic_info . cur_hp > 0 & & basic_info . cur_hp < basic_info . max_hp )
GetZone ( ) - > AddDamagedSpawn ( this ) ;
SendGroupUpdate ( ) ;
if ( IsNPC ( ) & & ( ( NPC * ) this ) - > IsPet ( ) & & ( ( NPC * ) this ) - > GetOwner ( ) ! = nullptr & & ( ( NPC * ) this ) - > GetOwner ( ) - > IsPlayer ( ) ) {
Player * player = ( Player * ) ( ( NPC * ) this ) - > GetOwner ( ) ;
if ( basic_info . max_hp & & player - > GetPet ( ) & & player - > GetCharmedPet ( ) ) {
if ( this = = player - > GetPet ( ) ) {
player - > GetInfoStruct ( ) - > set_pet_health_pct ( ( float ) basic_info . cur_hp / ( float ) basic_info . max_hp ) ;
player - > SetCharSheetChanged ( true ) ;
}
}
else if ( basic_info . max_hp ) {
player - > GetInfoStruct ( ) - > set_pet_health_pct ( ( float ) basic_info . cur_hp / ( float ) basic_info . max_hp ) ;
player - > SetCharSheetChanged ( true ) ;
}
}
}
void Spawn : : SetTotalHPBase ( sint32 new_val )
{
SetInfo ( & basic_info . hp_base , new_val ) ;
if ( GetZone ( ) & & basic_info . cur_hp > 0 & & basic_info . cur_hp < basic_info . max_hp )
GetZone ( ) - > AddDamagedSpawn ( this ) ;
SendGroupUpdate ( ) ;
}
void Spawn : : SetTotalHPBaseInstance ( sint32 new_val )
{
SetInfo ( & basic_info . hp_base_instance , new_val ) ;
}
sint32 Spawn : : GetHP ( )
{
return basic_info . cur_hp ;
}
sint32 Spawn : : GetTotalHP ( )
{
return basic_info . max_hp ;
}
sint32 Spawn : : GetTotalHPBase ( )
{
return basic_info . hp_base ;
}
sint32 Spawn : : GetTotalHPBaseInstance ( )
{
return basic_info . hp_base_instance ;
}
sint32 Spawn : : GetTotalPowerBaseInstance ( )
{
return basic_info . power_base_instance ;
}
/*** POWER ***/
void Spawn : : SetPower ( sint32 power , bool setUpdateFlags ) {
2024-11-22 07:34:53 -05:00
if ( power < 0 )
power = 0 ;
2024-07-22 08:52:28 -04:00
if ( power > basic_info . max_power )
SetInfo ( & basic_info . max_power , power , setUpdateFlags ) ;
SetInfo ( & basic_info . cur_power , power , setUpdateFlags ) ;
if ( /*IsPlayer() &&*/ GetZone ( ) & & basic_info . cur_power < basic_info . max_power )
GetZone ( ) - > AddDamagedSpawn ( this ) ;
SendGroupUpdate ( ) ;
if ( IsNPC ( ) & & ( ( NPC * ) this ) - > IsPet ( ) & & ( ( NPC * ) this ) - > GetOwner ( ) ! = nullptr & & ( ( NPC * ) this ) - > GetOwner ( ) - > IsPlayer ( ) ) {
Player * player = ( Player * ) ( ( NPC * ) this ) - > GetOwner ( ) ;
if ( player - > GetPet ( ) & & player - > GetCharmedPet ( ) ) {
if ( this = = player - > GetPet ( ) ) {
player - > GetInfoStruct ( ) - > set_pet_power_pct ( ( float ) basic_info . cur_power / ( float ) basic_info . max_power ) ;
player - > SetCharSheetChanged ( true ) ;
}
}
else {
player - > GetInfoStruct ( ) - > set_pet_power_pct ( ( float ) basic_info . cur_power / ( float ) basic_info . max_power ) ;
player - > SetCharSheetChanged ( true ) ;
}
}
}
void Spawn : : SetTotalPower ( sint32 new_val )
{
if ( basic_info . power_base = = 0 ) {
SetTotalPowerBase ( new_val ) ;
SetTotalPowerBaseInstance ( new_val ) ;
}
SetInfo ( & basic_info . max_power , new_val ) ;
if ( GetZone ( ) & & basic_info . cur_power < basic_info . max_power )
GetZone ( ) - > AddDamagedSpawn ( this ) ;
SendGroupUpdate ( ) ;
if ( IsNPC ( ) & & ( ( NPC * ) this ) - > IsPet ( ) & & ( ( NPC * ) this ) - > GetOwner ( ) ! = nullptr & & ( ( NPC * ) this ) - > GetOwner ( ) - > IsPlayer ( ) ) {
Player * player = ( Player * ) ( ( NPC * ) this ) - > GetOwner ( ) ;
if ( player - > GetPet ( ) & & player - > GetCharmedPet ( ) ) {
if ( this = = player - > GetPet ( ) ) {
player - > GetInfoStruct ( ) - > set_pet_power_pct ( ( float ) basic_info . cur_power / ( float ) basic_info . max_power ) ;
player - > SetCharSheetChanged ( true ) ;
}
}
else {
player - > GetInfoStruct ( ) - > set_pet_power_pct ( ( float ) basic_info . cur_power / ( float ) basic_info . max_power ) ;
player - > SetCharSheetChanged ( true ) ;
}
}
}
void Spawn : : SetTotalPowerBase ( sint32 new_val )
{
SetInfo ( & basic_info . power_base , new_val ) ;
if ( GetZone ( ) & & basic_info . cur_power < basic_info . max_power )
GetZone ( ) - > AddDamagedSpawn ( this ) ;
SendGroupUpdate ( ) ;
}
void Spawn : : SetTotalPowerBaseInstance ( sint32 new_val )
{
SetInfo ( & basic_info . power_base_instance , new_val ) ;
}
sint32 Spawn : : GetPower ( )
{
return basic_info . cur_power ;
}
sint32 Spawn : : GetTotalPower ( ) {
return basic_info . max_power ;
}
sint32 Spawn : : GetTotalPowerBase ( )
{
return basic_info . power_base ;
}
/*** SAVAGERY ***/
void Spawn : : SetSavagery ( sint32 savagery , bool setUpdateFlags )
{
/* JA: extremely limited functionality until we better understand Savagery */
if ( savagery > basic_info . max_savagery )
SetInfo ( & basic_info . max_savagery , savagery , setUpdateFlags ) ;
SetInfo ( & basic_info . cur_savagery , savagery , setUpdateFlags ) ;
}
void Spawn : : SetTotalSavagery ( sint32 new_val )
{
/* JA: extremely limited functionality until we better understand Savagery */
if ( basic_info . savagery_base = = 0 )
SetTotalSavageryBase ( new_val ) ;
SetInfo ( & basic_info . max_savagery , new_val ) ;
}
void Spawn : : SetTotalSavageryBase ( sint32 new_val ) {
SetInfo ( & basic_info . savagery_base , new_val ) ;
SendGroupUpdate ( ) ;
}
sint32 Spawn : : GetTotalSavagery ( )
{
return basic_info . max_savagery ;
}
sint32 Spawn : : GetSavagery ( )
{
return basic_info . cur_savagery ;
}
/*** DISSONANCE ***/
void Spawn : : SetDissonance ( sint32 dissonance , bool setUpdateFlags )
{
/* JA: extremely limited functionality until we better understand Dissonance */
if ( dissonance > basic_info . max_dissonance )
SetInfo ( & basic_info . max_dissonance , dissonance , setUpdateFlags ) ;
SetInfo ( & basic_info . cur_dissonance , dissonance , setUpdateFlags ) ;
}
void Spawn : : SetTotalDissonance ( sint32 new_val )
{
/* JA: extremely limited functionality until we better understand Dissonance */
if ( basic_info . dissonance_base = = 0 )
SetTotalDissonanceBase ( new_val ) ;
SetInfo ( & basic_info . max_dissonance , new_val ) ;
}
void Spawn : : SetTotalDissonanceBase ( sint32 new_val )
{
SetInfo ( & basic_info . dissonance_base , new_val ) ;
SendGroupUpdate ( ) ;
}
sint32 Spawn : : GetTotalDissonance ( )
{
return basic_info . max_dissonance ;
}
sint32 Spawn : : GetDissonance ( )
{
return basic_info . cur_dissonance ;
}
/* --< Alternate Advancement Points >-- */
void Spawn : : SetAssignedAA ( sint16 new_val )
{
SetInfo ( & basic_info . assigned_aa , new_val ) ;
}
void Spawn : : SetUnassignedAA ( sint16 new_val )
{
SetInfo ( & basic_info . unassigned_aa , new_val ) ;
}
void Spawn : : SetTradeskillAA ( sint16 new_val )
{
SetInfo ( & basic_info . tradeskill_aa , new_val ) ;
}
void Spawn : : SetUnassignedTradeskillAA ( sint16 new_val )
{
SetInfo ( & basic_info . unassigned_tradeskill_aa , new_val ) ;
}
void Spawn : : SetPrestigeAA ( sint16 new_val )
{
SetInfo ( & basic_info . prestige_aa , new_val ) ;
}
void Spawn : : SetUnassignedPrestigeAA ( sint16 new_val )
{
SetInfo ( & basic_info . unassigned_prestige_aa , new_val ) ;
}
void Spawn : : SetTradeskillPrestigeAA ( sint16 new_val )
{
SetInfo ( & basic_info . tradeskill_prestige_aa , new_val ) ;
}
void Spawn : : SetUnassignedTradeskillPrestigeAA ( sint16 new_val )
{
SetInfo ( & basic_info . unassigned_tradeskill_prestige_aa , new_val ) ;
}
void Spawn : : SetAAXPRewards ( int32 value )
{
SetInfo ( & basic_info . aaxp_rewards , value , false ) ;
}
sint16 Spawn : : GetAssignedAA ( )
{
return basic_info . assigned_aa ;
}
sint16 Spawn : : GetUnassignedAA ( )
{
return basic_info . unassigned_aa ;
}
sint16 Spawn : : GetTradeskillAA ( )
{
return basic_info . tradeskill_aa ;
}
sint16 Spawn : : GetUnassignedTradeskillAA ( )
{
return basic_info . unassigned_tradeskill_aa ;
}
sint16 Spawn : : GetPrestigeAA ( )
{
return basic_info . prestige_aa ;
}
sint16 Spawn : : GetUnassignedPretigeAA ( )
{
return basic_info . unassigned_prestige_aa ;
}
sint16 Spawn : : GetTradeskillPrestigeAA ( )
{
return basic_info . tradeskill_prestige_aa ;
}
sint16 Spawn : : GetUnassignedTradeskillPrestigeAA ( )
{
return basic_info . unassigned_tradeskill_prestige_aa ;
}
int32 Spawn : : GetAAXPRewards ( )
{
return basic_info . aaxp_rewards ;
}
float Spawn : : GetDistance ( float x1 , float y1 , float z1 , float x2 , float y2 , float z2 ) {
x1 = x1 - x2 ;
y1 = y1 - y2 ;
z1 = z1 - z2 ;
return sqrt ( x1 * x1 + y1 * y1 + z1 * z1 ) ;
}
float Spawn : : GetDistance ( float x , float y , float z , float radius , bool ignore_y ) {
if ( ignore_y )
return GetDistance ( x , y , z , GetX ( ) , y , GetZ ( ) ) - radius ;
else
return GetDistance ( x , y , z , GetX ( ) , GetY ( ) , GetZ ( ) ) - radius ;
}
float Spawn : : GetDistance ( float x , float y , float z , bool ignore_y ) {
return GetDistance ( x , y , z , 0.0f , ignore_y ) ;
}
float Spawn : : GetDistance ( Spawn * spawn , bool ignore_y , bool includeRadius ) {
float ret = 0 ;
if ( spawn )
{
float radius = 0.0f ;
if ( includeRadius )
radius = CalculateRadius ( spawn ) ;
ret = GetDistance ( spawn - > GetX ( ) , spawn - > GetY ( ) , spawn - > GetZ ( ) , radius , ignore_y ) ;
}
// maybe distance against ourselves, in that case we want to nullify the radius check
if ( ret < 0 )
ret = 0.0f ;
return ret ;
}
float Spawn : : GetDistance ( Spawn * spawn , float x1 , float y1 , float z1 , bool includeRadius ) {
float ret = 0 ;
if ( spawn )
{
float radius = 0.0f ;
if ( includeRadius )
radius = CalculateRadius ( spawn ) ;
ret = GetDistance ( x1 , y1 , z1 , spawn - > GetX ( ) , spawn - > GetY ( ) , spawn - > GetZ ( ) ) - radius ;
}
// maybe distance against ourselves, in that case we want to nullify the radius check
if ( ret < 0 )
ret = 0.0f ;
return ret ;
}
float Spawn : : CalculateRadius ( Spawn * target )
{
float srcRadius = short_to_float ( appearance . pos . collision_radius ) ;
if ( target )
{
float targRadius = short_to_float ( target - > appearance . pos . collision_radius ) ;
return ( targRadius / 32.0f ) + ( srcRadius / 32.0f ) ;
}
else
return ( srcRadius / 32.0f ) ;
}
int32 Spawn : : GetRespawnTime ( ) {
return respawn ;
}
void Spawn : : SetRespawnTime ( int32 time ) {
respawn = time ;
}
2024-12-19 13:02:18 -05:00
sint32 Spawn : : GetRespawnOffsetLow ( ) {
return respawn_offset_low ;
}
void Spawn : : SetRespawnOffsetLow ( sint32 time ) {
respawn_offset_low = time ;
}
sint32 Spawn : : GetRespawnOffsetHigh ( ) {
return respawn_offset_high ;
}
void Spawn : : SetRespawnOffsetHigh ( sint32 time ) {
respawn_offset_high = time ;
}
2024-07-22 08:52:28 -04:00
int32 Spawn : : GetExpireOffsetTime ( ) {
return expire_offset ;
}
void Spawn : : SetExpireOffsetTime ( int32 time ) {
expire_offset = time ;
}
int32 Spawn : : GetSpawnLocationID ( ) {
return spawn_location_id ;
}
void Spawn : : SetSpawnLocationID ( int32 id ) {
spawn_location_id = id ;
}
int32 Spawn : : GetSpawnEntryID ( ) {
return spawn_entry_id ;
}
void Spawn : : SetSpawnEntryID ( int32 id ) {
spawn_entry_id = id ;
}
int32 Spawn : : GetSpawnLocationPlacementID ( ) {
return spawn_location_spawns_id ;
}
void Spawn : : SetSpawnLocationPlacementID ( int32 id ) {
spawn_location_spawns_id = id ;
}
const char * Spawn : : GetSpawnScript ( ) {
if ( spawn_script . length ( ) > 0 )
return spawn_script . c_str ( ) ;
else
return 0 ;
}
void Spawn : : SetSpawnScript ( string name ) {
spawn_script = name ;
}
void Spawn : : SetPrimaryCommand ( const char * name , const char * command , float distance ) {
EntityCommand * entity_command = CreateEntityCommand ( name , distance , command , " " , 0 , 0 ) ;
if ( primary_command_list . size ( ) > 0 & & primary_command_list [ 0 ] ) {
safe_delete ( primary_command_list [ 0 ] ) ;
primary_command_list [ 0 ] = entity_command ;
}
else
primary_command_list . push_back ( entity_command ) ;
}
void Spawn : : SetSecondaryCommands ( vector < EntityCommand * > * commands ) {
if ( commands & & commands - > size ( ) > 0 ) {
vector < EntityCommand * > : : iterator itr ;
if ( secondary_command_list . size ( ) > 0 ) {
for ( itr = secondary_command_list . begin ( ) ; itr ! = secondary_command_list . end ( ) ; itr + + ) {
safe_delete ( * itr ) ;
}
secondary_command_list . clear ( ) ;
}
EntityCommand * command = 0 ;
for ( itr = commands - > begin ( ) ; itr ! = commands - > end ( ) ; itr + + ) {
command = CreateEntityCommand ( * itr ) ;
secondary_command_list . push_back ( command ) ;
}
}
}
void Spawn : : SetPrimaryCommands ( vector < EntityCommand * > * commands ) {
if ( commands & & commands - > size ( ) > 0 ) {
vector < EntityCommand * > : : iterator itr ;
if ( primary_command_list . size ( ) > 0 ) {
for ( itr = primary_command_list . begin ( ) ; itr ! = primary_command_list . end ( ) ; itr + + ) {
safe_delete ( * itr ) ;
}
primary_command_list . clear ( ) ;
}
EntityCommand * command = 0 ;
for ( itr = commands - > begin ( ) ; itr ! = commands - > end ( ) ; itr + + ) {
command = CreateEntityCommand ( * itr ) ;
primary_command_list . push_back ( command ) ;
}
}
}
EntityCommand * Spawn : : FindEntityCommand ( string command , bool primaryOnly ) {
EntityCommand * entity_command = 0 ;
if ( primary_command_list . size ( ) > 0 ) {
vector < EntityCommand * > : : iterator itr ;
for ( itr = primary_command_list . begin ( ) ; itr ! = primary_command_list . end ( ) ; itr + + ) {
if ( ( * itr ) - > command . compare ( command ) = = 0 ) {
entity_command = * itr ;
break ;
}
}
}
if ( primaryOnly )
return entity_command ;
if ( ! entity_command & & secondary_command_list . size ( ) > 0 ) {
vector < EntityCommand * > : : iterator itr ;
for ( itr = secondary_command_list . begin ( ) ; itr ! = secondary_command_list . end ( ) ; itr + + ) {
if ( ( * itr ) - > command = = command ) {
entity_command = * itr ;
break ;
}
}
}
return entity_command ;
}
void Spawn : : SetSizeOffset ( int8 offset ) {
size_offset = offset ;
}
int8 Spawn : : GetSizeOffset ( ) {
return size_offset ;
}
void Spawn : : SetMerchantID ( int32 val ) {
merchant_id = val ;
}
int32 Spawn : : GetMerchantID ( ) {
return merchant_id ;
}
void Spawn : : SetMerchantType ( int8 val ) {
merchant_type = val ;
}
int8 Spawn : : GetMerchantType ( ) {
return merchant_type ;
}
void Spawn : : SetMerchantLevelRange ( int32 minLvl , int32 maxLvl ) {
merchant_min_level = minLvl ;
merchant_max_level = maxLvl ;
}
int32 Spawn : : GetMerchantMinLevel ( ) {
return merchant_min_level ;
}
int32 Spawn : : GetMerchantMaxLevel ( ) {
return merchant_max_level ;
}
bool Spawn : : IsClientInMerchantLevelRange ( Client * client , bool sendMessageIfDenied )
{
if ( ! client )
return false ;
if ( GetMerchantMinLevel ( ) & & client - > GetPlayer ( ) - > GetLevel ( ) < GetMerchantMinLevel ( ) )
{
client - > Message ( CHANNEL_COLOR_RED , " You are unable to interact with this merchant due to a minimum level %u allowed. " , GetMerchantMinLevel ( ) ) ;
return false ;
}
else if ( GetMerchantMaxLevel ( ) & & client - > GetPlayer ( ) - > GetLevel ( ) > GetMerchantMaxLevel ( ) )
{
client - > Message ( CHANNEL_COLOR_RED , " You are unable to interact with this merchant due to a maximum level %u allowed. " , GetMerchantMaxLevel ( ) ) ;
return false ;
}
return true ;
}
void Spawn : : SetQuestsRequired ( Spawn * new_spawn ) {
m_requiredQuests . writelock ( __FUNCTION__ , __LINE__ ) ;
if ( required_quests . size ( ) > 0 ) {
map < int32 , vector < int16 > * > : : iterator itr ;
for ( itr = required_quests . begin ( ) ; itr ! = required_quests . end ( ) ; itr + + ) {
vector < int16 > * quest_steps = itr - > second ;
for ( int32 i = 0 ; i < quest_steps - > size ( ) ; i + + )
new_spawn - > SetQuestsRequired ( itr - > first , quest_steps - > at ( i ) ) ;
}
}
m_requiredQuests . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
}
void Spawn : : SetQuestsRequired ( int32 quest_id , int16 quest_step ) {
m_requiredQuests . writelock ( __FUNCTION__ , __LINE__ ) ;
if ( required_quests . count ( quest_id ) = = 0 )
required_quests . insert ( make_pair ( quest_id , new vector < int16 > ) ) ;
else {
for ( int32 i = 0 ; i < required_quests [ quest_id ] - > size ( ) ; i + + ) {
if ( required_quests [ quest_id ] - > at ( i ) = = quest_step ) {
m_requiredQuests . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
return ;
}
}
}
has_quests_required = true ;
required_quests [ quest_id ] - > push_back ( quest_step ) ;
m_requiredQuests . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
}
bool Spawn : : HasQuestsRequired ( ) {
return has_quests_required ;
}
bool Spawn : : HasHistoryRequired ( ) {
return has_history_required ;
}
void Spawn : : SetRequiredHistory ( int32 event_id , int32 value1 , int32 value2 ) {
LUAHistory * set_value = new LUAHistory ( ) ;
set_value - > Value = value1 ;
set_value - > Value2 = value2 ;
set_value - > SaveNeeded = false ;
m_requiredHistory . writelock ( __FUNCTION__ , __LINE__ ) ;
if ( required_history . count ( event_id ) = = 0 )
required_history . insert ( make_pair ( event_id , set_value ) ) ;
else
{
LUAHistory * tmp_value = required_history [ event_id ] ;
required_history [ event_id ] = set_value ;
safe_delete ( tmp_value ) ;
}
has_history_required = true ;
m_requiredHistory . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
}
void Spawn : : SetTransporterID ( int32 id ) {
transporter_id = id ;
}
int32 Spawn : : GetTransporterID ( ) {
return transporter_id ;
}
void Spawn : : InitializePosPacketData ( Player * player , PacketStruct * packet , bool bSpawnUpdate ) {
int16 version = packet - > GetVersion ( ) ;
int32 new_grid_id = 0 ;
int32 new_widget_id = 0 ;
float new_y = 0.0f ;
float ground_diff = 0.0f ;
if ( player - > GetMap ( ) ! = nullptr & & player - > GetMap ( ) - > IsMapLoaded ( ) )
{
m_GridMutex . writelock ( __FUNCTION__ , __LINE__ ) ;
std : : map < int32 , TimedGridData > : : iterator itr = established_grid_id . find ( version ) ;
2025-01-29 08:50:01 -05:00
if ( itr = = established_grid_id . end ( ) | | itr - > second . npc_save | | itr - > second . timestamp < = ( Timer : : GetCurrentTime2 ( ) ) )
2024-07-22 08:52:28 -04:00
{
if ( itr ! = established_grid_id . end ( ) & & itr - > second . x = = GetX ( ) & & itr - > second . z = = GetZ ( ) & & ! itr - > second . npc_save ) {
itr - > second . timestamp = Timer : : GetCurrentTime2 ( ) + 100 ;
itr - > second . npc_save = false ;
new_grid_id = itr - > second . grid_id ;
new_widget_id = itr - > second . widget_id ;
new_y = itr - > second . offset_y ;
if ( player - > GetMap ( ) ! = GetMap ( ) ) {
2025-05-04 13:29:35 -04:00
ground_diff = sqrtf ( ( GetY ( ) - itr - > second . zone_ground_y ) * ( GetY ( ) - itr - > second . zone_ground_y ) ) ;
2024-07-22 08:52:28 -04:00
}
2025-04-05 14:12:34 -04:00
itr - > second . timestamp = Timer : : GetCurrentTime2 ( ) + 1000 ;
2024-07-22 08:52:28 -04:00
}
else {
auto loc = glm : : vec3 ( GetX ( ) , GetZ ( ) , GetY ( ) ) ;
new_y = player - > FindBestZ ( loc , nullptr , & new_grid_id , & new_widget_id ) ;
float zone_ground_y = new_y ;
if ( player - > GetMap ( ) ! = GetMap ( ) ) {
2024-07-24 09:08:05 -04:00
zone_ground_y = FindBestZ ( loc , nullptr , nullptr , nullptr ) ;
2025-05-04 13:29:35 -04:00
ground_diff = sqrtf ( ( GetY ( ) - zone_ground_y ) * ( GetY ( ) - zone_ground_y ) ) ;
2024-07-22 08:52:28 -04:00
}
TimedGridData data ;
data . grid_id = new_grid_id ;
data . widget_id = new_widget_id ;
data . x = GetX ( ) ;
data . y = GetY ( ) ;
data . z = GetZ ( ) ;
data . offset_y = new_y ;
data . zone_ground_y = zone_ground_y ;
data . npc_save = false ;
2025-04-05 14:12:34 -04:00
data . timestamp = Timer : : GetCurrentTime2 ( ) + 1000 ;
2024-07-22 08:52:28 -04:00
established_grid_id . insert ( make_pair ( packet - > GetVersion ( ) , data ) ) ;
}
}
else {
new_grid_id = itr - > second . grid_id ;
new_widget_id = itr - > second . widget_id ;
new_y = itr - > second . offset_y ;
2025-04-05 14:12:34 -04:00
ground_diff = itr - > second . zone_ground_y - itr - > second . offset_y ;
2024-07-22 08:52:28 -04:00
}
m_GridMutex . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
}
if ( IsKnockedBack ( ) ) {
packet - > setDataByName ( " pos_grid_id " , 0 ) ;
}
else {
packet - > setDataByName ( " pos_grid_id " , new_grid_id ! = 0 ? new_grid_id : GetLocation ( ) ) ;
}
bool include_heading = true ;
if ( IsWidget ( ) & & ( ( Widget * ) this ) - > GetIncludeHeading ( ) = = false )
include_heading = false ;
else if ( IsSign ( ) & & ( ( Sign * ) this ) - > GetIncludeHeading ( ) = = false )
include_heading = false ;
if ( include_heading ) {
packet - > setDataByName ( " pos_heading1 " , appearance . pos . Dir1 ) ;
packet - > setDataByName ( " pos_heading2 " , appearance . pos . Dir2 ) ;
}
if ( version < = 910 ) {
packet - > setDataByName ( " pos_collision_radius " , appearance . pos . collision_radius > 0 ? appearance . pos . collision_radius : 32 ) ;
packet - > setDataByName ( " pos_size " , size > 0 ? size : 32 ) ;
packet - > setDataByName ( " pos_size_multiplier " , 32 ) ; //32 is normal
}
else {
if ( size = = 0 )
size = 32 ;
packet - > setDataByName ( " pos_collision_radius " , appearance . pos . collision_radius > 0 ? appearance . pos . collision_radius : 32 ) ;
packet - > setDataByName ( " pos_size " , 1.0f ) ;
if ( ! IsPlayer ( ) )
packet - > setDataByName ( " pos_size " , size > 0 ? ( ( ( float ) size ) / 32.0f ) : 1.0f ) ; // float not an integer
else
packet - > setDataByName ( " pos_size " , 1.0f ) ;
// please do not remove! This makes it so NPCs for example do not resize large/small when you are in combat with them!
packet - > setDataByName ( " pos_size_ratio " , 1.0f ) ;
}
packet - > setDataByName ( " pos_state " , appearance . pos . state ) ;
bool include_location = true ;
if ( IsWidget ( ) & & ( ( Widget * ) this ) - > GetIncludeLocation ( ) = = false )
include_location = false ;
else if ( IsSign ( ) & & ( ( Sign * ) this ) - > GetIncludeLocation ( ) = = false )
include_location = false ;
if ( include_location ) {
if ( IsWidget ( ) & & ( ( Widget * ) this ) - > GetMultiFloorLift ( ) ) {
Widget * widget = ( Widget * ) this ;
float x = appearance . pos . X - widget - > GetWidgetX ( ) ;
float y = appearance . pos . Y - widget - > GetWidgetY ( ) ;
float z = appearance . pos . Z - widget - > GetWidgetZ ( ) ;
packet - > setDataByName ( " pos_x " , x ) ;
packet - > setDataByName ( " pos_y " , y ) ;
packet - > setDataByName ( " pos_z " , z ) ;
}
else {
packet - > setDataByName ( " pos_x " , appearance . pos . X ) ;
float result_y = appearance . pos . Y ;
2024-08-06 12:46:35 -04:00
if ( ! IsWidget ( ) & & ! IsSign ( ) & & ! IsObject ( ) & & ! ( IsFlyingCreature ( ) | | IsWaterCreature ( ) | | InWater ( ) ) ) {
2024-07-22 08:52:28 -04:00
result_y = new_y ;
}
if ( GetMap ( ) ! = player - > GetMap ( ) ) {
2025-04-05 14:12:34 -04:00
if ( ! IsWidget ( ) & & ! IsSign ( ) & & ! IsObject ( ) )
result_y = new_y ;
2024-07-22 08:52:28 -04:00
if ( IsFlyingCreature ( ) | | IsWaterCreature ( ) | | InWater ( ) ) {
result_y + = ground_diff ;
}
}
packet - > setDataByName ( " pos_y " , result_y ) ;
packet - > setDataByName ( " pos_z " , appearance . pos . Z ) ;
}
if ( IsSign ( ) )
packet - > setDataByName ( " pos_unknown6 " , 3 , 2 ) ;
}
if ( IsPlayer ( ) ) {
Skill * skill = ( ( Player * ) this ) - > GetSkillByName ( " Swimming " , false ) ;
sint16 swim_modifier = rule_manager . GetGlobalRule ( R_Player , SwimmingSkillMinSpeed ) - > GetSInt16 ( ) ;
if ( skill ) {
sint16 max_val = 450 ;
if ( skill - > max_val > 0 )
max_val = skill - > max_val ;
sint16 max_swim_mod = rule_manager . GetGlobalRule ( R_Player , SwimmingSkillMaxSpeed ) - > GetSInt16 ( ) ;
float diff = ( float ) ( skill - > current_val + ( ( Player * ) this ) - > GetStat ( ITEM_STAT_SWIMMING ) ) / ( float ) max_val ;
sint16 diff_mod = ( sint16 ) ( float ) max_swim_mod * diff ;
if ( diff_mod > max_swim_mod )
swim_modifier = max_swim_mod ;
else if ( diff_mod > swim_modifier )
swim_modifier = diff_mod ;
}
packet - > setDataByName ( " pos_swim_speed_modifier " , swim_modifier ) ;
packet - > setDataByName ( " pos_x_velocity " , TransformFromFloat ( GetSpeedX ( ) , 5 ) ) ;
packet - > setDataByName ( " pos_y_velocity " , TransformFromFloat ( GetSpeedY ( ) , 5 ) ) ;
packet - > setDataByName ( " pos_z_velocity " , TransformFromFloat ( GetSpeedZ ( ) , 5 ) ) ;
}
if ( appearance . pos . X2 = = 0 & & appearance . pos . Y2 = = 0 & & appearance . pos . Z2 & & ( appearance . pos . X ! = 0 | | appearance . pos . Y ! = 0 | | appearance . pos . Z ! = 0 ) ) {
appearance . pos . X2 = appearance . pos . X ;
appearance . pos . Y2 = appearance . pos . Y ;
appearance . pos . Z2 = appearance . pos . Z ;
}
if ( appearance . pos . X3 = = 0 & & appearance . pos . Y3 = = 0 & & appearance . pos . Z3 & & ( appearance . pos . X ! = 0 | | appearance . pos . Y ! = 0 | | appearance . pos . Z ! = 0 ) ) {
appearance . pos . X3 = appearance . pos . X ;
appearance . pos . Y3 = appearance . pos . Y ;
appearance . pos . Z3 = appearance . pos . Z ;
}
//Transform To/From Float bits (original client)
//pos_loc_offset[3]: 5
//pos_x_velocity: 5
//pos_y_velocity: 5
//pos_z_velocity: 5
//pos_heading1: 6
//pos_heading2: 6
//pos_speed: 8
//pos_dest_loc_offset[3]: 5
//pos_dest_loc_offset2[3]: 5
//pos_heading_speed: 5
//pos_move_type: 5 (speed_modifier)
//pos_swim_speed_modifier: 5
//pos_side_speed: 8
//pos_vert_speed: 8
//pos_requested_pitch: 6
//pos_requested_pitch_speed: 5
//pos_pitch: 6
//pos_collision_radius: 5
//pos_size: 5
//actor_stop_range: 5
//this is for original box client, destinations used to be offsets
if ( version < = 373 | | version > 561 | | ! IsPlayer ( ) ) {
if ( appearance . pos . X2 ! = 0 | | appearance . pos . Y2 ! = 0 | | appearance . pos . Z2 ! = 0 ) {
packet - > setDataByName ( " pos_dest_loc_offset " , TransformFromFloat ( appearance . pos . X2 - appearance . pos . X , 5 ) ) ;
packet - > setDataByName ( " pos_dest_loc_offset " , TransformFromFloat ( appearance . pos . Y2 - appearance . pos . Y , 5 ) , 1 ) ;
packet - > setDataByName ( " pos_dest_loc_offset " , TransformFromFloat ( appearance . pos . Z2 - appearance . pos . Z , 5 ) , 2 ) ;
}
if ( appearance . pos . X3 ! = 0 | | appearance . pos . Y3 ! = 0 | | appearance . pos . Z3 ! = 0 ) {
packet - > setDataByName ( " pos_dest_loc_offset2 " , TransformFromFloat ( appearance . pos . X3 - appearance . pos . X , 5 ) ) ;
packet - > setDataByName ( " pos_dest_loc_offset2 " , TransformFromFloat ( appearance . pos . Y3 - appearance . pos . Y , 5 ) , 1 ) ;
packet - > setDataByName ( " pos_dest_loc_offset2 " , TransformFromFloat ( appearance . pos . Z3 - appearance . pos . Z , 5 ) , 2 ) ;
}
}
bool bSendSpeed = true ;
// fixes lifts dropping back to the floor in zones like northfreeport when set as TransportSpawn (Some zones do not support multifloor lifts)
if ( IsWidget ( ) & & ( ( ( Widget * ) this ) - > GetMultiFloorLift ( ) | | ( IsTransportSpawn ( ) ) ) ) {
Widget * widget = ( Widget * ) this ;
float x ;
float y ;
float z ;
bool setCoords = false ;
if ( ! widget - > GetMultiFloorLift ( ) & & version > = 561 ) {
packet - > setDataByName ( " pos_next_x " , appearance . pos . X2 ) ;
packet - > setDataByName ( " pos_next_y " , appearance . pos . Y2 ) ;
packet - > setDataByName ( " pos_next_z " , appearance . pos . Z2 ) ;
packet - > setDataByName ( " pos_x3 " , appearance . pos . X ) ;
packet - > setDataByName ( " pos_y3 " , appearance . pos . Y ) ;
packet - > setDataByName ( " pos_z3 " , appearance . pos . Z ) ;
setCoords = true ;
}
else if ( IsRunning ( ) ) {
x = appearance . pos . X2 - widget - > GetWidgetX ( ) ;
y = appearance . pos . Y2 - widget - > GetWidgetY ( ) ;
z = appearance . pos . Z2 - widget - > GetWidgetZ ( ) ;
}
else {
x = appearance . pos . X - widget - > GetWidgetX ( ) ;
y = appearance . pos . Y - widget - > GetWidgetY ( ) ;
z = appearance . pos . Z - widget - > GetWidgetZ ( ) ;
}
if ( ! setCoords ) {
packet - > setDataByName ( " pos_next_x " , x ) ;
packet - > setDataByName ( " pos_next_y " , y ) ;
packet - > setDataByName ( " pos_next_z " , z ) ;
packet - > setDataByName ( " pos_x3 " , x ) ;
packet - > setDataByName ( " pos_y3 " , y ) ;
packet - > setDataByName ( " pos_z3 " , z ) ;
}
}
else if ( IsFlyingCreature ( ) | | IsWaterCreature ( ) | | InWater ( ) ) {
packet - > setDataByName ( " pos_next_x " , appearance . pos . X2 ) ;
packet - > setDataByName ( " pos_next_y " , ( GetMap ( ) ! = player - > GetMap ( ) ) ? ( ground_diff + new_y ) : appearance . pos . Y2 ) ;
packet - > setDataByName ( " pos_next_z " , appearance . pos . Z2 ) ;
packet - > setDataByName ( " pos_x3 " , appearance . pos . X ) ;
packet - > setDataByName ( " pos_y3 " , ( GetMap ( ) ! = player - > GetMap ( ) ) ? ( ground_diff + new_y ) : appearance . pos . Y ) ;
packet - > setDataByName ( " pos_z3 " , appearance . pos . Z ) ;
}
//If this is a spawn update or this spawn is currently moving we can send these values, otherwise set speed and next_xyz to 0
//This fixes the bug where spawns with movement scripts face south when initially spawning if they are at their target location.
else if ( bSpawnUpdate | | memcmp ( & appearance . pos . X , & appearance . pos . X2 , sizeof ( float ) * 3 ) ! = 0 ) {
packet - > setDataByName ( " pos_next_x " , appearance . pos . X2 ) ;
packet - > setDataByName ( " pos_next_y " , appearance . pos . Y2 ) ;
packet - > setDataByName ( " pos_next_z " , appearance . pos . Z2 ) ;
packet - > setDataByName ( " pos_x3 " , appearance . pos . X3 ) ;
packet - > setDataByName ( " pos_y3 " , appearance . pos . Y3 ) ;
packet - > setDataByName ( " pos_z3 " , appearance . pos . Z3 ) ;
}
else
{
bSendSpeed = false ;
}
//packet->setDataByName("pos_unknown2", 4, 2);
int16 speed_multiplier = rule_manager . GetGlobalRule ( R_Spawn , SpeedMultiplier ) - > GetInt16 ( ) ; // was 1280, 600 and now 300... investigating why
int8 movement_mode = 0 ;
if ( IsPlayer ( ) ) {
Player * player = static_cast < Player * > ( this ) ;
sint16 pos_packet_speed = player - > GetPosPacketSpeed ( ) * speed_multiplier ;
sint16 side_speed = player - > GetSideSpeed ( ) * speed_multiplier ;
packet - > setDataByName ( " pos_speed " , pos_packet_speed ) ;
packet - > setDataByName ( " pos_side_speed " , side_speed ) ;
}
2024-11-29 07:52:07 -05:00
else if ( bSendSpeed & & ( ! IsNPC ( ) | | Alive ( ) ) & & GetBaseSpeed ( ) ! = 0.0f ) {
2024-07-22 08:52:28 -04:00
sint16 side_speed = GetSpeed ( ) * speed_multiplier ;
packet - > setDataByName ( " pos_speed " , side_speed ) ;
if ( side_speed ! = 0 & & ( ( IsWidget ( ) & & ( ( Widget * ) this ) - > GetMultiFloorLift ( ) ) | | IsTransportSpawn ( ) ) ) {
movement_mode = 2 ;
}
}
else if ( ( ( IsWidget ( ) & & ( ( Widget * ) this ) - > GetMultiFloorLift ( ) ) | | ( ! IsNPC ( ) & & version < = 561 & & ! IsRunning ( ) ) ) ) {
movement_mode = 2 ;
}
if ( IsFlyingCreature ( ) | | IsWaterCreature ( ) | | InWater ( ) ) {
movement_mode = 2 ;
}
if ( IsNPC ( ) | | IsPlayer ( ) ) {
packet - > setDataByName ( " pos_move_type " , 25 ) ;
}
else if ( IsWidget ( ) | | IsSign ( ) ) {
packet - > setDataByName ( " pos_move_type " , 11 ) ;
}
else if ( IsGroundSpawn ( ) ) {
packet - > setDataByName ( " pos_move_type " , 16 ) ;
}
if ( ! IsPlayer ( ) ) { // has to be 2 or NPC's warp around when moving
packet - > setDataByName ( " pos_movement_mode " , movement_mode ) ;
}
packet - > setDataByName ( " face_actor_id " , 0xFFFFFFFF ) ;
packet - > setDataByName ( " pos_pitch1 " , appearance . pos . Pitch1 ) ;
packet - > setDataByName ( " pos_pitch2 " , appearance . pos . Pitch2 ) ;
packet - > setDataByName ( " pos_roll " , appearance . pos . Roll ) ;
}
void Spawn : : InitializeInfoPacketData ( Player * spawn , PacketStruct * packet ) {
int16 version = packet - > GetVersion ( ) ;
bool spawnHiddenFromClient = false ;
int8 classicFlags = 0 ;
2024-12-12 15:38:27 -05:00
if ( version < = 561 & & IsPlayer ( ) ) {
InitializeFooterPacketData ( spawn , packet ) ;
}
2024-07-22 08:52:28 -04:00
// radius of 0 is always seen, -1 is never seen (unless items/spells override), larger than 0 is a defined radius to restrict visibility
2024-10-20 15:02:39 -04:00
sint32 radius = rule_manager . GetZoneRule ( GetZoneID ( ) , R_PVP , InvisPlayerDiscoveryRange ) - > GetSInt32 ( ) ;
2024-07-22 08:52:28 -04:00
if ( radius ! = 0 & & ( Spawn * ) spawn ! = this & & this - > IsPlayer ( ) & & ! spawn - > CanSeeInvis ( ( Entity * ) this ) )
spawnHiddenFromClient = true ;
if ( ! spawnHiddenFromClient & & ( appearance . targetable = = 1 | | appearance . show_level = = 1 | | appearance . display_name = = 1 ) ) {
if ( ! IsObject ( ) & & ! IsGroundSpawn ( ) & & ! IsWidget ( ) & & ! IsSign ( ) ) {
int8 percent = 0 ;
if ( GetHP ( ) > 0 )
percent = ( int8 ) ( ( ( float ) GetHP ( ) / GetTotalHP ( ) ) * 100 ) ;
if ( version > = 373 ) {
if ( percent < 100 ) {
packet - > setDataByName ( " hp_remaining " , 100 ^ percent ) ;
}
else
packet - > setDataByName ( " hp_remaining " , 0 ) ;
}
else {
if ( percent > 100 )
percent = 100 ;
packet - > setDataByName ( " hp_remaining " , percent ) ;
}
if ( GetTotalPower ( ) > 0 ) {
percent = ( int8 ) ( ( ( float ) GetPower ( ) / GetTotalPower ( ) ) * 100 ) ;
if ( percent > 0 )
packet - > setDataByName ( " power_percent " , percent ) ;
else
packet - > setDataByName ( " power_percent " , 0 ) ;
}
}
}
if ( version < = 561 ) {
packet - > setDataByName ( " name " , appearance . name ) ;
for ( int8 i = 0 ; i < 8 ; i + + )
packet - > setDataByName ( " unknown1 " , 0xFF , i ) ;
if ( appearance . show_level = = 0 )
packet - > setDataByName ( " hide_health " , 1 ) ;
}
if ( GetHP ( ) < = 0 & & IsEntity ( ) ) {
packet - > setDataByName ( " corpse " , 1 ) ;
if ( HasLoot ( ) )
packet - > setDataByName ( " loot_icon " , 1 ) ;
}
if ( ! IsPlayer ( ) )
packet - > setDataByName ( " npc " , 1 ) ;
if ( GetMerchantID ( ) > 0 )
packet - > setDataByName ( " merchant " , 1 ) ;
packet - > setDataByName ( " effective_level " , IsEntity ( ) & & ( ( Entity * ) this ) - > GetInfoStruct ( ) - > get_effective_level ( ) ! = 0 ? ( int8 ) ( ( Entity * ) this ) - > GetInfoStruct ( ) - > get_effective_level ( ) : ( int8 ) GetLevel ( ) ) ;
packet - > setDataByName ( " level " , ( int8 ) GetLevel ( ) ) ;
packet - > setDataByName ( " unknown4 " , ( int8 ) GetLevel ( ) ) ;
packet - > setDataByName ( " difficulty " , GetDifficulty ( ) ) ; //6);
packet - > setDataByName ( " unknown6 " , 1 ) ;
packet - > setDataByName ( " heroic_flag " , appearance . heroic_flag ) ;
packet - > setDataByName ( " class " , appearance . adventure_class ) ;
int16 model_type = appearance . model_type ;
if ( GetIllusionModel ( ) ! = 0 ) {
if ( IsPlayer ( ) ) {
if ( ( ( Player * ) this ) - > get_character_flag ( CF_SHOW_ILLUSION ) ) {
model_type = GetIllusionModel ( ) ;
}
}
else
model_type = GetIllusionModel ( ) ;
}
int16 sogaModelType = appearance . soga_model_type ;
if ( spawnHiddenFromClient )
{
model_type = 0 ;
sogaModelType = 0 ;
}
if ( version < = 373 & & ( model_type = = 5864 | | model_type = = 5865 | | model_type = = 4015 ) ) {
model_type = 4034 ;
}
else if ( version < = 561 & & model_type = = 7039 ) { // goblin
model_type = 145 ;
}
packet - > setDataByName ( " model_type " , model_type ) ;
if ( appearance . soga_model_type = = 0 )
packet - > setDataByName ( " soga_model_type " , model_type ) ;
else
packet - > setDataByName ( " soga_model_type " , sogaModelType ) ;
int16 action_state = appearance . action_state ;
if ( IsEntity ( ) ) {
std : : string actionState = " " ;
if ( GetTempActionState ( ) > = 0 ) {
action_state = GetTempActionState ( ) ;
actionState = ( ( Entity * ) this ) - > GetInfoStruct ( ) - > get_combat_action_state ( ) ;
}
else {
actionState = ( ( Entity * ) this ) - > GetInfoStruct ( ) - > get_action_state ( ) ;
}
Client * client = spawn - > GetClient ( ) ;
if ( IsEntity ( ) & & client ) {
if ( actionState . size ( ) > 0 ) {
Emote * emote = visual_states . FindEmote ( actionState , client - > GetVersion ( ) ) ;
if ( emote ! = NULL )
action_state = emote - > GetVisualState ( ) ;
}
}
}
packet - > setDataByName ( " action_state " , action_state ) ;
bool scaredOfPlayer = false ;
if ( IsCollector ( ) & & spawn - > GetCollectionList ( ) - > HasCollectionsToHandIn ( ) )
packet - > setDataByName ( " visual_state " , VISUAL_STATE_COLLECTION_TURN_IN ) ;
else if ( ! IsRunning ( ) & & IsNPC ( ) & & IsScaredByStrongPlayers ( ) & & spawn - > GetArrowColor ( GetLevel ( ) ) = = ARROW_COLOR_GRAY & &
( GetDistance ( spawn ) ) < = ( ( NPC * ) this ) - > GetAggroRadius ( ) & & CheckLoS ( spawn ) ) {
packet - > setDataByName ( " visual_state " , VISUAL_STATE_IDLE_AFRAID ) ;
scaredOfPlayer = true ;
}
else if ( GetTempVisualState ( ) > = 0 )
packet - > setDataByName ( " visual_state " , GetTempVisualState ( ) ) ;
else
packet - > setDataByName ( " visual_state " , appearance . visual_state ) ;
if ( IsNPC ( ) & & ! IsPet ( ) & & ! scaredOfPlayer )
{
if ( ( ( Entity * ) this ) - > GetInfoStruct ( ) - > get_interaction_flag ( ) ) {
if ( ( ( Entity * ) this ) - > GetInfoStruct ( ) - > get_interaction_flag ( ) = = 255 ) {
packet - > setDataByName ( " interaction_flag " , 0 ) ;
classicFlags + = INFO_CLASSIC_FLAG_NOLOOK ;
}
else {
packet - > setDataByName ( " interaction_flag " , ( ( Entity * ) this ) - > GetInfoStruct ( ) - > get_interaction_flag ( ) ) ; //this makes NPCs head turn to look at you (12)
}
}
else {
packet - > setDataByName ( " interaction_flag " , 12 ) ; //turn head since no other value is set
}
}
packet - > setDataByName ( " emote_state " , appearance . emote_state ) ;
packet - > setDataByName ( " mood_state " , appearance . mood_state ) ;
packet - > setDataByName ( " gender " , appearance . gender ) ;
packet - > setDataByName ( " race " , appearance . race ) ;
if ( IsEntity ( ) ) {
Entity * entity = ( ( Entity * ) this ) ;
packet - > setDataByName ( " combat_voice " , entity - > GetCombatVoice ( ) ) ;
packet - > setDataByName ( " emote_voice " , entity - > GetEmoteVoice ( ) ) ;
for ( int i = 0 ; i < 25 ; i + + ) {
if ( i = = 2 ) { //don't send helm if hidden flag
if ( IsPlayer ( ) ) {
if ( ( ( Player * ) this ) - > get_character_flag ( CF_HIDE_HELM ) ) {
packet - > setDataByName ( " equipment_types " , 0 , i ) ;
packet - > setColorByName ( " equipment_colors " , 0 , i ) ;
packet - > setColorByName ( " equipment_highlights " , 0 , i ) ;
continue ;
}
}
if ( IsBot ( ) ) {
if ( ! ( ( Bot * ) this ) - > ShowHelm ) {
packet - > setDataByName ( " equipment_types " , 0 , i ) ;
packet - > setColorByName ( " equipment_colors " , 0 , i ) ;
packet - > setColorByName ( " equipment_highlights " , 0 , i ) ;
continue ;
}
}
}
else if ( i = = 19 ) { //don't send cloak if hidden
if ( IsPlayer ( ) ) {
if ( ! ( ( Player * ) this ) - > get_character_flag ( CF_SHOW_CLOAK ) ) {
packet - > setDataByName ( " equipment_types " , 0 , i ) ;
packet - > setColorByName ( " equipment_colors " , 0 , i ) ;
packet - > setColorByName ( " equipment_highlights " , 0 , i ) ;
continue ;
}
}
if ( IsBot ( ) ) {
if ( ! ( ( Bot * ) this ) - > ShowCloak ) {
packet - > setDataByName ( " equipment_types " , 0 , i ) ;
packet - > setColorByName ( " equipment_colors " , 0 , i ) ;
packet - > setColorByName ( " equipment_highlights " , 0 , i ) ;
continue ;
}
}
}
entity - > MEquipment . lock ( ) ;
packet - > setDataByName ( " equipment_types " , entity - > equipment . equip_id [ i ] , i ) ;
packet - > setColorByName ( " equipment_colors " , entity - > equipment . color [ i ] , i ) ;
packet - > setColorByName ( " equipment_highlights " , entity - > equipment . highlight [ i ] , i ) ;
entity - > MEquipment . unlock ( ) ;
}
packet - > setDataByName ( " mount_type " , entity - > GetMount ( ) ) ;
// find the visual flags
int8 vis_flag = 0 ;
//Invis + crouch flag check
if ( entity - > IsStealthed ( ) ) {
vis_flag + = ( INFO_VIS_FLAG_INVIS + INFO_VIS_FLAG_CROUCH ) ;
classicFlags + = INFO_VIS_FLAG_INVIS + INFO_VIS_FLAG_CROUCH ;
}
//Invis flag check
else if ( entity - > IsInvis ( ) ) {
vis_flag + = INFO_VIS_FLAG_INVIS ;
classicFlags + = INFO_VIS_FLAG_INVIS ;
}
//Mount flag check
if ( entity - > GetMount ( ) > 0 ) {
vis_flag + = INFO_VIS_FLAG_MOUNTED ;
2024-12-13 09:27:43 -05:00
if ( ( classicFlags & INFO_CLASSIC_FLAG_NOLOOK ) = = 0 )
classicFlags + = INFO_CLASSIC_FLAG_NOLOOK ; // serves as dual purpose for NPC's
2024-07-22 08:52:28 -04:00
}
//Hide hood check
bool vis_hide_hood = false ;
if ( IsPlayer ( ) & & ( ( Player * ) this ) - > get_character_flag ( CF_HIDE_HOOD ) ) {
if ( version > 561 ) {
vis_flag + = INFO_VIS_FLAG_HIDE_HOOD ;
}
vis_hide_hood = true ;
}
else if ( IsPlayer ( ) ) {
classicFlags + = INFO_CLASSIC_FLAG_SHOW_HOOD ;
}
if ( ! vis_hide_hood & & appearance . hide_hood & & version > 561 ) {
vis_flag + = INFO_VIS_FLAG_HIDE_HOOD ;
}
2025-01-04 15:24:02 -05:00
if ( spawn = = this & & IsPlayer ( ) & & ( ( Player * ) this ) - > GetClient ( ) & & ( ( Player * ) this ) - > GetClient ( ) - > GetOnAutoMount ( ) ) {
classicFlags + = INFO_CLASSIC_FLAG_AUTOMOUNT ;
}
2024-07-22 08:52:28 -04:00
if ( version < = 561 ) {
packet - > setDataByName ( " flags " , classicFlags ) ;
}
packet - > setDataByName ( " visual_flag " , vis_flag ) ;
packet - > setColorByName ( " mount_saddle_color " , entity - > GetMountSaddleColor ( ) ) ;
packet - > setColorByName ( " mount_color " , entity - > GetMountColor ( ) ) ;
packet - > setDataByName ( " hair_type_id " , entity - > features . hair_type ) ;
packet - > setDataByName ( " chest_type_id " , entity - > features . chest_type ) ;
packet - > setDataByName ( " wing_type_id " , entity - > features . wing_type ) ;
packet - > setDataByName ( " legs_type_id " , entity - > features . legs_type ) ;
packet - > setDataByName ( " soga_hair_type_id " , entity - > features . soga_hair_type ) ;
packet - > setDataByName ( " facial_hair_type_id " , entity - > features . hair_face_type ) ;
packet - > setDataByName ( " soga_facial_hair_type_id " , entity - > features . soga_hair_face_type ) ;
for ( int i = 0 ; i < 3 ; i + + ) {
packet - > setDataByName ( " eye_type " , entity - > features . eye_type [ i ] , i ) ;
packet - > setDataByName ( " ear_type " , entity - > features . ear_type [ i ] , i ) ;
packet - > setDataByName ( " eye_brow_type " , entity - > features . eye_brow_type [ i ] , i ) ;
packet - > setDataByName ( " cheek_type " , entity - > features . cheek_type [ i ] , i ) ;
packet - > setDataByName ( " lip_type " , entity - > features . lip_type [ i ] , i ) ;
packet - > setDataByName ( " chin_type " , entity - > features . chin_type [ i ] , i ) ;
packet - > setDataByName ( " nose_type " , entity - > features . nose_type [ i ] , i ) ;
packet - > setDataByName ( " soga_eye_type " , entity - > features . soga_eye_type [ i ] , i ) ;
packet - > setDataByName ( " soga_ear_type " , entity - > features . soga_ear_type [ i ] , i ) ;
packet - > setDataByName ( " soga_eye_brow_type " , entity - > features . soga_eye_brow_type [ i ] , i ) ;
packet - > setDataByName ( " soga_cheek_type " , entity - > features . soga_cheek_type [ i ] , i ) ;
packet - > setDataByName ( " soga_lip_type " , entity - > features . soga_lip_type [ i ] , i ) ;
packet - > setDataByName ( " soga_chin_type " , entity - > features . soga_chin_type [ i ] , i ) ;
packet - > setDataByName ( " soga_nose_type " , entity - > features . soga_nose_type [ i ] , i ) ;
}
packet - > setColorByName ( " skin_color " , entity - > features . skin_color ) ;
packet - > setColorByName ( " model_color " , entity - > features . model_color ) ;
packet - > setColorByName ( " eye_color " , entity - > features . eye_color ) ;
packet - > setColorByName ( " hair_type_color " , entity - > features . hair_type_color ) ;
packet - > setColorByName ( " hair_type_highlight_color " , entity - > features . hair_type_highlight_color ) ;
packet - > setColorByName ( " hair_face_color " , entity - > features . hair_face_color ) ;
packet - > setColorByName ( " hair_face_highlight_color " , entity - > features . hair_face_highlight_color ) ;
packet - > setColorByName ( " hair_highlight " , entity - > features . hair_highlight_color ) ;
packet - > setColorByName ( " wing_color1 " , entity - > features . wing_color1 ) ;
packet - > setColorByName ( " wing_color2 " , entity - > features . wing_color2 ) ;
packet - > setColorByName ( " hair_color1 " , entity - > features . hair_color1 ) ;
packet - > setColorByName ( " hair_color2 " , entity - > features . hair_color2 ) ;
packet - > setColorByName ( " soga_skin_color " , entity - > features . soga_skin_color ) ;
packet - > setColorByName ( " soga_model_color " , entity - > features . soga_model_color ) ;
packet - > setColorByName ( " soga_eye_color " , entity - > features . soga_eye_color ) ;
packet - > setColorByName ( " soga_hair_color1 " , entity - > features . soga_hair_color1 ) ;
packet - > setColorByName ( " soga_hair_color2 " , entity - > features . soga_hair_color2 ) ;
packet - > setColorByName ( " soga_hair_type_color " , entity - > features . soga_hair_type_color ) ;
packet - > setColorByName ( " soga_hair_type_highlight_color " , entity - > features . soga_hair_type_highlight_color ) ;
packet - > setColorByName ( " soga_hair_face_color " , entity - > features . soga_hair_face_color ) ;
packet - > setColorByName ( " soga_hair_face_highlight_color " , entity - > features . soga_hair_face_highlight_color ) ;
packet - > setColorByName ( " soga_hair_highlight " , entity - > features . soga_hair_highlight_color ) ;
packet - > setDataByName ( " body_size " , entity - > features . body_size ) ;
packet - > setDataByName ( " body_age " , entity - > features . body_age ) ;
packet - > setDataByName ( " soga_body_size " , entity - > features . soga_body_size ) ;
packet - > setDataByName ( " soga_body_age " , entity - > features . soga_body_age ) ;
}
else {
EQ2_Color empty ;
empty . red = 255 ;
empty . blue = 255 ;
empty . green = 255 ;
packet - > setColorByName ( " skin_color " , empty ) ;
packet - > setColorByName ( " model_color " , empty ) ;
packet - > setColorByName ( " eye_color " , empty ) ;
packet - > setColorByName ( " soga_skin_color " , empty ) ;
packet - > setColorByName ( " soga_model_color " , empty ) ;
packet - > setColorByName ( " soga_eye_color " , empty ) ;
}
if ( appearance . icon = = 0 ) {
if ( appearance . attackable = = 1 )
appearance . icon = 0 ;
else if ( GetDifficulty ( ) > 0 )
appearance . icon = 4 ;
else
appearance . icon = 6 ;
}
// If Coe+ clients modify the values before we send
// if not then just send the value we have.
int8 temp_icon = appearance . icon ;
//Check if we need to add the hand icon..
if ( ( temp_icon & 6 ) ! = 6 & & appearance . display_hand_icon ) {
temp_icon | = 6 ;
}
//Icon value 28 for boats, set this without modifying the value
if ( version > = 1188 & & temp_icon ! = 28 ) {
if ( ( temp_icon & 64 ) > 0 ) {
temp_icon - = 64 ; // remove the DoV value;
temp_icon + = 128 ; // add the CoE value
}
if ( ( temp_icon & 32 ) > 0 ) {
temp_icon - = 32 ; // remove the DoV value;
temp_icon + = 64 ; // add the CoE value
}
if ( ( temp_icon & 4 ) > 0 ) {
temp_icon - = 4 ; // remove DoV value
temp_icon + = 8 ; // add the CoE icon
}
if ( ( temp_icon & 6 ) > 0 ) {
temp_icon - = 10 ; // remove DoV value
temp_icon + = 12 ; // add the CoE icon
}
}
packet - > setDataByName ( " icon " , temp_icon ) ; //appearance.icon);
2024-08-20 07:47:25 -04:00
if ( GetPickupItemID ( ) ) {
2024-08-28 08:47:49 -04:00
if ( version < = 546 ) {
2024-08-20 07:47:25 -04:00
packet - > setDataByName ( " house_icon " , 1 ) ; //appearance.icon);
}
}
2024-07-22 08:52:28 -04:00
int32 temp_activity_status = 0 ;
if ( ! Alive ( ) & & GetTotalHP ( ) > 0 & & ! IsObject ( ) & & ! IsGroundSpawn ( ) )
temp_activity_status = 1 ;
if ( version > = 1188 & & GetChestDropTime ( ) ) {
temp_activity_status = 0 ;
}
temp_activity_status + = ( IsNPC ( ) | | IsObject ( ) | | IsGroundSpawn ( ) ) ? 1 < < 1 : 0 ;
if ( version > 561 ) {
// Fix widget or sign having 'Play Legends of Norrath' or 'Tell' options in right click (client hard-coded entity commands)
if ( IsWidget ( ) | | IsSign ( ) )
temp_activity_status = 2 ;
if ( IsGroundSpawn ( ) | | GetShowHandIcon ( ) )
temp_activity_status + = ACTIVITY_STATUS_INTERACTABLE_1188 ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_ROLEPLAYING ) > 0 )
temp_activity_status + = ACTIVITY_STATUS_ROLEPLAYING_1188 ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_ANONYMOUS ) > 0 )
temp_activity_status + = ACTIVITY_STATUS_ANONYMOUS_1188 ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_LINKDEAD ) > 0 )
temp_activity_status + = ACTIVITY_STATUS_LINKDEAD_1188 ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_CAMPING ) > 0 )
temp_activity_status + = ACTIVITY_STATUS_CAMPING_1188 ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_LFG ) > 0 )
temp_activity_status + = ACTIVITY_STATUS_LFG_1188 ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_LFW ) > 0 )
temp_activity_status + = ACTIVITY_STATUS_LFW_1188 ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_SOLID ) > 0 )
temp_activity_status + = ACTIVITY_STATUS_SOLID_1188 ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_IMMUNITY_GAINED ) > 0 )
temp_activity_status + = ACTIVITY_STATUS_IMMUNITY_GAINED_1188 ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_IMMUNITY_REMAINING ) > 0 )
temp_activity_status + = ACTIVITY_STATUS_IMMUNITY_REMAINING_1188 ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_AFK ) > 0 )
temp_activity_status + = ACTIVITY_STATUS_AFK_1188 ;
if ( EngagedInCombat ( ) )
temp_activity_status + = ACTIVITY_STATUS_INCOMBAT_1188 ;
// if this is either a boat or lift let the client be manipulated by the object
// doesn't work for DoF client version 546
if ( appearance . icon = = 28 | | appearance . icon = = 12 | | IsTransportSpawn ( ) )
{
// there is some other flags that setting with a transport breaks their solidity/ability to properly transport
// thus we just consider the following flags for now as all necessary
temp_activity_status = ACTIVITY_STATUS_SOLID_1188 ;
temp_activity_status + = ACTIVITY_STATUS_ISTRANSPORT_1188 ;
}
}
else if ( version = = 561 ) {
// Fix widget or sign having 'Play Legends of Norrath' or 'Tell' options in right click (client hard-coded entity commands)
if ( IsWidget ( ) | | IsSign ( ) )
temp_activity_status = 2 ;
if ( IsGroundSpawn ( ) | | GetShowHandIcon ( ) )
temp_activity_status + = ACTIVITY_STATUS_INTERACTABLE_561 ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_ROLEPLAYING ) > 0 )
temp_activity_status + = ACTIVITY_STATUS_ROLEPLAYING_561 ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_ANONYMOUS ) > 0 )
temp_activity_status + = ACTIVITY_STATUS_ANONYMOUS_561 ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_LINKDEAD ) > 0 )
temp_activity_status + = ACTIVITY_STATUS_LINKDEAD_561 ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_CAMPING ) > 0 )
temp_activity_status + = ACTIVITY_STATUS_CAMPING_561 ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_LFG ) > 0 )
temp_activity_status + = ACTIVITY_STATUS_LFG_561 ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_LFW ) > 0 )
temp_activity_status + = ACTIVITY_STATUS_LFW_561 ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_SOLID ) > 0 )
temp_activity_status + = ACTIVITY_STATUS_SOLID_561 ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_IMMUNITY_GAINED ) > 0 )
temp_activity_status + = ACTIVITY_STATUS_IMMUNITY_GAINED_561 ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_IMMUNITY_REMAINING ) > 0 )
temp_activity_status + = ACTIVITY_STATUS_IMMUNITY_REMAINING_561 ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_AFK ) > 0 )
temp_activity_status + = ACTIVITY_STATUS_AFK_561 ;
if ( EngagedInCombat ( ) )
temp_activity_status + = ACTIVITY_STATUS_INCOMBAT_561 ;
// if this is either a boat or lift let the client be manipulated by the object
// doesn't work for DoF client version 546
if ( appearance . icon = = 28 | | appearance . icon = = 12 | | IsTransportSpawn ( ) )
{
// there is some other flags that setting with a transport breaks their solidity/ability to properly transport
// thus we just consider the following flags for now as all necessary
temp_activity_status = ACTIVITY_STATUS_SOLID_561 ;
temp_activity_status + = ACTIVITY_STATUS_ISTRANSPORT_561 ;
}
}
else
{
temp_activity_status = appearance . activity_status ;
if ( IsNPC ( ) )
temp_activity_status = 0xFF ;
// this only partially fixes lifts in classic 283 client if you move just as the lift starts to move
if ( appearance . icon = = 28 | | appearance . icon = = 12 )
packet - > setDataByName ( " is_transport " , 1 ) ;
if ( MeetsSpawnAccessRequirements ( spawn ) )
packet - > setDataByName ( " hand_icon " , appearance . display_hand_icon ) ;
else {
if ( ( req_quests_override & 256 ) > 0 )
packet - > setDataByName ( " hand_icon " , 1 ) ;
}
if ( IsPlayer ( ) ) {
if ( ( ( Player * ) this ) - > get_character_flag ( CF_AFK ) )
packet - > setDataByName ( " afk " , 1 ) ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_ROLEPLAYING ) > 0 )
packet - > setDataByName ( " roleplaying " , 1 ) ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_ANONYMOUS ) > 0 )
packet - > setDataByName ( " anonymous " , 1 ) ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_LINKDEAD ) > 0 )
packet - > setDataByName ( " linkdead " , 1 ) ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_CAMPING ) > 0 )
packet - > setDataByName ( " camping " , 1 ) ;
if ( ( appearance . activity_status & ACTIVITY_STATUS_LFG ) > 0 )
packet - > setDataByName ( " lfg " , 1 ) ;
}
if ( EngagedInCombat ( ) ) {
packet - > setDataByName ( " auto_attack " , 1 ) ;
}
if ( ( appearance . activity_status & ACTIVITY_STATUS_SOLID ) > 0 )
packet - > setDataByName ( " solid_object " , 1 ) ;
}
packet - > setDataByName ( " activity_status " , temp_activity_status ) ; //appearance.activity_status);
// If player and player has a follow target
/* Jan 2021 Note!! Setting follow_target 0xFFFFFFFF has the result in causing strange behavior in swimming. Targetting a mob makes you focus down to its swim level, unable to swim above it.
* * in the same respect the player will drop like a rock to the bottom of the ocean ( seems to be when self set to that flag ? )
* * for now disabling this , if DoF needs it enabled for whatever reason then we need a version check added .
*/
if ( IsPlayer ( ) ) {
if ( ( ( Player * ) this ) - > GetFollowTarget ( ) )
packet - > setDataByName ( " follow_target " , version < = 561 ? ( ( ( Player * ) this ) - > GetIDWithPlayerSpawn ( ( ( Player * ) this ) - > GetFollowTarget ( ) ) ) : ( ( ( ( Player * ) this ) - > GetIDWithPlayerSpawn ( ( ( Player * ) this ) - > GetFollowTarget ( ) ) * - 1 ) - 1 ) ) ;
else if ( version < = 561 ) {
packet - > setDataByName ( " follow_target " , 0xFFFFFFFF ) ;
}
//else
// packet->setDataByName("follow_target", 0xFFFFFFFF);
}
//else if (!IsPet()) {
// packet->setDataByName("follow_target", 0xFFFFFFFF);
//}
// i think this is used in DoF as a way to make a client say they are in combat with this target and cannot camp, it forces you to stand up if self spawn sends this data
if ( ( version > 561 | | spawn ! = this ) & & GetTarget ( ) & & GetTarget ( ) - > GetTargetable ( ) )
packet - > setDataByName ( " target_id " , ( ( spawn - > GetIDWithPlayerSpawn ( GetTarget ( ) ) * - 1 ) - 1 ) ) ;
else
packet - > setDataByName ( " target_id " , 0xFFFFFFFF ) ;
//Send spell effects for target window
if ( IsEntity ( ) ) {
InfoStruct * info = ( ( Entity * ) this ) - > GetInfoStruct ( ) ;
int8 i = 0 ;
int16 backdrop = 0 ;
int16 spell_icon = 0 ;
int32 spell_id = 0 ;
LuaSpell * spell = 0 ;
( ( Entity * ) this ) - > GetSpellEffectMutex ( ) - > readlock ( __FUNCTION__ , __LINE__ ) ;
while ( i < 30 ) {
//Change value of spell id for this packet if spell exists
spell_id = info - > spell_effects [ i ] . spell_id ;
if ( spell_id > 0 )
spell_id = 0xFFFFFFFF - spell_id ;
else
spell_id = 0 ;
packet - > setSubstructDataByName ( " spell_effects " , " spell_id " , spell_id , i ) ;
//Change value of spell icon for this packet if spell exists
spell_icon = info - > spell_effects [ i ] . icon ;
if ( spell_icon > 0 ) {
if ( ! ( spell_icon = = 0xFFFF ) )
spell_icon = 0xFFFF - spell_icon ;
}
else
spell_icon = 0 ;
packet - > setSubstructDataByName ( " spell_effects " , " spell_icon " , spell_icon , i ) ;
//Change backdrop values to match values in this packet
backdrop = info - > spell_effects [ i ] . icon_backdrop ;
switch ( backdrop ) {
case 312 :
backdrop = 33080 ;
break ;
case 313 :
backdrop = 33081 ;
break ;
case 314 :
backdrop = 33082 ;
break ;
case 315 :
backdrop = 33083 ;
break ;
case 316 :
backdrop = 33084 ;
break ;
case 317 :
backdrop = 33085 ;
break ;
case ( 318 | | 319 ) :
backdrop = 33086 ;
break ;
default :
break ;
}
packet - > setSubstructDataByName ( " spell_effects " , " spell_icon_backdrop " , backdrop , i ) ;
spell = info - > spell_effects [ i ] . spell ;
if ( spell )
packet - > setSubstructDataByName ( " spell_effects " , " spell_triggercount " , spell - > num_triggers , i ) ;
i + + ;
}
( ( Entity * ) this ) - > GetSpellEffectMutex ( ) - > releasereadlock ( __FUNCTION__ , __LINE__ ) ;
}
}
void Spawn : : MoveToLocation ( Spawn * spawn , float distance , bool immediate , bool mapped ) {
if ( ! spawn )
return ;
SetRunningTo ( spawn ) ;
FaceTarget ( spawn , false ) ;
if ( ! IsPlayer ( ) & & distance > 0.0f )
{
if ( ( IsFlyingCreature ( ) | | IsWaterCreature ( ) | | InWater ( ) ) & & CheckLoS ( spawn ) )
{
if ( immediate )
ClearRunningLocations ( ) ;
AddRunningLocation ( spawn - > GetX ( ) , spawn - > GetY ( ) , spawn - > GetZ ( ) , GetSpeed ( ) , distance , true , true , " " , true ) ;
}
else if ( /*!mapped && */ GetZone ( ) )
{
GetZone ( ) - > movementMgr - > NavigateTo ( ( Entity * ) this , spawn - > GetX ( ) , spawn - > GetY ( ) , spawn - > GetZ ( ) ) ;
last_grid_update = Timer : : GetCurrentTime2 ( ) ;
}
else
{
if ( immediate )
ClearRunningLocations ( ) ;
AddRunningLocation ( spawn - > GetX ( ) , spawn - > GetY ( ) , spawn - > GetZ ( ) , GetSpeed ( ) , distance , true , true , " " , mapped ) ;
}
}
}
void Spawn : : ProcessMovement ( bool isSpawnListLocked ) {
CheckProximities ( ) ;
if ( IsBot ( ) & & ( ( Bot * ) this ) - > IsCamping ( ) ) {
( ( Bot * ) this ) - > Begin_Camp ( ) ;
}
if ( IsPlayer ( ) ) {
//Check if player is riding a boat, if so update pos (boat's current location + XYZ offsets)
Player * player = ( ( Player * ) this ) ;
int32 boat_id = player - > GetBoatSpawn ( ) ;
Spawn * boat = 0 ;
if ( boat_id > 0 )
boat = GetZone ( ) - > GetSpawnByID ( boat_id , isSpawnListLocked ) ;
//TODO: MAYBE do this for real boats, not lifts... GetWidgetTypeNameByTypeID
/*if(boat){
SetX ( boat - > GetX ( ) + player - > GetBoatX ( ) ) ;
SetY ( boat - > GetY ( ) + player - > GetBoatY ( ) ) ;
SetZ ( boat - > GetZ ( ) + player - > GetBoatZ ( ) ) ;
} */
return ;
}
if ( IsKnockedBack ( ) ) {
if ( CalculateSpawnProjectilePosition ( GetX ( ) , GetY ( ) , GetZ ( ) ) )
return ; // being launched!
}
if ( reset_movement ) {
ResetMovement ( ) ;
reset_movement = false ;
}
if ( forceMapCheck & & GetZone ( ) ! = nullptr & & GetMap ( ) ! = nullptr & & GetMap ( ) - > IsMapLoaded ( ) )
{
FixZ ( true ) ;
forceMapCheck = false ;
}
if ( GetHP ( ) < = 0 & & ! IsWidget ( ) )
return ;
if ( EngagedInCombat ( ) )
{
if ( IsEntity ( ) & & ( ( ( Entity * ) this ) - > IsMezzedOrStunned ( ) | | ( ( Entity * ) this ) - > IsRooted ( ) ) ) {
SetAppearancePosition ( GetX ( ) , GetY ( ) , GetZ ( ) ) ;
if ( IsEntity ( ) )
( ( Entity * ) this ) - > SetSpeed ( 0.0f ) ;
SetSpeed ( 0.0f ) ;
position_changed = true ;
changed = true ;
GetZone ( ) - > AddChangedSpawn ( this ) ;
StopMovement ( ) ;
return ;
}
int locations = 0 ;
MMovementLocations . lock_shared ( ) ;
if ( movement_locations ) {
locations = movement_locations - > size ( ) ;
}
MMovementLocations . unlock_shared ( ) ;
if ( locations < 1 & & GetZone ( ) & & ( ( Entity * ) this ) - > IsFeared ( ) )
{
CalculateNewFearpoint ( ) ;
ValidateRunning ( true , true ) ;
}
}
Spawn * followTarget = GetZone ( ) - > GetSpawnByID ( m_followTarget , isSpawnListLocked ) ;
if ( ! followTarget & & m_followTarget > 0 )
m_followTarget = 0 ;
if ( following & & ! IsPauseMovementTimerActive ( ) & & followTarget & & ! ( ( Entity * ) this ) - > IsFeared ( ) ) {
// Need to clear m_followTarget before the zoneserver deletes it
if ( followTarget - > GetHP ( ) < = 0 ) {
followTarget = 0 ;
return ;
}
if ( ! IsEntity ( ) | | ( ! ( ( Entity * ) this ) - > IsCasting ( ) & & ! ( ( Entity * ) this ) - > IsMezzedOrStunned ( ) & & ! ( ( Entity * ) this ) - > IsRooted ( ) ) ) {
if ( GetBaseSpeed ( ) > 0 ) {
CalculateRunningLocation ( ) ;
}
else {
float speed = 4.0f ;
if ( IsEntity ( ) )
speed = ( ( Entity * ) this ) - > GetMaxSpeed ( ) ;
if ( IsEntity ( ) )
( ( Entity * ) this ) - > SetSpeed ( speed ) ;
SetSpeed ( speed ) ;
}
MovementLocation tmpLoc ;
MovementLocation * loc = 0 ;
MMovementLocations . lock_shared ( ) ;
if ( movement_locations & & movement_locations - > size ( ) > 0 ) {
loc = movement_locations - > front ( ) ;
if ( loc ) {
tmpLoc . attackable = loc - > attackable ;
tmpLoc . gridid = loc - > gridid ;
tmpLoc . lua_function = string ( loc - > lua_function ) ;
tmpLoc . mapped = loc - > mapped ;
tmpLoc . reset_hp_on_runback = loc - > reset_hp_on_runback ;
tmpLoc . speed = loc - > speed ;
tmpLoc . stage = loc - > stage ;
tmpLoc . x = loc - > x ;
tmpLoc . y = loc - > y ;
tmpLoc . z = loc - > z ;
2025-01-29 22:01:25 -05:00
tmpLoc . use_nav_path = loc - > use_nav_path ;
2024-07-22 08:52:28 -04:00
loc = & tmpLoc ;
}
}
MMovementLocations . unlock_shared ( ) ;
float dist = GetDistance ( followTarget , true ) ;
if ( ( ! EngagedInCombat ( ) & & m_followDistance > 0 & & dist < = m_followDistance ) | |
2024-10-20 15:02:39 -04:00
( dist < = rule_manager . GetZoneRule ( GetZoneID ( ) , R_Combat , MaxCombatRange ) - > GetFloat ( ) ) ) {
2024-07-22 08:52:28 -04:00
ClearRunningLocations ( ) ;
CalculateRunningLocation ( true ) ;
}
else if ( loc ) {
float distance = GetDistance ( followTarget , loc - > x , loc - > y , loc - > z ) ;
if ( ( ! EngagedInCombat ( ) & & m_followDistance > 0 & & distance > m_followDistance ) | |
2024-10-20 15:02:39 -04:00
( EngagedInCombat ( ) & & distance > rule_manager . GetZoneRule ( GetZoneID ( ) , R_Combat , MaxCombatRange ) - > GetFloat ( ) ) ) {
MoveToLocation ( followTarget , rule_manager . GetZoneRule ( GetZoneID ( ) , R_Combat , MaxCombatRange ) - > GetFloat ( ) , true , loc - > mapped ) ;
2024-07-22 08:52:28 -04:00
CalculateRunningLocation ( ) ;
}
}
else {
2024-10-20 15:02:39 -04:00
MoveToLocation ( followTarget , rule_manager . GetZoneRule ( GetZoneID ( ) , R_Combat , MaxCombatRange ) - > GetFloat ( ) , false ) ;
2024-07-22 08:52:28 -04:00
CalculateRunningLocation ( ) ;
}
}
}
bool movementCase = false ;
// Movement loop is only for scripted paths
if ( ! EngagedInCombat ( ) & & ! IsPauseMovementTimerActive ( ) & & ! NeedsToResumeMovement ( ) & & ( ! IsNPC ( ) | | ! ( ( NPC * ) this ) - > m_runningBack ) ) {
MMovementLoop . writelock ( ) ;
if ( movement_loop . size ( ) > 0 & & movement_index < movement_loop . size ( ) )
{
movementCase = true ;
// Get the target location
MovementData * data = movement_loop [ movement_index ] ;
// need to resume our movement
if ( resume_movement ) {
MMovementLocations . lock ( ) ;
if ( movement_locations ) {
while ( movement_locations - > size ( ) ) {
safe_delete ( movement_locations - > front ( ) ) ;
movement_locations - > pop_front ( ) ;
}
movement_locations - > clear ( ) ;
}
MMovementLocations . unlock ( ) ;
data = movement_loop [ movement_index ] ;
if ( data )
{
if ( IsEntity ( ) ) {
( ( Entity * ) this ) - > SetSpeed ( data - > speed ) ;
}
SetSpeed ( data - > speed ) ;
if ( data - > use_movement_location_heading )
SetHeading ( data - > heading ) ;
else if ( ! IsWidget ( ) )
FaceTarget ( data - > x , data - > z ) ;
// 0 delay at target location, need to set multiple locations
if ( data - > delay = = 0 & & movement_loop . size ( ) > 0 ) {
int16 tmp_index = movement_index + 1 ;
MovementData * data2 = 0 ;
if ( tmp_index < movement_loop . size ( ) )
data2 = movement_loop [ tmp_index ] ;
else
data2 = movement_loop [ 0 ] ;
2025-01-29 15:13:26 -05:00
AddRunningLocation ( data - > x , data - > y , data - > z , data - > speed , 0 , true , false , " " , true , data - > use_nav_path ) ;
AddRunningLocation ( data2 - > x , data2 - > y , data2 - > z , data2 - > speed , 0 , true , true , " " , true , data2 - > use_nav_path ) ;
2024-07-22 08:52:28 -04:00
}
// delay at target location, only need to set 1 location
else
2025-01-29 15:13:26 -05:00
AddRunningLocation ( data - > x , data - > y , data - > z , data - > speed , 0 , true , true , " " , true , data - > use_nav_path ) ;
2024-07-22 08:52:28 -04:00
}
movement_start_time = 0 ;
resume_movement = false ;
}
// If we are not moving or we have arrived at our destination
else if ( ! IsRunning ( ) | | ( data & & data - > x = = GetX ( ) & & data - > y = = GetY ( ) & & data - > z = = GetZ ( ) ) ) {
// If we were moving remove the last running location (the point we just arrived at)
if ( IsRunning ( ) ) {
RemoveRunningLocation ( ) ;
}
// If this waypoint has a delay and we just arrived here (movement_start_time == 0)
if ( data & & data - > delay > 0 & & movement_start_time = = 0 ) {
// Set the current time
movement_start_time = Timer : : GetCurrentTime2 ( ) ;
// If this waypoint had a lua function then call it
if ( data - > lua_function . length ( ) > 0 )
GetZone ( ) - > CallSpawnScript ( this , SPAWN_SCRIPT_CUSTOM , 0 , data - > lua_function . c_str ( ) ) ;
int16 nextMove ;
if ( ( int16 ) ( movement_index + 1 ) < movement_loop . size ( ) )
nextMove = movement_index + 1 ;
else
nextMove = 0 ;
// Get the next target location
data = movement_loop [ nextMove ] ;
//Go ahead and face the next location
if ( data ) {
FaceTarget ( data - > x , data - > z ) ;
}
}
// If this waypoint has no delay or we have waited the required time (current time >= delay + movement_start_time)
else if ( data & & data - > delay = = 0 | | ( data & & data - > delay > 0 & & Timer : : GetCurrentTime2 ( ) > = ( data - > delay + movement_start_time ) ) ) {
// if no delay at this waypoint but a lua function for it then call the function
if ( data - > delay = = 0 & & data - > lua_function . length ( ) > 0 )
GetZone ( ) - > CallSpawnScript ( this , SPAWN_SCRIPT_CUSTOM , 0 , data - > lua_function . c_str ( ) ) ;
// since we ran a lua function make sure the movement loop is still alive and accurate
if ( movement_loop . size ( ) > 0 )
{
// Advance the current movement loop index
if ( ( int16 ) ( movement_index + 1 ) < movement_loop . size ( ) )
movement_index + + ;
else
movement_index = 0 ;
// Get the next target location
data = movement_loop [ movement_index ] ;
// set the speed for that location
SetSpeed ( data - > speed ) ;
if ( ! IsWidget ( ) )
// turn towards the location
FaceTarget ( data - > x , data - > z ) ;
// If 0 delay at location get and set data for the point after it
if ( data - > delay = = 0 & & movement_loop . size ( ) > 0 ) {
MMovementLocations . lock ( ) ;
if ( movement_locations )
{
while ( movement_locations - > size ( ) ) {
safe_delete ( movement_locations - > front ( ) ) ;
movement_locations - > pop_front ( ) ;
}
// clear current target locations
movement_locations - > clear ( ) ;
}
MMovementLocations . unlock ( ) ;
// get the data for the location after out new location
int16 tmp_index = movement_index + 1 ;
MovementData * data2 = 0 ;
if ( tmp_index < movement_loop . size ( ) )
data2 = movement_loop [ tmp_index ] ;
else
data2 = movement_loop [ 0 ] ;
// set the first location (adds it to movement_locations that we just cleared)
2025-01-29 15:13:26 -05:00
AddRunningLocation ( data - > x , data - > y , data - > z , data - > speed , 0 , true , false , " " , true , data - > use_nav_path ) ;
2024-07-22 08:52:28 -04:00
// set the location after that
2025-01-29 15:13:26 -05:00
AddRunningLocation ( data2 - > x , data2 - > y , data2 - > z , data2 - > speed , 0 , true , true , " " , true , data2 - > use_nav_path ) ;
2024-07-22 08:52:28 -04:00
}
// there is a delay at the next location so we only need to set it
else {
2025-01-29 15:13:26 -05:00
AddRunningLocation ( data - > x , data - > y , data - > z , data - > speed , 0 , true , true , " " , true , data - > use_nav_path ) ;
2024-07-22 08:52:28 -04:00
}
// reset this timer to 0 now that we are moving again
movement_start_time = 0 ;
}
}
}
// moving and not at target location yet
else if ( GetBaseSpeed ( ) > 0 ) {
CalculateRunningLocation ( ) ;
}
// not moving, have a target location but not at it yet
else if ( data ) {
SetSpeed ( data - > speed ) ;
2025-01-29 15:13:26 -05:00
AddRunningLocation ( data - > x , data - > y , data - > z , data - > speed , 0 , true , true , " " , true , data - > use_nav_path ) ;
2024-07-22 08:52:28 -04:00
}
}
MMovementLoop . releasewritelock ( ) ;
}
if ( ! movementCase & & IsRunning ( ) & & ! IsPauseMovementTimerActive ( ) ) {
CalculateRunningLocation ( ) ;
//last_movement_update = Timer::GetCurrentTime2();
}
else if ( movementCase )
{
//last_movement_update = Timer::GetCurrentTime2();
}
/*else if (IsNPC() && !IsRunning() && !EngagedInCombat() && ((NPC*)this)->GetRunbackLocation()) {
// Is an npc that is not moving and not engaged in combat but has a run back location set then clear the runback location
LogWrite ( NPC_AI__DEBUG , 7 , " NPC_AI " , " Clear runback location for %s " , GetName ( ) ) ;
( ( NPC * ) this ) - > ClearRunback ( ) ;
resume_movement = true ;
NeedsToResumeMovement ( false ) ;
} */
}
void Spawn : : ResetMovement ( ) {
MMovementLoop . writelock ( ) ;
vector < MovementData * > : : iterator itr ;
for ( itr = movement_loop . begin ( ) ; itr ! = movement_loop . end ( ) ; itr + + ) {
safe_delete ( * itr ) ;
}
movement_loop . clear ( ) ;
movement_index = 0 ;
resume_movement = true ;
ClearRunningLocations ( ) ;
MMovementLoop . releasewritelock ( ) ;
ValidateRunning ( true , true ) ;
}
2025-01-29 15:13:26 -05:00
void Spawn : : AddMovementLocation ( float x , float y , float z , float speed , int16 delay , const char * lua_function , float heading , bool include_heading , bool use_nav_path ) {
2024-07-22 08:52:28 -04:00
LogWrite ( LUA__DEBUG , 5 , " LUA " , " AddMovementLocation: x: %.2f, y: %.2f, z: %.2f, speed: %.2f, delay: %i, lua: %s " ,
x , y , z , speed , delay , string ( lua_function ) . c_str ( ) ) ;
MovementData * data = new MovementData ;
data - > x = x ;
data - > y = y ;
data - > z = z ;
data - > speed = speed ;
data - > delay = delay * 1000 ;
if ( lua_function )
data - > lua_function = string ( lua_function ) ;
data - > heading = heading ;
data - > use_movement_location_heading = include_heading ;
2025-01-29 15:13:26 -05:00
data - > use_nav_path = use_nav_path ;
2024-07-22 08:52:28 -04:00
MMovementLoop . lock ( ) ;
movement_loop . push_back ( data ) ;
MMovementLoop . unlock ( ) ;
}
bool Spawn : : ValidateRunning ( bool lockMovementLocation , bool lockMovementLoop ) {
bool movement = false ;
if ( lockMovementLocation ) {
MMovementLocations . lock_shared ( ) ;
}
if ( movement_locations ) {
movement = movement_locations - > size ( ) > 0 ;
}
if ( lockMovementLocation ) {
MMovementLocations . unlock_shared ( ) ;
}
if ( IsPauseMovementTimerActive ( ) | | ( IsEntity ( ) & & ( ( ( Entity * ) this ) - > IsMezzedOrStunned ( ) | | ( ( Entity * ) this ) - > IsRooted ( ) ) ) ) {
is_running = false ;
return false ;
}
if ( movement ) {
is_running = true ;
return true ;
}
if ( lockMovementLoop ) {
MMovementLoop . lock ( ) ;
}
movement = movement_loop . size ( ) > 0 ;
if ( movement ) {
is_running = true ;
}
else {
is_running = false ;
}
if ( lockMovementLoop ) {
MMovementLoop . unlock ( ) ;
}
return movement ;
}
bool Spawn : : IsRunning ( ) {
return is_running ;
}
void Spawn : : RunToLocation ( float x , float y , float z , float following_x , float following_y , float following_z ) {
if ( IsPauseMovementTimerActive ( ) | | ( IsEntity ( ) & & ( ( ( Entity * ) this ) - > IsMezzedOrStunned ( ) | | ( ( Entity * ) this ) - > IsRooted ( ) ) ) ) {
is_running = false ;
return ;
}
2024-10-20 15:02:39 -04:00
if ( ! IsWidget ( ) & & ( ! EngagedInCombat ( ) | | GetDistance ( GetTarget ( ) ) > rule_manager . GetZoneRule ( GetZoneID ( ) , R_Combat , MaxCombatRange ) - > GetFloat ( ) ) )
2024-07-22 08:52:28 -04:00
FaceTarget ( x , z ) ;
SetPos ( & appearance . pos . X2 , x , false ) ;
SetPos ( & appearance . pos . Z2 , z , false ) ;
SetPos ( & appearance . pos . Y2 , y , false ) ;
if ( following_x = = 0 & & following_y = = 0 & & following_z = = 0 ) {
SetPos ( & appearance . pos . X3 , x , false ) ;
SetPos ( & appearance . pos . Z3 , z , false ) ;
SetPos ( & appearance . pos . Y3 , y , false ) ;
}
else {
SetPos ( & appearance . pos . X3 , following_x , false ) ;
SetPos ( & appearance . pos . Y3 , following_y , false ) ;
SetPos ( & appearance . pos . Z3 , following_z , false ) ;
}
is_running = true ;
position_changed = true ;
changed = true ;
GetZone ( ) - > AddChangedSpawn ( this ) ;
}
MovementLocation * Spawn : : GetCurrentRunningLocation ( ) {
MovementLocation * ret = 0 ;
MMovementLocations . lock_shared ( ) ;
if ( movement_locations & & movement_locations - > size ( ) > 0 ) {
ret = movement_locations - > front ( ) ;
}
MMovementLocations . unlock_shared ( ) ;
return ret ;
}
MovementLocation * Spawn : : GetLastRunningLocation ( ) {
MovementLocation * ret = 0 ;
MMovementLocations . lock_shared ( ) ;
if ( movement_locations & & movement_locations - > size ( ) > 0 ) {
ret = movement_locations - > back ( ) ;
}
MMovementLocations . unlock_shared ( ) ;
return ret ;
}
2025-01-29 15:13:26 -05:00
void Spawn : : AddRunningLocation ( float x , float y , float z , float speed , float distance_away , bool attackable , bool finished_adding_locations , string lua_function , bool isMapped , bool useNavPath ) {
2024-07-22 08:52:28 -04:00
if ( speed = = 0 )
return ;
if ( IsEntity ( ) )
( ( Entity * ) this ) - > SetSpeed ( speed ) ;
else
this - > SetSpeed ( speed ) ;
MovementLocation * current_location = 0 ;
float distance = GetDistance ( x , y , z , distance_away ! = 0 ) ;
if ( distance_away ! = 0 ) {
distance - = distance_away ;
x = x - ( GetX ( ) - x ) * distance_away / distance ;
z = z - ( GetZ ( ) - z ) * distance_away / distance ;
}
MMovementLocations . lock ( ) ;
if ( ! movement_locations ) {
movement_locations = new deque < MovementLocation * > ( ) ;
}
MMovementLocations . unlock ( ) ;
MovementLocation * data = new MovementLocation ;
data - > mapped = isMapped ;
data - > x = x ;
data - > y = y ;
data - > z = z ;
data - > speed = speed ;
data - > attackable = attackable ;
data - > lua_function = lua_function ;
data - > gridid = 0 ; // used for runback defaults
data - > reset_hp_on_runback = false ;
2025-01-29 15:13:26 -05:00
data - > use_nav_path = useNavPath ;
2024-07-22 08:52:28 -04:00
MMovementLocations . lock_shared ( ) ;
if ( movement_locations - > size ( ) > 0 )
current_location = movement_locations - > back ( ) ;
MMovementLocations . unlock_shared ( ) ;
if ( ! current_location ) {
SetSpawnOrigX ( GetX ( ) ) ;
SetSpawnOrigY ( GetY ( ) ) ;
SetSpawnOrigZ ( GetZ ( ) ) ;
SetSpawnOrigHeading ( GetHeading ( ) ) ;
}
is_running = true ;
MMovementLocations . lock ( ) ;
movement_locations - > push_back ( data ) ;
MMovementLocations . unlock ( ) ;
if ( ! IsPauseMovementTimerActive ( ) & & finished_adding_locations ) {
MMovementLocations . lock ( ) ;
current_location = movement_locations - > front ( ) ;
SetSpeed ( current_location - > speed ) ;
if ( movement_locations - > size ( ) > 1 ) {
data = movement_locations - > at ( 1 ) ;
RunToLocation ( current_location - > x , current_location - > y , current_location - > z , data - > x , data - > y , data - > z ) ;
}
else
RunToLocation ( current_location - > x , current_location - > y , current_location - > z , 0 , 0 , 0 ) ;
MMovementLocations . unlock ( ) ;
}
}
bool Spawn : : RemoveRunningLocation ( ) {
bool ret = false ;
MMovementLocations . lock ( ) ;
if ( movement_locations & & movement_locations - > size ( ) > 0 ) {
delete movement_locations - > front ( ) ;
movement_locations - > pop_front ( ) ;
ret = true ;
}
MMovementLocations . unlock ( ) ;
ValidateRunning ( true , false ) ;
return ret ;
}
void Spawn : : ClearRunningLocations ( ) {
while ( RemoveRunningLocation ( ) ) { }
}
void Spawn : : NewWaypointChange ( MovementLocation * data ) {
if ( data ) {
if ( NeedsToResumeMovement ( ) ) {
resume_movement = true ;
NeedsToResumeMovement ( false ) ;
}
if ( ! data - > attackable )
SetHeading ( GetSpawnOrigHeading ( ) ) ;
}
if ( data & & data - > lua_function . length ( ) > 0 )
GetZone ( ) - > CallSpawnScript ( this , SPAWN_SCRIPT_CUSTOM , 0 , data - > lua_function . c_str ( ) ) ;
RemoveRunningLocation ( ) ;
}
bool Spawn : : CalculateChange ( ) {
bool remove_needed = false ;
MovementLocation * data = 0 ;
MovementLocation tmpLoc ;
MMovementLocations . lock_shared ( ) ;
if ( movement_locations ) {
if ( movement_locations - > size ( ) > 0 ) {
// Target location
data = movement_locations - > front ( ) ;
if ( data ) {
tmpLoc . attackable = data - > attackable ;
tmpLoc . gridid = data - > gridid ;
tmpLoc . lua_function = string ( data - > lua_function ) ;
tmpLoc . mapped = data - > mapped ;
tmpLoc . reset_hp_on_runback = data - > reset_hp_on_runback ;
tmpLoc . speed = data - > speed ;
tmpLoc . stage = data - > stage ;
tmpLoc . x = data - > x ;
tmpLoc . y = data - > y ;
tmpLoc . z = data - > z ;
2025-01-29 22:01:25 -05:00
tmpLoc . use_nav_path = data - > use_nav_path ;
2024-07-22 08:52:28 -04:00
data = & tmpLoc ;
}
// If no target or we are at the target location need to remove this point
2025-01-30 09:51:28 -05:00
if ( ! data )
2024-07-22 08:52:28 -04:00
remove_needed = true ;
}
}
MMovementLocations . unlock_shared ( ) ;
if ( remove_needed ) {
NewWaypointChange ( data ) ;
}
else if ( data ) {
// Speed is per second so we need a time_step (amount of time since the last update) to modify movement by
float time_step = ( Timer : : GetCurrentTime2 ( ) - last_movement_update ) * 0.001 ; // * 0.001 is the same as / 1000, float muliplications is suppose to be faster though
2025-01-30 09:51:28 -05:00
// Ensure time_step has a minimum value to avoid excessively small movements
time_step = fmax ( time_step , 0.01f ) ; // Minimum time_step of 10ms to ensure meaningful movement
2024-07-22 08:52:28 -04:00
// Get current location
float nx = GetX ( ) ;
float ny = GetY ( ) ;
float nz = GetZ ( ) ;
// Get Forward vecotr
float tar_vx = data - > x - nx ;
float tar_vy = data - > y - ny ;
float tar_vz = data - > z - nz ;
2025-01-30 09:51:28 -05:00
2024-07-22 08:52:28 -04:00
float len = sqrtf ( tar_vx * tar_vx + tar_vy * tar_vy + tar_vz * tar_vz ) ;
2025-01-30 09:51:28 -05:00
float lenNoY = sqrtf ( tar_vx * tar_vx + tar_vz * tar_vz ) ;
// Scale speed based on distance, ensuring smooth slow down
float base_speed = GetSpeed ( ) ;
float snap_threshold = fmax ( 0.4f * base_speed , 0.5f ) ; // Minimum threshold of 0.5 ensures entities can snap even at low speeds
float speed = fmin ( base_speed , len / time_step ) ; // Prevent overshooting by capping speed to remaining distance / time_step
// Normalize the forward vector and scale by adjusted speed
if ( len > 0.0f ) { // Avoid division by zero
tar_vx = ( tar_vx / len ) * speed * time_step ;
tar_vy = ( tar_vy / len ) * speed * time_step ;
tar_vz = ( tar_vz / len ) * speed * time_step ;
}
2024-07-22 08:52:28 -04:00
// Distance less then 0.5 just set the npc to the target location
2025-01-30 09:51:28 -05:00
if ( len < = snap_threshold | | ( ! ( IsFlyingCreature ( ) | | IsWaterCreature ( ) | | InWater ( ) ) & & lenNoY < = snap_threshold ) ) {
2024-07-22 08:52:28 -04:00
SetX ( data - > x , false ) ;
SetZ ( data - > z , false ) ;
SetY ( data - > y , false , true ) ;
remove_needed = true ;
NewWaypointChange ( data ) ;
}
else {
SetX ( nx + tar_vx , false ) ;
SetZ ( nz + tar_vz , false ) ;
2025-01-28 09:52:44 -05:00
if ( IsWidget ( ) ) {
2024-07-22 08:52:28 -04:00
SetY ( ny + tar_vy , false , true ) ;
2025-01-28 09:52:44 -05:00
}
else {
2024-07-22 08:52:28 -04:00
SetY ( ny + tar_vy , false ) ;
2025-01-28 09:52:44 -05:00
}
2024-07-22 08:52:28 -04:00
}
int32 newGrid = GetLocation ( ) ;
if ( GetMap ( ) ! = nullptr ) {
m_GridMutex . writelock ( __FUNCTION__ , __LINE__ ) ;
std : : map < int32 , TimedGridData > : : iterator itr = established_grid_id . begin ( ) ;
if ( itr = = established_grid_id . end ( ) | | itr - > second . timestamp < = ( Timer : : GetCurrentTime2 ( ) ) )
{
if ( itr ! = established_grid_id . end ( ) & & itr - > second . x = = GetX ( ) & & itr - > second . z = = GetZ ( ) ) {
itr - > second . timestamp = Timer : : GetCurrentTime2 ( ) + 1000 ;
itr - > second . npc_save = true ;
newGrid = itr - > second . grid_id ;
}
else {
auto loc = glm : : vec3 ( GetX ( ) , GetZ ( ) , GetY ( ) ) ;
float new_z = FindBestZ ( loc , nullptr , & newGrid ) ;
TimedGridData data ;
data . grid_id = newGrid ;
data . x = GetX ( ) ;
data . y = GetY ( ) ;
data . z = GetZ ( ) ;
data . npc_save = true ;
data . zone_ground_y = new_z ;
data . offset_y = new_z ;
data . timestamp = Timer : : GetCurrentTime2 ( ) + 1000 ;
established_grid_id . insert ( make_pair ( 0 , data ) ) ;
}
}
else
newGrid = itr - > second . grid_id ;
m_GridMutex . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
}
if ( ( ! IsFlyingCreature ( ) | | IsTransportSpawn ( ) ) & & newGrid ! = 0 & & newGrid ! = GetLocation ( ) )
SetLocation ( newGrid ) ;
}
return remove_needed ;
}
void Spawn : : CalculateRunningLocation ( bool stop ) {
bool pauseTimerEnabled = IsPauseMovementTimerActive ( ) ;
2025-01-30 10:04:18 -05:00
if ( ! pauseTimerEnabled & & ! stop & & ( last_location_update + 10 ) > Timer : : GetCurrentTime2 ( ) )
2024-07-22 08:52:28 -04:00
return ;
else if ( ! pauseTimerEnabled & & ! stop )
last_location_update = Timer : : GetCurrentTime2 ( ) ;
bool continueElseIf = true ;
bool removed = CalculateChange ( ) ;
if ( stop | | pauseTimerEnabled ) {
//following = false;
SetPos ( & appearance . pos . X2 , GetX ( ) , false ) ;
SetPos ( & appearance . pos . Y2 , GetY ( ) , false ) ;
SetPos ( & appearance . pos . Z2 , GetZ ( ) , false ) ;
SetPos ( & appearance . pos . X3 , GetX ( ) , false ) ;
SetPos ( & appearance . pos . Y3 , GetY ( ) , false ) ;
SetPos ( & appearance . pos . Z3 , GetZ ( ) , false ) ;
continueElseIf = false ;
}
else if ( removed ) {
MMovementLocations . lock_shared ( ) ;
if ( movement_locations ) {
if ( movement_locations - > size ( ) > 0 ) {
MovementLocation * current_location = movement_locations - > at ( 0 ) ;
if ( movement_locations - > size ( ) > 1 ) {
MovementLocation * data = movement_locations - > at ( 1 ) ;
2025-01-29 15:13:26 -05:00
if ( current_location - > use_nav_path ) {
GetZone ( ) - > movementMgr - > NavigateTo ( ( Entity * ) this , current_location - > x , current_location - > y , current_location - > z ) ;
}
else {
RunToLocation ( current_location - > x , current_location - > y , current_location - > z , data - > x , data - > y , data - > z ) ;
}
}
else {
if ( current_location - > use_nav_path ) {
GetZone ( ) - > movementMgr - > NavigateTo ( ( Entity * ) this , current_location - > x , current_location - > y , current_location - > z ) ;
}
else {
RunToLocation ( current_location - > x , current_location - > y , current_location - > z , 0 , 0 , 0 ) ;
}
2024-07-22 08:52:28 -04:00
}
continueElseIf = false ;
}
}
MMovementLocations . unlock_shared ( ) ;
}
if ( continueElseIf & & GetZone ( ) & & GetTarget ( ) ! = NULL & & EngagedInCombat ( ) )
{
2024-10-20 15:02:39 -04:00
if ( GetDistance ( GetTarget ( ) ) > rule_manager . GetZoneRule ( GetZoneID ( ) , R_Combat , MaxCombatRange ) - > GetFloat ( ) )
2024-07-22 08:52:28 -04:00
{
if ( ( IsFlyingCreature ( ) | | IsWaterCreature ( ) | | InWater ( ) ) & & CheckLoS ( GetTarget ( ) ) )
AddRunningLocation ( GetTarget ( ) - > GetX ( ) , GetTarget ( ) - > GetY ( ) , GetTarget ( ) - > GetZ ( ) , GetSpeed ( ) , 0 , false ) ;
else
GetZone ( ) - > movementMgr - > NavigateTo ( ( Entity * ) this , GetTarget ( ) - > GetX ( ) , GetTarget ( ) - > GetY ( ) , GetTarget ( ) - > GetZ ( ) ) ;
}
else
( ( Entity * ) this ) - > HaltMovement ( ) ;
}
else if ( continueElseIf & & ! following )
{
position_changed = true ;
changed = true ;
GetZone ( ) - > AddChangedSpawn ( this ) ;
}
}
float Spawn : : GetFaceTarget ( float x , float z ) {
float angle ;
double diff_x = x - GetX ( ) ;
double diff_z = z - GetZ ( ) ;
if ( diff_z = = 0 ) {
if ( diff_x > 0 )
angle = 90 ;
else
angle = 270 ;
}
else
angle = ( ( atan ( diff_x / diff_z ) ) * 180 ) / 3.14159265358979323846 ;
if ( angle < 0 )
angle = angle + 360 ;
else if ( angle > - 0.0000001 & & angle < 0.0000001 )
angle = 0 ;
else
angle = angle + 180 ;
if ( ( diff_x < 0 & & diff_z ! = 0.0 ) | | ( diff_x = = 0 & & diff_z > 0.0 ) )
angle = angle + 180 ;
if ( angle > 360 )
angle = angle - 360.0 ;
return angle ;
}
void Spawn : : FaceTarget ( float x , float z ) {
float angle ;
double diff_x = x - GetX ( ) ;
double diff_z = z - GetZ ( ) ;
if ( diff_z = = 0 ) {
if ( diff_x > 0 )
angle = 90 ;
else
angle = 270 ;
}
else
angle = ( ( atan ( diff_x / diff_z ) ) * 180 ) / 3.14159265358979323846 ;
if ( angle < 0 )
angle = angle + 360 ;
else if ( angle > - 0.0000001 & & angle < 0.0000001 )
angle = 0 ;
else
angle = angle + 180 ;
if ( ( diff_x < 0 & & diff_z ! = 0.0 ) | | ( diff_x = = 0 & & diff_z > 0.0 ) )
angle = angle + 180 ;
if ( angle > 360 )
angle = angle - 360.0 ;
SetHeading ( angle ) ;
}
void Spawn : : FaceTarget ( Spawn * target , bool disable_action_state ) {
if ( ! target )
return ;
2025-05-04 13:29:35 -04:00
2025-04-26 08:33:00 -04:00
FaceTarget ( target - > GetX ( ) , target - > GetZ ( ) ) ;
2025-05-04 13:29:35 -04:00
2024-07-22 08:52:28 -04:00
if ( GetHP ( ) > 0 & & target - > IsPlayer ( ) & & ! EngagedInCombat ( ) ) {
if ( ! IsPet ( ) & & disable_action_state ) {
if ( IsNPC ( ) ) {
( ( NPC * ) this ) - > StartRunback ( ) ;
( ( NPC * ) this ) - > PauseMovement ( 30000 ) ;
}
2025-04-26 08:41:29 -04:00
SetTempActionState ( 0 ) ;
2024-07-22 08:52:28 -04:00
}
}
}
bool Spawn : : MeetsSpawnAccessRequirements ( Player * player ) {
bool ret = false ;
Quest * quest = 0 ;
//Check if we meet all quest requirements first..
m_requiredQuests . readlock ( __FUNCTION__ , __LINE__ ) ;
if ( player & & required_quests . size ( ) > 0 ) {
map < int32 , vector < int16 > * > : : iterator itr ;
for ( itr = required_quests . begin ( ) ; itr ! = required_quests . end ( ) ; itr + + ) {
player - > AddQuestRequiredSpawn ( this , itr - > first ) ;
vector < int16 > * quest_steps = itr - > second ;
for ( int32 i = 0 ; i < quest_steps - > size ( ) ; i + + ) {
quest = player - > GetQuest ( itr - > first ) ;
if ( req_quests_continued_access ) {
if ( quest ) {
if ( quest - > GetQuestStepCompleted ( quest_steps - > at ( i ) ) ) {
ret = true ;
break ;
}
}
else if ( player - > HasQuestBeenCompleted ( itr - > first ) ) {
ret = true ;
break ;
}
}
if ( quest & & quest - > QuestStepIsActive ( quest_steps - > at ( i ) ) ) {
ret = true ;
break ;
}
}
}
}
else
ret = true ;
m_requiredQuests . releasereadlock ( __FUNCTION__ , __LINE__ ) ;
if ( ! ret )
return ret ;
//Now check if the player meets all history requirements
m_requiredHistory . readlock ( __FUNCTION__ , __LINE__ ) ;
if ( required_history . size ( ) > 0 ) {
map < int32 , LUAHistory * > : : iterator itr ;
for ( itr = required_history . begin ( ) ; itr ! = required_history . end ( ) ; itr + + ) {
player - > AddHistoryRequiredSpawn ( this , itr - > first ) ;
LUAHistory * player_history = player - > GetLUAHistory ( itr - > first ) ;
if ( player_history ) {
if ( player_history - > Value ! = itr - > second - > Value | | player_history - > Value2 ! = itr - > second - > Value2 )
ret = false ;
}
else
ret = false ;
if ( ! ret )
break ;
}
}
m_requiredHistory . releasereadlock ( __FUNCTION__ , __LINE__ ) ;
return ret ;
}
vector < Spawn * > * Spawn : : GetSpawnGroup ( ) {
vector < Spawn * > * ret_list = 0 ;
if ( spawn_group_list ) {
ret_list = new vector < Spawn * > ( ) ;
if ( MSpawnGroup )
MSpawnGroup - > readlock ( __FUNCTION__ , __LINE__ ) ;
ret_list - > insert ( ret_list - > begin ( ) , spawn_group_list - > begin ( ) , spawn_group_list - > end ( ) ) ;
if ( MSpawnGroup )
MSpawnGroup - > releasereadlock ( __FUNCTION__ , __LINE__ ) ;
}
return ret_list ;
}
bool Spawn : : HasSpawnGroup ( ) {
return spawn_group_list & & spawn_group_list - > size ( ) > 0 ;
}
bool Spawn : : IsInSpawnGroup ( Spawn * spawn ) {
bool ret = false ;
if ( HasSpawnGroup ( ) & & spawn ) {
vector < Spawn * > : : iterator itr ;
for ( itr = spawn_group_list - > begin ( ) ; itr ! = spawn_group_list - > end ( ) ; itr + + ) {
if ( ( * itr ) = = spawn ) {
ret = true ;
break ;
}
}
}
return ret ;
}
Spawn * Spawn : : IsSpawnGroupMembersAlive ( Spawn * ignore_spawn , bool npc_only ) {
Spawn * ret = nullptr ;
if ( MSpawnGroup & & HasSpawnGroup ( ) ) {
MSpawnGroup - > readlock ( __FUNCTION__ , __LINE__ ) ;
vector < Spawn * > : : iterator itr ;
for ( itr = spawn_group_list - > begin ( ) ; itr ! = spawn_group_list - > end ( ) ; itr + + ) {
if ( ( * itr ) ! = ignore_spawn & & ( * itr ) - > Alive ( ) & & ( ! npc_only | | ( npc_only & & ( * itr ) - > IsNPC ( ) ) ) ) {
ret = ( * itr ) ;
break ;
}
}
MSpawnGroup - > releasereadlock ( __FUNCTION__ , __LINE__ ) ;
}
return ret ;
}
void Spawn : : UpdateEncounterState ( int8 new_state ) {
if ( MSpawnGroup & & HasSpawnGroup ( ) ) {
MSpawnGroup - > readlock ( __FUNCTION__ , __LINE__ ) ;
vector < Spawn * > : : iterator itr ;
for ( itr = spawn_group_list - > begin ( ) ; itr ! = spawn_group_list - > end ( ) ; itr + + ) {
if ( ( * itr ) - > Alive ( ) & & ( * itr ) - > IsNPC ( ) ) {
NPC * npc = ( NPC * ) ( * itr ) ;
( * itr ) - > SetLockedNoLoot ( new_state ) ;
if ( new_state = = ENCOUNTER_STATE_BROKEN & & npc - > Brain ( ) ) {
npc - > Brain ( ) - > ClearEncounter ( ) ;
}
}
}
MSpawnGroup - > releasereadlock ( __FUNCTION__ , __LINE__ ) ;
}
}
void Spawn : : CheckEncounterState ( Entity * victim , bool test_auto_lock ) {
if ( ! IsEntity ( ) | | ! victim - > IsNPC ( ) )
return ;
Entity * ent = ( ( Entity * ) this ) ;
if ( victim - > GetLockedNoLoot ( ) = = ENCOUNTER_STATE_AVAILABLE ) {
2024-09-28 09:00:42 -04:00
if ( IsInSpawnGroup ( victim ) )
return ; // can't aggro your own members
2024-07-22 08:52:28 -04:00
Entity * attacker = nullptr ;
if ( ent - > GetOwner ( ) )
attacker = ent - > GetOwner ( ) ;
else
attacker = ent ;
bool matchedAutoLock = false ;
if ( attacker - > IsEntity ( ) & & ( ( Entity * ) attacker ) - > GetGroupMemberInfo ( ) ) {
world . GetGroupManager ( ) - > GroupLock ( __FUNCTION__ , __LINE__ ) ;
GroupMemberInfo * gmi = ( ( Entity * ) attacker ) - > GetGroupMemberInfo ( ) ;
if ( gmi & & gmi - > group_id )
{
PlayerGroup * group = world . GetGroupManager ( ) - > GetGroup ( gmi - > group_id ) ;
if ( group & & ( ( group - > GetGroupOptions ( ) - > group_lock_method & & group - > GetGroupOptions ( ) - > group_autolock = = 1 ) | | attacker - > GetGroupMemberInfo ( ) - > leader ) )
{
matchedAutoLock = true ;
group - > MGroupMembers . readlock ( __FUNCTION__ , __LINE__ ) ;
deque < GroupMemberInfo * > * members = group - > GetMembers ( ) ;
for ( int8 i = 0 ; i < members - > size ( ) ; i + + ) {
Entity * member = members - > at ( i ) - > member ;
if ( ! member | | member - > GetZone ( ) ! = attacker - > GetZone ( ) )
continue ;
if ( member - > IsEntity ( ) ) {
if ( ! member - > GetInfoStruct ( ) - > get_engaged_encounter ( ) ) {
member - > GetInfoStruct ( ) - > set_engaged_encounter ( 1 ) ;
}
if ( ( ( NPC * ) victim ) - > Brain ( ) ) {
( ( NPC * ) victim ) - > Brain ( ) - > AddHate ( member , 0 ) ;
( ( NPC * ) victim ) - > Brain ( ) - > AddToEncounter ( member ) ;
victim - > AddTargetToEncounter ( member ) ;
}
}
}
group - > MGroupMembers . releasereadlock ( __FUNCTION__ , __LINE__ ) ;
}
}
world . GetGroupManager ( ) - > ReleaseGroupLock ( __FUNCTION__ , __LINE__ ) ;
}
2024-11-28 07:25:10 -05:00
else if ( attacker - > GetInfoStruct ( ) - > get_group_solo_autolock ( ) | | attacker - > GetInfoStruct ( ) - > get_group_autolock ( ) ) {
2024-07-22 08:52:28 -04:00
matchedAutoLock = true ;
if ( ( ( NPC * ) victim ) - > Brain ( ) ) {
( ( NPC * ) victim ) - > Brain ( ) - > AddHate ( attacker , 0 ) ;
( ( NPC * ) victim ) - > Brain ( ) - > AddToEncounter ( attacker ) ;
victim - > AddTargetToEncounter ( attacker ) ;
}
}
if ( test_auto_lock & & ! matchedAutoLock ) {
return ;
}
if ( ! ent - > GetInfoStruct ( ) - > get_engaged_encounter ( ) ) {
ent - > GetInfoStruct ( ) - > set_engaged_encounter ( 1 ) ;
}
if ( ! attacker - > GetInfoStruct ( ) - > get_engaged_encounter ( ) ) {
attacker - > GetInfoStruct ( ) - > set_engaged_encounter ( 1 ) ;
}
2024-10-20 15:02:39 -04:00
int8 skip_loot_gray_mob_flag = rule_manager . GetZoneRule ( GetZoneID ( ) , R_Loot , SkipLootGrayMob ) - > GetInt8 ( ) ;
2024-07-22 08:52:28 -04:00
int8 difficulty = attacker - > GetArrowColor ( victim - > GetLevel ( ) ) ;
int8 new_enc_state = ENCOUNTER_STATE_AVAILABLE ;
if ( skip_loot_gray_mob_flag & & difficulty = = ARROW_COLOR_GRAY ) {
if ( ! attacker - > IsPlayer ( ) & & ! attacker - > IsBot ( ) ) {
new_enc_state = ENCOUNTER_STATE_BROKEN ;
}
else {
new_enc_state = ENCOUNTER_STATE_OVERMATCHED ;
}
}
else {
if ( attacker - > IsPlayer ( ) | | attacker - > IsBot ( ) ) {
new_enc_state = ENCOUNTER_STATE_LOCKED ;
}
else {
new_enc_state = ENCOUNTER_STATE_BROKEN ;
}
}
victim - > SetLockedNoLoot ( new_enc_state ) ;
victim - > UpdateEncounterState ( new_enc_state ) ;
}
}
void Spawn : : AddTargetToEncounter ( Entity * entity ) {
if ( MSpawnGroup & & HasSpawnGroup ( ) ) {
MSpawnGroup - > readlock ( __FUNCTION__ , __LINE__ ) ;
vector < Spawn * > : : iterator itr ;
for ( itr = spawn_group_list - > begin ( ) ; itr ! = spawn_group_list - > end ( ) ; itr + + ) {
if ( ( * itr ) ! = this & & ( * itr ) - > Alive ( ) & & ( * itr ) - > IsNPC ( ) ) {
( ( NPC * ) ( * itr ) ) - > Brain ( ) - > AddToEncounter ( entity ) ;
}
}
MSpawnGroup - > releasereadlock ( __FUNCTION__ , __LINE__ ) ;
}
}
void Spawn : : AddSpawnToGroup ( Spawn * spawn ) {
if ( ! spawn )
return ;
if ( ! spawn_group_list ) {
spawn_group_list = new vector < Spawn * > ( ) ;
spawn_group_list - > push_back ( this ) ;
safe_delete ( MSpawnGroup ) ;
MSpawnGroup = new Mutex ( ) ;
MSpawnGroup - > SetName ( " Spawn::MSpawnGroup " ) ;
}
vector < Spawn * > : : iterator itr ;
MSpawnGroup - > writelock ( __FUNCTION__ , __LINE__ ) ;
for ( itr = spawn_group_list - > begin ( ) ; itr ! = spawn_group_list - > end ( ) ; itr + + ) {
if ( ( * itr ) = = spawn ) {
MSpawnGroup - > releasewritelock ( __FUNCTION__ , __LINE__ ) ;
return ;
}
}
spawn_group_list - > push_back ( spawn ) ;
spawn - > SetSpawnGroupList ( spawn_group_list , MSpawnGroup ) ;
MSpawnGroup - > releasewritelock ( __FUNCTION__ , __LINE__ ) ;
}
void Spawn : : SetSpawnGroupList ( vector < Spawn * > * list , Mutex * mutex ) {
spawn_group_list = list ;
MSpawnGroup = mutex ;
}
2025-01-16 12:29:24 -05:00
void Spawn : : RemoveSpawnFromGroup ( bool erase_all , bool ignore_death ) {
if ( ! ignore_death )
SetSpawnGroupID ( 0 ) ;
2024-07-22 08:52:28 -04:00
bool del = false ;
if ( MSpawnGroup ) {
MSpawnGroup - > writelock ( __FUNCTION__ , __LINE__ ) ;
if ( spawn_group_list ) {
vector < Spawn * > : : iterator itr ;
Spawn * spawn = 0 ;
2025-01-16 12:29:24 -05:00
if ( spawn_group_list - > size ( ) = = 1 & & ! ignore_death )
2024-07-22 08:52:28 -04:00
erase_all = true ;
for ( itr = spawn_group_list - > begin ( ) ; itr ! = spawn_group_list - > end ( ) ; itr + + ) {
spawn = * itr ;
if ( spawn ) {
if ( ! erase_all ) {
if ( spawn = = this ) {
spawn_group_list - > erase ( itr ) ;
MSpawnGroup - > releasewritelock ( __FUNCTION__ , __LINE__ ) ;
spawn_group_list = 0 ;
MSpawnGroup = 0 ;
return ;
}
}
else {
if ( spawn ! = this )
spawn - > SetSpawnGroupList ( 0 , 0 ) ;
}
}
}
if ( erase_all )
spawn_group_list - > clear ( ) ;
del = ( spawn_group_list - > size ( ) = = 0 ) ;
}
MSpawnGroup - > releasewritelock ( __FUNCTION__ , __LINE__ ) ;
if ( del ) {
safe_delete ( MSpawnGroup ) ;
safe_delete ( spawn_group_list ) ;
}
}
}
void Spawn : : SetSpawnGroupID ( int32 id ) {
m_SpawnMutex . writelock ( ) ;
group_id = id ;
m_SpawnMutex . releasewritelock ( ) ;
}
int32 Spawn : : GetSpawnGroupID ( ) {
int32 groupid = 0 ;
m_SpawnMutex . readlock ( ) ;
groupid = group_id ;
m_SpawnMutex . releasereadlock ( ) ;
return groupid ;
}
void Spawn : : AddChangedZoneSpawn ( ) {
if ( send_spawn_changes & & GetZone ( ) )
GetZone ( ) - > AddChangedSpawn ( this ) ;
}
void Spawn : : RemoveSpawnAccess ( Spawn * spawn ) {
if ( allowed_access . count ( spawn - > GetID ( ) ) > 0 ) {
allowed_access . erase ( spawn - > GetID ( ) ) ;
GetZone ( ) - > HidePrivateSpawn ( this ) ;
}
}
void Spawn : : SetFollowTarget ( Spawn * spawn , int32 follow_distance ) {
if ( spawn & & spawn ! = this ) {
m_followTarget = spawn - > GetID ( ) ;
m_followDistance = follow_distance ;
}
else {
m_followTarget = 0 ;
if ( following )
following = false ;
m_followDistance = 0 ;
}
}
void Spawn : : AddTempVariable ( string var , string val ) {
m_tempVariableTypes [ var ] = 5 ;
m_tempVariables [ var ] = val ;
}
void Spawn : : AddTempVariable ( string var , Spawn * val ) {
m_tempVariableTypes [ var ] = 1 ;
m_tempVariableSpawn [ var ] = val - > GetID ( ) ;
}
void Spawn : : AddTempVariable ( string var , ZoneServer * val ) {
m_tempVariableTypes [ var ] = 2 ;
m_tempVariableZone [ var ] = val ;
}
void Spawn : : AddTempVariable ( string var , Item * val ) {
m_tempVariableTypes [ var ] = 3 ;
m_tempVariableItem [ var ] = val ;
}
void Spawn : : AddTempVariable ( string var , Quest * val ) {
m_tempVariableTypes [ var ] = 4 ;
m_tempVariableQuest [ var ] = val ;
}
string Spawn : : GetTempVariable ( string var ) {
string ret = " " ;
if ( m_tempVariables . count ( var ) > 0 )
ret = m_tempVariables [ var ] ;
return ret ;
}
Spawn * Spawn : : GetTempVariableSpawn ( string var ) {
Spawn * ret = 0 ;
if ( m_tempVariableSpawn . count ( var ) > 0 )
ret = GetZone ( ) - > GetSpawnByID ( m_tempVariableSpawn [ var ] ) ;
return ret ;
}
ZoneServer * Spawn : : GetTempVariableZone ( string var ) {
ZoneServer * ret = 0 ;
if ( m_tempVariableZone . count ( var ) > 0 )
ret = m_tempVariableZone [ var ] ;
return ret ;
}
Item * Spawn : : GetTempVariableItem ( string var ) {
Item * ret = 0 ;
if ( m_tempVariableItem . count ( var ) > 0 )
ret = m_tempVariableItem [ var ] ;
return ret ;
}
Quest * Spawn : : GetTempVariableQuest ( string var ) {
Quest * ret = 0 ;
if ( m_tempVariableQuest . count ( var ) > 0 )
ret = m_tempVariableQuest [ var ] ;
return ret ;
}
int8 Spawn : : GetTempVariableType ( string var ) {
int8 ret = 0 ;
if ( m_tempVariableTypes . count ( var ) > 0 )
ret = m_tempVariableTypes [ var ] ;
return ret ;
}
void Spawn : : DeleteTempVariable ( string var ) {
int8 type = GetTempVariableType ( var ) ;
switch ( type ) {
case 1 :
m_tempVariableSpawn . erase ( var ) ;
break ;
case 2 :
m_tempVariableZone . erase ( var ) ;
break ;
case 3 :
m_tempVariableItem . erase ( var ) ;
break ;
case 4 :
m_tempVariableQuest . erase ( var ) ;
break ;
case 5 :
m_tempVariables . erase ( var ) ;
break ;
}
m_tempVariableTypes . erase ( var ) ;
}
Spawn * Spawn : : GetRunningTo ( ) {
return GetZone ( ) - > GetSpawnByID ( running_to ) ;
}
Spawn * Spawn : : GetFollowTarget ( ) {
return GetZone ( ) - > GetSpawnByID ( m_followTarget ) ;
}
void Spawn : : CopySpawnAppearance ( Spawn * spawn ) {
if ( ! spawn )
return ;
//This function copies the appearace of the provided spawn to this one
if ( spawn - > IsEntity ( ) & & IsEntity ( ) ) {
memcpy ( & ( ( Entity * ) this ) - > features , & ( ( Entity * ) spawn ) - > features , sizeof ( CharFeatures ) ) ;
memcpy ( & ( ( Entity * ) this ) - > equipment , & ( ( Entity * ) spawn ) - > equipment , sizeof ( EQ2_Equipment ) ) ;
}
SetSize ( spawn - > GetSize ( ) ) ;
SetModelType ( spawn - > GetModelType ( ) ) ;
}
void Spawn : : SetY ( float y , bool updateFlags , bool disableYMapCheck )
{
SetPos ( & appearance . pos . Y , y , updateFlags ) ;
if ( ! disableYMapCheck )
FixZ ( ) ;
}
float Spawn : : FindDestGroundZ ( glm : : vec3 dest , float z_offset )
{
float best_z = BEST_Z_INVALID ;
if ( GetZone ( ) ! = nullptr & & GetMap ( ) ! = nullptr )
{
dest . z + = z_offset ;
best_z = FindBestZ ( dest , nullptr ) ;
}
return best_z ;
}
float Spawn : : FindBestZ ( glm : : vec3 loc , glm : : vec3 * result , int32 * new_grid_id , int32 * new_widget_id ) {
std : : shared_lock lock ( MIgnoredWidgets ) ;
if ( ! GetMap ( ) )
return BEST_Z_INVALID ;
float new_z = GetMap ( ) - > FindBestZ ( loc , nullptr , & ignored_widgets , new_grid_id , new_widget_id ) ;
return new_z ;
}
float Spawn : : GetFixedZ ( const glm : : vec3 & destination , int32 z_find_offset ) {
BenchTimer timer ;
timer . reset ( ) ;
float new_z = destination . z ;
if ( GetZone ( ) ! = nullptr & & GetMap ( ) ! = nullptr ) {
/* if (flymode == GravityBehavior::Flying)
return new_z ;
*/
/* if (zone->HasWaterMap() && zone->watermap->InLiquid(glm::vec3(m_Position)))
return new_z ;
*/
/*
* Any more than 5 in the offset makes NPC ' s hop / snap to ceiling in small corridors
*/
new_z = this - > FindDestGroundZ ( destination , z_find_offset ) ;
if ( new_z ! = BEST_Z_INVALID ) {
if ( new_z < - 2000 ) {
new_z = GetY ( ) ;
}
}
auto duration = timer . elapsed ( ) ;
LogWrite ( MAP__DEBUG , 0 , " Map " , " Mob::GetFixedZ() ([{%s}]) returned [{%f}] at [{%f}], [{%f}], [{%f}] - Took [{%f}] " ,
this - > GetName ( ) ,
new_z ,
destination . x ,
destination . y ,
destination . z ,
duration ) ;
}
return new_z ;
}
void Spawn : : FixZ ( bool forceUpdate ) {
if ( ! GetZone ( ) ) {
return ;
}
/*
if ( flymode = = GravityBehavior : : Flying ) {
return ;
} */
/*
if ( zone - > watermap & & zone - > watermap - > InLiquid ( m_Position ) ) {
return ;
} */
// we do the inwater check here manually to avoid double calling for a Z coordinate
glm : : vec3 current_loc ( GetX ( ) , GetZ ( ) , GetY ( ) ) ;
uint32 GridID = 0 ;
uint32 WidgetID = 0 ;
float new_z = GetY ( ) ;
if ( GetMap ( ) ! = nullptr ) {
new_z = FindBestZ ( current_loc , nullptr , & GridID , & WidgetID ) ;
if ( ( IsTransportSpawn ( ) | | ! IsFlyingCreature ( ) ) & & GridID ! = 0 & & GridID ! = GetLocation ( ) ) {
LogWrite ( PLAYER__DEBUG , 0 , " Player " , " %s left grid %u and entered grid %u " , appearance . name , GetLocation ( ) , GridID ) ;
const char * zone_script = world . GetZoneScript ( GetZone ( ) - > GetZoneID ( ) ) ;
if ( zone_script & & lua_interface ) {
lua_interface - > RunZoneScript ( zone_script , " leave_location " , GetZone ( ) , this , GetLocation ( ) ) ;
}
SetLocation ( GridID ) ;
if ( zone_script & & lua_interface ) {
lua_interface - > RunZoneScript ( zone_script , " enter_location " , GetZone ( ) , this , GridID ) ;
}
}
trigger_widget_id = WidgetID ;
}
// no need to go any further for players, flying creatures or objects, just needed the grid id set
if ( IsPlayer ( ) | | IsFlyingCreature ( ) | | IsObject ( ) ) {
return ;
}
if ( region_map ! = nullptr )
{
glm : : vec3 targPos ( GetX ( ) , GetY ( ) , GetZ ( ) ) ;
if ( region_map - > InWater ( targPos , GetLocation ( ) ) )
return ;
}
if ( new_z = = GetY ( ) )
return ;
if ( ( new_z > - 2000 ) & & new_z ! = BEST_Z_INVALID ) {
SetY ( new_z , forceUpdate , true ) ;
}
else {
LogWrite ( MAP__DEBUG , 0 , " Map " , " [{%s}] is failing to find Z [{%f}] " , this - > GetName ( ) , std : : abs ( GetY ( ) - new_z ) ) ;
}
}
bool Spawn : : CheckLoS ( Spawn * target )
{
float radiusSrc = 2.0f ;
float radiusTarg = 2.0f ;
glm : : vec3 targpos ( target - > GetX ( ) , target - > GetZ ( ) , target - > GetY ( ) + radiusTarg ) ;
glm : : vec3 pos ( GetX ( ) , GetZ ( ) , GetY ( ) + radiusSrc ) ;
return CheckLoS ( pos , targpos ) ;
}
bool Spawn : : CheckLoS ( glm : : vec3 myloc , glm : : vec3 oloc )
{
bool res = false ;
ZoneServer * zone = GetZone ( ) ;
if ( zone = = NULL | | GetMap ( ) = = NULL | | ! GetMap ( ) - > IsMapLoaded ( ) )
return true ;
else {
MIgnoredWidgets . lock_shared ( ) ;
res = GetMap ( ) - > CheckLoS ( myloc , oloc , & ignored_widgets ) ;
MIgnoredWidgets . unlock_shared ( ) ;
}
return res ;
}
void Spawn : : CalculateNewFearpoint ( )
{
if ( GetZone ( ) & & GetZone ( ) - > pathing ) {
auto Node = zone - > pathing - > GetRandomLocation ( glm : : vec3 ( GetX ( ) , GetZ ( ) , GetY ( ) ) ) ;
if ( Node . x ! = 0.0f | | Node . y ! = 0.0f | | Node . z ! = 0.0f ) {
AddRunningLocation ( Node . x , Node . y , Node . z , GetSpeed ( ) , 0 , true , true , " " , true ) ;
}
}
}
Item * Spawn : : LootItem ( int32 id ) {
Item * ret = 0 ;
vector < Item * > : : iterator itr ;
MLootItems . lock ( ) ;
for ( itr = loot_items . begin ( ) ; itr ! = loot_items . end ( ) ; itr + + ) {
if ( ( * itr ) - > details . item_id = = id ) {
ret = * itr ;
loot_items . erase ( itr ) ;
break ;
}
}
MLootItems . unlock ( ) ;
return ret ;
}
void Spawn : : TransferLoot ( Spawn * spawn ) {
if ( spawn = = this | | spawn = = nullptr )
return ; // mmm no
vector < Item * > : : iterator itr ;
MLootItems . lock ( ) ;
for ( itr = loot_items . begin ( ) ; itr ! = loot_items . end ( ) ; ) {
if ( ! ( * itr ) - > IsBodyDrop ( ) ) {
spawn - > AddLootItem ( * itr ) ;
itr = loot_items . erase ( itr ) ;
}
else {
itr + + ;
}
}
MLootItems . unlock ( ) ;
}
int32 Spawn : : GetLootItemID ( ) {
int32 ret = 0 ;
vector < Item * > : : iterator itr ;
MLootItems . lock ( ) ;
for ( itr = loot_items . begin ( ) ; itr ! = loot_items . end ( ) ; itr + + ) {
ret = ( * itr ) - > details . item_id ;
break ;
}
MLootItems . unlock ( ) ;
return ret ;
}
void Spawn : : GetLootItemsList ( std : : vector < int32 > * out_entries ) {
if ( ! out_entries )
return ;
vector < Item * > : : iterator itr ;
for ( itr = loot_items . begin ( ) ; itr ! = loot_items . end ( ) ; itr + + ) {
out_entries - > push_back ( ( * itr ) - > details . item_id ) ;
}
}
bool Spawn : : HasLootItemID ( int32 id ) {
bool ret = false ;
vector < Item * > : : iterator itr ;
MLootItems . readlock ( __FUNCTION__ , __LINE__ ) ;
for ( itr = loot_items . begin ( ) ; itr ! = loot_items . end ( ) ; itr + + ) {
if ( ( * itr ) - > details . item_id = = id ) {
ret = true ;
break ;
}
}
MLootItems . releasereadlock ( __FUNCTION__ , __LINE__ ) ;
return ret ;
}
void Spawn : : CheckProximities ( )
{
if ( ! has_spawn_proximities )
return ;
if ( spawn_proximities . size ( ) > 0 )
{
MutexList < SpawnProximity * > : : iterator itr = spawn_proximities . begin ( ) ;
while ( itr . Next ( ) ) {
SpawnProximity * prox = itr . value ;
map < int32 , bool > : : iterator spawnsItr ;
for ( spawnsItr = prox - > spawns_in_proximity . begin ( ) ; spawnsItr ! = prox - > spawns_in_proximity . end ( ) ; spawnsItr + + ) {
Spawn * tmpSpawn = 0 ;
if ( spawnsItr - > first & &
( ( prox - > spawn_type = = SPAWNPROXIMITY_DATABASE_ID & & ( tmpSpawn = GetZone ( ) - > GetSpawnByDatabaseID ( spawnsItr - > first ) ) ! = 0 ) | |
( prox - > spawn_type = = SPAWNPROXIMITY_LOCATION_ID & & ( tmpSpawn = GetZone ( ) - > GetSpawnByLocationID ( spawnsItr - > first ) ) ! = 0 ) ) )
{
if ( ! spawnsItr - > second & & tmpSpawn - > GetDistance ( this ) < = prox - > distance )
{
if ( prox - > in_range_lua_function . size ( ) > 0 )
GetZone ( ) - > CallSpawnScript ( this , SPAWN_SCRIPT_CUSTOM , tmpSpawn , prox - > in_range_lua_function . c_str ( ) ) ;
spawnsItr - > second = true ;
}
else if ( spawnsItr - > second & & tmpSpawn - > GetDistance ( this ) > prox - > distance )
{
if ( prox - > leaving_range_lua_function . size ( ) > 0 )
GetZone ( ) - > CallSpawnScript ( this , SPAWN_SCRIPT_CUSTOM , tmpSpawn , prox - > leaving_range_lua_function . c_str ( ) ) ;
spawnsItr - > second = false ;
}
}
}
}
}
}
void Spawn : : AddSpawnToProximity ( int32 spawnValue , SpawnProximityType type )
{
if ( ! has_spawn_proximities )
return ;
if ( spawn_proximities . size ( ) > 0 )
{
MutexList < SpawnProximity * > : : iterator itr = spawn_proximities . begin ( ) ;
while ( itr . Next ( ) ) {
SpawnProximity * prox = itr - > value ;
if ( prox - > spawn_value = = spawnValue & & prox - > spawn_type = = type )
prox - > spawns_in_proximity . insert ( make_pair ( spawnValue , false ) ) ;
}
}
}
void Spawn : : RemoveSpawnFromProximity ( int32 spawnValue , SpawnProximityType type )
{
if ( ! has_spawn_proximities )
return ;
if ( spawn_proximities . size ( ) > 0 )
{
MutexList < SpawnProximity * > : : iterator itr = spawn_proximities . begin ( ) ;
while ( itr . Next ( ) ) {
SpawnProximity * prox = itr - > value ;
if ( prox - > spawn_value = = spawnValue & & prox - > spawn_type = = type & &
prox - > spawns_in_proximity . count ( spawnValue ) > 0 )
prox - > spawns_in_proximity . erase ( spawnValue ) ;
}
}
}
void Spawn : : AddPrimaryEntityCommand ( const char * name , float distance , const char * command , const char * error_text , int16 cast_time , int32 spell_visual , bool defaultDenyList , Player * player ) {
EntityCommand * cmd = FindEntityCommand ( string ( command ) , true ) ;
bool newCommand = false ;
if ( ! cmd )
{
newCommand = true ;
cmd = CreateEntityCommand ( name , distance , command , error_text , cast_time , spell_visual , ! defaultDenyList ) ;
}
if ( defaultDenyList )
SetPermissionToEntityCommand ( cmd , player , true ) ;
if ( newCommand )
primary_command_list . push_back ( cmd ) ;
}
void Spawn : : RemovePrimaryEntityCommand ( const char * command ) {
vector < EntityCommand * > : : iterator itr ;
string tmpStr ( command ) ;
for ( itr = primary_command_list . begin ( ) ; itr ! = primary_command_list . end ( ) ; itr + + ) {
EntityCommand * cmd = * itr ;
if ( cmd - > command . compare ( tmpStr ) = = 0 )
{
primary_command_list . erase ( itr ) ;
delete cmd ;
break ;
}
}
}
bool Spawn : : SetPermissionToEntityCommand ( EntityCommand * command , Player * player , bool permissionValue )
{
if ( ! player )
return false ;
return SetPermissionToEntityCommandByCharID ( command , player - > GetCharacterID ( ) , permissionValue ) ;
}
bool Spawn : : SetPermissionToEntityCommandByCharID ( EntityCommand * command , int32 charID , bool permissionValue )
{
map < int32 , bool > : : iterator itr = command - > allow_or_deny . find ( charID ) ;
if ( itr = = command - > allow_or_deny . end ( ) )
command - > allow_or_deny . insert ( make_pair ( charID , permissionValue ) ) ;
else if ( itr - > second ! = permissionValue )
itr - > second = permissionValue ;
return true ;
}
void Spawn : : RemoveSpawnFromPlayer ( Player * player )
{
m_Update . writelock ( __FUNCTION__ , __LINE__ ) ;
player - > RemoveSpawn ( this ) ; // sets it as removed
m_Update . releasewritelock ( __FUNCTION__ , __LINE__ ) ;
}
bool Spawn : : InWater ( )
{
bool inWater = false ;
if ( region_map ! = nullptr )
{
glm : : vec3 targPos ( GetX ( ) , GetY ( ) , GetZ ( ) ) ;
if ( IsGroundSpawn ( ) )
targPos . y - = .5f ;
if ( region_map - > InWater ( targPos , GetLocation ( ) ) )
inWater = true ;
}
return inWater ;
}
bool Spawn : : InLava ( )
{
bool inLava = false ;
if ( region_map ! = nullptr )
{
glm : : vec3 targPos ( GetX ( ) , GetY ( ) , GetZ ( ) ) ;
if ( IsGroundSpawn ( ) )
targPos . y - = .5f ;
if ( region_map - > InLava ( targPos , GetLocation ( ) ) )
inLava = true ;
}
return inLava ;
}
void Spawn : : DeleteRegion ( Region_Node * inNode , ZBSP_Node * rootNode )
{
map < map < Region_Node * , ZBSP_Node * > , Region_Status > : : iterator testitr ;
for ( testitr = Regions . begin ( ) ; testitr ! = Regions . end ( ) ; testitr + + )
{
map < Region_Node * , ZBSP_Node * > : : const_iterator actualItr = testitr - > first . begin ( ) ;
Region_Node * node = actualItr - > first ;
ZBSP_Node * BSP_Root = actualItr - > second ;
if ( inNode = = node & & rootNode = = BSP_Root )
{
testitr = Regions . erase ( testitr ) ;
break ;
}
}
}
bool Spawn : : InRegion ( Region_Node * inNode , ZBSP_Node * rootNode )
{
map < map < Region_Node * , ZBSP_Node * > , Region_Status > : : iterator testitr ;
for ( testitr = Regions . begin ( ) ; testitr ! = Regions . end ( ) ; testitr + + )
{
map < Region_Node * , ZBSP_Node * > : : const_iterator actualItr = testitr - > first . begin ( ) ;
Region_Node * node = actualItr - > first ;
ZBSP_Node * BSP_Root = actualItr - > second ;
if ( inNode = = node & & rootNode = = BSP_Root )
{
return testitr - > second . inRegion ;
}
}
return false ;
}
int32 Spawn : : GetRegionType ( Region_Node * inNode , ZBSP_Node * rootNode )
{
map < map < Region_Node * , ZBSP_Node * > , Region_Status > : : iterator testitr ;
for ( testitr = Regions . begin ( ) ; testitr ! = Regions . end ( ) ; testitr + + )
{
map < Region_Node * , ZBSP_Node * > : : const_iterator actualItr = testitr - > first . begin ( ) ;
Region_Node * node = actualItr - > first ;
ZBSP_Node * BSP_Root = actualItr - > second ;
if ( inNode = = node & & rootNode = = BSP_Root )
{
return testitr - > second . regionType ;
}
}
return false ;
}
float Spawn : : SpawnAngle ( Spawn * target , float selfx , float selfz )
{
if ( ! target | | target = = this )
return 0.0f ;
float angle , lengthb , vectorx , vectorz , dotp ;
float spx = ( target - > GetX ( ) ) ; // mob xloc (inverse because eq)
float spz = - ( target - > GetZ ( ) ) ; // mob yloc
float heading = target - > GetHeading ( ) ; // mob heading
if ( heading < 270 )
heading + = 90 ;
else
heading - = 270 ;
heading = heading * 3.1415f / 180.0f ; // convert to radians
vectorx = spx + ( 10.0f * std : : cos ( heading ) ) ; // create a vector based on heading
vectorz = spz + ( 10.0f * std : : sin ( heading ) ) ; // of spawn length 10
// length of spawn to player vector
lengthb = ( float ) std : : sqrt ( ( ( selfx - spx ) * ( selfx - spx ) ) + ( ( - selfz - spz ) * ( - selfz - spz ) ) ) ;
// calculate dot product to get angle
// Handle acos domain errors due to floating point rounding errors
dotp = ( ( vectorx - spx ) * ( selfx - spx ) +
( vectorz - spz ) * ( - selfz - spz ) ) / ( 10.0f * lengthb ) ;
if ( dotp > 1 )
return 0.0f ;
else if ( dotp < - 1 )
return 180.0f ;
angle = std : : acos ( dotp ) ;
angle = angle * 180.0f / 3.1415f ;
return angle ;
}
void Spawn : : StopMovement ( )
{
reset_movement = true ;
}
bool Spawn : : PauseMovement ( int32 period_of_time_ms )
{
if ( period_of_time_ms < 1 )
period_of_time_ms = 1 ;
RunToLocation ( GetX ( ) , GetY ( ) , GetZ ( ) ) ;
pause_timer . Start ( period_of_time_ms , true ) ;
return true ;
}
bool Spawn : : IsPauseMovementTimerActive ( )
{
if ( pause_timer . Check ( ) )
pause_timer . Disable ( ) ;
return pause_timer . Enabled ( ) ;
}
bool Spawn : : IsFlyingCreature ( )
{
if ( ! IsEntity ( ) )
return false ;
return ( ( Entity * ) this ) - > GetInfoStruct ( ) - > get_flying_type ( ) ;
}
bool Spawn : : IsWaterCreature ( )
{
if ( ! IsEntity ( ) )
return false ;
return ( ( Entity * ) this ) - > GetInfoStruct ( ) - > get_water_type ( ) ;
}
void Spawn : : SetFlyingCreature ( ) {
2024-10-20 15:02:39 -04:00
if ( ! IsEntity ( ) | | ! rule_manager . GetZoneRule ( GetZoneID ( ) , R_Spawn , UseHardCodeFlyingModelType ) - > GetInt8 ( ) )
2024-07-22 08:52:28 -04:00
return ;
if ( ( ( Entity * ) this ) - > GetInfoStruct ( ) - > get_flying_type ( ) > 0 ) // DB spawn npc flag already set
return ;
switch ( GetModelType ( ) )
{
case 260 :
case 295 :
( ( Entity * ) this ) - > GetInfoStruct ( ) - > set_flying_type ( 1 ) ;
is_flying_creature = true ;
break ;
default :
( ( Entity * ) this ) - > GetInfoStruct ( ) - > set_flying_type ( 0 ) ;
break ;
}
}
void Spawn : : SetWaterCreature ( ) {
2024-10-20 15:02:39 -04:00
if ( ! IsEntity ( ) | | ! rule_manager . GetZoneRule ( GetZoneID ( ) , R_Spawn , UseHardCodeWaterModelType ) - > GetInt8 ( ) )
2024-07-22 08:52:28 -04:00
return ;
if ( ( ( Entity * ) this ) - > GetInfoStruct ( ) - > get_water_type ( ) > 0 ) // DB spawn npc flag already set
return ;
switch ( GetModelType ( ) )
{
case 194 :
case 204 :
case 210 :
case 241 :
case 242 :
case 254 :
case 10668 :
case 20828 :
( ( Entity * ) this ) - > GetInfoStruct ( ) - > set_water_type ( 1 ) ;
break ;
default :
( ( Entity * ) this ) - > GetInfoStruct ( ) - > set_water_type ( 0 ) ;
break ;
}
}
void Spawn : : AddRailPassenger ( int32 char_id )
{
std : : lock_guard < std : : mutex > lk ( m_RailMutex ) ;
rail_passengers . insert ( make_pair ( char_id , true ) ) ;
}
void Spawn : : RemoveRailPassenger ( int32 char_id )
{
std : : lock_guard < std : : mutex > lk ( m_RailMutex ) ;
std : : map < int32 , bool > : : iterator itr = rail_passengers . find ( char_id ) ;
if ( itr ! = rail_passengers . end ( ) )
rail_passengers . erase ( itr ) ;
}
vector < Spawn * > Spawn : : GetPassengersOnRail ( ) {
vector < Spawn * > tmp_list ;
Spawn * spawn ;
m_RailMutex . lock ( ) ;
std : : map < int32 , bool > : : iterator itr = rail_passengers . begin ( ) ;
while ( itr ! = rail_passengers . end ( ) ) {
Client * client = zone_list . GetClientByCharID ( itr - > first ) ;
if ( ! client | | ! client - > GetPlayer ( ) )
continue ;
tmp_list . push_back ( client - > GetPlayer ( ) ) ;
itr + + ;
}
m_RailMutex . unlock ( ) ;
return tmp_list ;
}
void Spawn : : SetAppearancePosition ( float x , float y , float z ) {
appearance . pos . X = x ;
appearance . pos . Y = y ;
appearance . pos . Z = z ;
appearance . pos . X2 = appearance . pos . X ;
appearance . pos . Y2 = appearance . pos . Y ;
appearance . pos . Z2 = appearance . pos . Z ;
appearance . pos . X3 = appearance . pos . X ;
appearance . pos . Y3 = appearance . pos . Y ;
appearance . pos . Z3 = appearance . pos . Z ;
SetSpeedX ( 0 ) ;
SetSpeedY ( 0 ) ;
SetSpeedZ ( 0 ) ;
if ( IsPlayer ( ) ) {
( ( Player * ) this ) - > SetSideSpeed ( 0 ) ;
( ( Player * ) this ) - > pos_packet_speed = 0 ;
}
}
int32 Spawn : : InsertRegionToSpawn ( Region_Node * node , ZBSP_Node * bsp_root , WaterRegionType regionType , bool in_region ) {
std : : map < Region_Node * , ZBSP_Node * > newMap ;
newMap . insert ( make_pair ( node , bsp_root ) ) ;
Region_Status status ;
status . inRegion = in_region ;
status . regionType = regionType ;
int32 returnValue = 0 ;
if ( in_region ) {
lua_interface - > RunRegionScript ( node - > regionScriptName , " EnterRegion " , GetZone ( ) , this , regionType , & returnValue ) ;
}
status . timerTic = returnValue ;
status . lastTimerTic = returnValue ? Timer : : GetCurrentTime2 ( ) : 0 ;
Regions . insert ( make_pair ( newMap , status ) ) ;
return returnValue ;
}
bool Spawn : : HasRegionTracked ( Region_Node * node , ZBSP_Node * bsp_root , bool in_region ) {
map < map < Region_Node * , ZBSP_Node * > , Region_Status > : : iterator testitr ;
for ( testitr = Regions . begin ( ) ; testitr ! = Regions . end ( ) ; testitr + + )
{
map < Region_Node * , ZBSP_Node * > : : const_iterator actualItr = testitr - > first . begin ( ) ;
Region_Node * node = actualItr - > first ;
ZBSP_Node * BSP_Root = actualItr - > second ;
if ( node = = actualItr - > first & & BSP_Root = = actualItr - > second ) {
if ( testitr - > second . inRegion = = in_region )
return true ;
else
break ;
}
}
return false ;
}
void Spawn : : SetLocation ( int32 id , bool setUpdateFlags )
{
if ( GetZone ( ) ) {
GetZone ( ) - > RemoveSpawnFromGrid ( this , GetLocation ( ) ) ;
SetPos ( & appearance . pos . grid_id , id , setUpdateFlags ) ;
GetZone ( ) - > AddSpawnToGrid ( this , id ) ;
}
else {
SetPos ( & appearance . pos . grid_id , id , setUpdateFlags ) ;
}
}
int8 Spawn : : GetArrowColor ( int8 spawn_level ) {
int8 color = 0 ;
sint16 diff = spawn_level - GetLevel ( ) ;
if ( GetLevel ( ) < 10 )
diff * = 3 ;
else if ( GetLevel ( ) < = 20 )
diff * = 2 ;
if ( diff > = 9 )
color = ARROW_COLOR_RED ;
else if ( diff > = 5 )
color = ARROW_COLOR_ORANGE ;
else if ( diff > = 1 )
color = ARROW_COLOR_YELLOW ;
else if ( diff = = 0 )
color = ARROW_COLOR_WHITE ;
else if ( diff < = - 11 )
color = ARROW_COLOR_GRAY ;
else if ( diff < = - 6 )
color = ARROW_COLOR_GREEN ;
else //if(diff < 0)
color = ARROW_COLOR_BLUE ;
return color ;
}
void Spawn : : AddIgnoredWidget ( int32 id ) {
std : : unique_lock lock ( MIgnoredWidgets ) ;
if ( ignored_widgets . find ( id ) = = ignored_widgets . end ( ) ) {
ignored_widgets . insert ( make_pair ( id , true ) ) ;
}
}
void Spawn : : SendGroupUpdate ( ) {
if ( IsEntity ( ) & & ( ( Entity * ) this ) - > GetGroupMemberInfo ( ) ) {
( ( Entity * ) this ) - > UpdateGroupMemberInfo ( ) ;
Project Nebulark Part 1, So much to list, this is a rough summary
- Raid support and cross peer support for Isle of Refuge, DoF, KoS and AoM clients.
- Zone Persistence added to non-instanced zones.
- Commands: /whogroup, /whoraid, /raidinvite, /raid_looter, /kickfromgroup, /kickfromraid, /leaveraid, /split, /raidsay (rsay) added.
- Cross peer zone and instance support
- Cross tell support (along with ignore)
- Cross ooc support
- Cross group support (can chat, leave group, disband cross peers, update group options)
- Cross who all support
- houses/instances fixed no more cross objects/spawns/etc from other houses
- houses now display characters name with the house zone description
- 1000's of house items now properly work with wall/ceiling
- debug messages removed from housing placement
- Encounters locked to raid instead of group
- group options restricted to raid leader
- reload rules for following are peer wide:
COMMAND_RELOADSTRUCTS
COMMAND_RELOAD_QUESTS
COMMAND_RELOAD_SPELLS
COMMAND_RELOAD_ZONESCRIPTS
COMMAND_RELOAD_FACTIONS
COMMAND_RELOAD_MAIL
COMMAND_RELOAD_GUILDS
COMMAND_RELOAD_RULES
COMMAND_RELOAD_STARTABILITIES
COMMAND_RELOAD_VOICEOVERS
COMMAND_RELOADSPAWNSCRIPTS
COMMAND_RELOADREGIONSCRIPTS
COMMAND_RELOADLUASYSTEM
- special/static zones (always_loaded) are now defined by a peer_priority unsigned short (smallint(5)) in zones table. peer_priority = server_config world.peerpriority will spawn on that exe instance, if it is not available it is distributed to all peers. Using the value of 0 (assuming no peer has priority of 0) or 65535 will result in peer distribution of zones.
server_config.json "WorldServer" block updated with the following (web peer port information), priority must be unique for EACH peer:
"peeraddress": "10.1.1.2",
"peerport": "9102",
"peerpriority": "1",
New Command Line Run Arguments for World Exe to override server_config.json values
Allowed options:
--worldaddress arg World address
--internalworldaddress arg Internal world address
--worldport arg (=0) Web world port
--webworldaddress arg Web world address
--webworldport arg (=0) Web world port
--peerpriority arg (=0) Peer priority
- fixed Isle of Refuge client group struct (raids added also)
- new log category Peering
- new LUA Functions AddRespawn(Zone, LocationID, RespawnTime) and CreatePersistedRespawn(LocationID, SpawnType, RespawnTime, ZoneID)
2024-11-18 11:13:04 -05:00
world . GetGroupManager ( ) - > SendGroupUpdate ( ( ( Entity * ) this ) - > GetGroupMemberInfo ( ) - > group_id ) ;
2024-07-22 08:52:28 -04:00
}
}
bool Spawn : : AddNeedGreedItemRequest ( int32 item_id , int32 spawn_id , bool need_item ) {
LogWrite ( LOOT__INFO , 0 , " Loot " , " %s: AddNeedGreedItemRequest Item ID: %u, Spawn ID: %u, Need Item: %u " , GetName ( ) , item_id , spawn_id , need_item ) ;
if ( HasSpawnNeedGreedEntry ( item_id , spawn_id ) ) {
return false ;
}
need_greed_items . insert ( make_pair ( item_id , std : : make_pair ( spawn_id , need_item ) ) ) ;
AddSpawnLootWindowCompleted ( spawn_id , false ) ;
return true ;
}
bool Spawn : : AddLottoItemRequest ( int32 item_id , int32 spawn_id ) {
LogWrite ( LOOT__INFO , 0 , " Loot " , " %s: AddLottoItemRequest Item ID: %u, Spawn ID: %u " , GetName ( ) , item_id , spawn_id ) ;
if ( HasSpawnLottoEntry ( item_id , spawn_id ) ) {
return false ;
}
lotto_items . insert ( make_pair ( item_id , spawn_id ) ) ;
AddSpawnLootWindowCompleted ( spawn_id , false ) ;
return true ;
}
void Spawn : : AddSpawnLootWindowCompleted ( int32 spawn_id , bool status_ ) {
if ( loot_complete . find ( spawn_id ) = = loot_complete . end ( ) ) {
loot_complete . insert ( make_pair ( spawn_id , status_ ) ) ;
}
is_loot_complete = HasLootWindowCompleted ( ) ;
}
bool Spawn : : SetSpawnLootWindowCompleted ( int32 spawn_id ) {
std : : map < int32 , bool > : : iterator itr = loot_complete . find ( spawn_id ) ;
if ( itr ! = loot_complete . end ( ) ) {
itr - > second = true ;
is_loot_complete = HasLootWindowCompleted ( ) ;
return true ;
}
return false ;
}
bool Spawn : : HasSpawnLootWindowCompleted ( int32 spawn_id ) {
std : : map < int32 , bool > : : iterator itr = loot_complete . find ( spawn_id ) ;
if ( itr ! = loot_complete . end ( ) & & itr - > second ) {
return true ;
}
return false ;
}
bool Spawn : : HasSpawnNeedGreedEntry ( int32 item_id , int32 spawn_id ) {
for ( auto [ itr , rangeEnd ] = need_greed_items . equal_range ( item_id ) ; itr ! = rangeEnd ; itr + + ) {
Project Nebulark Part 1, So much to list, this is a rough summary
- Raid support and cross peer support for Isle of Refuge, DoF, KoS and AoM clients.
- Zone Persistence added to non-instanced zones.
- Commands: /whogroup, /whoraid, /raidinvite, /raid_looter, /kickfromgroup, /kickfromraid, /leaveraid, /split, /raidsay (rsay) added.
- Cross peer zone and instance support
- Cross tell support (along with ignore)
- Cross ooc support
- Cross group support (can chat, leave group, disband cross peers, update group options)
- Cross who all support
- houses/instances fixed no more cross objects/spawns/etc from other houses
- houses now display characters name with the house zone description
- 1000's of house items now properly work with wall/ceiling
- debug messages removed from housing placement
- Encounters locked to raid instead of group
- group options restricted to raid leader
- reload rules for following are peer wide:
COMMAND_RELOADSTRUCTS
COMMAND_RELOAD_QUESTS
COMMAND_RELOAD_SPELLS
COMMAND_RELOAD_ZONESCRIPTS
COMMAND_RELOAD_FACTIONS
COMMAND_RELOAD_MAIL
COMMAND_RELOAD_GUILDS
COMMAND_RELOAD_RULES
COMMAND_RELOAD_STARTABILITIES
COMMAND_RELOAD_VOICEOVERS
COMMAND_RELOADSPAWNSCRIPTS
COMMAND_RELOADREGIONSCRIPTS
COMMAND_RELOADLUASYSTEM
- special/static zones (always_loaded) are now defined by a peer_priority unsigned short (smallint(5)) in zones table. peer_priority = server_config world.peerpriority will spawn on that exe instance, if it is not available it is distributed to all peers. Using the value of 0 (assuming no peer has priority of 0) or 65535 will result in peer distribution of zones.
server_config.json "WorldServer" block updated with the following (web peer port information), priority must be unique for EACH peer:
"peeraddress": "10.1.1.2",
"peerport": "9102",
"peerpriority": "1",
New Command Line Run Arguments for World Exe to override server_config.json values
Allowed options:
--worldaddress arg World address
--internalworldaddress arg Internal world address
--worldport arg (=0) Web world port
--webworldaddress arg Web world address
--webworldport arg (=0) Web world port
--peerpriority arg (=0) Peer priority
- fixed Isle of Refuge client group struct (raids added also)
- new log category Peering
- new LUA Functions AddRespawn(Zone, LocationID, RespawnTime) and CreatePersistedRespawn(LocationID, SpawnType, RespawnTime, ZoneID)
2024-11-18 11:13:04 -05:00
LogWrite ( LOOT__DEBUG , 1 , " Loot " , " %s: HasSpawnNeedGreedEntry Item ID: %u, Spawn ID: %u " , GetName ( ) , itr - > first , itr - > second . first ) ;
2024-07-22 08:52:28 -04:00
if ( spawn_id = = itr - > second . first ) {
return true ;
}
}
return false ;
}
bool Spawn : : HasSpawnLottoEntry ( int32 item_id , int32 spawn_id ) {
for ( auto [ itr , rangeEnd ] = lotto_items . equal_range ( item_id ) ; itr ! = rangeEnd ; itr + + ) {
Project Nebulark Part 1, So much to list, this is a rough summary
- Raid support and cross peer support for Isle of Refuge, DoF, KoS and AoM clients.
- Zone Persistence added to non-instanced zones.
- Commands: /whogroup, /whoraid, /raidinvite, /raid_looter, /kickfromgroup, /kickfromraid, /leaveraid, /split, /raidsay (rsay) added.
- Cross peer zone and instance support
- Cross tell support (along with ignore)
- Cross ooc support
- Cross group support (can chat, leave group, disband cross peers, update group options)
- Cross who all support
- houses/instances fixed no more cross objects/spawns/etc from other houses
- houses now display characters name with the house zone description
- 1000's of house items now properly work with wall/ceiling
- debug messages removed from housing placement
- Encounters locked to raid instead of group
- group options restricted to raid leader
- reload rules for following are peer wide:
COMMAND_RELOADSTRUCTS
COMMAND_RELOAD_QUESTS
COMMAND_RELOAD_SPELLS
COMMAND_RELOAD_ZONESCRIPTS
COMMAND_RELOAD_FACTIONS
COMMAND_RELOAD_MAIL
COMMAND_RELOAD_GUILDS
COMMAND_RELOAD_RULES
COMMAND_RELOAD_STARTABILITIES
COMMAND_RELOAD_VOICEOVERS
COMMAND_RELOADSPAWNSCRIPTS
COMMAND_RELOADREGIONSCRIPTS
COMMAND_RELOADLUASYSTEM
- special/static zones (always_loaded) are now defined by a peer_priority unsigned short (smallint(5)) in zones table. peer_priority = server_config world.peerpriority will spawn on that exe instance, if it is not available it is distributed to all peers. Using the value of 0 (assuming no peer has priority of 0) or 65535 will result in peer distribution of zones.
server_config.json "WorldServer" block updated with the following (web peer port information), priority must be unique for EACH peer:
"peeraddress": "10.1.1.2",
"peerport": "9102",
"peerpriority": "1",
New Command Line Run Arguments for World Exe to override server_config.json values
Allowed options:
--worldaddress arg World address
--internalworldaddress arg Internal world address
--worldport arg (=0) Web world port
--webworldaddress arg Web world address
--webworldport arg (=0) Web world port
--peerpriority arg (=0) Peer priority
- fixed Isle of Refuge client group struct (raids added also)
- new log category Peering
- new LUA Functions AddRespawn(Zone, LocationID, RespawnTime) and CreatePersistedRespawn(LocationID, SpawnType, RespawnTime, ZoneID)
2024-11-18 11:13:04 -05:00
LogWrite ( LOOT__DEBUG , 1 , " Loot " , " %s: HasSpawnLottoEntry Item ID: %u, Spawn ID: %u " , GetName ( ) , itr - > first , itr - > second ) ;
2024-07-22 08:52:28 -04:00
if ( spawn_id = = itr - > second ) {
return true ;
}
}
return false ;
}
void Spawn : : GetSpawnLottoEntries ( int32 item_id , std : : map < int32 , int32 > * out_entries ) {
if ( ! out_entries )
return ;
std : : map < int32 , bool > spawn_matches ;
for ( auto [ itr , endrange ] = lotto_items . equal_range ( item_id ) ; itr ! = endrange ; itr + + ) {
out_entries - > insert ( std : : make_pair ( itr - > second , ( int32 ) MakeRandomInt ( 0 , 100 ) ) ) ;
spawn_matches [ itr - > second ] = true ;
}
// 0xFFFFFFFF represents selecting "All" on the lotto screen
for ( auto [ itr , endrange ] = lotto_items . equal_range ( 0xFFFFFFFF ) ; itr ! = endrange ; itr + + ) {
if ( spawn_matches . find ( itr - > second ) = = spawn_matches . end ( ) ) {
out_entries - > insert ( std : : make_pair ( itr - > second , ( int32 ) MakeRandomInt ( 0 , 100 ) ) ) ;
}
}
}
void Spawn : : GetSpawnNeedGreedEntries ( int32 item_id , bool need_item , std : : map < int32 , int32 > * out_entries ) {
if ( ! out_entries )
return ;
for ( auto [ itr , rangeEnd ] = need_greed_items . equal_range ( item_id ) ; itr ! = rangeEnd ; itr + + ) {
out_entries - > insert ( std : : make_pair ( itr - > second . first , ( int32 ) MakeRandomInt ( 0 , 100 ) ) ) ;
}
}
bool Spawn : : HasLootWindowCompleted ( ) {
std : : map < int32 , bool > : : iterator itr ;
for ( itr = loot_complete . begin ( ) ; itr ! = loot_complete . end ( ) ; itr + + ) {
if ( ! itr - > second )
return false ;
}
return true ;
}
void Spawn : : StartLootTimer ( Spawn * looter ) {
if ( ! IsLootTimerRunning ( ) ) {
2024-10-20 15:02:39 -04:00
int32 loot_timer_time = rule_manager . GetZoneRule ( GetZoneID ( ) , R_Loot , LootDistributionTime ) - > GetInt32 ( ) * 1000 ;
if ( rule_manager . GetZoneRule ( GetZoneID ( ) , R_Loot , AllowChestUnlockByDropTime ) - > GetBool ( ) & & loot_timer_time > rule_manager . GetZoneRule ( GetZoneID ( ) , R_Loot , ChestUnlockedTimeDrop ) - > GetInt32 ( ) * 1000 ) {
loot_timer_time = ( rule_manager . GetZoneRule ( GetZoneID ( ) , R_Loot , ChestUnlockedTimeDrop ) - > GetInt32 ( ) * 1000 ) / 2 ;
2024-07-22 08:52:28 -04:00
}
2024-10-20 15:02:39 -04:00
if ( rule_manager . GetZoneRule ( GetZoneID ( ) , R_Loot , AllowChestUnlockByTrapTime ) - > GetBool ( ) & & loot_timer_time > rule_manager . GetZoneRule ( GetZoneID ( ) , R_Loot , ChestUnlockedTimeTrap ) - > GetInt32 ( ) * 1000 ) {
loot_timer_time = ( rule_manager . GetZoneRule ( GetZoneID ( ) , R_Loot , ChestUnlockedTimeTrap ) - > GetInt32 ( ) * 1000 ) / 2 ;
2024-07-22 08:52:28 -04:00
}
if ( loot_timer_time < 1000 ) {
loot_timer_time = 60000 ; // hardcode assure they aren't setting some really ridiculous low number
}
loot_timer . Start ( loot_timer_time , true ) ;
}
if ( looter ) {
looter_spawn_id = looter - > GetID ( ) ;
}
}
void Spawn : : CloseLoot ( Spawn * sender ) {
if ( sender ) {
SetSpawnLootWindowCompleted ( sender - > GetID ( ) ) ;
}
if ( sender & & looter_spawn_id > 0 & & sender - > GetID ( ) ! = looter_spawn_id ) {
LogWrite ( LOOT__ERROR , 0 , " Loot " , " %s: CloseLoot Looter Spawn ID: %u does not match sender %u. " , GetName ( ) , looter_spawn_id , sender - > GetID ( ) ) ;
return ;
}
if ( ! IsLootTimerRunning ( ) & & GetLootMethod ( ) ! = GroupLootMethod : : METHOD_LOTTO & & GetLootMethod ( ) ! = GroupLootMethod : : METHOD_NEED_BEFORE_GREED ) {
loot_timer . Disable ( ) ;
}
looter_spawn_id = 0 ;
}
void Spawn : : SetLootMethod ( GroupLootMethod method , int8 item_rarity , int32 group_id ) {
LogWrite ( LOOT__INFO , 0 , " Loot " , " %s: Set Loot Method : %u, group id : %u " , GetName ( ) , ( int32 ) method , group_id ) ;
loot_group_id = group_id ;
loot_method = method ;
loot_rarity = item_rarity ;
if ( loot_name . size ( ) < 1 ) {
loot_name = std : : string ( GetName ( ) ) ;
}
}
bool Spawn : : IsItemInLootTier ( Item * item ) {
if ( ! item )
return true ;
bool skipItem = true ;
switch ( GetLootRarity ( ) ) {
case LootTier : : ITEMS_TREASURED_PLUS : {
if ( item - > details . tier > = ITEM_TAG_TREASURED ) {
skipItem = false ;
}
break ;
}
case LootTier : : ITEMS_LEGENDARY_PLUS : {
if ( item - > details . tier > = ITEM_TAG_LEGENDARY ) {
skipItem = false ;
}
break ;
}
case LootTier : : ITEMS_FABLED_PLUS : {
if ( item - > details . tier > = ITEM_TAG_FABLED ) {
skipItem = false ;
}
break ;
}
default : {
skipItem = false ;
break ;
}
}
return skipItem ;
}
void Spawn : : DistributeGroupLoot_RoundRobin ( std : : vector < int32 > * item_list , bool roundRobinTrashLoot ) {
std : : vector < int32 > : : iterator item_itr ;
for ( item_itr = item_list - > begin ( ) ; item_itr ! = item_list - > end ( ) ; item_itr + + ) {
int32 item_id = * item_itr ;
Item * tmpItem = master_item_list . GetItem ( item_id ) ;
Spawn * looter = nullptr ;
2024-12-28 18:36:59 -05:00
int8 group_idx = 0 ;
2024-07-22 08:52:28 -04:00
bool skipItem = IsItemInLootTier ( tmpItem ) ;
if ( ( skipItem & & ! roundRobinTrashLoot ) | | ( ! skipItem & & roundRobinTrashLoot ) )
continue ;
world . GetGroupManager ( ) - > GroupLock ( __FUNCTION__ , __LINE__ ) ;
PlayerGroup * group = world . GetGroupManager ( ) - > GetGroup ( GetLootGroupID ( ) ) ;
if ( group ) {
2024-12-28 18:36:59 -05:00
std : : vector < int32 > raidGroups ;
world . GetGroupManager ( ) - > GetRaidGroups ( GetLootGroupID ( ) , & raidGroups ) ;
2024-07-22 08:52:28 -04:00
int8 index = group - > GetLastLooterIndex ( ) ;
2024-12-28 18:36:59 -05:00
int8 actual_index = index ;
if ( raidGroups . size ( ) > 0 ) {
group_idx = ( index / 6 ) ;
if ( group_idx < raidGroups . size ( ) ) {
group = world . GetGroupManager ( ) - > GetGroup ( raidGroups . at ( group_idx ) ) ;
if ( ! group ) {
LogWrite ( LOOT__ERROR , 0 , " Loot " , " DistributeGroupLoot_RoundRobin: Failed to find group at index %u with value %u " , group_idx , raidGroups . at ( group_idx ) ) ;
world . GetGroupManager ( ) - > ReleaseGroupLock ( __FUNCTION__ , __LINE__ ) ;
return ;
}
if ( index > = 6 ) {
actual_index = index - ( group_idx * 6 ) ;
}
else {
actual_index = index ;
}
}
else {
LogWrite ( LOOT__ERROR , 0 , " Loot " , " DistributeGroupLoot_RoundRobin: Group at index %u out of range with raidGroups size %u " , group_idx , raidGroups . size ( ) ) ;
world . GetGroupManager ( ) - > ReleaseGroupLock ( __FUNCTION__ , __LINE__ ) ;
return ;
}
2024-07-22 08:52:28 -04:00
}
2024-12-28 18:36:59 -05:00
group - > MGroupMembers . readlock ( __FUNCTION__ , __LINE__ ) ;
int8 size = group - > GetMembers ( ) - > size ( ) ;
group - > MGroupMembers . releasereadlock ( __FUNCTION__ , __LINE__ ) ;
if ( actual_index > = size ) {
if ( raidGroups . size ( ) < 1 ) {
actual_index = 0 ;
index = 0 ;
}
else if ( index > = raidGroups . size ( ) * 6 ) {
group = world . GetGroupManager ( ) - > GetGroup ( raidGroups . at ( 0 ) ) ;
if ( ! group ) {
LogWrite ( LOOT__ERROR , 0 , " Loot " , " DistributeGroupLoot_RoundRobin: Failed to find main group " ) ;
world . GetGroupManager ( ) - > ReleaseGroupLock ( __FUNCTION__ , __LINE__ ) ;
return ;
}
actual_index = 0 ;
index = 0 ;
group_idx = 0 ;
}
else {
index = ( group_idx + 1 ) * 6 ;
actual_index = 0 ;
group_idx = ( index / 6 ) ;
if ( group_idx < raidGroups . size ( ) ) {
group = world . GetGroupManager ( ) - > GetGroup ( raidGroups . at ( group_idx ) ) ;
if ( ! group ) {
LogWrite ( LOOT__ERROR , 0 , " Loot " , " DistributeGroupLoot_RoundRobin: Failed to find group %u at index %u " , raidGroups . at ( group_idx ) , group_idx ) ;
world . GetGroupManager ( ) - > ReleaseGroupLock ( __FUNCTION__ , __LINE__ ) ;
return ;
}
}
else {
group = world . GetGroupManager ( ) - > GetGroup ( raidGroups . at ( 0 ) ) ;
group_idx = 0 ;
index = 0 ;
actual_index = 0 ;
if ( ! group ) {
LogWrite ( LOOT__ERROR , 0 , " Loot " , " DistributeGroupLoot_RoundRobin: Failed to find main group " ) ;
world . GetGroupManager ( ) - > ReleaseGroupLock ( __FUNCTION__ , __LINE__ ) ;
return ;
}
}
}
2024-07-22 08:52:28 -04:00
}
2024-12-28 18:36:59 -05:00
group - > MGroupMembers . readlock ( __FUNCTION__ , __LINE__ ) ;
deque < GroupMemberInfo * > * members = group - > GetMembers ( ) ;
bool foundLooter = false ;
2024-07-22 08:52:28 -04:00
bool loopAttempted = false ;
2024-12-28 18:36:59 -05:00
while ( ! foundLooter ) {
// If we've iterated past the current group's members
if ( actual_index > = members - > size ( ) ) {
if ( loopAttempted ) {
// If we've looped through all groups and members without finding a player, stop
looter = nullptr ;
break ;
}
// Move to the next group in the raid
group_idx + + ;
if ( group_idx > = raidGroups . size ( ) ) {
// If we've iterated through all raid groups, loop back to the first group
2024-07-22 08:52:28 -04:00
loopAttempted = true ;
2024-12-28 18:36:59 -05:00
group_idx = 0 ;
2024-07-22 08:52:28 -04:00
}
2024-12-28 18:36:59 -05:00
index = ( group_idx ) * 6 ;
actual_index = 0 ;
// Update the group pointer to the next group
group = world . GetGroupManager ( ) - > GetGroup ( raidGroups . at ( group_idx ) ) ;
if ( ! group ) {
LogWrite ( LOOT__ERROR , 0 , " Loot " , " Failed to find group with ID %u " , raidGroups . at ( group_idx ) ) ;
break ;
2024-07-22 08:52:28 -04:00
}
2024-12-28 18:36:59 -05:00
// Update members pointer for the new group
members = group - > GetMembers ( ) ;
2024-07-22 08:52:28 -04:00
}
2024-12-28 18:36:59 -05:00
// Get the current member
GroupMemberInfo * gmi = members - > at ( actual_index ) ;
if ( gmi & & gmi - > member & & gmi - > member - > IsPlayer ( ) ) {
looter = gmi - > member ;
foundLooter = true ;
} else {
// Move to the next member
actual_index + + ;
2024-07-22 08:52:28 -04:00
}
}
2024-12-28 18:36:59 -05:00
group - > MGroupMembers . releasereadlock ( __FUNCTION__ , __LINE__ ) ;
// Update the loot index across all raid groups
index + + ;
if ( raidGroups . size ( ) > 0 ) {
for ( size_t i = 0 ; i < raidGroups . size ( ) ; i + + ) {
PlayerGroup * tmpGroup = world . GetGroupManager ( ) - > GetGroup ( raidGroups . at ( i ) ) ;
if ( tmpGroup ) {
tmpGroup - > SetNextLooterIndex ( index ) ;
}
}
}
else if ( group ) {
group - > SetNextLooterIndex ( index ) ;
}
2024-07-22 08:52:28 -04:00
}
world . GetGroupManager ( ) - > ReleaseGroupLock ( __FUNCTION__ , __LINE__ ) ;
if ( looter ) {
if ( looter - > IsPlayer ( ) ) {
Item * item = LootItem ( item_id ) ;
bool success = false ;
success = ( ( Player * ) looter ) - > GetClient ( ) - > HandleLootItem ( this , item , ( ( Player * ) looter ) , roundRobinTrashLoot ) ;
if ( ! success )
AddLootItem ( item ) ;
}
else {
Item * item = LootItem ( item_id ) ;
safe_delete ( item ) ;
}
}
}
}
const double g = 9.81 ; // acceleration due to gravity (m/s^2)
void Spawn : : CalculateInitialVelocity ( float heading , float distanceHorizontal , float distanceVertical , float distanceDepth , float duration ) {
float vx = distanceHorizontal / duration ;
float vy = ( distanceVertical + 0.5 * g * duration * duration ) / duration ;
float vz = distanceDepth / duration ;
// Convert heading angle to radians
knocked_velocity . x = vx * cos ( heading ) ;
knocked_velocity . y = vy ;
knocked_velocity . z = vz * sin ( heading ) ;
}
// Function to calculate the projectile position at a given time
glm : : vec3 Spawn : : CalculateProjectilePosition ( glm : : vec3 initialVelocity , float time ) {
glm : : vec3 position ;
position . x = knocked_back_start_x + initialVelocity . x * time ;
position . y = knocked_back_start_y + initialVelocity . y * time - 0.5 * g * time * time ;
position . z = knocked_back_start_z + initialVelocity . z * time ;
auto loc = glm : : vec3 ( position . x , position . z , position . y ) ;
float new_z = FindBestZ ( loc , nullptr ) ;
if ( new_z > position . y )
position . y = new_z ;
return position ;
}
bool Spawn : : CalculateSpawnProjectilePosition ( float x , float y , float z ) {
float currentTimeOffset = ( Timer : : GetCurrentTime2 ( ) - last_movement_update ) * 0.001 ; // * 0.001 is the same as / 1000, float muliplications is suppose to be faster though
float stepAheadOne = currentTimeOffset + currentTimeOffset ;
knocked_back_time_step + = currentTimeOffset ;
if ( Timer : : GetCurrentTime2 ( ) > = knocked_back_end_time ) {
ResetKnockedBack ( ) ;
FixZ ( true ) ;
return false ;
}
glm : : vec3 position = CalculateProjectilePosition ( knocked_velocity , knocked_back_time_step ) ;
glm : : vec3 position_two = position ;
if ( Timer : : GetCurrentTime2 ( ) < = knocked_back_end_time + stepAheadOne ) {
position_two = CalculateProjectilePosition ( knocked_velocity , knocked_back_time_step + stepAheadOne ) ;
}
if ( GetMap ( ) ) {
glm : : vec3 loc ( GetX ( ) , GetZ ( ) , GetY ( ) + .5f ) ;
glm : : vec3 dest_loc ( position_two . x , position_two . z , position_two . y ) ;
MIgnoredWidgets . lock_shared ( ) ;
glm : : vec3 outNorm ;
float dist = 0.0f ;
bool collide_ = GetMap ( ) - > DoCollisionCheck ( loc , dest_loc , & ignored_widgets , outNorm , dist ) ;
if ( collide_ ) {
LogWrite ( SPAWN__ERROR , 0 , " Spawn " , " Collision Hit: cur loc x,y,z: %f %f %f. to loc %f %f %f. TimeOffset: %f Total Time: %f Duration: %f " , GetX ( ) , GetY ( ) , GetZ ( ) , position_two . x , position_two . y , position_two . z , currentTimeOffset , knocked_back_time_step , knocked_back_duration ) ;
MIgnoredWidgets . unlock_shared ( ) ;
ResetKnockedBack ( ) ;
FixZ ( true ) ;
return false ;
}
MIgnoredWidgets . unlock_shared ( ) ;
}
LogWrite ( SPAWN__ERROR , 0 , " Spawn " , " x,y,z: %f %f %f. Final %f %f %f. TimeOffset: %f Total Time: %f Duration: %f " , GetX ( ) , GetY ( ) , GetZ ( ) , position . x , position . y , position . z , currentTimeOffset , knocked_back_time_step , knocked_back_duration ) ;
SetX ( position . x , false ) ;
SetZ ( position . z , false ) ;
SetY ( position . y , false , true ) ;
SetPos ( & appearance . pos . X2 , position_two . x , false ) ;
SetPos ( & appearance . pos . Z2 , position_two . z , false ) ;
SetPos ( & appearance . pos . Y2 , position_two . y , false ) ;
SetPos ( & appearance . pos . X3 , position_two . x , false ) ;
SetPos ( & appearance . pos . Z3 , position_two . z , false ) ;
SetPos ( & appearance . pos . Y3 , position_two . y , false ) ;
position_changed = true ;
changed = true ;
GetZone ( ) - > AddChangedSpawn ( this ) ;
return true ;
}
void Spawn : : SetKnockback ( Spawn * target , int32 duration , float vertical , float horizontal ) {
if ( knocked_back ) {
return ; // already being knocked back
}
// Calculate the direction vector from source to destination
glm : : vec3 direction = { GetX ( ) - target - > GetX ( ) , GetZ ( ) - target - > GetZ ( ) , GetY ( ) - target - > GetY ( ) } ;
// Calculate the heading angle in radians
double headingRad = atan2 ( direction . y , sqrt ( direction . x * direction . x + direction . z * direction . z ) ) ;
knocked_angle = headingRad ;
knocked_back_start_x = GetX ( ) ;
knocked_back_start_y = GetY ( ) ;
knocked_back_start_z = GetZ ( ) ;
knocked_back_h_distance = horizontal / 10.0f ;
knocked_back_v_distance = vertical / 10.0f ;
knocked_back_duration = static_cast < float > ( duration ) / 1000.0f ;
knocked_back_end_time = Timer : : GetCurrentTime2 ( ) + duration ;
CalculateInitialVelocity ( knocked_angle , knocked_back_h_distance , knocked_back_h_distance , knocked_back_h_distance , knocked_back_duration ) ;
knocked_back = true ;
}
void Spawn : : ResetKnockedBack ( ) {
knocked_back = false ;
knocked_back_time_step = 0.0f ;
knocked_back_h_distance = 0.0f ;
knocked_back_v_distance = 0.0f ;
knocked_back_duration = 0.0f ;
knocked_back_end_time = 0 ;
knocked_back_start_x = 0.0f ;
knocked_back_start_y = 0.0f ;
knocked_back_start_z = 0.0f ;
knocked_angle = 0.0f ;
knocked_velocity . x = 0.0f ;
knocked_velocity . y = 0.0f ;
knocked_velocity . z = 0.0f ;
2025-05-04 13:29:35 -04:00
}