2013-05-09 10:44:08 -04:00
/* EQEMu: Everquest Server Emulator
Copyright ( C ) 2001 - 2003 EQEMu Development Team ( http : //eqemulator.net)
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
2016-10-30 22:12:46 -06:00
the Free Software Foundation ; version 2 of the License .
2013-05-06 13:07:41 -04:00
2013-05-09 10:44:08 -04:00
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY except by those people which sell it , which
2013-02-16 16:14:39 -08:00
are required to give you total support for your newly bought product ;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
2013-05-09 10:44:08 -04:00
A PARTICULAR PURPOSE . See the GNU General Public License for more details .
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
2013-05-09 10:44:08 -04:00
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
client_process . cpp :
Handles client login sequence and packets sent from client to zone
*/
2015-01-10 15:26:38 -06:00
# include "../common/eqemu_logsys.h"
2015-01-19 04:12:09 -06:00
# include "../common/global_define.h"
2013-02-16 16:14:39 -08:00
# include <iostream>
# include <stdio.h>
# include <zlib.h>
# ifdef _WINDOWS
2016-05-25 13:46:47 -04:00
# include <winsock2.h>
2013-02-16 16:14:39 -08:00
# include <windows.h>
# define snprintf _snprintf
# define strncasecmp _strnicmp
2013-05-09 10:44:08 -04:00
# define strcasecmp _stricmp
2013-02-16 16:14:39 -08:00
# else
# include <pthread.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <unistd.h>
# endif
2017-09-17 09:48:10 -05:00
# include "../common/data_verification.h"
2014-11-29 03:24:48 -06:00
# include "../common/rulesys.h"
2013-02-16 16:14:39 -08:00
# include "../common/skills.h"
2014-11-29 03:24:48 -06:00
# include "../common/spdat.h"
# include "../common/string_util.h"
2013-02-16 16:14:39 -08:00
# include "event_codes.h"
# include "guild_mgr.h"
2014-11-29 03:24:48 -06:00
# include "map.h"
# include "petitions.h"
Changed zone process window title format, example: 'crushbone :: clients: 6 inst_id: 1 inst_ver: 0 :: port: 7015'
Most of the following changes are QueryServ related, fully implemented its original functionality to be able to offload
intensive or metric based logging to a remote server process that could exist on another server entirely
Implemented Player Event Logging Types (Go to table `qs_player_events`):
1 = Player_Log_Quest,
2 = Player_Log_Zoning,
3 = Player_Log_Deaths,
4 = Player_Log_Connect_State,
5 = Player_Log_Levels,
6 = Player_Log_Keyring_Addition,
7 = Player_Log_QGlobal_Update,
8 = Player_Log_Task_Updates,
9 = Player_Log_AA_Purchases,
10 = Player_Log_Trade_Skill_Events,
11 = Player_Log_Issued_Commands,
12 = Player_Log_Money_Transactions,
13 = Player_Log_Alternate_Currency_Transactions,
- All QueryServ logging will be implemented with a front end in EoC 2.0 very soon
Changed all QS Error related logging to 'QUERYSERV__ERROR'
(Natedog) (Crash Fix) Legacy MySQL bug revert for loading AA's COALESCE( from COALESCE (
Implemented Perl Quest objects (LUA still needed to be exported):
- quest::qs_send_query("MySQL query") - Will send a raw query to the QueryServ process, useful for custom logging
- quest::qs_player_event(char_id, event_desc); - Will process a quest type event to table `qs_player_events`
Added MySQL Tables:
- `qs_player_aa_rate_hourly`
- `qs_player_events`
- Source table structures from:
- utils\sql\git\queryserv\required\08_23_2014_player_events_and_player_aa_rate_hourly
To get the complete QueryServ schema, source from here:
- utils\sql\git\queryserv\required\Complete_QueryServ_Table_Structures.sql
Added rules for each logging type, source rules here with them enabled by default:
- utils\sql\git\queryserv\required\Complete_QueryServ_Rules_Enabled.sql
Spawn related logging cleanup
General code cleanup
Added queryserv.cpp and queryserv.h with QueryServ class
2014-08-23 23:59:20 -05:00
# include "queryserv.h"
2014-11-29 03:24:48 -06:00
# include "quest_parser_collection.h"
# include "string_ids.h"
# include "worldserver.h"
# include "zone.h"
# include "zonedb.h"
2013-02-16 16:14:39 -08:00
Changed zone process window title format, example: 'crushbone :: clients: 6 inst_id: 1 inst_ver: 0 :: port: 7015'
Most of the following changes are QueryServ related, fully implemented its original functionality to be able to offload
intensive or metric based logging to a remote server process that could exist on another server entirely
Implemented Player Event Logging Types (Go to table `qs_player_events`):
1 = Player_Log_Quest,
2 = Player_Log_Zoning,
3 = Player_Log_Deaths,
4 = Player_Log_Connect_State,
5 = Player_Log_Levels,
6 = Player_Log_Keyring_Addition,
7 = Player_Log_QGlobal_Update,
8 = Player_Log_Task_Updates,
9 = Player_Log_AA_Purchases,
10 = Player_Log_Trade_Skill_Events,
11 = Player_Log_Issued_Commands,
12 = Player_Log_Money_Transactions,
13 = Player_Log_Alternate_Currency_Transactions,
- All QueryServ logging will be implemented with a front end in EoC 2.0 very soon
Changed all QS Error related logging to 'QUERYSERV__ERROR'
(Natedog) (Crash Fix) Legacy MySQL bug revert for loading AA's COALESCE( from COALESCE (
Implemented Perl Quest objects (LUA still needed to be exported):
- quest::qs_send_query("MySQL query") - Will send a raw query to the QueryServ process, useful for custom logging
- quest::qs_player_event(char_id, event_desc); - Will process a quest type event to table `qs_player_events`
Added MySQL Tables:
- `qs_player_aa_rate_hourly`
- `qs_player_events`
- Source table structures from:
- utils\sql\git\queryserv\required\08_23_2014_player_events_and_player_aa_rate_hourly
To get the complete QueryServ schema, source from here:
- utils\sql\git\queryserv\required\Complete_QueryServ_Table_Structures.sql
Added rules for each logging type, source rules here with them enabled by default:
- utils\sql\git\queryserv\required\Complete_QueryServ_Rules_Enabled.sql
Spawn related logging cleanup
General code cleanup
Added queryserv.cpp and queryserv.h with QueryServ class
2014-08-23 23:59:20 -05:00
extern QueryServ * QServ ;
2013-02-16 16:14:39 -08:00
extern Zone * zone ;
2015-10-31 20:19:57 -05:00
extern volatile bool is_zone_loaded ;
2013-02-16 16:14:39 -08:00
extern WorldServer worldserver ;
extern PetitionList petition_list ;
extern EntityList entity_list ;
bool Client : : Process ( ) {
bool ret = true ;
2017-07-14 20:49:57 -05:00
if ( Connected ( ) | | IsLD ( ) ) {
2013-05-09 10:44:08 -04:00
// try to send all packets that weren't sent before
2017-07-14 20:49:57 -05:00
if ( ! IsLD ( ) & & zoneinpacket_timer . Check ( ) ) {
2013-02-16 16:14:39 -08:00
SendAllPackets ( ) ;
}
2013-05-06 13:07:41 -04:00
2017-07-14 20:49:57 -05:00
if ( adventure_request_timer ) {
if ( adventure_request_timer - > Check ( ) ) {
2013-02-16 16:14:39 -08:00
safe_delete ( adventure_request_timer ) ;
}
}
2017-07-14 20:49:57 -05:00
if ( adventure_create_timer ) {
if ( adventure_create_timer - > Check ( ) ) {
2013-02-16 16:14:39 -08:00
safe_delete ( adventure_create_timer ) ;
}
}
2017-07-14 20:49:57 -05:00
if ( adventure_leave_timer ) {
if ( adventure_leave_timer - > Check ( ) ) {
2013-02-16 16:14:39 -08:00
safe_delete ( adventure_leave_timer ) ;
}
}
2017-07-14 20:49:57 -05:00
if ( adventure_door_timer ) {
if ( adventure_door_timer - > Check ( ) ) {
2013-02-16 16:14:39 -08:00
safe_delete ( adventure_door_timer ) ;
}
}
2017-07-14 20:49:57 -05:00
if ( adventure_stats_timer ) {
if ( adventure_stats_timer - > Check ( ) ) {
2013-02-16 16:14:39 -08:00
safe_delete ( adventure_stats_timer ) ;
}
}
2017-07-14 20:49:57 -05:00
if ( adventure_leaderboard_timer ) {
if ( adventure_leaderboard_timer - > Check ( ) ) {
2013-02-16 16:14:39 -08:00
safe_delete ( adventure_leaderboard_timer ) ;
}
}
2017-07-14 20:49:57 -05:00
if ( dead ) {
2013-02-16 16:14:39 -08:00
SetHP ( - 100 ) ;
2017-03-28 01:30:42 -05:00
if ( RespawnFromHoverTimer . Check ( ) )
2013-02-16 16:14:39 -08:00
HandleRespawnFromHover ( 0 ) ;
}
2013-05-06 13:07:41 -04:00
2016-04-22 03:49:17 -04:00
if ( IsTracking ( ) & & ( ClientVersion ( ) > = EQEmu : : versions : : ClientVersion : : SoD ) & & TrackingTimer . Check ( ) )
2013-02-16 16:14:39 -08:00
DoTracking ( ) ;
2015-06-06 17:46:53 -04:00
// SendHPUpdate calls hpupdate_timer.Start so it can delay this timer, so lets not reset with the check
// since the function will anyways
2017-03-28 01:30:42 -05:00
if ( hpupdate_timer . Check ( false ) )
2013-05-06 13:07:41 -04:00
SendHPUpdate ( ) ;
2013-02-16 16:14:39 -08:00
2017-07-14 20:49:57 -05:00
/* I haven't naturally updated my position in 10 seconds, updating manually */
2017-07-15 17:05:28 -05:00
if ( ! is_client_moving & & position_update_timer . Check ( ) ) {
2017-07-14 20:49:57 -05:00
SendPositionUpdate ( ) ;
}
2017-03-28 01:30:42 -05:00
if ( mana_timer . Check ( ) )
2017-07-11 17:54:46 -05:00
CheckManaEndUpdate ( ) ;
2014-11-18 01:02:11 -06:00
2017-03-28 01:30:42 -05:00
if ( dead & & dead_timer . Check ( ) ) {
2015-08-19 14:44:12 -04:00
database . MoveCharacterToZone ( GetName ( ) , database . GetZoneName ( m_pp . binds [ 0 ] . zoneId ) ) ;
2014-11-18 21:12:48 -08:00
2013-02-16 16:14:39 -08:00
m_pp . zone_id = m_pp . binds [ 0 ] . zoneId ;
2014-11-18 21:12:48 -08:00
m_pp . zoneInstance = m_pp . binds [ 0 ] . instance_id ;
2013-02-16 16:14:39 -08:00
m_pp . x = m_pp . binds [ 0 ] . x ;
m_pp . y = m_pp . binds [ 0 ] . y ;
m_pp . z = m_pp . binds [ 0 ] . z ;
Save ( ) ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
Group * mygroup = GetGroup ( ) ;
2013-06-06 14:29:45 -07:00
if ( mygroup )
2013-02-16 16:14:39 -08:00
{
2017-03-28 01:30:42 -05:00
entity_list . MessageGroup ( this , true , 15 , " %s died. " , GetName ( ) ) ;
2013-02-16 16:14:39 -08:00
mygroup - > MemberZoned ( this ) ;
}
Raid * myraid = entity_list . GetRaidByClient ( this ) ;
if ( myraid )
{
myraid - > MemberZoned ( this ) ;
}
return ( false ) ;
}
2013-05-06 13:07:41 -04:00
2017-03-28 01:30:42 -05:00
if ( charm_update_timer . Check ( ) ) {
2013-02-16 16:14:39 -08:00
CalcItemScale ( ) ;
}
2017-03-28 01:30:42 -05:00
if ( TaskPeriodic_Timer . Check ( ) & & taskstate )
2013-02-16 16:14:39 -08:00
taskstate - > TaskPeriodicChecks ( this ) ;
2017-03-28 01:30:42 -05:00
if ( linkdead_timer . Check ( ) ) {
2014-11-18 01:02:11 -06:00
LeaveGroup ( ) ;
2013-02-16 16:14:39 -08:00
Save ( ) ;
2017-03-28 01:30:42 -05:00
if ( GetMerc ( ) ) {
2013-02-18 00:50:19 -05:00
GetMerc ( ) - > Save ( ) ;
2013-02-16 16:14:39 -08:00
GetMerc ( ) - > Depop ( ) ;
}
2014-11-25 20:16:04 -08:00
2013-02-16 16:14:39 -08:00
Raid * myraid = entity_list . GetRaidByClient ( this ) ;
2017-03-28 01:30:42 -05:00
if ( myraid ) {
2013-02-16 16:14:39 -08:00
myraid - > MemberZoned ( this ) ;
}
return false ; //delete client
}
2017-03-28 01:30:42 -05:00
if ( camp_timer . Check ( ) ) {
2013-02-16 16:14:39 -08:00
LeaveGroup ( ) ;
Save ( ) ;
if ( GetMerc ( ) )
{
2013-02-18 00:50:19 -05:00
GetMerc ( ) - > Save ( ) ;
2013-02-16 16:14:39 -08:00
GetMerc ( ) - > Depop ( ) ;
}
instalog = true ;
}
2013-05-06 13:07:41 -04:00
2015-05-08 00:59:38 -04:00
if ( IsStunned ( ) & & stunned_timer . Check ( ) )
Mob : : UnStun ( ) ;
2013-05-06 13:07:41 -04:00
2017-03-28 01:30:42 -05:00
if ( ! m_CheatDetectMoved ) {
2013-02-16 16:14:39 -08:00
m_TimeSinceLastPositionCheck = Timer : : GetCurrentTime ( ) ;
}
if ( bardsong_timer . Check ( ) & & bardsong ! = 0 ) {
//NOTE: this is kinda a heavy-handed check to make sure the mob still exists before
//doing the next pulse on them...
2017-03-23 04:35:36 -04:00
Mob * song_target = nullptr ;
2017-03-28 01:30:42 -05:00
if ( bardsong_target_id = = GetID ( ) ) {
2013-02-16 16:14:39 -08:00
song_target = this ;
2017-03-28 01:30:42 -05:00
}
else {
2013-02-16 16:14:39 -08:00
song_target = entity_list . GetMob ( bardsong_target_id ) ;
}
2013-05-06 13:07:41 -04:00
2013-05-04 18:06:58 -07:00
if ( song_target = = nullptr ) {
2013-02-16 16:14:39 -08:00
InterruptSpell ( SONG_ENDS_ABRUPTLY , 0x121 , bardsong ) ;
2017-03-28 01:30:42 -05:00
}
else {
if ( ! ApplyNextBardPulse ( bardsong , song_target , bardsong_slot ) )
2013-02-16 16:14:39 -08:00
InterruptSpell ( SONG_ENDS_ABRUPTLY , 0x121 , bardsong ) ;
2014-11-18 01:02:11 -06:00
//SpellFinished(bardsong, bardsong_target, bardsong_slot, spells[bardsong].mana);
2013-02-16 16:14:39 -08:00
}
}
2017-03-28 01:30:42 -05:00
if ( GetMerc ( ) ) {
2014-11-18 01:02:11 -06:00
UpdateMercTimer ( ) ;
2013-02-16 16:14:39 -08:00
}
2017-03-28 01:30:42 -05:00
if ( GetMercInfo ( ) . MercTemplateID ! = 0 & & GetMercInfo ( ) . IsSuspended ) {
2015-01-25 11:28:34 -06:00
CheckMercSuspendTimer ( ) ;
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
2017-03-28 01:30:42 -05:00
if ( IsAIControlled ( ) )
2013-02-16 16:14:39 -08:00
AI_Process ( ) ;
2013-05-06 13:07:41 -04:00
2015-02-07 14:35:43 -05:00
// Don't reset the bindwound timer so we can check it in BindWound as well.
2015-02-07 12:34:50 -05:00
if ( bindwound_timer . Check ( false ) & & bindwound_target ! = 0 ) {
2013-05-09 10:44:08 -04:00
BindWound ( bindwound_target , false ) ;
2013-02-16 16:14:39 -08:00
}
2017-03-28 01:30:42 -05:00
if ( KarmaUpdateTimer ) {
if ( KarmaUpdateTimer - > Check ( false ) ) {
2013-02-16 16:14:39 -08:00
KarmaUpdateTimer - > Start ( RuleI ( Chat , KarmaUpdateIntervalMS ) ) ;
database . UpdateKarma ( AccountID ( ) , + + TotalKarma ) ;
}
}
2017-03-28 01:30:42 -05:00
if ( qGlobals ) {
if ( qglobal_purge_timer . Check ( ) ) {
2013-02-16 16:14:39 -08:00
qGlobals - > PurgeExpiredGlobals ( ) ;
}
}
2013-05-06 13:07:41 -04:00
2017-03-28 01:30:42 -05:00
/* Build a close range list of NPC's */
if ( npc_close_scan_timer . Check ( ) ) {
2017-07-11 01:58:47 -05:00
close_mobs . clear ( ) ;
2017-03-28 01:30:42 -05:00
2017-08-22 02:13:56 -05:00
/* Force spawn updates when traveled far */
bool force_spawn_updates = false ;
float client_update_range = ( RuleI ( Range , ClientForceSpawnUpdateRange ) * RuleI ( Range , ClientForceSpawnUpdateRange ) ) ;
if ( DistanceSquared ( last_major_update_position , m_Position ) > = client_update_range ) {
last_major_update_position = m_Position ;
force_spawn_updates = true ;
}
2017-07-10 23:03:40 -05:00
2017-08-22 02:13:56 -05:00
float scan_range = ( RuleI ( Range , ClientNPCScan ) * RuleI ( Range , ClientNPCScan ) ) ;
auto & mob_list = entity_list . GetMobList ( ) ;
2017-07-10 23:03:40 -05:00
for ( auto itr = mob_list . begin ( ) ; itr ! = mob_list . end ( ) ; + + itr ) {
Mob * mob = itr - > second ;
2017-08-22 02:13:56 -05:00
2017-07-10 23:03:40 -05:00
float distance = DistanceSquared ( m_Position , mob - > GetPosition ( ) ) ;
if ( mob - > IsNPC ( ) ) {
if ( distance < = scan_range ) {
2017-07-11 01:58:47 -05:00
close_mobs . insert ( std : : pair < Mob * , float > ( mob , distance ) ) ;
2017-07-10 23:03:40 -05:00
}
2018-01-04 14:38:29 -06:00
else if ( ( mob - > GetAggroRange ( ) * mob - > GetAggroRange ( ) ) > scan_range ) {
2017-07-11 01:58:47 -05:00
close_mobs . insert ( std : : pair < Mob * , float > ( mob , distance ) ) ;
2017-07-10 23:03:40 -05:00
}
2017-03-28 01:30:42 -05:00
}
2017-08-22 02:13:56 -05:00
2017-11-21 21:25:20 -06:00
if ( force_spawn_updates & & mob ! = this ) {
if ( mob - > is_distance_roamer ) {
mob - > SendPositionUpdateToClient ( this ) ;
continue ;
}
if ( distance < = client_update_range )
mob - > SendPositionUpdateToClient ( this ) ;
}
2017-08-22 02:13:56 -05:00
2017-03-28 01:30:42 -05:00
}
}
2013-02-16 16:14:39 -08:00
bool may_use_attacks = false ;
/*
Things which prevent us from attacking :
- being under AI control , the AI does attacks
- being dead
2013-12-20 18:43:16 -05:00
- casting a spell and bard check
2013-02-16 16:14:39 -08:00
- not having a target
- being stunned or mezzed
- having used a ranged weapon recently
*/
2017-03-28 01:30:42 -05:00
if ( auto_attack ) {
if ( ! IsAIControlled ( ) & & ! dead
2013-12-20 18:43:16 -05:00
& & ! ( spellend_timer . Enabled ( ) & & casting_spell_id & & ! IsBardSong ( casting_spell_id ) )
2013-02-16 16:14:39 -08:00
& & ! IsStunned ( ) & & ! IsFeared ( ) & & ! IsMezzed ( ) & & GetAppearance ( ) ! = eaDead & & ! IsMeleeDisabled ( )
)
may_use_attacks = true ;
2013-05-06 13:07:41 -04:00
2017-03-28 01:30:42 -05:00
if ( may_use_attacks & & ranged_timer . Enabled ( ) ) {
2013-02-16 16:14:39 -08:00
//if the range timer is enabled, we need to consider it
2017-03-28 01:30:42 -05:00
if ( ! ranged_timer . Check ( false ) ) {
2013-02-16 16:14:39 -08:00
//the ranged timer has not elapsed, cannot attack.
may_use_attacks = false ;
}
}
}
2017-03-28 01:30:42 -05:00
if ( AutoFireEnabled ( ) ) {
2016-10-16 21:36:39 -04:00
EQEmu : : ItemInstance * ranged = GetInv ( ) . GetItem ( EQEmu : : inventory : : slotRange ) ;
2017-03-28 01:30:42 -05:00
if ( ranged )
2013-02-16 16:14:39 -08:00
{
2017-03-28 01:30:42 -05:00
if ( ranged - > GetItem ( ) & & ranged - > GetItem ( ) - > ItemType = = EQEmu : : item : : ItemTypeBow ) {
if ( ranged_timer . Check ( false ) ) {
if ( GetTarget ( ) & & ( GetTarget ( ) - > IsNPC ( ) | | GetTarget ( ) - > IsClient ( ) ) ) {
if ( GetTarget ( ) - > InFrontMob ( this , GetTarget ( ) - > GetX ( ) , GetTarget ( ) - > GetY ( ) ) ) {
if ( CheckLosFN ( GetTarget ( ) ) ) {
2013-02-16 16:14:39 -08:00
//client has built in los check, but auto fire does not.. done last.
RangedAttack ( GetTarget ( ) ) ;
2017-03-28 01:30:42 -05:00
if ( CheckDoubleRangedAttack ( ) )
RangedAttack ( GetTarget ( ) , true ) ;
2013-02-16 16:14:39 -08:00
}
else
ranged_timer . Start ( ) ;
}
else
ranged_timer . Start ( ) ;
}
else
ranged_timer . Start ( ) ;
}
}
2017-03-28 01:30:42 -05:00
else if ( ranged - > GetItem ( ) & & ( ranged - > GetItem ( ) - > ItemType = = EQEmu : : item : : ItemTypeLargeThrowing | | ranged - > GetItem ( ) - > ItemType = = EQEmu : : item : : ItemTypeSmallThrowing ) ) {
if ( ranged_timer . Check ( false ) ) {
if ( GetTarget ( ) & & ( GetTarget ( ) - > IsNPC ( ) | | GetTarget ( ) - > IsClient ( ) ) ) {
if ( GetTarget ( ) - > InFrontMob ( this , GetTarget ( ) - > GetX ( ) , GetTarget ( ) - > GetY ( ) ) ) {
if ( CheckLosFN ( GetTarget ( ) ) ) {
2013-02-16 16:14:39 -08:00
//client has built in los check, but auto fire does not.. done last.
ThrowingAttack ( GetTarget ( ) ) ;
}
else
ranged_timer . Start ( ) ;
}
else
ranged_timer . Start ( ) ;
}
else
ranged_timer . Start ( ) ;
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
}
}
}
Mob * auto_attack_target = GetTarget ( ) ;
2013-05-06 13:07:41 -04:00
if ( auto_attack & & auto_attack_target ! = nullptr & & may_use_attacks & & attack_timer . Check ( ) )
2013-02-16 16:14:39 -08:00
{
//check if change
//only check on primary attack.. sorry offhand you gotta wait!
2017-03-28 01:30:42 -05:00
if ( aa_los_them_mob )
2013-02-16 16:14:39 -08:00
{
2017-03-28 01:30:42 -05:00
if ( auto_attack_target ! = aa_los_them_mob | |
2015-01-23 00:01:10 -08:00
m_AutoAttackPosition . x ! = GetX ( ) | |
m_AutoAttackPosition . y ! = GetY ( ) | |
m_AutoAttackPosition . z ! = GetZ ( ) | |
m_AutoAttackTargetLocation . x ! = aa_los_them_mob - > GetX ( ) | |
m_AutoAttackTargetLocation . y ! = aa_los_them_mob - > GetY ( ) | |
m_AutoAttackTargetLocation . z ! = aa_los_them_mob - > GetZ ( ) )
2013-02-16 16:14:39 -08:00
{
aa_los_them_mob = auto_attack_target ;
2014-12-01 01:12:19 -08:00
m_AutoAttackPosition = GetPosition ( ) ;
2015-01-23 00:01:10 -08:00
m_AutoAttackTargetLocation = glm : : vec3 ( aa_los_them_mob - > GetPosition ( ) ) ;
2013-12-20 17:47:28 -05:00
los_status = CheckLosFN ( auto_attack_target ) ;
2014-05-05 15:29:29 -04:00
los_status_facing = IsFacingMob ( aa_los_them_mob ) ;
2013-12-20 17:47:28 -05:00
}
// If only our heading changes, we can skip the CheckLosFN call
// but above we still need to update los_status_facing
2015-01-23 00:01:10 -08:00
if ( m_AutoAttackPosition . w ! = GetHeading ( ) ) {
m_AutoAttackPosition . w = GetHeading ( ) ;
2014-05-05 15:29:29 -04:00
los_status_facing = IsFacingMob ( aa_los_them_mob ) ;
2013-02-16 16:14:39 -08:00
}
}
else
{
aa_los_them_mob = auto_attack_target ;
2014-12-01 01:12:19 -08:00
m_AutoAttackPosition = GetPosition ( ) ;
2015-01-23 00:01:10 -08:00
m_AutoAttackTargetLocation = glm : : vec3 ( aa_los_them_mob - > GetPosition ( ) ) ;
2013-12-20 17:47:28 -05:00
los_status = CheckLosFN ( auto_attack_target ) ;
2014-05-05 15:29:29 -04:00
los_status_facing = IsFacingMob ( aa_los_them_mob ) ;
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
if ( ! CombatRange ( auto_attack_target ) )
2013-02-16 16:14:39 -08:00
{
2017-03-28 01:30:42 -05:00
Message_StringID ( MT_TooFarAway , TARGET_TOO_FAR ) ;
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
else if ( auto_attack_target = = this )
2013-02-16 16:14:39 -08:00
{
2017-03-28 01:30:42 -05:00
Message_StringID ( MT_TooFarAway , TRY_ATTACKING_SOMEONE ) ;
2013-02-16 16:14:39 -08:00
}
2013-12-20 17:47:28 -05:00
else if ( ! los_status | | ! los_status_facing )
2013-02-16 16:14:39 -08:00
{
2013-05-06 13:07:41 -04:00
//you can't see your target
2013-02-16 16:14:39 -08:00
}
else if ( auto_attack_target - > GetHP ( ) > - 10 ) // -10 so we can watch people bleed in PvP
{
2016-10-16 21:36:39 -04:00
EQEmu : : ItemInstance * wpn = GetInv ( ) . GetItem ( EQEmu : : inventory : : slotPrimary ) ;
TryWeaponProc ( wpn , auto_attack_target , EQEmu : : inventory : : slotPrimary ) ;
2016-12-09 18:59:44 -05:00
TriggerDefensiveProcs ( auto_attack_target , EQEmu : : inventory : : slotPrimary , false ) ;
2013-05-06 13:07:41 -04:00
2016-10-16 21:36:39 -04:00
DoAttackRounds ( auto_attack_target , EQEmu : : inventory : : slotPrimary ) ;
2015-06-21 02:01:48 -04:00
if ( CheckAATimer ( aaTimerRampage ) )
entity_list . AEAttack ( this , 30 ) ;
2013-02-16 16:14:39 -08:00
}
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if ( GetClass ( ) = = WARRIOR | | GetClass ( ) = = BERSERKER ) {
2013-12-11 01:49:51 -05:00
if ( ! dead & & ! IsBerserk ( ) & & GetHPRatio ( ) < RuleI ( Combat , BerserkerFrenzyStart ) ) {
2013-02-16 16:14:39 -08:00
entity_list . MessageClose_StringID ( this , false , 200 , 0 , BERSERK_START , GetName ( ) ) ;
2013-12-11 01:49:51 -05:00
berserk = true ;
2013-02-16 16:14:39 -08:00
}
2013-12-11 01:49:51 -05:00
if ( IsBerserk ( ) & & GetHPRatio ( ) > RuleI ( Combat , BerserkerFrenzyEnd ) ) {
2013-02-16 16:14:39 -08:00
entity_list . MessageClose_StringID ( this , false , 200 , 0 , BERSERK_END , GetName ( ) ) ;
2013-12-11 01:49:51 -05:00
berserk = false ;
2013-02-16 16:14:39 -08:00
}
}
2013-05-06 13:07:41 -04:00
2017-03-28 01:30:42 -05:00
if ( auto_attack & & may_use_attacks & & auto_attack_target ! = nullptr
2013-05-06 13:07:41 -04:00
& & CanThisClassDualWield ( ) & & attack_dw_timer . Check ( ) )
{
2013-02-16 16:14:39 -08:00
// Range check
2017-03-28 01:30:42 -05:00
if ( ! CombatRange ( auto_attack_target ) ) {
2013-02-16 16:14:39 -08:00
// this is a duplicate message don't use it.
2013-12-20 17:47:28 -05:00
//Message_StringID(MT_TooFarAway,TARGET_TOO_FAR);
2013-02-16 16:14:39 -08:00
}
// Don't attack yourself
2017-03-28 01:30:42 -05:00
else if ( auto_attack_target = = this ) {
2013-12-20 17:47:28 -05:00
//Message_StringID(MT_TooFarAway,TRY_ATTACKING_SOMEONE);
2013-02-16 16:14:39 -08:00
}
2013-12-20 17:47:28 -05:00
else if ( ! los_status | | ! los_status_facing )
2013-02-16 16:14:39 -08:00
{
2013-05-06 13:07:41 -04:00
//you can't see your target
2013-02-16 16:14:39 -08:00
}
2017-03-28 01:30:42 -05:00
else if ( auto_attack_target - > GetHP ( ) > - 10 ) {
2016-05-25 18:50:26 -04:00
CheckIncreaseSkill ( EQEmu : : skills : : SkillDualWield , auto_attack_target , - 10 ) ;
2015-07-05 00:45:46 -04:00
if ( CheckDualWield ( ) ) {
2016-10-16 21:36:39 -04:00
EQEmu : : ItemInstance * wpn = GetInv ( ) . GetItem ( EQEmu : : inventory : : slotSecondary ) ;
TryWeaponProc ( wpn , auto_attack_target , EQEmu : : inventory : : slotSecondary ) ;
2013-05-06 13:07:41 -04:00
2016-10-16 21:36:39 -04:00
DoAttackRounds ( auto_attack_target , EQEmu : : inventory : : slotSecondary ) ;
2013-02-16 16:14:39 -08:00
}
}
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if ( position_timer . Check ( ) ) {
if ( IsAIControlled ( ) )
{
2017-03-28 01:30:42 -05:00
if ( ! IsMoving ( ) )
2013-02-16 16:14:39 -08:00
{
animation = 0 ;
2015-01-23 00:01:10 -08:00
m_Delta = glm : : vec4 ( 0.0f , 0.0f , 0.0f , m_Delta . w ) ;
2017-07-14 20:49:57 -05:00
SendPositionUpdate ( 2 ) ;
2013-02-16 16:14:39 -08:00
}
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// Send a position packet every 8 seconds - if not done, other clients
// see this char disappear after 10-12 seconds of inactivity
if ( position_timer_counter > = 36 ) { // Approx. 4 ticks per second
2017-07-10 23:03:40 -05:00
entity_list . SendPositionUpdates ( this , pLastUpdateWZ , RuleI ( Range , MobPositionUpdates ) , GetTarget ( ) , true ) ;
2013-02-16 16:14:39 -08:00
pLastUpdate = Timer : : GetCurrentTime ( ) ;
pLastUpdateWZ = pLastUpdate ;
position_timer_counter = 0 ;
}
else {
pLastUpdate = Timer : : GetCurrentTime ( ) ;
position_timer_counter + + ;
}
}
2013-05-06 13:07:41 -04:00
2017-03-28 01:30:42 -05:00
if ( HasVirus ( ) ) {
if ( viral_timer . Check ( ) ) {
2013-02-16 16:14:39 -08:00
viral_timer_counter + + ;
2017-03-28 01:30:42 -05:00
for ( int i = 0 ; i < MAX_SPELL_TRIGGER * 2 ; i + = 2 ) {
if ( viral_spells [ i ] ) {
if ( viral_timer_counter % spells [ viral_spells [ i ] ] . viral_timer = = 0 ) {
SpreadVirus ( viral_spells [ i ] , viral_spells [ i + 1 ] ) ;
2013-02-16 16:14:39 -08:00
}
}
}
}
2017-03-28 01:30:42 -05:00
if ( viral_timer_counter > 999 )
2013-02-16 16:14:39 -08:00
viral_timer_counter = 0 ;
}
2013-05-06 13:07:41 -04:00
2014-11-27 22:12:13 -05:00
ProjectileAttack ( ) ;
2015-05-08 00:59:38 -04:00
2017-03-28 01:30:42 -05:00
if ( spellbonuses . GravityEffect = = 1 ) {
if ( gravity_timer . Check ( ) )
2013-02-16 16:14:39 -08:00
DoGravityEffect ( ) ;
}
if ( shield_timer . Check ( ) )
{
if ( shield_target )
{
if ( ! CombatRange ( shield_target ) )
{
2014-02-28 02:57:43 -05:00
entity_list . MessageClose_StringID ( this , false , 100 , 0 ,
END_SHIELDING , GetCleanName ( ) , shield_target - > GetCleanName ( ) ) ;
2013-02-16 16:14:39 -08:00
for ( int y = 0 ; y < 2 ; y + + )
{
if ( shield_target - > shielder [ y ] . shielder_id = = GetID ( ) )
{
shield_target - > shielder [ y ] . shielder_id = 0 ;
shield_target - > shielder [ y ] . shielder_bonus = 0 ;
}
}
shield_target = 0 ;
shield_timer . Disable ( ) ;
}
}
else
{
shield_target = 0 ;
shield_timer . Disable ( ) ;
}
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
SpellProcess ( ) ;
2017-03-28 01:30:42 -05:00
if ( endupkeep_timer . Check ( ) & & ! dead ) {
2013-02-16 16:14:39 -08:00
DoEnduranceUpkeep ( ) ;
}
2017-09-19 02:01:06 -04:00
// this is independent of the tick timer
if ( consume_food_timer . Check ( ) )
DoStaminaHungerUpdate ( ) ;
2017-09-17 09:48:10 -05:00
2013-02-16 16:14:39 -08:00
if ( tic_timer . Check ( ) & & ! dead ) {
CalcMaxHP ( ) ;
CalcMaxMana ( ) ;
CalcATK ( ) ;
CalcMaxEndurance ( ) ;
CalcRestState ( ) ;
DoHPRegen ( ) ;
DoManaRegen ( ) ;
DoEnduranceRegen ( ) ;
BuffProcess ( ) ;
2013-05-06 13:07:41 -04:00
2017-03-28 01:30:42 -05:00
if ( tribute_timer . Check ( ) ) {
2013-02-16 16:14:39 -08:00
ToggleTribute ( true ) ; //re-activate the tribute.
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if ( fishing_timer . Check ( ) ) {
GoFish ( ) ;
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if ( autosave_timer . Check ( ) ) {
Save ( 0 ) ;
}
2017-03-28 01:30:42 -05:00
if ( m_pp . intoxication > 0 )
2013-02-16 16:14:39 -08:00
{
- - m_pp . intoxication ;
CalcBonuses ( ) ;
}
2013-04-24 15:58:51 -05:00
2017-03-28 01:30:42 -05:00
if ( ItemTickTimer . Check ( ) )
2013-06-19 22:56:46 -07:00
{
TickItemCheck ( ) ;
}
2017-03-28 01:30:42 -05:00
if ( ItemQuestTimer . Check ( ) )
2013-06-19 22:56:46 -07:00
{
ItemTimerCheck ( ) ;
}
2013-02-16 16:14:39 -08:00
}
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if ( client_state = = CLIENT_KICKED ) {
Save ( ) ;
OnDisconnect ( true ) ;
2013-05-22 16:17:19 -04:00
std : : cout < < " Client disconnected (cs=k): " < < GetName ( ) < < std : : endl ;
2013-02-16 16:14:39 -08:00
return false ;
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if ( client_state = = DISCONNECTED ) {
OnDisconnect ( true ) ;
2013-05-22 16:17:19 -04:00
std : : cout < < " Client disconnected (cs=d): " < < GetName ( ) < < std : : endl ;
2013-02-16 16:14:39 -08:00
database . SetMQDetectionFlag ( this - > AccountName ( ) , GetName ( ) , " /MQInstantCamp: Possible instant camp disconnect. " , zone - > GetShortName ( ) ) ;
return false ;
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if ( client_state = = CLIENT_ERROR ) {
OnDisconnect ( true ) ;
2013-05-22 16:17:19 -04:00
std : : cout < < " Client disconnected (cs=e): " < < GetName ( ) < < std : : endl ;
2013-02-16 16:14:39 -08:00
return false ;
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if ( client_state ! = CLIENT_LINKDEAD & & ! eqs - > CheckState ( ESTABLISHED ) ) {
OnDisconnect ( true ) ;
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Zone_Server , " Client linkdead: %s " , name ) ;
2013-02-16 16:14:39 -08:00
if ( GetGM ( ) ) {
if ( GetMerc ( ) )
{
2013-02-18 00:50:19 -05:00
GetMerc ( ) - > Save ( ) ;
2013-02-16 16:14:39 -08:00
GetMerc ( ) - > Depop ( ) ;
}
return false ;
}
2017-03-28 01:30:42 -05:00
else if ( ! linkdead_timer . Enabled ( ) ) {
linkdead_timer . Start ( RuleI ( Zone , ClientLinkdeadMS ) ) ;
2013-02-16 16:14:39 -08:00
client_state = CLIENT_LINKDEAD ;
AI_Start ( CLIENT_LD_TIMEOUT ) ;
SendAppearancePacket ( AT_Linkdead , 1 ) ;
}
}
/************ Get all packets from packet manager out queue and process them ************/
2013-06-25 14:51:06 -07:00
EQApplicationPacket * app = nullptr ;
2017-03-28 01:30:42 -05:00
if ( ! eqs - > CheckState ( CLOSING ) )
2013-02-16 16:14:39 -08:00
{
2017-03-28 01:30:42 -05:00
while ( ret & & ( app = ( EQApplicationPacket * ) eqs - > PopPacket ( ) ) ) {
if ( app )
2013-02-16 16:14:39 -08:00
ret = HandlePacket ( app ) ;
safe_delete ( app ) ;
}
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
//At this point, we are still connected, everything important has taken
//place, now check to see if anybody wants to aggro us.
2013-02-17 23:38:51 -05:00
// only if client is not feigned
2017-03-28 01:30:42 -05:00
if ( zone - > CanDoCombat ( ) & & ret & & ! GetFeigned ( ) & & client_scan_npc_aggro_timer . Check ( ) ) {
int npc_scan_count = 0 ;
2017-07-11 01:58:47 -05:00
for ( auto it = close_mobs . begin ( ) ; it ! = close_mobs . end ( ) ; + + it ) {
Mob * mob = it - > first ;
2017-03-28 01:30:42 -05:00
2017-07-11 02:08:00 -05:00
if ( ! mob )
continue ;
2017-07-11 01:58:47 -05:00
if ( mob - > IsClient ( ) )
continue ;
if ( mob - > CheckWillAggro ( this ) & & ! mob - > CheckAggro ( this ) ) {
mob - > AddToHateList ( this , 25 ) ;
2017-03-28 01:30:42 -05:00
}
npc_scan_count + + ;
}
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Aggro , " Checking Reverse Aggro (client->npc) scanned_npcs (%i) " , npc_scan_count ) ;
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
2014-11-18 01:02:11 -06:00
if ( client_state ! = CLIENT_LINKDEAD & & ( client_state = = CLIENT_ERROR | | client_state = = DISCONNECTED | | client_state = = CLIENT_KICKED | | ! eqs - > CheckState ( ESTABLISHED ) ) )
{
2013-02-16 16:14:39 -08:00
//client logged out or errored out
//ResetTrade();
2016-08-03 00:33:22 -04:00
if ( client_state ! = CLIENT_KICKED & & ! zoning & & ! instalog ) {
2013-02-16 16:14:39 -08:00
Save ( ) ;
}
2013-08-29 15:46:40 -07:00
2013-02-16 16:14:39 -08:00
client_state = CLIENT_LINKDEAD ;
2013-08-29 15:46:40 -07:00
if ( zoning | | instalog | | GetGM ( ) )
2013-02-16 16:14:39 -08:00
{
Group * mygroup = GetGroup ( ) ;
if ( mygroup )
{
2014-11-18 01:02:11 -06:00
if ( ! zoning )
{
2013-08-29 15:46:40 -07:00
entity_list . MessageGroup ( this , true , 15 , " %s logged out. " , GetName ( ) ) ;
2014-11-18 01:02:11 -06:00
LeaveGroup ( ) ;
}
else
{
2013-08-29 15:46:40 -07:00
entity_list . MessageGroup ( this , true , 15 , " %s left the zone. " , GetName ( ) ) ;
2013-02-16 16:14:39 -08:00
mygroup - > MemberZoned ( this ) ;
2014-11-22 00:22:12 -06:00
if ( GetMerc ( ) & & GetMerc ( ) - > HasGroup ( ) )
2014-11-18 01:02:11 -06:00
{
2014-11-22 00:22:12 -06:00
GetMerc ( ) - > RemoveMercFromGroup ( GetMerc ( ) , GetMerc ( ) - > GetGroup ( ) ) ;
2014-11-18 01:02:11 -06:00
}
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
}
Raid * myraid = entity_list . GetRaidByClient ( this ) ;
if ( myraid )
{
2014-11-18 01:02:11 -06:00
if ( ! zoning )
{
2013-02-16 16:14:39 -08:00
//entity_list.MessageGroup(this,true,15,"%s logged out.",GetName());
myraid - > MemberZoned ( this ) ;
2014-11-18 01:02:11 -06:00
}
else
{
2013-02-16 16:14:39 -08:00
//entity_list.MessageGroup(this,true,15,"%s left the zone.",GetName());
myraid - > MemberZoned ( this ) ;
}
}
OnDisconnect ( false ) ;
return false ;
}
else
{
LinkDead ( ) ;
}
OnDisconnect ( true ) ;
}
2013-02-17 23:38:51 -05:00
// Feign Death 2 minutes and zone forgets you
2013-02-16 16:14:39 -08:00
if ( forget_timer . Check ( ) ) {
forget_timer . Disable ( ) ;
entity_list . ClearZoneFeignAggro ( this ) ;
2013-08-29 15:46:40 -07:00
Message ( 0 , " Your enemies have forgotten you! " ) ;
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
2017-02-17 21:04:48 -05:00
if ( client_state = = CLIENT_CONNECTED ) {
if ( m_dirtyautohaters )
ProcessXTargetAutoHaters ( ) ;
2017-02-18 22:27:34 -05:00
if ( aggro_meter_timer . Check ( ) )
ProcessAggroMeter ( ) ;
2017-02-17 21:04:48 -05:00
}
2013-02-16 16:14:39 -08:00
return ret ;
}
Changed zone process window title format, example: 'crushbone :: clients: 6 inst_id: 1 inst_ver: 0 :: port: 7015'
Most of the following changes are QueryServ related, fully implemented its original functionality to be able to offload
intensive or metric based logging to a remote server process that could exist on another server entirely
Implemented Player Event Logging Types (Go to table `qs_player_events`):
1 = Player_Log_Quest,
2 = Player_Log_Zoning,
3 = Player_Log_Deaths,
4 = Player_Log_Connect_State,
5 = Player_Log_Levels,
6 = Player_Log_Keyring_Addition,
7 = Player_Log_QGlobal_Update,
8 = Player_Log_Task_Updates,
9 = Player_Log_AA_Purchases,
10 = Player_Log_Trade_Skill_Events,
11 = Player_Log_Issued_Commands,
12 = Player_Log_Money_Transactions,
13 = Player_Log_Alternate_Currency_Transactions,
- All QueryServ logging will be implemented with a front end in EoC 2.0 very soon
Changed all QS Error related logging to 'QUERYSERV__ERROR'
(Natedog) (Crash Fix) Legacy MySQL bug revert for loading AA's COALESCE( from COALESCE (
Implemented Perl Quest objects (LUA still needed to be exported):
- quest::qs_send_query("MySQL query") - Will send a raw query to the QueryServ process, useful for custom logging
- quest::qs_player_event(char_id, event_desc); - Will process a quest type event to table `qs_player_events`
Added MySQL Tables:
- `qs_player_aa_rate_hourly`
- `qs_player_events`
- Source table structures from:
- utils\sql\git\queryserv\required\08_23_2014_player_events_and_player_aa_rate_hourly
To get the complete QueryServ schema, source from here:
- utils\sql\git\queryserv\required\Complete_QueryServ_Table_Structures.sql
Added rules for each logging type, source rules here with them enabled by default:
- utils\sql\git\queryserv\required\Complete_QueryServ_Rules_Enabled.sql
Spawn related logging cleanup
General code cleanup
Added queryserv.cpp and queryserv.h with QueryServ class
2014-08-23 23:59:20 -05:00
/* Just a set of actions preformed all over in Client::Process */
2013-02-16 16:14:39 -08:00
void Client : : OnDisconnect ( bool hard_disconnect ) {
2014-11-18 01:02:11 -06:00
if ( hard_disconnect )
{
LeaveGroup ( ) ;
if ( GetMerc ( ) )
{
GetMerc ( ) - > Save ( ) ;
GetMerc ( ) - > Depop ( ) ;
}
2013-02-16 16:14:39 -08:00
Raid * MyRaid = entity_list . GetRaidByClient ( this ) ;
if ( MyRaid )
MyRaid - > MemberZoned ( this ) ;
2014-11-25 20:16:04 -08:00
parse - > EventPlayer ( EVENT_DISCONNECT , this , " " , 0 ) ;
2013-02-16 16:14:39 -08:00
Changed zone process window title format, example: 'crushbone :: clients: 6 inst_id: 1 inst_ver: 0 :: port: 7015'
Most of the following changes are QueryServ related, fully implemented its original functionality to be able to offload
intensive or metric based logging to a remote server process that could exist on another server entirely
Implemented Player Event Logging Types (Go to table `qs_player_events`):
1 = Player_Log_Quest,
2 = Player_Log_Zoning,
3 = Player_Log_Deaths,
4 = Player_Log_Connect_State,
5 = Player_Log_Levels,
6 = Player_Log_Keyring_Addition,
7 = Player_Log_QGlobal_Update,
8 = Player_Log_Task_Updates,
9 = Player_Log_AA_Purchases,
10 = Player_Log_Trade_Skill_Events,
11 = Player_Log_Issued_Commands,
12 = Player_Log_Money_Transactions,
13 = Player_Log_Alternate_Currency_Transactions,
- All QueryServ logging will be implemented with a front end in EoC 2.0 very soon
Changed all QS Error related logging to 'QUERYSERV__ERROR'
(Natedog) (Crash Fix) Legacy MySQL bug revert for loading AA's COALESCE( from COALESCE (
Implemented Perl Quest objects (LUA still needed to be exported):
- quest::qs_send_query("MySQL query") - Will send a raw query to the QueryServ process, useful for custom logging
- quest::qs_player_event(char_id, event_desc); - Will process a quest type event to table `qs_player_events`
Added MySQL Tables:
- `qs_player_aa_rate_hourly`
- `qs_player_events`
- Source table structures from:
- utils\sql\git\queryserv\required\08_23_2014_player_events_and_player_aa_rate_hourly
To get the complete QueryServ schema, source from here:
- utils\sql\git\queryserv\required\Complete_QueryServ_Table_Structures.sql
Added rules for each logging type, source rules here with them enabled by default:
- utils\sql\git\queryserv\required\Complete_QueryServ_Rules_Enabled.sql
Spawn related logging cleanup
General code cleanup
Added queryserv.cpp and queryserv.h with QueryServ class
2014-08-23 23:59:20 -05:00
/* QS: PlayerLogConnectDisconnect */
if ( RuleB ( QueryServ , PlayerLogConnectDisconnect ) ) {
std : : string event_desc = StringFormat ( " Disconnect :: in zoneid:%i instid:%i " , this - > GetZoneID ( ) , this - > GetInstanceID ( ) ) ;
QServ - > PlayerLogEvent ( Player_Log_Connect_State , this - > CharacterID ( ) , event_desc ) ;
2014-11-25 20:16:04 -08:00
}
Changed zone process window title format, example: 'crushbone :: clients: 6 inst_id: 1 inst_ver: 0 :: port: 7015'
Most of the following changes are QueryServ related, fully implemented its original functionality to be able to offload
intensive or metric based logging to a remote server process that could exist on another server entirely
Implemented Player Event Logging Types (Go to table `qs_player_events`):
1 = Player_Log_Quest,
2 = Player_Log_Zoning,
3 = Player_Log_Deaths,
4 = Player_Log_Connect_State,
5 = Player_Log_Levels,
6 = Player_Log_Keyring_Addition,
7 = Player_Log_QGlobal_Update,
8 = Player_Log_Task_Updates,
9 = Player_Log_AA_Purchases,
10 = Player_Log_Trade_Skill_Events,
11 = Player_Log_Issued_Commands,
12 = Player_Log_Money_Transactions,
13 = Player_Log_Alternate_Currency_Transactions,
- All QueryServ logging will be implemented with a front end in EoC 2.0 very soon
Changed all QS Error related logging to 'QUERYSERV__ERROR'
(Natedog) (Crash Fix) Legacy MySQL bug revert for loading AA's COALESCE( from COALESCE (
Implemented Perl Quest objects (LUA still needed to be exported):
- quest::qs_send_query("MySQL query") - Will send a raw query to the QueryServ process, useful for custom logging
- quest::qs_player_event(char_id, event_desc); - Will process a quest type event to table `qs_player_events`
Added MySQL Tables:
- `qs_player_aa_rate_hourly`
- `qs_player_events`
- Source table structures from:
- utils\sql\git\queryserv\required\08_23_2014_player_events_and_player_aa_rate_hourly
To get the complete QueryServ schema, source from here:
- utils\sql\git\queryserv\required\Complete_QueryServ_Table_Structures.sql
Added rules for each logging type, source rules here with them enabled by default:
- utils\sql\git\queryserv\required\Complete_QueryServ_Rules_Enabled.sql
Spawn related logging cleanup
General code cleanup
Added queryserv.cpp and queryserv.h with QueryServ class
2014-08-23 23:59:20 -05:00
}
2013-02-16 16:14:39 -08:00
2017-07-18 17:52:39 -04:00
RemoveAllAuras ( ) ;
2014-11-25 20:16:04 -08:00
Mob * Other = trade - > With ( ) ;
2014-11-18 01:02:11 -06:00
if ( Other )
{
2017-04-01 03:51:46 -05:00
Log ( Logs : : Detail , Logs : : Trading , " Client disconnected during a trade. Returning their items. " ) ;
2013-02-16 16:14:39 -08:00
FinishTrade ( this ) ;
if ( Other - > IsClient ( ) )
Other - > CastToClient ( ) - > FinishTrade ( Other ) ;
Changed zone process window title format, example: 'crushbone :: clients: 6 inst_id: 1 inst_ver: 0 :: port: 7015'
Most of the following changes are QueryServ related, fully implemented its original functionality to be able to offload
intensive or metric based logging to a remote server process that could exist on another server entirely
Implemented Player Event Logging Types (Go to table `qs_player_events`):
1 = Player_Log_Quest,
2 = Player_Log_Zoning,
3 = Player_Log_Deaths,
4 = Player_Log_Connect_State,
5 = Player_Log_Levels,
6 = Player_Log_Keyring_Addition,
7 = Player_Log_QGlobal_Update,
8 = Player_Log_Task_Updates,
9 = Player_Log_AA_Purchases,
10 = Player_Log_Trade_Skill_Events,
11 = Player_Log_Issued_Commands,
12 = Player_Log_Money_Transactions,
13 = Player_Log_Alternate_Currency_Transactions,
- All QueryServ logging will be implemented with a front end in EoC 2.0 very soon
Changed all QS Error related logging to 'QUERYSERV__ERROR'
(Natedog) (Crash Fix) Legacy MySQL bug revert for loading AA's COALESCE( from COALESCE (
Implemented Perl Quest objects (LUA still needed to be exported):
- quest::qs_send_query("MySQL query") - Will send a raw query to the QueryServ process, useful for custom logging
- quest::qs_player_event(char_id, event_desc); - Will process a quest type event to table `qs_player_events`
Added MySQL Tables:
- `qs_player_aa_rate_hourly`
- `qs_player_events`
- Source table structures from:
- utils\sql\git\queryserv\required\08_23_2014_player_events_and_player_aa_rate_hourly
To get the complete QueryServ schema, source from here:
- utils\sql\git\queryserv\required\Complete_QueryServ_Table_Structures.sql
Added rules for each logging type, source rules here with them enabled by default:
- utils\sql\git\queryserv\required\Complete_QueryServ_Rules_Enabled.sql
Spawn related logging cleanup
General code cleanup
Added queryserv.cpp and queryserv.h with QueryServ class
2014-08-23 23:59:20 -05:00
/* Reset both sides of the trade */
2014-11-25 20:16:04 -08:00
trade - > Reset ( ) ;
2013-02-16 16:14:39 -08:00
Other - > trade - > Reset ( ) ;
}
database . SetFirstLogon ( CharacterID ( ) , 0 ) ; //We change firstlogon status regardless of if a player logs out to zone or not, because we only want to trigger it on their first login from world.
2014-11-25 20:16:04 -08:00
/* Remove ourself from all proximities */
2013-02-16 16:14:39 -08:00
ClearAllProximities ( ) ;
2013-05-06 13:07:41 -04:00
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket ( OP_LogoutReply ) ;
2013-02-16 16:14:39 -08:00
FastQueuePacket ( & outapp ) ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
Disconnect ( ) ;
}
// Sends the client complete inventory used in character login
2016-05-10 21:27:40 -04:00
void Client : : BulkSendInventoryItems ( )
{
2013-02-16 16:14:39 -08:00
// LINKDEAD TRADE ITEMS
2015-02-23 19:57:47 -05:00
// Move trade slot items back into normal inventory..need them there now for the proceeding validity checks
2016-05-10 21:27:40 -04:00
for ( int16 slot_id = EQEmu : : legacy : : TRADE_BEGIN ; slot_id < = EQEmu : : legacy : : TRADE_END ; slot_id + + ) {
2016-10-16 05:10:54 -04:00
EQEmu : : ItemInstance * inst = m_inv . PopItem ( slot_id ) ;
2013-02-16 16:14:39 -08:00
if ( inst ) {
2016-05-21 04:54:18 -04:00
bool is_arrow = ( inst - > GetItem ( ) - > ItemType = = EQEmu : : item : : ItemTypeArrow ) ? true : false ;
int16 free_slot_id = m_inv . FindFreeSlot ( inst - > IsClassBag ( ) , true , inst - > GetItem ( ) - > Size , is_arrow ) ;
2017-04-01 03:51:46 -05:00
Log ( Logs : : Detail , Logs : : Inventory , " Incomplete Trade Transaction: Moving %s from slot %i to %i " , inst - > GetItem ( ) - > Name , slot_id , free_slot_id ) ;
2013-02-16 16:14:39 -08:00
PutItemInInventory ( free_slot_id , * inst , false ) ;
2013-05-04 18:06:58 -07:00
database . SaveInventory ( character_id , nullptr , slot_id ) ;
2013-02-16 16:14:39 -08:00
safe_delete ( inst ) ;
}
}
2013-05-06 13:07:41 -04:00
bool deletenorent = database . NoRentExpired ( GetName ( ) ) ;
2015-06-07 22:07:40 -04:00
if ( deletenorent ) { //client was offline for more than 30 minutes, delete no rent items
if ( RuleB ( Inventory , TransformSummonedBags ) )
DisenchantSummonedBags ( false ) ;
RemoveNoRent ( false ) ;
}
2013-02-16 16:14:39 -08:00
RemoveDuplicateLore ( false ) ;
MoveSlotNotAllowed ( false ) ;
2016-05-18 22:38:41 -04:00
EQEmu : : OutBuffer ob ;
EQEmu : : OutBuffer : : pos_type last_pos = ob . tellp ( ) ;
2016-05-10 21:27:40 -04:00
// Possessions items
2016-10-16 21:36:39 -04:00
for ( int16 slot_id = EQEmu : : inventory : : slotBegin ; slot_id < EQEmu : : legacy : : TYPE_POSSESSIONS_SIZE ; slot_id + + ) {
2016-10-16 05:10:54 -04:00
const EQEmu : : ItemInstance * inst = m_inv [ slot_id ] ;
2016-05-10 21:27:40 -04:00
if ( ! inst )
continue ;
2016-05-18 22:38:41 -04:00
inst - > Serialize ( ob , slot_id ) ;
2016-05-10 21:27:40 -04:00
2016-05-18 22:38:41 -04:00
if ( ob . tellp ( ) = = last_pos )
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Inventory , " Serialization failed on item slot %d during BulkSendInventoryItems. Item skipped. " , slot_id ) ;
2016-05-10 21:27:40 -04:00
2016-05-18 22:38:41 -04:00
last_pos = ob . tellp ( ) ;
2013-02-16 16:14:39 -08:00
}
2016-05-10 21:27:40 -04:00
// PowerSource item
2016-04-22 03:49:17 -04:00
if ( ClientVersion ( ) > = EQEmu : : versions : : ClientVersion : : SoF ) {
2016-10-16 21:36:39 -04:00
const EQEmu : : ItemInstance * inst = m_inv [ EQEmu : : inventory : : slotPowerSource ] ;
2016-05-10 21:27:40 -04:00
if ( inst ) {
2016-10-16 21:36:39 -04:00
inst - > Serialize ( ob , EQEmu : : inventory : : slotPowerSource ) ;
2013-02-16 16:14:39 -08:00
2016-05-18 22:38:41 -04:00
if ( ob . tellp ( ) = = last_pos )
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Inventory , " Serialization failed on item slot %d during BulkSendInventoryItems. Item skipped. " , EQEmu : : inventory : : slotPowerSource ) ;
2013-02-16 16:14:39 -08:00
2016-05-18 22:38:41 -04:00
last_pos = ob . tellp ( ) ;
2013-02-16 16:14:39 -08:00
}
}
// Bank items
2016-05-10 21:27:40 -04:00
for ( int16 slot_id = EQEmu : : legacy : : BANK_BEGIN ; slot_id < = EQEmu : : legacy : : BANK_END ; slot_id + + ) {
2016-10-16 05:10:54 -04:00
const EQEmu : : ItemInstance * inst = m_inv [ slot_id ] ;
2016-05-10 21:27:40 -04:00
if ( ! inst )
continue ;
2013-05-06 13:07:41 -04:00
2016-05-18 22:38:41 -04:00
inst - > Serialize ( ob , slot_id ) ;
2016-05-10 21:27:40 -04:00
2016-05-18 22:38:41 -04:00
if ( ob . tellp ( ) = = last_pos )
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Inventory , " Serialization failed on item slot %d during BulkSendInventoryItems. Item skipped. " , slot_id ) ;
2016-05-10 21:27:40 -04:00
2016-05-18 22:38:41 -04:00
last_pos = ob . tellp ( ) ;
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
2016-05-10 21:27:40 -04:00
// SharedBank items
for ( int16 slot_id = EQEmu : : legacy : : SHARED_BANK_BEGIN ; slot_id < = EQEmu : : legacy : : SHARED_BANK_END ; slot_id + + ) {
2016-10-16 05:10:54 -04:00
const EQEmu : : ItemInstance * inst = m_inv [ slot_id ] ;
2016-05-10 21:27:40 -04:00
if ( ! inst )
continue ;
2016-05-18 22:38:41 -04:00
inst - > Serialize ( ob , slot_id ) ;
2016-05-10 21:27:40 -04:00
2016-05-18 22:38:41 -04:00
if ( ob . tellp ( ) = = last_pos )
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Inventory , " Serialization failed on item slot %d during BulkSendInventoryItems. Item skipped. " , slot_id ) ;
2016-05-10 21:27:40 -04:00
2016-05-18 22:38:41 -04:00
last_pos = ob . tellp ( ) ;
2013-02-16 16:14:39 -08:00
}
2016-05-10 21:27:40 -04:00
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket ( OP_CharInventory ) ;
2016-05-18 22:38:41 -04:00
outapp - > size = ob . size ( ) ;
outapp - > pBuffer = ob . detach ( ) ;
2016-05-10 21:27:40 -04:00
QueuePacket ( outapp ) ;
safe_delete ( outapp ) ;
2013-02-16 16:14:39 -08:00
}
void Client : : BulkSendMerchantInventory ( int merchant_id , int npcid ) {
2016-10-16 05:10:54 -04:00
const EQEmu : : ItemData * handyitem = nullptr ;
2014-10-09 23:11:18 -04:00
uint32 numItemSlots = 80 ; //The max number of items passed in the transaction.
2016-04-22 03:49:17 -04:00
if ( m_ClientVersionBit & EQEmu : : versions : : bit_RoFAndLater ) { // RoF+ can send 200 items
2015-01-31 11:17:28 -08:00
numItemSlots = 200 ;
}
2017-03-23 04:35:36 -04:00
const EQEmu : : ItemData * item = nullptr ;
2013-02-16 16:14:39 -08:00
std : : list < MerchantList > merlist = zone - > merchanttable [ merchant_id ] ;
std : : list < MerchantList > : : const_iterator itr ;
Mob * merch = entity_list . GetMobByNpcTypeID ( npcid ) ;
2014-10-09 23:11:18 -04:00
if ( merlist . size ( ) = = 0 ) { //Attempt to load the data, it might have been missed if someone spawned the merchant after the zone was loaded
2013-02-16 16:14:39 -08:00
zone - > LoadNewMerchantData ( merchant_id ) ;
merlist = zone - > merchanttable [ merchant_id ] ;
2014-10-09 23:11:18 -04:00
if ( merlist . size ( ) = = 0 )
2013-02-16 16:14:39 -08:00
return ;
}
std : : list < TempMerchantList > tmp_merlist = zone - > tmpmerchanttable [ npcid ] ;
std : : list < TempMerchantList > : : iterator tmp_itr ;
2014-10-09 23:11:18 -04:00
uint32 i = 1 ;
2013-02-16 16:14:39 -08:00
uint8 handychance = 0 ;
2014-10-09 23:11:18 -04:00
for ( itr = merlist . begin ( ) ; itr ! = merlist . end ( ) & & i < = numItemSlots ; + + itr ) {
2013-02-16 16:14:39 -08:00
MerchantList ml = * itr ;
2014-08-20 23:41:47 -04:00
if ( merch - > CastToNPC ( ) - > GetMerchantProbability ( ) > ml . probability )
continue ;
2014-10-09 23:11:18 -04:00
if ( GetLevel ( ) < ml . level_required )
2013-05-09 10:44:08 -04:00
continue ;
2013-02-16 16:14:39 -08:00
2013-12-26 16:29:09 -05:00
if ( ! ( ml . classes_required & ( 1 < < ( GetClass ( ) - 1 ) ) ) )
continue ;
2013-05-09 10:44:08 -04:00
int32 fac = merch ? merch - > GetPrimaryFaction ( ) : 0 ;
2016-10-09 10:22:55 -04:00
int32 cur_fac_level ;
2016-10-15 14:54:57 -05:00
if ( fac = = 0 | | sneaking ) {
2016-10-09 10:22:55 -04:00
cur_fac_level = 0 ;
2016-10-15 14:54:57 -05:00
}
else {
2016-10-09 10:22:55 -04:00
cur_fac_level = GetModCharacterFactionLevel ( fac ) ;
2016-10-15 14:54:57 -05:00
}
2016-10-09 10:22:55 -04:00
if ( cur_fac_level < ml . faction_required )
2013-05-09 10:44:08 -04:00
continue ;
2013-05-06 13:07:41 -04:00
2014-12-01 18:13:12 -05:00
handychance = zone - > random . Int ( 0 , merlist . size ( ) + tmp_merlist . size ( ) - 1 ) ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
item = database . GetItem ( ml . item ) ;
2014-10-09 23:11:18 -04:00
if ( item ) {
if ( handychance = = 0 )
handyitem = item ;
2013-02-16 16:14:39 -08:00
else
handychance - - ;
2014-10-09 23:11:18 -04:00
int charges = 1 ;
2016-05-21 04:54:18 -04:00
if ( item - > IsClassCommon ( ) )
2014-10-09 23:11:18 -04:00
charges = item - > MaxCharges ;
2016-10-16 05:10:54 -04:00
EQEmu : : ItemInstance * inst = database . CreateItem ( item , charges ) ;
2013-02-16 16:14:39 -08:00
if ( inst ) {
2014-10-09 23:11:18 -04:00
if ( RuleB ( Merchant , UsePriceMod ) ) {
inst - > SetPrice ( ( item - > Price * ( RuleR ( Merchant , SellCostMod ) ) * item - > SellRate * Client : : CalcPriceMod ( merch , false ) ) ) ;
2013-02-16 16:14:39 -08:00
}
else
2014-10-09 23:11:18 -04:00
inst - > SetPrice ( ( item - > Price * ( RuleR ( Merchant , SellCostMod ) ) * item - > SellRate ) ) ;
2013-02-16 16:14:39 -08:00
inst - > SetMerchantSlot ( ml . slot ) ;
inst - > SetMerchantCount ( - 1 ) ; //unlimited
2014-10-09 23:11:18 -04:00
if ( charges > 0 )
2013-02-16 16:14:39 -08:00
inst - > SetCharges ( charges ) ;
else
inst - > SetCharges ( 1 ) ;
2014-10-09 23:11:18 -04:00
SendItemPacket ( ml . slot - 1 , inst , ItemPacketMerchant ) ;
2013-02-16 16:14:39 -08:00
safe_delete ( inst ) ;
}
}
// Account for merchant lists with gaps.
2014-10-09 23:11:18 -04:00
if ( ml . slot > = i ) {
if ( ml . slot > i )
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : None , " (WARNING) Merchantlist contains gap at slot %d. Merchant: %d, NPC: %d " , i , merchant_id , npcid ) ;
2013-02-16 16:14:39 -08:00
i = ml . slot + 1 ;
2014-10-09 23:11:18 -04:00
}
2013-02-16 16:14:39 -08:00
}
std : : list < TempMerchantList > origtmp_merlist = zone - > tmpmerchanttable [ npcid ] ;
tmp_merlist . clear ( ) ;
2014-10-09 23:11:18 -04:00
for ( tmp_itr = origtmp_merlist . begin ( ) ; tmp_itr ! = origtmp_merlist . end ( ) & & i < = numItemSlots ; + + tmp_itr ) {
2013-02-16 16:14:39 -08:00
TempMerchantList ml = * tmp_itr ;
2014-10-09 23:11:18 -04:00
item = database . GetItem ( ml . item ) ;
ml . slot = i ;
2013-02-16 16:14:39 -08:00
if ( item ) {
2014-10-09 23:11:18 -04:00
if ( handychance = = 0 )
handyitem = item ;
2013-02-16 16:14:39 -08:00
else
handychance - - ;
2014-10-09 23:11:18 -04:00
int charges = 1 ;
2013-02-16 16:14:39 -08:00
//if(item->ItemClass==ItemClassCommon && (int16)ml.charges <= item->MaxCharges)
// charges=ml.charges;
//else
charges = item - > MaxCharges ;
2016-10-16 05:10:54 -04:00
EQEmu : : ItemInstance * inst = database . CreateItem ( item , charges ) ;
2013-02-16 16:14:39 -08:00
if ( inst ) {
2014-10-09 23:11:18 -04:00
if ( RuleB ( Merchant , UsePriceMod ) ) {
inst - > SetPrice ( ( item - > Price * ( RuleR ( Merchant , SellCostMod ) ) * item - > SellRate * Client : : CalcPriceMod ( merch , false ) ) ) ;
2013-02-16 16:14:39 -08:00
}
else
2014-10-09 23:11:18 -04:00
inst - > SetPrice ( ( item - > Price * ( RuleR ( Merchant , SellCostMod ) ) * item - > SellRate ) ) ;
2013-02-16 16:14:39 -08:00
inst - > SetMerchantSlot ( ml . slot ) ;
inst - > SetMerchantCount ( ml . charges ) ;
if ( charges > 0 )
inst - > SetCharges ( item - > MaxCharges ) ; //inst->SetCharges(charges);
else
inst - > SetCharges ( 1 ) ;
SendItemPacket ( ml . slot - 1 , inst , ItemPacketMerchant ) ;
safe_delete ( inst ) ;
}
}
tmp_merlist . push_back ( ml ) ;
i + + ;
}
//this resets the slot
zone - > tmpmerchanttable [ npcid ] = tmp_merlist ;
2014-10-09 23:11:18 -04:00
if ( merch ! = nullptr & & handyitem ) {
char handy_id [ 8 ] = { 0 } ;
2014-12-01 18:13:12 -05:00
int greeting = zone - > random . Int ( 0 , 4 ) ;
2014-10-09 23:11:18 -04:00
int greet_id = 0 ;
switch ( greeting ) {
2013-02-16 16:14:39 -08:00
case 1 :
2014-10-09 23:11:18 -04:00
greet_id = MERCHANT_GREETING ;
2013-02-16 16:14:39 -08:00
break ;
case 2 :
2014-10-09 23:11:18 -04:00
greet_id = MERCHANT_HANDY_ITEM1 ;
2013-02-16 16:14:39 -08:00
break ;
case 3 :
2014-10-09 23:11:18 -04:00
greet_id = MERCHANT_HANDY_ITEM2 ;
2013-02-16 16:14:39 -08:00
break ;
case 4 :
2014-10-09 23:11:18 -04:00
greet_id = MERCHANT_HANDY_ITEM3 ;
2013-02-16 16:14:39 -08:00
break ;
default :
2014-10-09 23:11:18 -04:00
greet_id = MERCHANT_HANDY_ITEM4 ;
2013-05-09 10:44:08 -04:00
}
2014-10-09 23:11:18 -04:00
sprintf ( handy_id , " %i " , greet_id ) ;
2013-05-06 13:07:41 -04:00
2014-10-09 23:11:18 -04:00
if ( greet_id ! = MERCHANT_GREETING )
Message_StringID ( 10 , GENERIC_STRINGID_SAY , merch - > GetCleanName ( ) , handy_id , this - > GetName ( ) , handyitem - > Name ) ;
2013-05-09 10:44:08 -04:00
else
2014-10-09 23:11:18 -04:00
Message_StringID ( 10 , GENERIC_STRINGID_SAY , merch - > GetCleanName ( ) , handy_id , this - > GetName ( ) ) ;
2013-05-09 10:44:08 -04:00
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// safe_delete_array(cpi);
}
uint8 Client : : WithCustomer ( uint16 NewCustomer ) {
if ( NewCustomer = = 0 ) {
CustomerID = 0 ;
return 0 ;
}
if ( CustomerID = = 0 ) {
CustomerID = NewCustomer ;
return 1 ;
}
// Check that the player browsing our wares hasn't gone away.
Client * c = entity_list . GetClientByID ( CustomerID ) ;
if ( ! c ) {
2017-04-01 03:51:46 -05:00
Log ( Logs : : Detail , Logs : : Trading , " Previous customer has gone away. " ) ;
2013-02-16 16:14:39 -08:00
CustomerID = NewCustomer ;
return 1 ;
}
return 0 ;
}
void Client : : OPRezzAnswer ( uint32 Action , uint32 SpellID , uint16 ZoneID , uint16 InstanceID , float x , float y , float z )
{
if ( PendingRezzXP < 0 ) {
// pendingrezexp is set to -1 if we are not expecting an OP_RezzAnswer
2017-04-01 03:51:46 -05:00
Log ( Logs : : Detail , Logs : : Spells , " Unexpected OP_RezzAnswer. Ignoring it. " ) ;
2013-02-16 16:14:39 -08:00
Message ( 13 , " You have already been resurrected. \n " ) ;
return ;
}
if ( Action = = 1 )
{
2013-05-06 13:07:41 -04:00
// Mark the corpse as rezzed in the database, just in case the corpse has buried, or the zone the
2013-02-16 16:14:39 -08:00
// corpse is in has shutdown since the rez spell was cast.
database . MarkCorpseAsRezzed ( PendingRezzDBID ) ;
2017-04-01 03:51:46 -05:00
Log ( Logs : : Detail , Logs : : Spells , " Player %s got a %i Rezz, spellid %i in zone%i, instance id %i " ,
2013-05-09 10:44:08 -04:00
this - > name , ( uint16 ) spells [ SpellID ] . base [ 0 ] ,
SpellID , ZoneID , InstanceID ) ;
2013-02-16 16:14:39 -08:00
2014-01-07 23:29:46 -05:00
this - > BuffFadeNonPersistDeath ( ) ;
2013-02-16 16:14:39 -08:00
int SpellEffectDescNum = GetSpellEffectDescNum ( SpellID ) ;
// Rez spells with Rez effects have this DescNum (first is Titanium, second is 6.2 Client)
if ( ( SpellEffectDescNum = = 82 ) | | ( SpellEffectDescNum = = 39067 ) ) {
SetMana ( 0 ) ;
SetHP ( GetMaxHP ( ) / 5 ) ;
2016-07-24 22:53:57 -04:00
int rez_eff = 756 ;
2017-11-19 01:45:05 -05:00
if ( RuleB ( Character , UseOldRaceRezEffects ) & &
( GetRace ( ) = = BARBARIAN | | GetRace ( ) = = DWARF | | GetRace ( ) = = TROLL | | GetRace ( ) = = OGRE ) )
2016-07-24 22:58:48 -04:00
rez_eff = 757 ;
2016-07-24 22:53:57 -04:00
SpellOnTarget ( rez_eff , this ) ; // Rezz effects
2013-02-16 16:14:39 -08:00
}
else {
SetMana ( GetMaxMana ( ) ) ;
SetHP ( GetMaxHP ( ) ) ;
}
2013-05-06 13:07:41 -04:00
if ( spells [ SpellID ] . base [ 0 ] < 100 & & spells [ SpellID ] . base [ 0 ] > 0 & & PendingRezzXP > 0 )
2013-02-16 16:14:39 -08:00
{
SetEXP ( ( ( int ) ( GetEXP ( ) + ( ( float ) ( ( PendingRezzXP / 100 ) * spells [ SpellID ] . base [ 0 ] ) ) ) ) ,
2013-05-09 10:44:08 -04:00
GetAAXP ( ) , true ) ;
2013-02-16 16:14:39 -08:00
}
else if ( spells [ SpellID ] . base [ 0 ] = = 100 & & PendingRezzXP > 0 ) {
SetEXP ( ( GetEXP ( ) + PendingRezzXP ) , GetAAXP ( ) , true ) ;
}
2013-05-06 13:07:41 -04:00
//Was sending the packet back to initiate client zone...
2013-02-16 16:14:39 -08:00
//but that could be abusable, so lets go through proper channels
MovePC ( ZoneID , InstanceID , x , y , z , GetHeading ( ) , 0 , ZoneSolicited ) ;
entity_list . RefreshClientXTargets ( this ) ;
}
PendingRezzXP = - 1 ;
PendingRezzSpellID = 0 ;
}
void Client : : OPTGB ( const EQApplicationPacket * app )
{
if ( ! app ) return ;
if ( ! app - > pBuffer ) return ;
uint32 tgb_flag = * ( uint32 * ) app - > pBuffer ;
if ( tgb_flag = = 2 )
Message_StringID ( 0 , TGB ( ) ? TGB_ON : TGB_OFF ) ;
else
tgb = tgb_flag ;
}
void Client : : OPMemorizeSpell ( const EQApplicationPacket * app )
{
if ( app - > size ! = sizeof ( MemorizeSpell_Struct ) )
{
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Error , " Wrong size on OP_MemorizeSpell. Got: %i, Expected: %i " , app - > size , sizeof ( MemorizeSpell_Struct ) ) ;
2013-02-16 16:14:39 -08:00
DumpPacket ( app ) ;
return ;
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
const MemorizeSpell_Struct * memspell = ( const MemorizeSpell_Struct * ) app - > pBuffer ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if ( ! IsValidSpell ( memspell - > spell_id ) )
{
Message ( 13 , " Unexpected error: spell id out of range " ) ;
return ;
}
if
(
GetClass ( ) > 16 | |
GetLevel ( ) < spells [ memspell - > spell_id ] . classes [ GetClass ( ) - 1 ]
)
{
char val1 [ 20 ] = { 0 } ;
Message_StringID ( 13 , SPELL_LEVEL_TO_LOW , ConvertArray ( spells [ memspell - > spell_id ] . classes [ GetClass ( ) - 1 ] , val1 ) , spells [ memspell - > spell_id ] . name ) ;
//Message(13, "Unexpected error: Class cant use this spell at your level!");
return ;
}
switch ( memspell - > scribing )
{
case memSpellScribing : { // scribing spell to book
2016-10-16 21:36:39 -04:00
const EQEmu : : ItemInstance * inst = m_inv [ EQEmu : : inventory : : slotCursor ] ;
2013-02-16 16:14:39 -08:00
2016-05-21 04:54:18 -04:00
if ( inst & & inst - > IsClassCommon ( ) )
2013-02-16 16:14:39 -08:00
{
2016-10-16 05:10:54 -04:00
const EQEmu : : ItemData * item = inst - > GetItem ( ) ;
2013-05-06 13:07:41 -04:00
2015-12-01 20:30:43 -05:00
if ( RuleB ( Character , RestrictSpellScribing ) & & ! item - > IsEquipable ( GetRace ( ) , GetClass ( ) ) ) {
Message_StringID ( 13 , CANNOT_USE_ITEM ) ;
break ;
}
2013-02-16 16:14:39 -08:00
if ( item & & item - > Scroll . Effect = = ( int32 ) ( memspell - > spell_id ) )
{
ScribeSpell ( memspell - > spell_id , memspell - > slot ) ;
2016-10-16 21:36:39 -04:00
DeleteItemInInventory ( EQEmu : : inventory : : slotCursor , 1 , true ) ;
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
else
2013-02-16 16:14:39 -08:00
Message ( 0 , " Scribing spell: inst exists but item does not or spell ids do not match. " ) ;
}
else
Message ( 0 , " Scribing a spell without an inst on your cursor? " ) ;
break ;
}
case memSpellMemorize : { // memming spell
if ( HasSpellScribed ( memspell - > spell_id ) )
{
MemSpell ( memspell - > spell_id , memspell - > slot ) ;
}
else
{
database . SetMQDetectionFlag ( AccountName ( ) , GetName ( ) , " OP_MemorizeSpell but we don't have this spell scribed... " , zone - > GetShortName ( ) ) ;
}
break ;
}
case memSpellForget : { // unmemming spell
UnmemSpell ( memspell - > slot ) ;
break ;
}
}
Save ( ) ;
}
2016-06-30 17:50:31 -04:00
void Client : : CancelSneakHide ( )
{
if ( hidden | | improved_hidden ) {
auto app = new EQApplicationPacket ( OP_CancelSneakHide , 0 ) ;
FastQueuePacket ( & app ) ;
// SoF and Tit send back a OP_SpawnAppearance turning off AT_Invis
// so we need to handle our sneaking flag only
// The later clients send back a OP_Hide (this has a size but data is 0)
// as well as OP_SpawnAppearance with AT_Invis and one with AT_Sneak
// So we don't have to handle any of those flags
if ( ClientVersionBit ( ) & EQEmu : : versions : : bit_SoFAndEarlier )
sneaking = false ;
}
}
2013-02-16 16:14:39 -08:00
void Client : : BreakInvis ( )
{
if ( invisible )
{
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket ( OP_SpawnAppearance , sizeof ( SpawnAppearance_Struct ) ) ;
2013-02-16 16:14:39 -08:00
SpawnAppearance_Struct * sa_out = ( SpawnAppearance_Struct * ) outapp - > pBuffer ;
sa_out - > spawn_id = GetID ( ) ;
sa_out - > type = 0x03 ;
sa_out - > parameter = 0 ;
entity_list . QueueClients ( this , outapp , true ) ;
safe_delete ( outapp ) ;
invisible = false ;
invisible_undead = false ;
invisible_animals = false ;
hidden = false ;
improved_hidden = false ;
}
}
static uint64 CoinTypeCoppers ( uint32 type ) {
switch ( type ) {
case COINTYPE_PP :
return ( 1000 ) ;
case COINTYPE_GP :
return ( 100 ) ;
case COINTYPE_SP :
return ( 10 ) ;
case COINTYPE_CP :
default :
break ;
}
return ( 1 ) ;
}
void Client : : OPMoveCoin ( const EQApplicationPacket * app )
{
MoveCoin_Struct * mc = ( MoveCoin_Struct * ) app - > pBuffer ;
uint64 value = 0 , amount_to_take = 0 , amount_to_add = 0 ;
int32 * from_bucket = 0 , * to_bucket = 0 ;
Mob * trader = trade - > With ( ) ;
2013-03-20 04:00:08 -04:00
2016-10-30 22:04:10 -06:00
// if amount < 0, client is sending a malicious packet
if ( mc - > amount < 0 )
{
return ;
}
2013-02-16 16:14:39 -08:00
// could just do a range, but this is clearer and explicit
if
(
(
mc - > cointype1 ! = COINTYPE_PP & &
mc - > cointype1 ! = COINTYPE_GP & &
mc - > cointype1 ! = COINTYPE_SP & &
mc - > cointype1 ! = COINTYPE_CP
) | |
(
mc - > cointype2 ! = COINTYPE_PP & &
mc - > cointype2 ! = COINTYPE_GP & &
mc - > cointype2 ! = COINTYPE_SP & &
mc - > cointype2 ! = COINTYPE_CP
)
)
{
return ;
}
switch ( mc - > from_slot )
{
case - 1 : // destroy
{
2013-05-06 13:07:41 -04:00
// I don't think you can move coin from the void,
2013-02-16 16:14:39 -08:00
// but need to check this
break ;
}
case 0 : // cursor
{
switch ( mc - > cointype1 )
{
case COINTYPE_PP :
from_bucket = ( int32 * ) & m_pp . platinum_cursor ; break ;
case COINTYPE_GP :
from_bucket = ( int32 * ) & m_pp . gold_cursor ; break ;
case COINTYPE_SP :
from_bucket = ( int32 * ) & m_pp . silver_cursor ; break ;
case COINTYPE_CP :
from_bucket = ( int32 * ) & m_pp . copper_cursor ; break ;
}
break ;
}
case 1 : // inventory
{
switch ( mc - > cointype1 )
{
case COINTYPE_PP :
from_bucket = ( int32 * ) & m_pp . platinum ; break ;
case COINTYPE_GP :
from_bucket = ( int32 * ) & m_pp . gold ; break ;
case COINTYPE_SP :
from_bucket = ( int32 * ) & m_pp . silver ; break ;
case COINTYPE_CP :
from_bucket = ( int32 * ) & m_pp . copper ; break ;
}
break ;
}
case 2 : // bank
{
uint32 distance = 0 ;
NPC * banker = entity_list . GetClosestBanker ( this , distance ) ;
if ( ! banker | | distance > USE_NPC_RANGE2 )
{
2013-05-04 18:06:58 -07:00
char * hacked_string = nullptr ;
2013-05-06 13:07:41 -04:00
MakeAnyLenString ( & hacked_string , " Player tried to make use of a banker(coin move) but %s is non-existant or too far away (%u units). " ,
2013-02-16 16:14:39 -08:00
banker ? banker - > GetName ( ) : " UNKNOWN NPC " , distance ) ;
database . SetMQDetectionFlag ( AccountName ( ) , GetName ( ) , hacked_string , zone - > GetShortName ( ) ) ;
safe_delete_array ( hacked_string ) ;
return ;
}
switch ( mc - > cointype1 )
{
case COINTYPE_PP :
from_bucket = ( int32 * ) & m_pp . platinum_bank ; break ;
case COINTYPE_GP :
from_bucket = ( int32 * ) & m_pp . gold_bank ; break ;
case COINTYPE_SP :
from_bucket = ( int32 * ) & m_pp . silver_bank ; break ;
case COINTYPE_CP :
from_bucket = ( int32 * ) & m_pp . copper_bank ; break ;
}
break ;
}
case 3 : // trade
{
// can't move coin from trade
break ;
}
case 4 : // shared bank
{
uint32 distance = 0 ;
NPC * banker = entity_list . GetClosestBanker ( this , distance ) ;
if ( ! banker | | distance > USE_NPC_RANGE2 )
{
2013-05-04 18:06:58 -07:00
char * hacked_string = nullptr ;
2013-05-06 13:07:41 -04:00
MakeAnyLenString ( & hacked_string , " Player tried to make use of a banker(shared coin move) but %s is non-existant or too far away (%u units). " ,
2013-02-16 16:14:39 -08:00
banker ? banker - > GetName ( ) : " UNKNOWN NPC " , distance ) ;
database . SetMQDetectionFlag ( AccountName ( ) , GetName ( ) , hacked_string , zone - > GetShortName ( ) ) ;
safe_delete_array ( hacked_string ) ;
return ;
}
if ( mc - > cointype1 = = COINTYPE_PP ) // there's only platinum here
from_bucket = ( int32 * ) & m_pp . platinum_shared ;
break ;
}
}
switch ( mc - > to_slot )
{
case - 1 : // destroy
{
// no action required
break ;
}
case 0 : // cursor
{
switch ( mc - > cointype2 )
{
case COINTYPE_PP :
to_bucket = ( int32 * ) & m_pp . platinum_cursor ; break ;
case COINTYPE_GP :
to_bucket = ( int32 * ) & m_pp . gold_cursor ; break ;
case COINTYPE_SP :
to_bucket = ( int32 * ) & m_pp . silver_cursor ; break ;
case COINTYPE_CP :
to_bucket = ( int32 * ) & m_pp . copper_cursor ; break ;
}
break ;
}
case 1 : // inventory
{
switch ( mc - > cointype2 )
{
case COINTYPE_PP :
to_bucket = ( int32 * ) & m_pp . platinum ; break ;
case COINTYPE_GP :
to_bucket = ( int32 * ) & m_pp . gold ; break ;
case COINTYPE_SP :
to_bucket = ( int32 * ) & m_pp . silver ; break ;
case COINTYPE_CP :
to_bucket = ( int32 * ) & m_pp . copper ; break ;
}
break ;
}
case 2 : // bank
{
uint32 distance = 0 ;
NPC * banker = entity_list . GetClosestBanker ( this , distance ) ;
if ( ! banker | | distance > USE_NPC_RANGE2 )
{
2013-05-04 18:06:58 -07:00
char * hacked_string = nullptr ;
2013-05-06 13:07:41 -04:00
MakeAnyLenString ( & hacked_string , " Player tried to make use of a banker(coin move) but %s is non-existant or too far away (%u units). " ,
2013-02-16 16:14:39 -08:00
banker ? banker - > GetName ( ) : " UNKNOWN NPC " , distance ) ;
database . SetMQDetectionFlag ( AccountName ( ) , GetName ( ) , hacked_string , zone - > GetShortName ( ) ) ;
safe_delete_array ( hacked_string ) ;
return ;
}
switch ( mc - > cointype2 )
{
case COINTYPE_PP :
to_bucket = ( int32 * ) & m_pp . platinum_bank ; break ;
case COINTYPE_GP :
to_bucket = ( int32 * ) & m_pp . gold_bank ; break ;
case COINTYPE_SP :
to_bucket = ( int32 * ) & m_pp . silver_bank ; break ;
case COINTYPE_CP :
to_bucket = ( int32 * ) & m_pp . copper_bank ; break ;
}
break ;
}
case 3 : // trade
{
if ( trader )
{
switch ( mc - > cointype2 )
{
case COINTYPE_PP :
to_bucket = ( int32 * ) & trade - > pp ; break ;
case COINTYPE_GP :
to_bucket = ( int32 * ) & trade - > gp ; break ;
case COINTYPE_SP :
to_bucket = ( int32 * ) & trade - > sp ; break ;
case COINTYPE_CP :
to_bucket = ( int32 * ) & trade - > cp ; break ;
}
}
break ;
}
case 4 : // shared bank
{
uint32 distance = 0 ;
NPC * banker = entity_list . GetClosestBanker ( this , distance ) ;
if ( ! banker | | distance > USE_NPC_RANGE2 )
{
2013-05-04 18:06:58 -07:00
char * hacked_string = nullptr ;
2013-05-06 13:07:41 -04:00
MakeAnyLenString ( & hacked_string , " Player tried to make use of a banker(shared coin move) but %s is non-existant or too far away (%u units). " ,
2013-02-16 16:14:39 -08:00
banker ? banker - > GetName ( ) : " UNKNOWN NPC " , distance ) ;
database . SetMQDetectionFlag ( AccountName ( ) , GetName ( ) , hacked_string , zone - > GetShortName ( ) ) ;
safe_delete_array ( hacked_string ) ;
return ;
}
if ( mc - > cointype2 = = COINTYPE_PP ) // there's only platinum here
to_bucket = ( int32 * ) & m_pp . platinum_shared ;
break ;
}
}
if ( ! from_bucket )
{
return ;
}
// don't allow them to go into negatives (from our point of view)
amount_to_take = * from_bucket < mc - > amount ? * from_bucket : mc - > amount ;
2013-02-17 23:38:51 -05:00
// if you move 11 gold into a bank platinum location, the packet
2013-02-16 16:14:39 -08:00
// will say 11, but the client will have 1 left on their cursor, so we have
// to figure out the conversion ourselves
amount_to_add = amount_to_take * ( ( float ) CoinTypeCoppers ( mc - > cointype1 ) / ( float ) CoinTypeCoppers ( mc - > cointype2 ) ) ;
// the amount we're adding could be different than what was requested, so
// we have to adjust the amount we take as well
amount_to_take = amount_to_add * ( ( float ) CoinTypeCoppers ( mc - > cointype2 ) / ( float ) CoinTypeCoppers ( mc - > cointype1 ) ) ;
2013-02-17 23:38:51 -05:00
// now we should have a from_bucket, a to_bucket, an amount_to_take
2013-02-16 16:14:39 -08:00
// and an amount_to_add
2013-05-09 10:44:08 -04:00
// now we actually take it from the from bucket. if there's an error
2013-02-16 16:14:39 -08:00
// with the destination slot, they lose their money
* from_bucket - = amount_to_take ;
// why are intentionally inducing a crash here rather than letting the code attempt to stumble on?
// assert(*from_bucket >= 0);
if ( to_bucket )
{
if ( * to_bucket + amount_to_add > * to_bucket ) // overflow check
* to_bucket + = amount_to_add ;
//shared bank plat
if ( RuleB ( Character , SharedBankPlat ) )
{
if ( to_bucket = = & m_pp . platinum_shared | | from_bucket = = & m_pp . platinum_shared )
{
2013-05-06 13:07:41 -04:00
if ( from_bucket = = & m_pp . platinum_shared )
2013-02-16 16:14:39 -08:00
amount_to_add = 0 - amount_to_take ;
2014-11-25 20:16:04 -08:00
database . SetSharedPlatinum ( AccountID ( ) , amount_to_add ) ;
2014-09-09 16:03:24 -05:00
}
}
else {
if ( to_bucket = = & m_pp . platinum_shared | | from_bucket = = & m_pp . platinum_shared ) {
this - > Message ( 13 , " ::: WARNING! ::: SHARED BANK IS DISABLED AND YOUR PLATINUM WILL BE DESTROYED IF YOU PUT IT HERE " ) ;
2013-02-16 16:14:39 -08:00
}
}
}
// if this is a trade move, inform the person being traded with
if ( mc - > to_slot = = 3 & & trader & & trader - > IsClient ( ) )
{
// If one party accepted the trade then some coin was added, their state needs to be reset
trade - > state = Trading ;
Mob * with = trade - > With ( ) ;
if ( with )
with - > trade - > state = Trading ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
Client * recipient = trader - > CastToClient ( ) ;
recipient - > Message ( 15 , " %s adds some coins to the trade. " , GetName ( ) ) ;
recipient - > Message ( 15 , " The total trade is: %i PP, %i GP, %i SP, %i CP " ,
trade - > pp , trade - > gp ,
trade - > sp , trade - > cp
) ;
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket ( OP_TradeCoins , sizeof ( TradeCoin_Struct ) ) ;
2013-02-16 16:14:39 -08:00
TradeCoin_Struct * tcs = ( TradeCoin_Struct * ) outapp - > pBuffer ;
tcs - > trader = trader - > GetID ( ) ;
tcs - > slot = mc - > cointype2 ;
tcs - > unknown5 = 0x4fD2 ;
tcs - > unknown7 = 0 ;
tcs - > amount = amount_to_add ;
recipient - > QueuePacket ( outapp ) ;
safe_delete ( outapp ) ;
}
2014-08-31 02:53:59 -05:00
SaveCurrency ( ) ;
2013-02-16 16:14:39 -08:00
}
void Client : : OPGMTraining ( const EQApplicationPacket * app )
{
EQApplicationPacket * outapp = app - > Copy ( ) ;
GMTrainee_Struct * gmtrain = ( GMTrainee_Struct * ) outapp - > pBuffer ;
Mob * pTrainer = entity_list . GetMob ( gmtrain - > npcid ) ;
if ( ! pTrainer | | ! pTrainer - > IsNPC ( ) | | pTrainer - > GetClass ( ) < WARRIORGM | | pTrainer - > GetClass ( ) > BERSERKERGM )
return ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
//you can only use your own trainer, client enforces this, but why trust it
int trains_class = pTrainer - > GetClass ( ) - ( WARRIORGM - WARRIOR ) ;
if ( GetClass ( ) ! = trains_class )
return ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
//you have to be somewhat close to a trainer to be properly using them
2015-01-23 00:01:10 -08:00
if ( DistanceSquared ( m_Position , pTrainer - > GetPosition ( ) ) > USE_NPC_RANGE2 )
2013-02-16 16:14:39 -08:00
return ;
2014-11-12 23:41:38 -05:00
// if this for-loop acts up again (crashes linux), try enabling the before and after #pragmas
//#pragma GCC push_options
//#pragma GCC optimize ("O0")
2016-05-25 18:50:26 -04:00
for ( int sk = EQEmu : : skills : : Skill1HBlunt ; sk < = EQEmu : : skills : : HIGHEST_SKILL ; + + sk ) {
if ( sk = = EQEmu : : skills : : SkillTinkering & & GetRace ( ) ! = GNOME ) {
2013-02-16 16:14:39 -08:00
gmtrain - > skills [ sk ] = 0 ; //Non gnomes can't tinker!
} else {
2016-05-25 18:50:26 -04:00
gmtrain - > skills [ sk ] = GetMaxSkillAfterSpecializationRules ( ( EQEmu : : skills : : SkillType ) sk , MaxSkill ( ( EQEmu : : skills : : SkillType ) sk , GetClass ( ) , RuleI ( Character , MaxLevel ) ) ) ;
2013-02-16 16:14:39 -08:00
//this is the highest level that the trainer can train you to, this is enforced clientside so we can't just
//Set it to 1 with CanHaveSkill or you wont be able to train past 1.
}
}
2016-01-26 15:08:41 -05:00
2016-04-22 03:49:17 -04:00
if ( ClientVersion ( ) < EQEmu : : versions : : ClientVersion : : RoF2 & & GetClass ( ) = = BERSERKER ) {
2016-05-25 18:50:26 -04:00
gmtrain - > skills [ EQEmu : : skills : : Skill1HPiercing ] = gmtrain - > skills [ EQEmu : : skills : : Skill2HPiercing ] ;
gmtrain - > skills [ EQEmu : : skills : : Skill2HPiercing ] = 0 ;
2016-01-26 15:08:41 -05:00
}
2014-11-12 23:41:38 -05:00
//#pragma GCC pop_options
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
uchar ending [ ] = { 0x34 , 0x87 , 0x8a , 0x3F , 0x01
, 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9
, 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9 , 0xC9
, 0x76 , 0x75 , 0x3f } ;
memcpy ( & outapp - > pBuffer [ outapp - > size - 40 ] , ending , sizeof ( ending ) ) ;
FastQueuePacket ( & outapp ) ;
// welcome message
if ( pTrainer & & pTrainer - > IsNPC ( ) )
{
2014-12-01 18:13:12 -05:00
pTrainer - > Say_StringID ( zone - > random . Int ( 1204 , 1207 ) , GetCleanName ( ) ) ;
2013-02-16 16:14:39 -08:00
}
}
void Client : : OPGMEndTraining ( const EQApplicationPacket * app )
{
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket ( OP_GMEndTrainingResponse , 0 ) ;
2013-02-16 16:14:39 -08:00
GMTrainEnd_Struct * p = ( GMTrainEnd_Struct * ) app - > pBuffer ;
FastQueuePacket ( & outapp ) ;
Mob * pTrainer = entity_list . GetMob ( p - > npcid ) ;
if ( ! pTrainer | | ! pTrainer - > IsNPC ( ) | | pTrainer - > GetClass ( ) < WARRIORGM | | pTrainer - > GetClass ( ) > BERSERKERGM )
return ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
//you can only use your own trainer, client enforces this, but why trust it
int trains_class = pTrainer - > GetClass ( ) - ( WARRIORGM - WARRIOR ) ;
if ( GetClass ( ) ! = trains_class )
return ;
//you have to be somewhat close to a trainer to be properly using them
2015-01-23 00:01:10 -08:00
if ( DistanceSquared ( m_Position , pTrainer - > GetPosition ( ) ) > USE_NPC_RANGE2 )
2013-02-16 16:14:39 -08:00
return ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// goodbye message
if ( pTrainer - > IsNPC ( ) )
{
2014-12-01 18:13:12 -05:00
pTrainer - > Say_StringID ( zone - > random . Int ( 1208 , 1211 ) , GetCleanName ( ) ) ;
2013-02-16 16:14:39 -08:00
}
}
void Client : : OPGMTrainSkill ( const EQApplicationPacket * app )
{
if ( ! m_pp . points )
return ;
int Cost = 0 ;
GMSkillChange_Struct * gmskill = ( GMSkillChange_Struct * ) app - > pBuffer ;
Mob * pTrainer = entity_list . GetMob ( gmskill - > npcid ) ;
if ( ! pTrainer | | ! pTrainer - > IsNPC ( ) | | pTrainer - > GetClass ( ) < WARRIORGM | | pTrainer - > GetClass ( ) > BERSERKERGM )
return ;
//you can only use your own trainer, client enforces this, but why trust it
int trains_class = pTrainer - > GetClass ( ) - ( WARRIORGM - WARRIOR ) ;
if ( GetClass ( ) ! = trains_class )
return ;
//you have to be somewhat close to a trainer to be properly using them
2015-01-23 00:01:10 -08:00
if ( DistanceSquared ( m_Position , pTrainer - > GetPosition ( ) ) > USE_NPC_RANGE2 )
2013-02-16 16:14:39 -08:00
return ;
if ( gmskill - > skillbank = = 0x01 )
{
// languages go here
if ( gmskill - > skill_id > 25 )
{
2013-05-22 16:17:19 -04:00
std : : cout < < " Wrong Training Skill (languages) " < < std : : endl ;
2013-02-16 16:14:39 -08:00
DumpPacket ( app ) ;
return ;
}
int AdjustedSkillLevel = GetLanguageSkill ( gmskill - > skill_id ) - 10 ;
if ( AdjustedSkillLevel > 0 )
Cost = AdjustedSkillLevel * AdjustedSkillLevel * AdjustedSkillLevel / 100 ;
IncreaseLanguageSkill ( gmskill - > skill_id ) ;
}
else if ( gmskill - > skillbank = = 0x00 )
{
// normal skills go here
2016-05-25 18:50:26 -04:00
if ( gmskill - > skill_id > EQEmu : : skills : : HIGHEST_SKILL )
2013-02-16 16:14:39 -08:00
{
2013-05-22 16:17:19 -04:00
std : : cout < < " Wrong Training Skill (abilities) " < < std : : endl ;
2013-02-16 16:14:39 -08:00
DumpPacket ( app ) ;
return ;
}
2016-05-25 18:50:26 -04:00
EQEmu : : skills : : SkillType skill = ( EQEmu : : skills : : SkillType ) gmskill - > skill_id ;
2013-02-16 16:14:39 -08:00
if ( ! CanHaveSkill ( skill ) ) {
2017-04-01 03:51:46 -05:00
Log ( Logs : : Detail , Logs : : Skills , " Tried to train skill %d, which is not allowed. " , skill ) ;
2013-02-16 16:14:39 -08:00
return ;
}
2013-08-29 15:52:17 -07:00
if ( MaxSkill ( skill ) = = 0 ) {
2017-04-01 03:51:46 -05:00
Log ( Logs : : Detail , Logs : : Skills , " Tried to train skill %d, but training is not allowed at this level. " , skill ) ;
2013-08-29 15:52:17 -07:00
return ;
}
2013-02-16 16:14:39 -08:00
uint16 skilllevel = GetRawSkill ( skill ) ;
State of Commit: Testable if you ask me (Akkadius) what you need to do
- Need to convert a list of functions and columns and should be ready to start intensive testing phase
- All preliminary tests show things working great
- All of player profile is saved and loaded from the database
- DBAsync has been completely removed from all code
- Removed zone/dbasync.cpp/.h
- Removed common/dbasync.cpp/.h
- Removed dbasync from cmake commmon and zone
- Cleaned up a ton of functions
- Added several tables to world CheckDatabaseConversions script:
- `character_skills`
- `character_languages`
- `character_bind`
- `character_alternate_abilities`
- `character_currency`
- `character_data`
- `character_spells`
- `character_memmed_spells`
- `character_disciplines`
- `character_material`
- `character_tribute`
- `character_bandolier`
- `character_potionbelt`
- Character select now loads from `character_data`
- Character creation now creates to `character_data`
- Updated function Database::UpdateName to use `character_data`
- Updated function Database::CheckUsedName to use `character_data`
- Updated function Database::MoveCharacterToZone to use `character_data`
- Updated function Database::SetLoginFlags to use `character_data`
- Updated function Database::SetFirstLogon to use `character_data`
- Updated function Database::SetLFG to use `character_data`
- Removed CopyCharacter functions and commands, to be recreated later since it never worked to begin with
- Removed SharedDatabase::SetPlayerProfile
- Trimmed down redundant case switch statements for World sendpackets to QueryServ
- Added Character Methods to Database class:
Loads:
bool LoadCharacterBandolier(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterPotions(uint32 character_id, PlayerProfile_Struct* pp);
Saves:
bool SaveCharacterBindPoint(uint32 character_id, uint32 zone_id, uint32 instance_id, float x, float y, float z, float heading, uint8 is_home);
bool SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp);
bool SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp);
bool SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level);
bool SaveCharacterSpellSwap(uint32 character_id, uint32 spell_id, uint32 from_slot, uint32 to_slot);
bool SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id);
bool SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id);
bool SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color);
bool SaveCharacterSkill(uint32 character_id, uint32 skill_id, uint32 value);
bool SaveCharacterLanguage(uint32 character_id, uint32 lang_id, uint32 value);
bool SaveCharacterDisc(uint32 character_id, uint32 slot_id, uint32 disc_id);
bool SaveCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp);
bool SaveCharacterBandolier(uint32 character_id, uint8 bandolier_id, uint8 bandolier_slot, uint32 item_id, uint32 icon, const char* bandolier_name);
bool SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon);
Deletes:
bool DeleteCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id);
bool DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id);
bool DeleteCharacterDisc(uint32 character_id, uint32 slot_id);
bool DeleteCharacterBandolier(uint32 character_id, uint32 band_id);
2014-09-04 07:24:17 -05:00
2013-02-16 16:14:39 -08:00
if ( skilllevel = = 0 ) {
//this is a new skill..
uint16 t_level = SkillTrainLevel ( skill , GetClass ( ) ) ;
if ( t_level = = 0 )
{
return ;
}
2013-08-29 15:52:17 -07:00
2013-02-16 16:14:39 -08:00
SetSkill ( skill , t_level ) ;
2014-11-25 20:16:04 -08:00
} else {
2013-02-16 16:14:39 -08:00
switch ( skill ) {
2016-05-25 18:50:26 -04:00
case EQEmu : : skills : : SkillBrewing :
case EQEmu : : skills : : SkillMakePoison :
case EQEmu : : skills : : SkillTinkering :
case EQEmu : : skills : : SkillResearch :
case EQEmu : : skills : : SkillAlchemy :
case EQEmu : : skills : : SkillBaking :
case EQEmu : : skills : : SkillTailoring :
case EQEmu : : skills : : SkillBlacksmithing :
case EQEmu : : skills : : SkillFletching :
case EQEmu : : skills : : SkillJewelryMaking :
case EQEmu : : skills : : SkillPottery :
2013-02-16 16:14:39 -08:00
if ( skilllevel > = RuleI ( Skills , MaxTrainTradeskills ) ) {
Message_StringID ( 13 , MORE_SKILLED_THAN_I , pTrainer - > GetCleanName ( ) ) ;
return ;
}
break ;
2016-05-25 18:50:26 -04:00
case EQEmu : : skills : : SkillSpecializeAbjure :
case EQEmu : : skills : : SkillSpecializeAlteration :
case EQEmu : : skills : : SkillSpecializeConjuration :
case EQEmu : : skills : : SkillSpecializeDivination :
case EQEmu : : skills : : SkillSpecializeEvocation :
2013-02-16 16:14:39 -08:00
if ( skilllevel > = RuleI ( Skills , MaxTrainSpecializations ) ) {
Message_StringID ( 13 , MORE_SKILLED_THAN_I , pTrainer - > GetCleanName ( ) ) ;
return ;
}
default :
break ;
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
int MaxSkillValue = MaxSkill ( skill ) ;
if ( skilllevel > = MaxSkillValue )
{
// Don't allow training over max skill level
Message_StringID ( 13 , MORE_SKILLED_THAN_I , pTrainer - > GetCleanName ( ) ) ;
return ;
}
2013-05-06 13:07:41 -04:00
2016-05-25 18:50:26 -04:00
if ( gmskill - > skill_id > = EQEmu : : skills : : SkillSpecializeAbjure & & gmskill - > skill_id < = EQEmu : : skills : : SkillSpecializeEvocation )
2013-02-16 16:14:39 -08:00
{
int MaxSpecSkill = GetMaxSkillAfterSpecializationRules ( skill , MaxSkillValue ) ;
if ( skilllevel > = MaxSpecSkill )
{
// Restrict specialization training to follow the rules
Message_StringID ( 13 , MORE_SKILLED_THAN_I , pTrainer - > GetCleanName ( ) ) ;
return ;
}
}
// Client train a valid skill
//
int AdjustedSkillLevel = skilllevel - 10 ;
if ( AdjustedSkillLevel > 0 )
Cost = AdjustedSkillLevel * AdjustedSkillLevel * AdjustedSkillLevel / 100 ;
SetSkill ( skill , skilllevel + 1 ) ;
}
}
2016-04-22 03:49:17 -04:00
if ( ClientVersion ( ) > = EQEmu : : versions : : ClientVersion : : SoF ) {
2013-02-16 16:14:39 -08:00
// The following packet decreases the skill points left in the Training Window and
// produces the 'You have increased your skill / learned the basics of' message.
//
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket ( OP_GMTrainSkillConfirm , sizeof ( GMTrainSkillConfirm_Struct ) ) ;
2013-02-16 16:14:39 -08:00
GMTrainSkillConfirm_Struct * gmtsc = ( GMTrainSkillConfirm_Struct * ) outapp - > pBuffer ;
gmtsc - > SkillID = gmskill - > skill_id ;
if ( gmskill - > skillbank = = 1 ) {
gmtsc - > NewSkill = ( GetLanguageSkill ( gmtsc - > SkillID ) = = 1 ) ;
gmtsc - > SkillID + = 100 ;
}
else
2016-05-25 18:50:26 -04:00
gmtsc - > NewSkill = ( GetRawSkill ( ( EQEmu : : skills : : SkillType ) gmtsc - > SkillID ) = = 1 ) ;
2013-02-16 16:14:39 -08:00
gmtsc - > Cost = Cost ;
strcpy ( gmtsc - > TrainerName , pTrainer - > GetCleanName ( ) ) ;
QueuePacket ( outapp ) ;
safe_delete ( outapp ) ;
}
if ( Cost )
TakeMoneyFromPP ( Cost ) ;
m_pp . points - - ;
}
// this is used for /summon and /corpse
void Client : : OPGMSummon ( const EQApplicationPacket * app )
{
GMSummon_Struct * gms = ( GMSummon_Struct * ) app - > pBuffer ;
Mob * st = entity_list . GetMob ( gms - > charname ) ;
if ( st & & st - > IsCorpse ( ) )
{
st - > CastToCorpse ( ) - > Summon ( this , false , true ) ;
}
else
{
if ( admin < 80 )
{
return ;
}
if ( st )
{
Message ( 0 , " Local: Summoning %s to %f, %f, %f " , gms - > charname , gms - > x , gms - > y , gms - > z ) ;
if ( st - > IsClient ( ) & & ( st - > CastToClient ( ) - > GetAnon ( ) ! = 1 | | this - > Admin ( ) > = st - > CastToClient ( ) - > Admin ( ) ) )
st - > CastToClient ( ) - > MovePC ( zone - > GetZoneID ( ) , zone - > GetInstanceID ( ) , ( float ) gms - > x , ( float ) gms - > y , ( float ) gms - > z , this - > GetHeading ( ) , true ) ;
else
st - > GMMove ( this - > GetX ( ) , this - > GetY ( ) , this - > GetZ ( ) , this - > GetHeading ( ) ) ;
}
else
{
uint8 tmp = gms - > charname [ strlen ( gms - > charname ) - 1 ] ;
if ( ! worldserver . Connected ( ) )
{
Message ( 0 , " Error: World server disconnected " ) ;
}
else if ( tmp < ' 0 ' | | tmp > ' 9 ' ) // dont send to world if it's not a player's name
{
2016-05-25 16:10:28 -04:00
auto pack = new ServerPacket ( ServerOP_ZonePlayer , sizeof ( ServerZonePlayer_Struct ) ) ;
2013-02-16 16:14:39 -08:00
ServerZonePlayer_Struct * szp = ( ServerZonePlayer_Struct * ) pack - > pBuffer ;
strcpy ( szp - > adminname , this - > GetName ( ) ) ;
szp - > adminrank = this - > Admin ( ) ;
strcpy ( szp - > name , gms - > charname ) ;
strcpy ( szp - > zone , zone - > GetShortName ( ) ) ;
szp - > x_pos = ( float ) gms - > x ;
szp - > y_pos = ( float ) gms - > y ;
szp - > z_pos = ( float ) gms - > z ;
szp - > ignorerestrictions = 2 ;
worldserver . SendPacket ( pack ) ;
safe_delete ( pack ) ;
}
else {
//all options have been exhausted
//summon our target...
if ( GetTarget ( ) & & GetTarget ( ) - > IsCorpse ( ) ) {
GetTarget ( ) - > CastToCorpse ( ) - > Summon ( this , false , true ) ;
}
}
}
}
}
void Client : : DoHPRegen ( ) {
2017-10-08 00:13:53 -04:00
SetHP ( GetHP ( ) + CalcHPRegen ( ) ) ;
2013-02-16 16:14:39 -08:00
SendHPUpdate ( ) ;
}
void Client : : DoManaRegen ( ) {
2015-02-21 06:32:41 -05:00
if ( GetMana ( ) > = max_mana & & spellbonuses . ManaRegen > = 0 )
2013-02-16 16:14:39 -08:00
return ;
2013-05-06 13:07:41 -04:00
2017-10-08 00:13:53 -04:00
if ( GetMana ( ) < max_mana & & ( IsSitting ( ) | | CanMedOnHorse ( ) ) & & HasSkill ( EQEmu : : skills : : SkillMeditate ) )
CheckIncreaseSkill ( EQEmu : : skills : : SkillMeditate , nullptr , - 5 ) ;
SetMana ( GetMana ( ) + CalcManaRegen ( ) ) ;
2017-07-11 17:54:46 -05:00
CheckManaEndUpdate ( ) ;
2013-02-16 16:14:39 -08:00
}
2017-09-19 02:01:06 -04:00
void Client : : DoStaminaHungerUpdate ( )
{
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket ( OP_Stamina , sizeof ( Stamina_Struct ) ) ;
2017-09-19 02:01:06 -04:00
Stamina_Struct * sta = ( Stamina_Struct * ) outapp - > pBuffer ;
2013-02-16 16:14:39 -08:00
2017-09-19 02:01:06 -04:00
Log ( Logs : : General , Logs : : Food , " Client::DoStaminaHungerUpdate() hunger_level: %i thirst_level: %i before loss " ,
m_pp . hunger_level , m_pp . thirst_level ) ;
2017-09-17 09:48:10 -05:00
2017-09-19 02:01:06 -04:00
if ( zone - > GetZoneID ( ) ! = 151 & & ! GetGM ( ) ) {
int loss = RuleI ( Character , FoodLossPerUpdate ) ;
if ( GetHorseId ( ) ! = 0 )
loss * = 3 ;
m_pp . hunger_level = EQEmu : : Clamp ( m_pp . hunger_level - loss , 0 , 6000 ) ;
m_pp . thirst_level = EQEmu : : Clamp ( m_pp . thirst_level - loss , 0 , 6000 ) ;
if ( spellbonuses . hunger ) {
m_pp . hunger_level = EQEmu : : ClampLower ( m_pp . hunger_level , 3500 ) ;
m_pp . thirst_level = EQEmu : : ClampLower ( m_pp . thirst_level , 3500 ) ;
}
sta - > food = m_pp . hunger_level ;
sta - > water = m_pp . thirst_level ;
} else {
2013-02-16 16:14:39 -08:00
// No auto food/drink consumption in the Bazaar
sta - > food = 6000 ;
sta - > water = 6000 ;
}
2017-09-17 09:48:10 -05:00
2017-09-19 02:01:06 -04:00
Log ( Logs : : General , Logs : : Food ,
" Client::DoStaminaHungerUpdate() Current hunger_level: %i = (%i minutes left) thirst_level: %i = (%i "
" minutes left) - after loss " ,
m_pp . hunger_level , m_pp . hunger_level , m_pp . thirst_level , m_pp . thirst_level ) ;
2017-09-17 09:48:10 -05:00
2013-02-16 16:14:39 -08:00
FastQueuePacket ( & outapp ) ;
}
void Client : : DoEnduranceRegen ( )
{
2017-10-08 00:13:53 -04:00
// endurance has some negative mods that could result in a negative regen when starved
int regen = CalcEnduranceRegen ( ) ;
2013-02-16 16:14:39 -08:00
2017-10-08 00:13:53 -04:00
if ( regen < 0 | | ( regen > 0 & & GetEndurance ( ) < GetMaxEndurance ( ) ) )
SetEndurance ( GetEndurance ( ) + regen ) ;
2013-02-16 16:14:39 -08:00
}
void Client : : DoEnduranceUpkeep ( ) {
2014-11-12 01:54:21 -05:00
if ( ! HasEndurUpkeep ( ) )
return ;
2013-02-16 16:14:39 -08:00
2014-11-12 01:54:21 -05:00
int upkeep_sum = 0 ;
int cost_redux = spellbonuses . EnduranceReduction + itembonuses . EnduranceReduction + aabonuses . EnduranceReduction ;
2014-11-25 20:16:04 -08:00
2014-11-12 01:54:21 -05:00
bool has_effect = false ;
2013-02-16 16:14:39 -08:00
uint32 buffs_i ;
uint32 buff_count = GetMaxTotalSlots ( ) ;
for ( buffs_i = 0 ; buffs_i < buff_count ; buffs_i + + ) {
if ( buffs [ buffs_i ] . spellid ! = SPELL_UNKNOWN ) {
int upkeep = spells [ buffs [ buffs_i ] . spellid ] . EndurUpkeep ;
if ( upkeep > 0 ) {
2014-11-12 02:03:24 -05:00
has_effect = true ;
2013-02-16 16:14:39 -08:00
if ( cost_redux > 0 ) {
if ( upkeep < = cost_redux )
continue ; //reduced to 0
upkeep - = cost_redux ;
}
if ( ( upkeep + upkeep_sum ) > GetEndurance ( ) ) {
//they do not have enough to keep this one going.
BuffFadeBySlot ( buffs_i ) ;
} else {
upkeep_sum + = upkeep ;
}
}
}
}
2013-05-06 13:07:41 -04:00
2013-12-17 21:51:13 -05:00
if ( upkeep_sum ! = 0 ) {
2013-02-16 16:14:39 -08:00
SetEndurance ( GetEndurance ( ) - upkeep_sum ) ;
2013-12-17 21:51:13 -05:00
TryTriggerOnValueAmount ( false , false , true ) ;
}
2014-11-12 01:54:21 -05:00
if ( ! has_effect )
SetEndurUpkeep ( false ) ;
2013-02-16 16:14:39 -08:00
}
void Client : : CalcRestState ( ) {
// This method calculates rest state HP and mana regeneration.
// The client must have been out of combat for RuleI(Character, RestRegenTimeToActivate) seconds,
// must be sitting down, and must not have any detrimental spells affecting them.
//
2017-10-08 00:13:53 -04:00
if ( ! RuleB ( Character , RestRegenEnabled ) )
2013-02-16 16:14:39 -08:00
return ;
2017-10-08 00:13:53 -04:00
ooc_regen = false ;
2013-02-16 16:14:39 -08:00
2017-10-08 00:13:53 -04:00
if ( AggroCount | | ! ( IsSitting ( ) | | CanMedOnHorse ( ) ) )
2013-02-16 16:14:39 -08:00
return ;
if ( ! rest_timer . Check ( false ) )
return ;
uint32 buff_count = GetMaxTotalSlots ( ) ;
for ( unsigned int j = 0 ; j < buff_count ; j + + ) {
if ( buffs [ j ] . spellid ! = SPELL_UNKNOWN ) {
if ( IsDetrimentalSpell ( buffs [ j ] . spellid ) & & ( buffs [ j ] . ticsremaining > 0 ) )
if ( ! DetrimentalSpellAllowsRest ( buffs [ j ] . spellid ) )
return ;
}
}
2017-10-08 00:13:53 -04:00
ooc_regen = true ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
}
void Client : : DoTracking ( )
{
if ( TrackingID = = 0 )
return ;
Mob * m = entity_list . GetMob ( TrackingID ) ;
if ( ! m | | m - > IsCorpse ( ) )
{
Message_StringID ( MT_Skills , TRACK_LOST_TARGET ) ;
TrackingID = 0 ;
return ;
}
float RelativeHeading = GetHeading ( ) - CalculateHeadingToTarget ( m - > GetX ( ) , m - > GetY ( ) ) ;
if ( RelativeHeading < 0 )
RelativeHeading + = 256 ;
if ( ( RelativeHeading < = 16 ) | | ( RelativeHeading > = 240 ) )
{
Message_StringID ( MT_Skills , TRACK_STRAIGHT_AHEAD , m - > GetCleanName ( ) ) ;
}
else if ( ( RelativeHeading > 16 ) & & ( RelativeHeading < = 48 ) )
{
Message_StringID ( MT_Skills , TRACK_AHEAD_AND_TO , m - > GetCleanName ( ) , " right " ) ;
}
else if ( ( RelativeHeading > 48 ) & & ( RelativeHeading < = 80 ) )
{
Message_StringID ( MT_Skills , TRACK_TO_THE , m - > GetCleanName ( ) , " right " ) ;
}
else if ( ( RelativeHeading > 80 ) & & ( RelativeHeading < = 112 ) )
{
Message_StringID ( MT_Skills , TRACK_BEHIND_AND_TO , m - > GetCleanName ( ) , " right " ) ;
}
else if ( ( RelativeHeading > 112 ) & & ( RelativeHeading < = 144 ) )
{
Message_StringID ( MT_Skills , TRACK_BEHIND_YOU , m - > GetCleanName ( ) ) ;
}
else if ( ( RelativeHeading > 144 ) & & ( RelativeHeading < = 176 ) )
{
Message_StringID ( MT_Skills , TRACK_BEHIND_AND_TO , m - > GetCleanName ( ) , " left " ) ;
}
else if ( ( RelativeHeading > 176 ) & & ( RelativeHeading < = 208 ) )
{
Message_StringID ( MT_Skills , TRACK_TO_THE , m - > GetCleanName ( ) , " left " ) ;
}
else if ( ( RelativeHeading > 208 ) & & ( RelativeHeading < 240 ) )
{
Message_StringID ( MT_Skills , TRACK_AHEAD_AND_TO , m - > GetCleanName ( ) , " left " ) ;
}
}
void Client : : HandleRespawnFromHover ( uint32 Option )
{
RespawnFromHoverTimer . Disable ( ) ;
2013-06-22 08:35:33 -07:00
RespawnOption * chosen = nullptr ;
bool is_rez = false ;
//Find the selected option
if ( Option = = 0 )
{
chosen = & respawn_options . front ( ) ;
}
else if ( Option = = ( respawn_options . size ( ) - 1 ) )
2013-02-16 16:14:39 -08:00
{
2013-06-22 08:35:33 -07:00
chosen = & respawn_options . back ( ) ;
is_rez = true ; //Rez must always be the last option
}
else
2013-02-16 16:14:39 -08:00
{
2013-06-22 08:35:33 -07:00
std : : list < RespawnOption > : : iterator itr ;
uint32 pos = 0 ;
for ( itr = respawn_options . begin ( ) ; itr ! = respawn_options . end ( ) ; + + itr )
2013-02-16 16:14:39 -08:00
{
2013-06-22 08:35:33 -07:00
if ( pos + + = = Option )
{
chosen = & ( * itr ) ;
break ;
}
2013-02-16 16:14:39 -08:00
}
2013-06-22 08:35:33 -07:00
}
2013-02-16 16:14:39 -08:00
2013-06-22 08:35:33 -07:00
//If they somehow chose an option they don't have, just send them to bind
2013-06-25 11:05:03 -07:00
RespawnOption * default_to_bind = nullptr ;
2013-06-22 08:35:33 -07:00
if ( ! chosen )
{
/* put error logging here */
BindStruct * b = & m_pp . binds [ 0 ] ;
2013-06-25 11:05:03 -07:00
default_to_bind = new RespawnOption ;
default_to_bind - > name = " Bind Location " ;
2014-11-18 21:12:48 -08:00
default_to_bind - > zone_id = b - > zoneId ;
default_to_bind - > instance_id = b - > instance_id ;
2013-06-25 11:05:03 -07:00
default_to_bind - > x = b - > x ;
default_to_bind - > y = b - > y ;
default_to_bind - > z = b - > z ;
default_to_bind - > heading = b - > heading ;
chosen = default_to_bind ;
2013-06-22 08:35:33 -07:00
is_rez = false ;
}
2014-11-18 21:12:48 -08:00
if ( chosen - > zone_id = = zone - > GetZoneID ( ) & & chosen - > instance_id = = zone - > GetInstanceID ( ) ) //If they should respawn in the current zone...
2013-06-22 08:35:33 -07:00
{
if ( is_rez )
2013-02-16 16:14:39 -08:00
{
2013-06-22 08:35:33 -07:00
if ( PendingRezzXP < 0 | | PendingRezzSpellID = = 0 )
{
2017-04-01 03:51:46 -05:00
Log ( Logs : : Detail , Logs : : Spells , " Unexpected Rezz from hover request. " ) ;
2013-06-22 08:35:33 -07:00
return ;
}
SetHP ( GetMaxHP ( ) / 5 ) ;
2013-02-16 16:14:39 -08:00
2013-06-22 08:35:33 -07:00
Corpse * corpse = entity_list . GetCorpseByName ( PendingRezzCorpseName . c_str ( ) ) ;
2013-05-06 13:07:41 -04:00
2013-06-22 08:35:33 -07:00
if ( corpse )
{
2015-01-23 00:01:10 -08:00
m_Position . x = corpse - > GetX ( ) ;
m_Position . y = corpse - > GetY ( ) ;
m_Position . z = corpse - > GetZ ( ) ;
2013-06-22 08:35:33 -07:00
}
2013-02-16 16:14:39 -08:00
2016-05-25 16:10:28 -04:00
auto outapp =
new EQApplicationPacket ( OP_ZonePlayerToBind , sizeof ( ZonePlayerToBind_Struct ) + 10 ) ;
2013-06-22 08:35:33 -07:00
ZonePlayerToBind_Struct * gmg = ( ZonePlayerToBind_Struct * ) outapp - > pBuffer ;
2013-02-16 16:14:39 -08:00
2013-06-22 08:35:33 -07:00
gmg - > bind_zone_id = zone - > GetZoneID ( ) ;
gmg - > bind_instance_id = zone - > GetInstanceID ( ) ;
gmg - > x = GetX ( ) ;
gmg - > y = GetY ( ) ;
gmg - > z = GetZ ( ) ;
gmg - > heading = GetHeading ( ) ;
strcpy ( gmg - > zone_name , " Resurrect " ) ;
2013-02-16 16:14:39 -08:00
2013-06-22 08:35:33 -07:00
FastQueuePacket ( & outapp ) ;
2013-02-16 16:14:39 -08:00
2013-06-22 08:35:33 -07:00
ClearHover ( ) ;
SendHPUpdate ( ) ;
OPRezzAnswer ( 1 , PendingRezzSpellID , zone - > GetZoneID ( ) , zone - > GetInstanceID ( ) , GetX ( ) , GetY ( ) , GetZ ( ) ) ;
if ( corpse & & corpse - > IsCorpse ( ) )
{
2017-04-01 03:51:46 -05:00
Log ( Logs : : Detail , Logs : : Spells , " Hover Rez in zone %s for corpse %s " ,
2013-06-22 08:35:33 -07:00
zone - > GetShortName ( ) , PendingRezzCorpseName . c_str ( ) ) ;
2013-02-16 16:14:39 -08:00
2017-04-01 03:51:46 -05:00
Log ( Logs : : Detail , Logs : : Spells , " Found corpse. Marking corpse as rezzed. " ) ;
2013-02-16 16:14:39 -08:00
2014-11-22 17:55:48 -06:00
corpse - > IsRezzed ( true ) ;
2014-11-25 23:54:59 -06:00
corpse - > CompleteResurrection ( ) ;
2013-06-22 08:35:33 -07:00
}
2013-02-16 16:14:39 -08:00
}
2013-06-22 08:35:33 -07:00
else //Not rez
{
PendingRezzSpellID = 0 ;
2013-02-16 16:14:39 -08:00
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket ( OP_ZonePlayerToBind , sizeof ( ZonePlayerToBind_Struct ) +
chosen - > name . length ( ) + 1 ) ;
2013-06-22 08:35:33 -07:00
ZonePlayerToBind_Struct * gmg = ( ZonePlayerToBind_Struct * ) outapp - > pBuffer ;
2013-02-16 16:14:39 -08:00
2013-06-22 08:35:33 -07:00
gmg - > bind_zone_id = zone - > GetZoneID ( ) ;
2014-11-18 21:12:48 -08:00
gmg - > bind_instance_id = chosen - > instance_id ;
2013-06-22 08:35:33 -07:00
gmg - > x = chosen - > x ;
gmg - > y = chosen - > y ;
gmg - > z = chosen - > z ;
gmg - > heading = chosen - > heading ;
strcpy ( gmg - > zone_name , chosen - > name . c_str ( ) ) ;
2013-05-06 13:07:41 -04:00
2013-06-22 08:35:33 -07:00
FastQueuePacket ( & outapp ) ;
2013-02-16 16:14:39 -08:00
2013-06-22 08:35:33 -07:00
CalcBonuses ( ) ;
SetHP ( GetMaxHP ( ) ) ;
SetMana ( GetMaxMana ( ) ) ;
SetEndurance ( GetMaxEndurance ( ) ) ;
2013-02-16 16:14:39 -08:00
2015-01-23 00:01:10 -08:00
m_Position . x = chosen - > x ;
m_Position . y = chosen - > y ;
m_Position . z = chosen - > z ;
m_Position . w = chosen - > heading ;
2013-02-16 16:14:39 -08:00
2013-06-22 08:35:33 -07:00
ClearHover ( ) ;
entity_list . RefreshClientXTargets ( this ) ;
SendHPUpdate ( ) ;
}
2013-02-16 16:14:39 -08:00
2013-06-22 08:35:33 -07:00
//After they've respawned into the same zone, trigger EVENT_RESPAWN
parse - > EventPlayer ( EVENT_RESPAWN , this , static_cast < std : : string > ( itoa ( Option ) ) , is_rez ? 1 : 0 ) ;
2013-02-16 16:14:39 -08:00
2014-11-25 20:16:04 -08:00
//Pop Rez option from the respawn options list;
2013-06-22 08:35:33 -07:00
//easiest way to make sure it stays at the end and
//doesn't disrupt adding/removing scripted options
respawn_options . pop_back ( ) ;
2013-02-16 16:14:39 -08:00
}
else
{
2013-06-22 08:35:33 -07:00
//Heading to a different zone
2013-02-16 16:14:39 -08:00
if ( isgrouped )
{
Group * g = GetGroup ( ) ;
if ( g )
g - > MemberZoned ( this ) ;
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
Raid * r = entity_list . GetRaidByClient ( this ) ;
if ( r )
r - > MemberZoned ( this ) ;
2014-11-18 21:12:48 -08:00
m_pp . zone_id = chosen - > zone_id ;
m_pp . zoneInstance = chosen - > instance_id ;
database . MoveCharacterToZone ( CharacterID ( ) , database . GetZoneName ( chosen - > zone_id ) ) ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
Save ( ) ;
2013-05-06 13:07:41 -04:00
2014-11-18 21:12:48 -08:00
MovePC ( chosen - > zone_id , chosen - > instance_id , chosen - > x , chosen - > y , chosen - > z , chosen - > heading , 1 ) ;
2013-02-16 16:14:39 -08:00
}
2013-06-25 11:05:03 -07:00
safe_delete ( default_to_bind ) ;
2013-02-16 16:14:39 -08:00
}
void Client : : ClearHover ( )
{
// Our Entity ID is currently zero, set in Client::Death
SetID ( entity_list . GetFreeID ( ) ) ;
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket ( OP_ZoneEntry , sizeof ( ServerZoneEntry_Struct ) ) ;
2013-02-16 16:14:39 -08:00
ServerZoneEntry_Struct * sze = ( ServerZoneEntry_Struct * ) outapp - > pBuffer ;
FillSpawnStruct ( & sze - > player , CastToMob ( ) ) ;
sze - > player . spawn . NPC = 0 ;
sze - > player . spawn . z + = 6 ; //arbitrary lift, seems to help spawning under zone.
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
entity_list . QueueClients ( this , outapp , false ) ;
safe_delete ( outapp ) ;
2016-04-22 03:49:17 -04:00
if ( IsClient ( ) & & CastToClient ( ) - > ClientVersionBit ( ) & EQEmu : : versions : : bit_UFAndLater )
2013-02-16 16:14:39 -08:00
{
EQApplicationPacket * outapp = MakeBuffsPacket ( false ) ;
CastToClient ( ) - > FastQueuePacket ( & outapp ) ;
}
dead = false ;
}
void Client : : HandleLFGuildResponse ( ServerPacket * pack )
{
pack - > SetReadPosition ( 8 ) ;
char Tmp [ 257 ] ;
pack - > ReadString ( Tmp ) ;
pack - > ReadSkipBytes ( 4 ) ;
uint32 SubType , NumberOfMatches ;
SubType = pack - > ReadUInt32 ( ) ;
switch ( SubType )
{
case QSG_LFGuild_PlayerMatches :
{
NumberOfMatches = pack - > ReadUInt32 ( ) ;
uint32 StartOfMatches = pack - > GetReadPosition ( ) ;
uint32 i = NumberOfMatches ;
uint32 PacketSize = 12 ;
while ( i > 0 )
{
pack - > ReadString ( Tmp ) ;
PacketSize + = strlen ( Tmp ) + 1 ;
pack - > ReadString ( Tmp ) ;
PacketSize + = strlen ( Tmp ) + 1 ;
PacketSize + = 16 ;
pack - > ReadSkipBytes ( 16 ) ;
- - i ;
}
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket ( OP_LFGuild , PacketSize ) ;
2013-02-16 16:14:39 -08:00
outapp - > WriteUInt32 ( 3 ) ;
outapp - > WriteUInt32 ( 0xeb63 ) ; // Don't know the significance of this value.
outapp - > WriteUInt32 ( NumberOfMatches ) ;
pack - > SetReadPosition ( StartOfMatches ) ;
while ( NumberOfMatches > 0 )
{
pack - > ReadString ( Tmp ) ;
outapp - > WriteString ( Tmp ) ;
pack - > ReadString ( Tmp ) ;
uint32 Level = pack - > ReadUInt32 ( ) ;
uint32 Class = pack - > ReadUInt32 ( ) ;
uint32 AACount = pack - > ReadUInt32 ( ) ;
uint32 TimeZone = pack - > ReadUInt32 ( ) ;
outapp - > WriteUInt32 ( Level ) ;
outapp - > WriteUInt32 ( Class ) ;
outapp - > WriteUInt32 ( AACount ) ;
outapp - > WriteUInt32 ( TimeZone ) ;
outapp - > WriteString ( Tmp ) ;
- - NumberOfMatches ;
}
FastQueuePacket ( & outapp ) ;
break ;
}
case QSG_LFGuild_RequestPlayerInfo :
{
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket ( OP_LFGuild , sizeof ( LFGuild_PlayerToggle_Struct ) ) ;
2013-02-16 16:14:39 -08:00
LFGuild_PlayerToggle_Struct * pts = ( LFGuild_PlayerToggle_Struct * ) outapp - > pBuffer ;
pts - > Command = 0 ;
pack - > ReadString ( pts - > Comment ) ;
pts - > TimeZone = pack - > ReadUInt32 ( ) ;
pts - > TimePosted = pack - > ReadUInt32 ( ) ;
pts - > Toggle = pack - > ReadUInt32 ( ) ;
FastQueuePacket ( & outapp ) ;
break ;
}
case QSG_LFGuild_GuildMatches :
{
NumberOfMatches = pack - > ReadUInt32 ( ) ;
uint32 StartOfMatches = pack - > GetReadPosition ( ) ;
uint32 i = NumberOfMatches ;
uint32 PacketSize = 12 ;
while ( i > 0 )
{
pack - > ReadString ( Tmp ) ;
PacketSize + = strlen ( Tmp ) + 1 ;
pack - > ReadSkipBytes ( 4 ) ;
pack - > ReadString ( Tmp ) ;
PacketSize + = strlen ( Tmp ) + 1 ;
PacketSize + = 4 ;
- - i ;
}
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket ( OP_LFGuild , PacketSize ) ;
2013-02-16 16:14:39 -08:00
outapp - > WriteUInt32 ( 4 ) ;
outapp - > WriteUInt32 ( 0xeb63 ) ;
outapp - > WriteUInt32 ( NumberOfMatches ) ;
pack - > SetReadPosition ( StartOfMatches ) ;
while ( NumberOfMatches > 0 )
{
pack - > ReadString ( Tmp ) ;
uint32 TimeZone = pack - > ReadUInt32 ( ) ;
outapp - > WriteString ( Tmp ) ;
outapp - > WriteUInt32 ( TimeZone ) ;
pack - > ReadString ( Tmp ) ;
outapp - > WriteString ( Tmp ) ;
- - NumberOfMatches ;
}
FastQueuePacket ( & outapp ) ;
break ;
}
case QSG_LFGuild_RequestGuildInfo :
{
char Comments [ 257 ] ;
uint32 FromLevel , ToLevel , Classes , AACount , TimeZone , TimePosted ;
pack - > ReadString ( Comments ) ;
FromLevel = pack - > ReadUInt32 ( ) ;
ToLevel = pack - > ReadUInt32 ( ) ;
Classes = pack - > ReadUInt32 ( ) ;
AACount = pack - > ReadUInt32 ( ) ;
TimeZone = pack - > ReadUInt32 ( ) ;
TimePosted = pack - > ReadUInt32 ( ) ;
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket ( OP_LFGuild , sizeof ( LFGuild_GuildToggle_Struct ) ) ;
2013-02-16 16:14:39 -08:00
LFGuild_GuildToggle_Struct * gts = ( LFGuild_GuildToggle_Struct * ) outapp - > pBuffer ;
gts - > Command = 1 ;
2013-05-06 13:07:41 -04:00
strcpy ( gts - > Comment , Comments ) ;
2013-02-16 16:14:39 -08:00
gts - > FromLevel = FromLevel ;
gts - > ToLevel = ToLevel ;
gts - > Classes = Classes ;
gts - > AACount = AACount ;
gts - > TimeZone = TimeZone ;
gts - > Toggle = 1 ;
gts - > TimePosted = TimePosted ;
gts - > Name [ 0 ] = 0 ;
FastQueuePacket ( & outapp ) ;
break ;
}
default :
break ;
}
}
void Client : : SendLFGuildStatus ( )
{
2016-05-25 16:10:28 -04:00
auto pack = new ServerPacket ( ServerOP_QueryServGeneric , strlen ( GetName ( ) ) + 17 ) ;
2013-02-16 16:14:39 -08:00
pack - > WriteUInt32 ( zone - > GetZoneID ( ) ) ;
pack - > WriteUInt32 ( zone - > GetInstanceID ( ) ) ;
pack - > WriteString ( GetName ( ) ) ;
pack - > WriteUInt32 ( QSG_LFGuild ) ;
pack - > WriteUInt32 ( QSG_LFGuild_RequestPlayerInfo ) ;
worldserver . SendPacket ( pack ) ;
safe_delete ( pack ) ;
}
void Client : : SendGuildLFGuildStatus ( )
{
2016-05-25 16:10:28 -04:00
auto pack = new ServerPacket ( ServerOP_QueryServGeneric ,
strlen ( GetName ( ) ) + + strlen ( guild_mgr . GetGuildName ( GuildID ( ) ) ) + 18 ) ;
2013-02-16 16:14:39 -08:00
pack - > WriteUInt32 ( zone - > GetZoneID ( ) ) ;
pack - > WriteUInt32 ( zone - > GetInstanceID ( ) ) ;
pack - > WriteString ( GetName ( ) ) ;
pack - > WriteUInt32 ( QSG_LFGuild ) ;
pack - > WriteUInt32 ( QSG_LFGuild_RequestGuildInfo ) ;
pack - > WriteString ( guild_mgr . GetGuildName ( GuildID ( ) ) ) ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
worldserver . SendPacket ( pack ) ;
safe_delete ( pack ) ;
}