2013-02-16 16:14:39 -08:00
2013-05-09 10:44:08 -04:00
/* EQEMu: Everquest Server Emulator
2013-02-16 16:14:39 -08:00
Copyright ( C ) 2001 - 2008 EQEMu Development Team ( http : //eqemulator.net)
2013-05-09 10:44:08 -04: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
the Free Software Foundation ; version 2 of the License .
2013-02-16 16:14:39 -08: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-02-16 16:14:39 -08:00
2013-05-09 10:44:08 -04:00
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
2013-02-16 16:14:39 -08:00
*/
2015-01-19 04:12:09 -06:00
# include "../common/global_define.h"
2013-02-16 16:14:39 -08:00
# include "tasks.h"
# include <string.h>
# ifdef _WINDOWS
# define strcasecmp _stricmp
# endif
2014-08-21 19:33:02 -07:00
# include "../common/misc_functions.h"
2013-02-16 16:14:39 -08:00
# include "../common/rulesys.h"
2014-12-15 18:54:23 -06:00
# include "../common/string_util.h"
2016-05-25 18:50:26 -04:00
# include "../common/say_link.h"
2014-12-15 18:54:23 -06:00
# include "client.h"
# include "entity.h"
2014-08-19 10:55:29 -05:00
# include "mob.h"
2018-06-16 22:42:26 -04:00
# include "string_ids.h"
2014-12-15 18:54:23 -06: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
# include "queryserv.h"
2014-12-15 18:54:23 -06:00
# include "quest_parser_collection.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
TaskManager : : TaskManager ( ) {
for ( int i = 0 ; i < MAXTASKS ; i + + )
2013-05-04 18:06:58 -07:00
Tasks [ i ] = nullptr ;
2013-02-16 16:14:39 -08:00
}
TaskManager : : ~ TaskManager ( ) {
for ( int i = 0 ; i < MAXTASKS ; i + + ) {
2013-05-04 18:06:58 -07:00
if ( Tasks [ i ] ! = nullptr ) {
2013-02-16 16:14:39 -08:00
safe_delete ( Tasks [ i ] ) ;
}
}
}
2019-09-01 22:05:44 -05:00
bool TaskManager : : LoadTaskSets ( )
{
2013-02-16 16:14:39 -08:00
// Clear all task sets in memory. Done so we can reload them on the fly if required by just calling
// this method again.
2019-09-01 22:05:44 -05:00
for ( int i = 0 ; i < MAXTASKSETS ; i + + )
2013-02-16 16:14:39 -08:00
TaskSets [ i ] . clear ( ) ;
2019-09-01 22:05:44 -05:00
std : : string query = StringFormat (
" SELECT `id`, `taskid` from `tasksets` "
" WHERE `id` > 0 AND `id` < %i "
" AND `taskid` >= 0 AND `taskid` < %i "
" ORDER BY `id`, `taskid` ASC " ,
MAXTASKSETS , MAXTASKS
) ;
auto results = database . QueryDatabase ( query ) ;
if ( ! results . Success ( ) ) {
LogError ( " Error in TaskManager::LoadTaskSets: [{}] " , results . ErrorMessage ( ) . c_str ( ) ) ;
2014-10-07 11:36:42 -07:00
return false ;
2019-09-01 22:05:44 -05:00
}
2013-02-16 16:14:39 -08:00
2014-10-07 11:36:42 -07:00
for ( auto row = results . begin ( ) ; row ! = results . end ( ) ; + + row ) {
2019-09-01 22:05:44 -05:00
int taskSet = atoi ( row [ 0 ] ) ;
int taskID = atoi ( row [ 1 ] ) ;
2013-02-16 16:14:39 -08:00
2019-09-01 22:05:44 -05:00
TaskSets [ taskSet ] . push_back ( taskID ) ;
Log ( Logs : : General , Logs : : Tasks , " [GLOBALLOAD] Adding TaskID %4i to TaskSet %4i " , taskID , taskSet ) ;
}
2013-02-16 16:14:39 -08:00
return true ;
}
bool TaskManager : : LoadSingleTask ( int TaskID ) {
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [GLOBALLOAD] TaskManager::LoadSingleTask(%i) " , TaskID ) ;
2013-02-16 16:14:39 -08:00
if ( ( TaskID < = 0 ) | | ( TaskID > = MAXTASKS ) ) return false ;
// If this task already exists in memory, free all the dynamically allocated strings.
if ( Tasks [ TaskID ] ) {
safe_delete ( Tasks [ TaskID ] ) ;
}
return LoadTasks ( TaskID ) ;
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
void TaskManager : : ReloadGoalLists ( ) {
2013-05-06 13:07:41 -04:00
if ( ! GoalListManager . LoadLists ( ) )
2017-04-01 03:51:46 -05:00
Log ( Logs : : Detail , Logs : : Tasks , " TaskManager::LoadTasks LoadLists failed " ) ;
2013-02-16 16:14:39 -08:00
}
2018-06-12 00:32:13 -04:00
bool TaskManager : : LoadTasks ( int singleTask )
{
2013-02-16 16:14:39 -08:00
// If TaskID !=0, then just load the task specified.
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [GLOBALLOAD] TaskManager::LoadTasks Called " ) ;
2013-02-16 16:14:39 -08:00
2018-06-12 00:32:13 -04:00
std : : string query ;
if ( singleTask = = 0 ) {
if ( ! GoalListManager . LoadLists ( ) )
Log ( Logs : : Detail , Logs : : Tasks , " TaskManager::LoadTasks LoadLists failed " ) ;
2013-02-16 16:14:39 -08:00
2018-06-12 00:32:13 -04:00
if ( ! LoadTaskSets ( ) )
Log ( Logs : : Detail , Logs : : Tasks , " TaskManager::LoadTasks LoadTaskSets failed " ) ;
2013-02-16 16:14:39 -08:00
2018-06-13 17:08:21 -04:00
query = StringFormat ( " SELECT `id`, `type`, `duration`, `duration_code`, `title`, `description`, "
2018-07-08 21:40:04 -04:00
" `reward`, `rewardid`, `cashreward`, `xpreward`, `rewardmethod`, `faction_reward`, "
2018-07-20 14:06:07 -04:00
" `minlevel`, `maxlevel`, `repeatable`, `completion_emote` FROM `tasks` WHERE `id` < %i " ,
2018-06-12 00:32:13 -04:00
MAXTASKS ) ;
} else
2018-06-13 17:08:21 -04:00
query = StringFormat ( " SELECT `id`, `type`, `duration`, `duration_code`, `title`, `description`, "
2018-07-08 21:40:04 -04:00
" `reward`, `rewardid`, `cashreward`, `xpreward`, `rewardmethod`, `faction_reward`, "
2018-07-20 14:06:07 -04:00
" `minlevel`, `maxlevel`, `repeatable`, `completion_emote` FROM `tasks` WHERE `id` = %i " ,
2018-06-12 00:32:13 -04:00
singleTask ) ;
2013-02-16 16:14:39 -08:00
2018-06-12 00:32:13 -04:00
const char * ERR_MYSQLERROR = " [TASKS]Error in TaskManager::LoadTasks: %s " ;
2013-02-16 16:14:39 -08:00
2018-06-12 00:32:13 -04:00
auto results = database . QueryDatabase ( query ) ;
if ( ! results . Success ( ) ) {
2019-09-01 22:05:44 -05:00
LogError ( ERR_MYSQLERROR , results . ErrorMessage ( ) . c_str ( ) ) ;
2013-02-16 16:14:39 -08:00
return false ;
2018-06-12 00:32:13 -04:00
}
2013-02-16 16:14:39 -08:00
2018-06-12 00:32:13 -04:00
for ( auto row = results . begin ( ) ; row ! = results . end ( ) ; + + row ) {
int taskID = atoi ( row [ 0 ] ) ;
2014-10-07 11:56:38 -07:00
2018-06-12 00:32:13 -04:00
if ( ( taskID < = 0 ) | | ( taskID > = MAXTASKS ) ) {
// This shouldn't happen, as the SELECT is bounded by MAXTASKS
2019-09-02 03:10:03 -05:00
LogError ( " [TASKS]Task ID [{}] out of range while loading tasks from database " , taskID ) ;
2014-10-07 11:56:38 -07:00
continue ;
2018-06-12 00:32:13 -04:00
}
2014-10-07 11:56:38 -07:00
Tasks [ taskID ] = new TaskInformation ;
2018-06-13 17:08:21 -04:00
Tasks [ taskID ] - > type = static_cast < TaskType > ( atoi ( row [ 1 ] ) ) ;
Tasks [ taskID ] - > Duration = atoi ( row [ 2 ] ) ;
Tasks [ taskID ] - > dur_code = static_cast < DurationCode > ( atoi ( row [ 3 ] ) ) ;
Tasks [ taskID ] - > Title = row [ 4 ] ;
Tasks [ taskID ] - > Description = row [ 5 ] ;
Tasks [ taskID ] - > Reward = row [ 6 ] ;
Tasks [ taskID ] - > RewardID = atoi ( row [ 7 ] ) ;
Tasks [ taskID ] - > CashReward = atoi ( row [ 8 ] ) ;
Tasks [ taskID ] - > XPReward = atoi ( row [ 9 ] ) ;
Tasks [ taskID ] - > RewardMethod = ( TaskMethodType ) atoi ( row [ 10 ] ) ;
2018-07-08 21:40:04 -04:00
Tasks [ taskID ] - > faction_reward = atoi ( row [ 11 ] ) ;
Tasks [ taskID ] - > MinLevel = atoi ( row [ 12 ] ) ;
Tasks [ taskID ] - > MaxLevel = atoi ( row [ 13 ] ) ;
Tasks [ taskID ] - > Repeatable = atoi ( row [ 14 ] ) ;
2018-07-20 14:06:07 -04:00
Tasks [ taskID ] - > completion_emote = row [ 15 ] ;
2014-10-07 11:56:38 -07:00
Tasks [ taskID ] - > ActivityCount = 0 ;
Tasks [ taskID ] - > SequenceMode = ActivitiesSequential ;
Tasks [ taskID ] - > LastStep = 0 ;
2018-06-12 00:32:13 -04:00
Log ( Logs : : General , Logs : : Tasks ,
2018-07-08 18:42:26 -04:00
" [GLOBALLOAD] TaskID: %5i, Duration: %8i, Reward: %s MinLevel %i MaxLevel %i "
2018-06-12 00:32:13 -04:00
" Repeatable: %s " ,
2018-07-08 18:42:26 -04:00
taskID , Tasks [ taskID ] - > Duration , Tasks [ taskID ] - > Reward . c_str ( ) ,
2018-06-12 00:32:13 -04:00
Tasks [ taskID ] - > MinLevel , Tasks [ taskID ] - > MaxLevel , Tasks [ taskID ] - > Repeatable ? " Yes " : " No " ) ;
2018-06-12 13:56:17 -04:00
Log ( Logs : : General , Logs : : Tasks , " [GLOBALLOAD] Title: %s " , Tasks [ taskID ] - > Title . c_str ( ) ) ;
2018-06-12 00:32:13 -04:00
}
2013-02-16 16:14:39 -08:00
2018-06-12 00:32:13 -04:00
if ( singleTask = = 0 )
2018-07-02 22:01:17 -04:00
query =
StringFormat ( " SELECT `taskid`, `step`, `activityid`, `activitytype`, `target_name`, `item_list`, "
" `skill_list`, `spell_list`, `description_override`, `goalid`, `goalmethod`, "
2018-07-10 00:26:53 -05:00
" `goalcount`, `delivertonpc`, `zones`, `optional` FROM `task_activities` WHERE `taskid` < "
2018-07-02 22:01:17 -04:00
" %i AND `activityid` < %i ORDER BY taskid, activityid ASC " ,
MAXTASKS , MAXACTIVITIESPERTASK ) ;
2014-10-07 11:56:38 -07:00
else
2018-07-02 22:01:17 -04:00
query =
StringFormat ( " SELECT `taskid`, `step`, `activityid`, `activitytype`, `target_name`, `item_list`, "
" `skill_list`, `spell_list`, `description_override`, `goalid`, `goalmethod`, "
2018-07-10 00:26:53 -05:00
" `goalcount`, `delivertonpc`, `zones`, `optional` FROM `task_activities` WHERE `taskid` = "
2018-07-02 22:01:17 -04:00
" %i AND `activityid` < %i ORDER BY taskid, activityid ASC " ,
singleTask , MAXACTIVITIESPERTASK ) ;
2018-06-12 00:32:13 -04:00
results = database . QueryDatabase ( query ) ;
if ( ! results . Success ( ) ) {
2019-09-01 22:05:44 -05:00
LogError ( ERR_MYSQLERROR , results . ErrorMessage ( ) . c_str ( ) ) ;
2013-02-16 16:14:39 -08:00
return false ;
2018-06-12 00:32:13 -04:00
}
2014-10-07 11:56:38 -07:00
2018-06-12 00:32:13 -04:00
for ( auto row = results . begin ( ) ; row ! = results . end ( ) ; + + row ) {
int taskID = atoi ( row [ 0 ] ) ;
int step = atoi ( row [ 1 ] ) ;
2014-10-07 11:56:38 -07:00
2018-06-12 00:32:13 -04:00
int activityID = atoi ( row [ 2 ] ) ;
2014-10-07 11:56:38 -07:00
2018-06-12 00:32:13 -04:00
if ( ( taskID < = 0 ) | | ( taskID > = MAXTASKS ) | | ( activityID < 0 ) | | ( activityID > = MAXACTIVITIESPERTASK ) ) {
// This shouldn't happen, as the SELECT is bounded by MAXTASKS
2019-09-02 03:10:03 -05:00
LogError ( " [TASKS]Task or Activity ID ([{}], [{}]) out of range while loading activities from database " , taskID , activityID ) ;
2018-06-12 00:32:13 -04:00
continue ;
}
2014-10-07 11:56:38 -07:00
2018-06-12 00:32:13 -04:00
if ( Tasks [ taskID ] = = nullptr ) {
2019-09-02 03:10:03 -05:00
LogError ( " [TASKS]Activity for non-existent task ([{}], [{}]) while loading activities from database " ,
2018-06-12 00:32:13 -04:00
taskID , activityID ) ;
continue ;
}
2014-10-07 11:56:38 -07:00
2018-06-12 00:32:13 -04:00
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . StepNumber = step ;
2014-10-07 11:56:38 -07:00
2018-06-12 00:32:13 -04:00
if ( step ! = 0 )
Tasks [ taskID ] - > SequenceMode = ActivitiesStepped ;
2014-10-07 11:56:38 -07:00
2018-06-12 00:32:13 -04:00
if ( step > Tasks [ taskID ] - > LastStep )
Tasks [ taskID ] - > LastStep = step ;
2014-10-07 11:56:38 -07:00
2018-06-12 00:32:13 -04:00
// Task Activities MUST be numbered sequentially from 0. If not, log an error
// and set the task to nullptr. Subsequent activities for this task will raise
// ERR_NOTASK errors.
// Change to (activityID != (Tasks[taskID]->ActivityCount + 1)) to index from 1
if ( activityID ! = Tasks [ taskID ] - > ActivityCount ) {
2019-09-02 03:10:03 -05:00
LogError ( " [TASKS]Activities for Task [{}] are not sequential starting at 0. Not loading task " , taskID ,
2018-06-12 00:32:13 -04:00
activityID ) ;
Tasks [ taskID ] = nullptr ;
continue ;
}
2014-10-07 11:56:38 -07:00
2018-06-12 00:32:13 -04:00
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . Type = atoi ( row [ 3 ] ) ;
2014-10-07 11:56:38 -07:00
2018-07-02 22:01:17 -04:00
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . target_name = row [ 4 ] ;
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . item_list = row [ 5 ] ;
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . skill_list = row [ 6 ] ;
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . skill_id = atoi ( row [ 6 ] ) ; // for older clients
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . spell_list = row [ 7 ] ;
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . spell_id = atoi ( row [ 7 ] ) ; // for older clients
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . desc_override = row [ 8 ] ;
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . GoalID = atoi ( row [ 9 ] ) ;
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . GoalMethod = ( TaskMethodType ) atoi ( row [ 10 ] ) ;
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . GoalCount = atoi ( row [ 11 ] ) ;
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . DeliverToNPC = atoi ( row [ 12 ] ) ;
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . zones = row [ 13 ] ;
2018-08-01 13:50:09 -04:00
auto zones = SplitString ( Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . zones , ' ; ' ) ;
for ( auto & & e : zones )
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . ZoneIDs . push_back ( std : : stoi ( e ) ) ;
2018-07-02 22:01:17 -04:00
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . Optional = atoi ( row [ 14 ] ) ;
2014-10-07 11:56:38 -07:00
2018-06-12 00:32:13 -04:00
Log ( Logs : : General , Logs : : Tasks ,
" [GLOBALLOAD] Activity Slot %2i: ID %i for Task %5i. Type: %3i, GoalID: %8i, "
2018-07-02 22:01:17 -04:00
" GoalMethod: %i, GoalCount: %3i, Zones:%s " ,
2018-06-12 00:32:13 -04:00
Tasks [ taskID ] - > ActivityCount , activityID , taskID ,
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . Type ,
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . GoalID ,
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . GoalMethod ,
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . GoalCount ,
2018-07-02 22:01:17 -04:00
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . zones . c_str ( ) ) ;
Log ( Logs : : General , Logs : : Tasks , " [GLOBALLOAD] target_name: %s " ,
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . target_name . c_str ( ) ) ;
Log ( Logs : : General , Logs : : Tasks , " [GLOBALLOAD] item_list: %s " ,
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . item_list . c_str ( ) ) ;
Log ( Logs : : General , Logs : : Tasks , " [GLOBALLOAD] skill_list: %s " ,
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . skill_list . c_str ( ) ) ;
Log ( Logs : : General , Logs : : Tasks , " [GLOBALLOAD] spell_list: %s " ,
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . spell_list . c_str ( ) ) ;
Log ( Logs : : General , Logs : : Tasks , " [GLOBALLOAD] description_override: %s " ,
Tasks [ taskID ] - > Activity [ Tasks [ taskID ] - > ActivityCount ] . desc_override . c_str ( ) ) ;
2014-10-07 11:56:38 -07:00
2018-06-12 00:32:13 -04:00
Tasks [ taskID ] - > ActivityCount + + ;
2013-02-16 16:14:39 -08:00
}
2014-10-07 11:56:38 -07:00
2013-02-16 16:14:39 -08:00
return true ;
}
2018-07-08 17:35:37 -04:00
bool TaskManager : : SaveClientState ( Client * c , ClientTaskState * state )
{
// I am saving the slot in the ActiveTasks table, because unless a Task is cancelled/completed, the client
// doesn't seem to like tasks moving slots between zoning and you can end up with 'bogus' activities if the task
// previously in that slot had more activities than the one now occupying it. Hopefully retaining the slot
// number for the duration of a session will overcome this.
if ( ! c | | ! state )
return false ;
2013-02-16 16:14:39 -08:00
const char * ERR_MYSQLERROR = " [TASKS]Error in TaskManager::SaveClientState %s " ;
2014-10-08 11:11:33 -07:00
int characterID = c - > CharacterID ( ) ;
2013-02-16 16:14:39 -08:00
2018-07-08 17:35:37 -04:00
Log ( Logs : : Detail , Logs : : Tasks , " TaskManager::SaveClientState for character ID %d " , characterID ) ;
2013-02-16 16:14:39 -08:00
2018-07-08 18:07:08 -04:00
if ( state - > ActiveTaskCount > 0 | | state - > ActiveTask . TaskID ! = TASKSLOTEMPTY ) { // TODO: tasks
for ( int task = 0 ; task < MAXACTIVEQUESTS + 1 ; task + + ) {
int taskID = state - > ActiveTasks [ task ] . TaskID ;
2018-07-08 17:35:37 -04:00
if ( taskID = = TASKSLOTEMPTY )
continue ;
2018-07-09 14:01:52 -04:00
int slot = state - > ActiveTasks [ task ] . slot ;
2018-07-08 18:07:08 -04:00
if ( state - > ActiveTasks [ task ] . Updated ) {
2018-07-08 17:35:37 -04:00
Log ( Logs : : General , Logs : : Tasks ,
" [CLIENTSAVE] TaskManager::SaveClientState for character ID %d, Updating TaskIndex "
" %i TaskID %i " ,
2018-07-09 14:01:52 -04:00
characterID , slot , taskID ) ;
2018-07-08 17:35:37 -04:00
std : : string query = StringFormat (
" REPLACE INTO character_tasks (charid, taskid, slot, type, acceptedtime) "
" VALUES (%i, %i, %i, %i, %i) " ,
2018-07-09 14:01:52 -04:00
characterID , taskID , slot , static_cast < int > ( Tasks [ taskID ] - > type ) ,
2018-07-08 18:07:08 -04:00
state - > ActiveTasks [ task ] . AcceptedTime ) ;
2018-07-08 17:35:37 -04:00
auto results = database . QueryDatabase ( query ) ;
2017-04-01 03:51:46 -05:00
if ( ! results . Success ( ) ) {
2019-09-01 22:05:44 -05:00
LogError ( ERR_MYSQLERROR , results . ErrorMessage ( ) . c_str ( ) ) ;
2018-07-08 17:35:37 -04:00
} else {
2018-07-08 18:07:08 -04:00
state - > ActiveTasks [ task ] . Updated = false ;
2017-04-01 03:51:46 -05:00
}
2013-02-16 16:14:39 -08:00
}
2018-07-08 17:35:37 -04:00
std : : string query =
" REPLACE INTO character_activities (charid, taskid, activityid, donecount, completed) "
" VALUES " ;
int updatedActivityCount = 0 ;
for ( int activityIndex = 0 ; activityIndex < Tasks [ taskID ] - > ActivityCount ; + + activityIndex ) {
2018-07-08 18:07:08 -04:00
if ( ! state - > ActiveTasks [ task ] . Activity [ activityIndex ] . Updated )
2018-07-08 17:35:37 -04:00
continue ;
Log ( Logs : : General , Logs : : Tasks ,
" [CLIENTSAVE] TaskManager::SaveClientSate for character ID %d, Updating Activity "
" %i, %i " ,
2018-07-09 14:01:52 -04:00
characterID , slot , activityIndex ) ;
2018-07-08 17:35:37 -04:00
if ( updatedActivityCount = = 0 )
query + =
StringFormat ( " (%i, %i, %i, %i, %i) " , characterID , taskID , activityIndex ,
2018-07-08 18:07:08 -04:00
state - > ActiveTasks [ task ] . Activity [ activityIndex ] . DoneCount ,
state - > ActiveTasks [ task ] . Activity [ activityIndex ] . State = =
2018-07-08 17:35:37 -04:00
ActivityCompleted ) ;
else
query + =
StringFormat ( " , (%i, %i, %i, %i, %i) " , characterID , taskID , activityIndex ,
2018-07-08 18:07:08 -04:00
state - > ActiveTasks [ task ] . Activity [ activityIndex ] . DoneCount ,
state - > ActiveTasks [ task ] . Activity [ activityIndex ] . State = =
2018-07-08 17:35:37 -04:00
ActivityCompleted ) ;
updatedActivityCount + + ;
2013-02-16 16:14:39 -08:00
}
2018-07-08 17:35:37 -04:00
if ( updatedActivityCount = = 0 )
continue ;
2013-05-06 13:07:41 -04:00
2018-07-08 17:35:37 -04:00
Log ( Logs : : General , Logs : : Tasks , " [CLIENTSAVE] Executing query %s " , query . c_str ( ) ) ;
auto results = database . QueryDatabase ( query ) ;
2013-02-16 16:14:39 -08:00
2018-07-08 17:35:37 -04:00
if ( ! results . Success ( ) ) {
2019-09-01 22:05:44 -05:00
LogError ( ERR_MYSQLERROR , results . ErrorMessage ( ) . c_str ( ) ) ;
2018-07-08 17:35:37 -04:00
continue ;
}
2013-02-16 16:14:39 -08:00
2018-07-08 18:07:08 -04:00
state - > ActiveTasks [ task ] . Updated = false ;
2018-07-08 17:35:37 -04:00
for ( int activityIndex = 0 ; activityIndex < Tasks [ taskID ] - > ActivityCount ; + + activityIndex )
2018-07-08 18:07:08 -04:00
state - > ActiveTasks [ task ] . Activity [ activityIndex ] . Updated = false ;
2013-02-16 16:14:39 -08:00
}
}
2018-07-08 17:35:37 -04:00
if ( ! RuleB ( TaskSystem , RecordCompletedTasks ) | |
( state - > CompletedTasks . size ( ) < = ( unsigned int ) state - > LastCompletedTaskLoaded ) ) {
state - > LastCompletedTaskLoaded = state - > CompletedTasks . size ( ) ;
return true ;
}
2014-10-08 11:11:33 -07:00
2018-07-08 17:35:37 -04:00
const char * completedTaskQuery = " REPLACE INTO completed_tasks (charid, completedtime, taskid, activityid) "
" VALUES (%i, %i, %i, %i) " ;
2014-10-08 11:11:33 -07:00
2018-07-08 17:35:37 -04:00
for ( unsigned int i = state - > LastCompletedTaskLoaded ; i < state - > CompletedTasks . size ( ) ; i + + ) {
2014-10-08 11:11:33 -07:00
2018-07-08 17:35:37 -04:00
Log ( Logs : : General , Logs : : Tasks ,
" [CLIENTSAVE] TaskManager::SaveClientState Saving Completed Task at slot %i " , i ) ;
int taskID = state - > CompletedTasks [ i ] . TaskID ;
2014-10-08 11:11:33 -07:00
2018-07-08 17:35:37 -04:00
if ( ( taskID < = 0 ) | | ( taskID > = MAXTASKS ) | | ( Tasks [ taskID ] = = nullptr ) )
continue ;
2014-10-08 11:11:33 -07:00
2018-07-08 17:35:37 -04:00
// First we save a record with an ActivityID of -1.
// This indicates this task was completed at the given time. We infer that all
// none optional activities were completed.
//
std : : string query =
StringFormat ( completedTaskQuery , characterID , state - > CompletedTasks [ i ] . CompletedTime , taskID , - 1 ) ;
auto results = database . QueryDatabase ( query ) ;
if ( ! results . Success ( ) ) {
2019-09-01 22:05:44 -05:00
LogError ( ERR_MYSQLERROR , results . ErrorMessage ( ) . c_str ( ) ) ;
2018-07-08 17:35:37 -04:00
continue ;
}
2014-10-08 11:11:33 -07:00
2018-07-08 17:35:37 -04:00
// If the Rule to record non-optional task completion is not enabled, don't save it
if ( ! RuleB ( TaskSystem , RecordCompletedOptionalActivities ) )
continue ;
2014-10-08 11:11:33 -07:00
2018-07-08 17:35:37 -04:00
// Insert one record for each completed optional task.
2014-10-08 11:11:33 -07:00
2018-07-08 17:35:37 -04:00
for ( int j = 0 ; j < Tasks [ taskID ] - > ActivityCount ; j + + ) {
if ( ! Tasks [ taskID ] - > Activity [ j ] . Optional | | ! state - > CompletedTasks [ i ] . ActivityDone [ j ] )
continue ;
2014-10-08 11:11:33 -07:00
2018-07-08 17:35:37 -04:00
query = StringFormat ( completedTaskQuery , characterID , state - > CompletedTasks [ i ] . CompletedTime ,
taskID , j ) ;
results = database . QueryDatabase ( query ) ;
if ( ! results . Success ( ) )
2019-09-01 22:05:44 -05:00
LogError ( ERR_MYSQLERROR , results . ErrorMessage ( ) . c_str ( ) ) ;
2018-07-08 17:35:37 -04:00
}
}
2014-10-08 11:11:33 -07:00
2018-07-08 17:35:37 -04:00
state - > LastCompletedTaskLoaded = state - > CompletedTasks . size ( ) ;
2013-02-16 16:14:39 -08:00
return true ;
}
void Client : : LoadClientTaskState ( ) {
if ( RuleB ( TaskSystem , EnableTaskSystem ) & & taskmanager ) {
if ( taskstate )
safe_delete ( taskstate ) ;
taskstate = new ClientTaskState ;
if ( ! taskmanager - > LoadClientState ( this , taskstate ) ) {
safe_delete ( taskstate ) ;
}
else {
taskmanager - > SendActiveTasksToClient ( this ) ;
taskmanager - > SendCompletedTasksToClient ( this , taskstate ) ;
}
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
}
void Client : : RemoveClientTaskState ( ) {
if ( taskstate ) {
taskstate - > CancelAllTasks ( this ) ;
safe_delete ( taskstate ) ;
}
}
2013-05-06 13:07:41 -04:00
2018-07-08 17:35:37 -04:00
bool TaskManager : : LoadClientState ( Client * c , ClientTaskState * state )
{
if ( ! c | | ! state )
return false ;
2013-02-16 16:14:39 -08:00
2014-10-08 12:30:08 -07:00
int characterID = c - > CharacterID ( ) ;
2013-02-16 16:14:39 -08:00
state - > ActiveTaskCount = 0 ;
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [CLIENTLOAD] TaskManager::LoadClientState for character ID %d " , characterID ) ;
2013-02-16 16:14:39 -08:00
2018-07-08 18:01:14 -04:00
std : : string query = StringFormat ( " SELECT `taskid`, `slot`,`type`, `acceptedtime` "
2018-07-08 17:35:37 -04:00
" FROM `character_tasks` "
" WHERE `charid` = %i ORDER BY acceptedtime " ,
characterID ) ;
auto results = database . QueryDatabase ( query ) ;
if ( ! results . Success ( ) ) {
2019-09-02 03:10:03 -05:00
LogError ( " [TASKS]Error in TaskManager::LoadClientState load Tasks: [{}] " ,
2018-07-08 17:35:37 -04:00
results . ErrorMessage ( ) . c_str ( ) ) ;
2014-10-08 12:30:08 -07:00
return false ;
2018-07-08 17:35:37 -04:00
}
2013-02-16 16:14:39 -08:00
2018-07-08 17:35:37 -04:00
for ( auto row = results . begin ( ) ; row ! = results . end ( ) ; + + row ) {
int taskID = atoi ( row [ 0 ] ) ;
int slot = atoi ( row [ 1 ] ) ;
2018-07-08 18:01:14 -04:00
TaskType type = static_cast < TaskType > ( atoi ( row [ 2 ] ) ) ;
2013-02-16 16:14:39 -08:00
2018-07-08 17:35:37 -04:00
if ( ( taskID < 0 ) | | ( taskID > = MAXTASKS ) ) {
2019-09-02 03:10:03 -05:00
LogError ( " [TASKS]Task ID [{}] out of range while loading character tasks from database " , taskID ) ;
2018-07-08 17:35:37 -04:00
continue ;
}
2013-02-16 16:14:39 -08:00
2018-07-08 18:01:14 -04:00
auto task_info = state - > GetClientTaskInfo ( type , slot ) ;
if ( task_info = = nullptr ) {
2019-09-02 03:10:03 -05:00
LogError ( " [TASKS] Slot [{}] out of range while loading character tasks from database " , slot ) ;
2018-07-08 17:35:37 -04:00
continue ;
}
2013-02-16 16:14:39 -08:00
2018-07-08 18:01:14 -04:00
if ( task_info - > TaskID ! = TASKSLOTEMPTY ) {
2019-09-02 03:10:03 -05:00
LogError ( " [TASKS] Slot [{}] for Task [{}]s is already occupied " , slot ,
2018-07-08 17:35:37 -04:00
taskID ) ;
continue ;
}
2013-02-16 16:14:39 -08:00
2018-07-08 18:01:14 -04:00
int acceptedtime = atoi ( row [ 3 ] ) ;
2013-02-16 16:14:39 -08:00
2018-07-08 18:01:14 -04:00
task_info - > TaskID = taskID ;
task_info - > CurrentStep = - 1 ;
task_info - > AcceptedTime = acceptedtime ;
task_info - > Updated = false ;
2013-02-16 16:14:39 -08:00
2018-07-08 17:35:37 -04:00
for ( int i = 0 ; i < MAXACTIVITIESPERTASK ; i + + )
2018-07-08 18:01:14 -04:00
task_info - > Activity [ i ] . ActivityID = - 1 ;
2013-02-16 16:14:39 -08:00
2018-07-08 18:01:14 -04:00
if ( type = = TaskType : : Quest )
+ + state - > ActiveTaskCount ;
2013-02-16 16:14:39 -08:00
2018-07-08 17:35:37 -04:00
Log ( Logs : : General , Logs : : Tasks ,
" [CLIENTLOAD] TaskManager::LoadClientState. Char: %i Task ID %i, Accepted Time: %8X " , characterID ,
taskID , acceptedtime ) ;
2013-02-16 16:14:39 -08:00
}
// Load Activities
2018-07-08 17:35:37 -04:00
Log ( Logs : : General , Logs : : Tasks , " [CLIENTLOAD] LoadClientState. Loading activities for character ID %d " ,
characterID ) ;
query = StringFormat ( " SELECT `taskid`, `activityid`, `donecount`, `completed` "
" FROM `character_activities` "
" WHERE `charid` = %i "
" ORDER BY `taskid` ASC, `activityid` ASC " ,
characterID ) ;
results = database . QueryDatabase ( query ) ;
if ( ! results . Success ( ) ) {
2019-09-02 03:10:03 -05:00
LogError ( " [TASKS]Error in TaskManager::LoadClientState load Activities: [{}] " ,
2018-07-08 17:35:37 -04:00
results . ErrorMessage ( ) . c_str ( ) ) ;
2014-10-08 12:30:08 -07:00
return false ;
}
2013-02-16 16:14:39 -08:00
2018-07-08 17:35:37 -04:00
for ( auto row = results . begin ( ) ; row ! = results . end ( ) ; + + row ) {
int taskID = atoi ( row [ 0 ] ) ;
if ( ( taskID < 0 ) | | ( taskID > = MAXTASKS ) ) {
2019-09-02 03:10:03 -05:00
LogError ( " [TASKS]Task ID [{}] out of range while loading character activities from database " , taskID ) ;
2018-07-08 17:35:37 -04:00
continue ;
}
2013-02-16 16:14:39 -08:00
2018-07-08 17:35:37 -04:00
int activityID = atoi ( row [ 1 ] ) ;
if ( ( activityID < 0 ) | | ( activityID > = MAXACTIVITIESPERTASK ) ) {
2019-09-02 03:10:03 -05:00
LogError ( " [TASKS]Activity ID [{}] out of range while loading character activities from database " ,
2018-07-08 17:35:37 -04:00
activityID ) ;
continue ;
}
2013-02-16 16:14:39 -08:00
2018-07-08 18:01:14 -04:00
ClientTaskInformation * task_info = nullptr ;
2013-02-16 16:14:39 -08:00
2018-07-08 18:01:14 -04:00
if ( state - > ActiveTask . TaskID = = taskID )
task_info = & state - > ActiveTask ;
2018-07-08 18:07:08 -04:00
// wasn't task
2018-07-08 18:01:14 -04:00
if ( task_info = = nullptr )
for ( int i = 0 ; i < MAXACTIVEQUESTS ; i + + )
if ( state - > ActiveQuests [ i ] . TaskID = = taskID )
task_info = & state - > ActiveQuests [ i ] ;
2013-02-16 16:14:39 -08:00
2018-07-08 18:01:14 -04:00
if ( task_info = = nullptr ) {
2019-09-02 03:10:03 -05:00
LogError ( " [TASKS]Activity [{}] found for task [{}] which client does not have " , activityID , taskID ) ;
2018-07-08 17:35:37 -04:00
continue ;
}
2013-02-16 16:14:39 -08:00
2018-07-08 17:35:37 -04:00
int doneCount = atoi ( row [ 2 ] ) ;
bool completed = atoi ( row [ 3 ] ) ;
2018-07-08 18:01:14 -04:00
task_info - > Activity [ activityID ] . ActivityID = activityID ;
task_info - > Activity [ activityID ] . DoneCount = doneCount ;
2018-07-08 17:35:37 -04:00
if ( completed )
2018-07-08 18:01:14 -04:00
task_info - > Activity [ activityID ] . State = ActivityCompleted ;
2018-07-08 17:35:37 -04:00
else
2018-07-08 18:01:14 -04:00
task_info - > Activity [ activityID ] . State = ActivityHidden ;
2013-02-16 16:14:39 -08:00
2018-07-08 18:01:14 -04:00
task_info - > Activity [ activityID ] . Updated = false ;
2013-02-16 16:14:39 -08:00
2018-07-08 17:35:37 -04:00
Log ( Logs : : General , Logs : : Tasks ,
" [CLIENTLOAD] TaskManager::LoadClientState. Char: %i Task ID %i, ActivityID: %i, DoneCount: %i, "
" Completed: %i " ,
characterID , taskID , activityID , doneCount , completed ) ;
}
if ( RuleB ( TaskSystem , RecordCompletedTasks ) ) {
query = StringFormat ( " SELECT `taskid`, `activityid`, `completedtime` "
" FROM `completed_tasks` "
" WHERE `charid` = %i ORDER BY completedtime, taskid, activityid " ,
characterID ) ;
results = database . QueryDatabase ( query ) ;
if ( ! results . Success ( ) ) {
2019-09-02 03:10:03 -05:00
LogError ( " [TASKS]Error in TaskManager::LoadClientState load completed tasks: [{}] " ,
2018-07-08 17:35:37 -04:00
results . ErrorMessage ( ) . c_str ( ) ) ;
return false ;
}
2013-02-16 16:14:39 -08:00
2018-07-08 17:35:37 -04:00
CompletedTaskInformation cti ;
2013-02-16 16:14:39 -08:00
2018-07-08 17:35:37 -04:00
for ( int i = 0 ; i < MAXACTIVITIESPERTASK ; i + + )
cti . ActivityDone [ i ] = false ;
int previousTaskID = - 1 ;
int previousCompletedTime = - 1 ;
for ( auto row = results . begin ( ) ; row ! = results . end ( ) ; + + row ) {
int taskID = atoi ( row [ 0 ] ) ;
if ( ( taskID < = 0 ) | | ( taskID > = MAXTASKS ) ) {
2019-09-02 03:10:03 -05:00
LogError ( " [TASKS]Task ID [{}] out of range while loading completed tasks from database " ,
2018-07-08 17:35:37 -04:00
taskID ) ;
continue ;
}
2013-02-16 16:14:39 -08:00
2018-07-08 17:35:37 -04:00
// An ActivityID of -1 means mark all the none optional activities in the
// task as complete. If the Rule to record optional activities is enabled,
// subsequent records for this task will flag any optional tasks that were
// completed.
int activityID = atoi ( row [ 1 ] ) ;
if ( ( activityID < - 1 ) | | ( activityID > = MAXACTIVITIESPERTASK ) ) {
2019-09-02 03:10:03 -05:00
LogError ( " [TASKS]Activity ID [{}] out of range while loading completed tasks from database " ,
2018-07-08 17:35:37 -04:00
activityID ) ;
continue ;
}
2013-02-16 16:14:39 -08:00
2018-07-08 17:35:37 -04:00
int completedTime = atoi ( row [ 2 ] ) ;
if ( ( previousTaskID ! = - 1 ) & &
( ( taskID ! = previousTaskID ) | | ( completedTime ! = previousCompletedTime ) ) ) {
state - > CompletedTasks . push_back ( cti ) ;
for ( int i = 0 ; i < MAXACTIVITIESPERTASK ; i + + )
cti . ActivityDone [ i ] = false ;
}
2013-05-06 13:07:41 -04:00
2018-07-08 17:35:37 -04:00
cti . TaskID = previousTaskID = taskID ;
cti . CompletedTime = previousCompletedTime = completedTime ;
2013-02-16 16:14:39 -08:00
2018-07-08 17:35:37 -04:00
// If ActivityID is -1, Mark all the non-optional tasks as completed.
if ( activityID < 0 ) {
TaskInformation * task = Tasks [ taskID ] ;
if ( task = = nullptr )
continue ;
2013-02-16 16:14:39 -08:00
2018-07-08 17:35:37 -04:00
for ( int i = 0 ; i < task - > ActivityCount ; i + + )
if ( ! task - > Activity [ i ] . Optional )
cti . ActivityDone [ i ] = true ;
} else
cti . ActivityDone [ activityID ] = true ;
}
2013-02-16 16:14:39 -08:00
2018-07-08 17:35:37 -04:00
if ( previousTaskID ! = - 1 )
state - > CompletedTasks . push_back ( cti ) ;
2013-02-16 16:14:39 -08:00
2018-07-08 17:35:37 -04:00
state - > LastCompletedTaskLoaded = state - > CompletedTasks . size ( ) ;
2013-02-16 16:14:39 -08:00
}
2018-07-08 17:35:37 -04:00
query = StringFormat ( " SELECT `taskid` FROM character_enabledtasks "
" WHERE `charid` = %i AND `taskid` >0 AND `taskid` < %i "
" ORDER BY `taskid` ASC " ,
characterID , MAXTASKS ) ;
results = database . QueryDatabase ( query ) ;
2017-04-01 03:51:46 -05:00
if ( ! results . Success ( ) ) {
2019-09-02 03:10:03 -05:00
LogError ( " [TASKS]Error in TaskManager::LoadClientState load enabled tasks: [{}] " ,
2018-07-08 17:35:37 -04:00
results . ErrorMessage ( ) . c_str ( ) ) ;
} else {
2017-04-01 03:51:46 -05:00
for ( auto row = results . begin ( ) ; row ! = results . end ( ) ; + + row ) {
2014-10-08 12:30:08 -07:00
int taskID = atoi ( row [ 0 ] ) ;
state - > EnabledTasks . push_back ( taskID ) ;
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [CLIENTLOAD] Adding TaskID %i to enabled tasks " , taskID ) ;
2013-02-16 16:14:39 -08:00
}
2017-04-01 03:51:46 -05:00
}
2013-02-16 16:14:39 -08:00
// Check that there is an entry in the client task state for every activity in each task
// This should only break if a ServerOP adds or deletes activites for a task that players already
// have active, or due to a bug.
2018-07-08 18:01:14 -04:00
for ( int i = 0 ; i < MAXACTIVEQUESTS + 1 ; i + + ) {
int taskID = state - > ActiveTasks [ i ] . TaskID ;
2018-07-08 17:35:37 -04:00
if ( taskID = = TASKSLOTEMPTY )
continue ;
if ( ! Tasks [ taskID ] ) {
2019-08-11 00:00:55 -05:00
c - > Message ( Chat : : Red ,
2018-07-08 17:35:37 -04:00
" Active Task Slot %i, references a task (%i), that does not exist. "
" Removing from memory. Contact a GM to resolve this. " ,
i , taskID ) ;
2019-09-02 03:10:03 -05:00
LogError ( " [TASKS]Character [{}] has task [{}] which does not exist " ,
2018-07-08 17:35:37 -04:00
characterID , taskID ) ;
2018-07-08 18:01:14 -04:00
state - > ActiveTasks [ i ] . TaskID = TASKSLOTEMPTY ;
2013-02-16 16:14:39 -08:00
continue ;
}
2018-07-08 17:35:37 -04:00
for ( int j = 0 ; j < Tasks [ taskID ] - > ActivityCount ; j + + ) {
2018-07-08 18:01:14 -04:00
if ( state - > ActiveTasks [ i ] . Activity [ j ] . ActivityID ! = j ) {
2019-08-11 00:00:55 -05:00
c - > Message ( Chat : : Red ,
2018-07-08 17:35:37 -04:00
" Active Task %i, %s. Activity count does not match expected value. "
" Removing from memory. Contact a GM to resolve this. " ,
taskID , Tasks [ taskID ] - > Title . c_str ( ) ) ;
2019-09-02 03:10:03 -05:00
LogError ( " [TASKS]Fatal error in character [{}] task state. Activity [{}] for Task [{}] either missing from client state or from task " ,
2018-07-08 17:35:37 -04:00
characterID , j , taskID ) ;
2018-07-08 18:01:14 -04:00
state - > ActiveTasks [ i ] . TaskID = TASKSLOTEMPTY ;
2013-02-16 16:14:39 -08:00
break ;
}
}
}
2018-06-24 23:21:35 -04:00
if ( state - > ActiveTask . TaskID ! = TASKSLOTEMPTY )
state - > UnlockActivities ( characterID , state - > ActiveTask ) ;
// TODO: shared
for ( int i = 0 ; i < MAXACTIVEQUESTS ; i + + )
if ( state - > ActiveQuests [ i ] . TaskID ! = TASKSLOTEMPTY )
state - > UnlockActivities ( characterID , state - > ActiveQuests [ i ] ) ;
2013-02-16 16:14:39 -08:00
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [CLIENTLOAD] LoadClientState for Character ID %d DONE! " , characterID ) ;
2013-02-16 16:14:39 -08:00
return true ;
}
2014-10-08 13:07:03 -07:00
void ClientTaskState : : EnableTask ( int characterID , int taskCount , int * tasks ) {
2013-02-16 16:14:39 -08:00
// Check if the Task is already enabled for this client
//
2014-10-08 13:07:03 -07:00
std : : vector < int > tasksEnabled ;
2013-02-16 16:14:39 -08:00
2014-10-08 13:07:03 -07:00
for ( int i = 0 ; i < taskCount ; i + + ) {
auto iterator = EnabledTasks . begin ( ) ;
bool addTask = true ;
2013-02-16 16:14:39 -08:00
2014-10-08 13:07:03 -07:00
while ( iterator ! = EnabledTasks . end ( ) ) {
2013-02-16 16:14:39 -08:00
// If this task is already enabled, stop looking
2014-10-08 13:07:03 -07:00
if ( ( * iterator ) = = tasks [ i ] ) {
addTask = false ;
2013-02-16 16:14:39 -08:00
break ;
}
// Our list of enabled tasks is sorted, so we can quit if we find a taskid higher than
// the one we are looking for.
2014-10-08 13:07:03 -07:00
if ( ( * iterator ) > tasks [ i ] )
break ;
+ + iterator ;
2013-02-16 16:14:39 -08:00
}
2014-10-08 13:07:03 -07:00
if ( addTask ) {
EnabledTasks . insert ( iterator , tasks [ i ] ) ;
2013-02-16 16:14:39 -08:00
// Make a note of the task we enabled, for later SQL generation
2014-10-08 13:07:03 -07:00
tasksEnabled . push_back ( tasks [ i ] ) ;
2013-02-16 16:14:39 -08:00
}
}
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] New enabled task list " ) ;
2013-02-16 16:14:39 -08:00
for ( unsigned int i = 0 ; i < EnabledTasks . size ( ) ; i + + )
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] %i " , EnabledTasks [ i ] ) ;
2013-02-16 16:14:39 -08:00
2016-05-25 14:57:47 -04:00
if ( tasksEnabled . empty ( ) )
2014-10-08 13:07:03 -07:00
return ;
2013-02-16 16:14:39 -08:00
2016-07-15 19:32:46 -04:00
std : : stringstream queryStream ;
queryStream < < " REPLACE INTO character_enabledtasks (charid, taskid) VALUES " ;
2014-10-08 13:07:03 -07:00
for ( unsigned int i = 0 ; i < tasksEnabled . size ( ) ; i + + )
2016-07-15 19:32:46 -04:00
queryStream < < ( i ? " , " : " " ) < < StringFormat ( " (%i, %i) " , characterID , tasksEnabled [ i ] ) ;
2013-02-16 16:14:39 -08:00
2014-10-08 13:07:03 -07:00
std : : string query = queryStream . str ( ) ;
2016-07-14 12:47:55 -04:00
if ( tasksEnabled . size ( ) ) {
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Executing query %s " , query . c_str ( ) ) ;
2016-07-14 12:47:55 -04:00
database . QueryDatabase ( query ) ;
}
else {
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] EnableTask called for characterID: %u .. but, no tasks exist " , characterID ) ;
2016-07-14 12:47:55 -04:00
}
2013-02-16 16:14:39 -08:00
}
2014-10-08 13:17:03 -07:00
void ClientTaskState : : DisableTask ( int charID , int taskCount , int * taskList ) {
2013-02-16 16:14:39 -08:00
// Check if the Task is enabled for this client
//
2014-10-08 13:17:03 -07:00
std : : vector < int > tasksDisabled ;
2013-05-06 13:07:41 -04:00
2014-10-08 13:17:03 -07:00
for ( int i = 0 ; i < taskCount ; i + + ) {
auto iterator = EnabledTasks . begin ( ) ;
bool removeTask = false ;
2013-02-16 16:14:39 -08:00
2014-10-08 13:17:03 -07:00
while ( iterator ! = EnabledTasks . end ( ) ) {
if ( ( * iterator ) = = taskList [ i ] ) {
removeTask = true ;
2013-02-16 16:14:39 -08:00
break ;
}
2014-10-08 13:17:03 -07:00
if ( ( * iterator ) > taskList [ i ] )
break ;
+ + iterator ;
2013-02-16 16:14:39 -08:00
}
2014-10-08 13:17:03 -07:00
if ( removeTask ) {
EnabledTasks . erase ( iterator ) ;
tasksDisabled . push_back ( taskList [ i ] ) ;
2013-02-16 16:14:39 -08:00
}
}
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] New enabled task list " ) ;
2013-02-16 16:14:39 -08:00
for ( unsigned int i = 0 ; i < EnabledTasks . size ( ) ; i + + )
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] %i " , EnabledTasks [ i ] ) ;
2013-02-16 16:14:39 -08:00
2016-05-25 14:57:47 -04:00
if ( tasksDisabled . empty ( ) )
2014-10-08 13:17:03 -07:00
return ;
2013-02-16 16:14:39 -08:00
2016-07-15 19:32:46 -04:00
std : : stringstream queryStream ;
queryStream < < StringFormat ( " DELETE FROM character_enabledtasks WHERE charid = %i AND ( " , charID ) ;
2013-02-16 16:14:39 -08:00
2014-10-08 13:17:03 -07:00
for ( unsigned int i = 0 ; i < tasksDisabled . size ( ) ; i + + )
2016-07-15 19:32:46 -04:00
queryStream < < ( i ? StringFormat ( " taskid = %i " , tasksDisabled [ i ] ) : StringFormat ( " OR taskid = %i " , tasksDisabled [ i ] ) ) ;
2013-02-16 16:14:39 -08:00
2014-10-08 13:17:03 -07:00
queryStream < < " ) " ;
2016-07-14 12:47:55 -04:00
2014-10-08 13:17:03 -07:00
std : : string query = queryStream . str ( ) ;
2016-07-14 12:47:55 -04:00
if ( tasksDisabled . size ( ) ) {
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Executing query %s " , query . c_str ( ) ) ;
2016-07-14 12:47:55 -04:00
database . QueryDatabase ( query ) ;
}
else {
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] DisableTask called for characterID: %u .. but, no tasks exist " , charID ) ;
2016-07-14 12:47:55 -04:00
}
2013-02-16 16:14:39 -08:00
}
bool ClientTaskState : : IsTaskEnabled ( int TaskID ) {
2013-05-22 16:17:19 -04:00
std : : vector < int > : : iterator Iterator ;
2013-02-16 16:14:39 -08:00
Iterator = EnabledTasks . begin ( ) ;
while ( Iterator ! = EnabledTasks . end ( ) ) {
if ( ( * Iterator ) = = TaskID ) return true ;
if ( ( * Iterator ) > TaskID ) break ;
2014-01-13 22:14:02 -05:00
+ + Iterator ;
2013-02-16 16:14:39 -08:00
}
return false ;
}
int ClientTaskState : : EnabledTaskCount ( int TaskSetID ) {
// Return the number of tasks in TaskSet that this character is enabled for.
unsigned int EnabledTaskIndex = 0 ;
unsigned int TaskSetIndex = 0 ;
int EnabledTaskCount = 0 ;
if ( ( TaskSetID < = 0 ) | | ( TaskSetID > = MAXTASKSETS ) ) return - 1 ;
while ( ( EnabledTaskIndex < EnabledTasks . size ( ) ) & & ( TaskSetIndex < taskmanager - > TaskSets [ TaskSetID ] . size ( ) ) ) {
if ( EnabledTasks [ EnabledTaskIndex ] = = taskmanager - > TaskSets [ TaskSetID ] [ TaskSetIndex ] ) {
EnabledTaskCount + + ;
EnabledTaskIndex + + ;
TaskSetIndex + + ;
continue ;
}
if ( EnabledTasks [ EnabledTaskIndex ] < taskmanager - > TaskSets [ TaskSetID ] [ TaskSetIndex ] )
EnabledTaskIndex + + ;
else
TaskSetIndex + + ;
}
return EnabledTaskCount ;
}
int ClientTaskState : : ActiveTasksInSet ( int TaskSetID ) {
if ( ( TaskSetID < = 0 ) | | ( TaskSetID > = MAXTASKSETS ) ) return - 1 ;
int Count = 0 ;
2013-05-06 13:07:41 -04:00
for ( unsigned int i = 0 ; i < taskmanager - > TaskSets [ TaskSetID ] . size ( ) ; i + + )
2013-02-16 16:14:39 -08:00
if ( IsTaskActive ( taskmanager - > TaskSets [ TaskSetID ] [ i ] ) )
Count + + ;
return Count ;
}
int ClientTaskState : : CompletedTasksInSet ( int TaskSetID ) {
if ( ( TaskSetID < = 0 ) | | ( TaskSetID > = MAXTASKSETS ) ) return - 1 ;
int Count = 0 ;
2013-05-06 13:07:41 -04:00
for ( unsigned int i = 0 ; i < taskmanager - > TaskSets [ TaskSetID ] . size ( ) ; i + + )
2013-02-16 16:14:39 -08:00
if ( IsTaskCompleted ( taskmanager - > TaskSets [ TaskSetID ] [ i ] ) )
Count + + ;
return Count ;
}
2018-06-16 22:42:26 -04:00
bool ClientTaskState : : HasSlotForTask ( TaskInformation * task )
{
if ( task = = nullptr )
return false ;
switch ( task - > type ) {
case TaskType : : Task :
return ActiveTask . TaskID = = TASKSLOTEMPTY ;
case TaskType : : Shared :
return false ; // todo
case TaskType : : Quest :
for ( int i = 0 ; i < MAXACTIVEQUESTS ; + + i )
if ( ActiveQuests [ i ] . TaskID = = TASKSLOTEMPTY )
return true ;
case TaskType : : E :
return false ; // removed on live
}
return false ;
}
2013-02-16 16:14:39 -08:00
int TaskManager : : FirstTaskInSet ( int TaskSetID ) {
if ( ( TaskSetID < = 0 ) | | ( TaskSetID > = MAXTASKSETS ) ) return 0 ;
2016-05-25 14:57:47 -04:00
if ( TaskSets [ TaskSetID ] . empty ( ) ) return 0 ;
2013-02-16 16:14:39 -08:00
2016-05-25 16:10:28 -04:00
auto Iterator = TaskSets [ TaskSetID ] . begin ( ) ;
2013-02-16 16:14:39 -08:00
while ( Iterator ! = TaskSets [ TaskSetID ] . end ( ) ) {
if ( ( * Iterator ) > 0 )
return ( * Iterator ) ;
2014-01-13 22:14:02 -05:00
+ + Iterator ;
2013-02-16 16:14:39 -08:00
}
return 0 ;
}
int TaskManager : : LastTaskInSet ( int TaskSetID ) {
if ( ( TaskSetID < = 0 ) | | ( TaskSetID > = MAXTASKSETS ) ) return 0 ;
2016-05-25 14:57:47 -04:00
if ( TaskSets [ TaskSetID ] . empty ( ) ) return 0 ;
2013-02-16 16:14:39 -08:00
return TaskSets [ TaskSetID ] [ TaskSets [ TaskSetID ] . size ( ) - 1 ] ;
}
int TaskManager : : NextTaskInSet ( int TaskSetID , int TaskID ) {
if ( ( TaskSetID < = 0 ) | | ( TaskSetID > = MAXTASKSETS ) ) return 0 ;
2016-05-25 14:57:47 -04:00
if ( TaskSets [ TaskSetID ] . empty ( ) ) return 0 ;
2013-02-16 16:14:39 -08:00
for ( unsigned int i = 0 ; i < TaskSets [ TaskSetID ] . size ( ) ; i + + ) {
if ( TaskSets [ TaskSetID ] [ i ] > TaskID ) return TaskSets [ TaskSetID ] [ i ] ;
}
return 0 ;
}
bool TaskManager : : AppropriateLevel ( int TaskID , int PlayerLevel ) {
2013-05-04 18:06:58 -07:00
if ( Tasks [ TaskID ] = = nullptr ) return false ;
2013-02-16 16:14:39 -08:00
if ( Tasks [ TaskID ] - > MinLevel & & ( PlayerLevel < Tasks [ TaskID ] - > MinLevel ) ) return false ;
if ( Tasks [ TaskID ] - > MaxLevel & & ( PlayerLevel > Tasks [ TaskID ] - > MaxLevel ) ) return false ;
return true ;
}
2020-03-31 22:24:09 -04:00
std : : string TaskManager : : GetTaskName ( uint32 task_id )
{
if ( task_id > 0 & & task_id < MAXTASKS ) {
if ( Tasks [ task_id ] ! = nullptr ) {
return Tasks [ task_id ] - > Title ;
}
}
return std : : string ( ) ;
}
2015-07-15 19:16:51 -04:00
int TaskManager : : GetTaskMinLevel ( int TaskID )
{
if ( Tasks [ TaskID ] - > MinLevel )
{
return Tasks [ TaskID ] - > MinLevel ;
}
2015-07-16 13:46:29 -04:00
return - 1 ;
2015-07-15 19:16:51 -04:00
}
int TaskManager : : GetTaskMaxLevel ( int TaskID )
{
if ( Tasks [ TaskID ] - > MaxLevel )
{
return Tasks [ TaskID ] - > MaxLevel ;
}
2015-07-16 13:46:29 -04:00
return - 1 ;
2015-07-15 19:16:51 -04:00
}
2018-06-16 22:42:26 -04:00
void TaskManager : : TaskSetSelector ( Client * c , ClientTaskState * state , Mob * mob , int TaskSetID )
{
2013-02-16 16:14:39 -08:00
int TaskList [ MAXCHOOSERENTRIES ] ;
int TaskListIndex = 0 ;
int PlayerLevel = c - > GetLevel ( ) ;
2018-06-16 22:42:26 -04:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] TaskSetSelector called for taskset %i. EnableTaskSize is %i " ,
TaskSetID , state - > EnabledTasks . size ( ) ) ;
2013-02-16 16:14:39 -08:00
2018-06-16 22:42:26 -04:00
if ( TaskSetID < = 0 | | TaskSetID > = MAXTASKSETS )
return ;
2013-02-16 16:14:39 -08:00
2018-06-16 22:42:26 -04:00
if ( TaskSets [ TaskSetID ] . empty ( ) ) {
2019-08-11 01:09:11 -05:00
mob - > SayString ( c , Chat : : Yellow , MAX_ACTIVE_TASKS , c - > GetName ( ) ) ; // I think this is suppose to be yellow
2018-06-16 22:42:26 -04:00
return ;
2013-02-16 16:14:39 -08:00
}
2018-06-16 22:42:26 -04:00
bool all_enabled = false ;
2013-02-16 16:14:39 -08:00
2018-06-16 22:42:26 -04:00
// A TaskID of 0 in a TaskSet indicates that all Tasks in the set are enabled for all players.
if ( TaskSets [ TaskSetID ] [ 0 ] = = 0 ) {
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] TaskSets[%i][0] == 0. All Tasks in Set enabled. " , TaskSetID ) ;
all_enabled = true ;
}
2013-02-16 16:14:39 -08:00
2018-06-16 22:42:26 -04:00
auto Iterator = TaskSets [ TaskSetID ] . begin ( ) ;
2013-02-16 16:14:39 -08:00
2018-06-16 22:42:26 -04:00
if ( all_enabled )
+ + Iterator ; // skip first when all enabled since it's useless data
2013-02-16 16:14:39 -08:00
2018-06-16 22:42:26 -04:00
while ( Iterator ! = TaskSets [ TaskSetID ] . end ( ) & & TaskListIndex < MAXCHOOSERENTRIES ) {
auto task = * Iterator ;
// verify level, we're not currently on it, repeatable status, if it's a (shared) task
// we aren't currently on another, and if it's enabled if not all_enabled
if ( ( all_enabled | | state - > IsTaskEnabled ( task ) ) & & AppropriateLevel ( task , PlayerLevel ) & &
! state - > IsTaskActive ( task ) & & state - > HasSlotForTask ( Tasks [ task ] ) & & // this slot checking is a bit silly, but we allow mixing of task types ...
( IsTaskRepeatable ( task ) | | ! state - > IsTaskCompleted ( task ) ) )
TaskList [ TaskListIndex + + ] = task ;
2013-02-16 16:14:39 -08:00
2018-06-16 22:42:26 -04:00
+ + Iterator ;
2013-02-16 16:14:39 -08:00
}
2018-06-16 22:42:26 -04:00
if ( TaskListIndex > 0 ) {
SendTaskSelector ( c , mob , TaskListIndex , TaskList ) ;
} else {
2019-08-11 01:09:11 -05:00
mob - > SayString ( c , Chat : : Yellow , MAX_ACTIVE_TASKS , c - > GetName ( ) ) ; // check color, I think this might be only for (Shared) Tasks, w/e -- think should be yellow
2018-06-16 22:42:26 -04:00
}
2013-02-16 16:14:39 -08:00
2018-06-16 22:42:26 -04:00
return ;
2013-02-16 16:14:39 -08:00
}
2018-06-16 23:06:46 -04:00
// unlike the non-Quest version of this function, it does not check enabled, that is assumed the responsibility of the quest to handle
// we do however still want it to check the other stuff like level, active, room, etc
void TaskManager : : TaskQuestSetSelector ( Client * c , ClientTaskState * state , Mob * mob , int count , int * tasks )
{
int TaskList [ MAXCHOOSERENTRIES ] ;
int TaskListIndex = 0 ;
int PlayerLevel = c - > GetLevel ( ) ;
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] TaskQuestSetSelector called for array size %d " , count ) ;
if ( count < = 0 )
return ;
for ( int i = 0 ; i < count ; + + i ) {
auto task = tasks [ i ] ;
// verify level, we're not currently on it, repeatable status, if it's a (shared) task
// we aren't currently on another, and if it's enabled if not all_enabled
if ( AppropriateLevel ( task , PlayerLevel ) & &
! state - > IsTaskActive ( task ) & & state - > HasSlotForTask ( Tasks [ task ] ) & & // this slot checking is a bit silly, but we allow mixing of task types ...
( IsTaskRepeatable ( task ) | | ! state - > IsTaskCompleted ( task ) ) )
TaskList [ TaskListIndex + + ] = task ;
}
if ( TaskListIndex > 0 ) {
SendTaskSelector ( c , mob , TaskListIndex , TaskList ) ;
} else {
2019-08-11 01:09:11 -05:00
mob - > SayString ( c , Chat : : Yellow , MAX_ACTIVE_TASKS , c - > GetName ( ) ) ; // check color, I think this might be only for (Shared) Tasks, w/e -- think should be yellow
2018-06-16 23:06:46 -04:00
}
return ;
}
2013-02-16 16:14:39 -08:00
void TaskManager : : SendTaskSelector ( Client * c , Mob * mob , int TaskCount , int * TaskList ) {
2020-05-17 18:36:06 -07:00
if ( c - > ClientVersion ( ) > = EQ : : versions : : ClientVersion : : RoF )
2013-02-16 16:14:39 -08:00
{
SendTaskSelectorNew ( c , mob , TaskCount , TaskList ) ;
return ;
}
// Titanium OpCode: 0x5e7c
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] TaskSelector for %i Tasks " , TaskCount ) ;
2013-02-16 16:14:39 -08:00
int PlayerLevel = c - > GetLevel ( ) ;
2013-05-09 10:44:08 -04:00
// Check if any of the tasks exist
2018-07-04 01:57:54 -04:00
for ( int i = 0 ; i < TaskCount ; i + + ) {
if ( Tasks [ TaskList [ i ] ] ! = nullptr )
break ;
2013-02-16 16:14:39 -08:00
}
int ValidTasks = 0 ;
2018-07-04 01:57:54 -04:00
for ( int i = 0 ; i < TaskCount ; i + + ) {
if ( ! AppropriateLevel ( TaskList [ i ] , PlayerLevel ) )
continue ;
if ( c - > IsTaskActive ( TaskList [ i ] ) )
continue ;
if ( ! IsTaskRepeatable ( TaskList [ i ] ) & & c - > IsTaskCompleted ( TaskList [ i ] ) )
continue ;
2013-02-16 16:14:39 -08:00
ValidTasks + + ;
}
2018-07-04 01:57:54 -04:00
if ( ValidTasks = = 0 )
return ;
2013-05-06 13:07:41 -04:00
2018-07-04 01:57:54 -04:00
SerializeBuffer buf ( 50 * ValidTasks ) ;
2013-02-16 16:14:39 -08:00
2018-07-04 01:57:54 -04:00
buf . WriteUInt32 ( ValidTasks ) ;
buf . WriteUInt32 ( 2 ) ; // task type, live doesn't let you send more than one type, but we do?
buf . WriteUInt32 ( mob - > GetID ( ) ) ;
2013-02-16 16:14:39 -08:00
2018-07-04 01:57:54 -04:00
for ( int i = 0 ; i < TaskCount ; i + + ) {
if ( ! AppropriateLevel ( TaskList [ i ] , PlayerLevel ) )
continue ;
if ( c - > IsTaskActive ( TaskList [ i ] ) )
continue ;
if ( ! IsTaskRepeatable ( TaskList [ i ] ) & & c - > IsTaskCompleted ( TaskList [ i ] ) )
continue ;
2013-02-16 16:14:39 -08:00
2018-07-04 01:57:54 -04:00
buf . WriteUInt32 ( TaskList [ i ] ) ; // TaskID
2020-05-17 18:36:06 -07:00
if ( c - > ClientVersion ( ) ! = EQ : : versions : : ClientVersion : : Titanium )
2018-07-04 01:57:54 -04:00
buf . WriteFloat ( 1.0f ) ; // affects color, difficulty?
buf . WriteUInt32 ( Tasks [ TaskList [ i ] ] - > Duration ) ;
buf . WriteUInt32 ( static_cast < int > ( Tasks [ TaskList [ i ] ] - > dur_code ) ) ;
2013-02-16 16:14:39 -08:00
2018-07-04 01:57:54 -04:00
buf . WriteString ( Tasks [ TaskList [ i ] ] - > Title ) ; // max 64 with null
buf . WriteString ( Tasks [ TaskList [ i ] ] - > Description ) ; // max 4000 with null
2013-02-16 16:14:39 -08:00
2020-05-17 18:36:06 -07:00
if ( c - > ClientVersion ( ) ! = EQ : : versions : : ClientVersion : : Titanium )
2018-07-04 01:57:54 -04:00
buf . WriteUInt8 ( 0 ) ; // Has reward set flag
2013-02-16 16:14:39 -08:00
2018-07-04 01:57:54 -04:00
buf . WriteUInt32 ( Tasks [ TaskList [ i ] ] - > ActivityCount ) ;
2013-02-16 16:14:39 -08:00
2018-07-04 01:57:54 -04:00
for ( int j = 0 ; j < Tasks [ TaskList [ i ] ] - > ActivityCount ; + + j ) {
buf . WriteUInt32 ( j ) ; // ActivityNumber
auto & activity = Tasks [ TaskList [ i ] ] - > Activity [ j ] ;
buf . WriteUInt32 ( activity . Type ) ;
buf . WriteUInt32 ( 0 ) ; // solo, group, raid?
buf . WriteString ( activity . target_name ) ; // max length 64, "target name" so like loot x foo from bar (this is bar)
2013-02-16 16:14:39 -08:00
2018-07-04 01:57:54 -04:00
buf . WriteString ( activity . item_list ) ; // max length 64 in these clients
2013-02-16 16:14:39 -08:00
2018-07-04 01:57:54 -04:00
buf . WriteUInt32 ( activity . GoalCount ) ;
2013-05-06 13:07:41 -04:00
2018-07-04 01:57:54 -04:00
buf . WriteInt32 ( activity . skill_id ) ;
buf . WriteInt32 ( activity . spell_id ) ;
2018-08-01 13:50:09 -04:00
buf . WriteInt32 ( activity . ZoneIDs . empty ( ) ? 0 : activity . ZoneIDs . front ( ) ) ;
2018-07-04 01:57:54 -04:00
buf . WriteString ( activity . desc_override ) ;
}
2013-02-16 16:14:39 -08:00
}
2018-07-08 23:03:53 -04:00
auto outapp = new EQApplicationPacket ( OP_OpenNewTasksWindow , buf ) ;
2013-02-16 16:14:39 -08:00
c - > QueuePacket ( outapp ) ;
safe_delete ( outapp ) ;
}
2018-07-02 22:01:17 -04:00
void TaskManager : : SendTaskSelectorNew ( Client * c , Mob * mob , int TaskCount , int * TaskList )
{
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] TaskSelector for %i Tasks " , TaskCount ) ;
2013-02-16 16:14:39 -08:00
int PlayerLevel = c - > GetLevel ( ) ;
2013-05-09 10:44:08 -04:00
// Check if any of the tasks exist
2018-07-02 22:01:17 -04:00
for ( int i = 0 ; i < TaskCount ; i + + )
if ( Tasks [ TaskList [ i ] ] ! = nullptr )
break ;
2013-02-16 16:14:39 -08:00
int ValidTasks = 0 ;
2018-07-02 22:01:17 -04:00
for ( int i = 0 ; i < TaskCount ; i + + ) {
if ( ! AppropriateLevel ( TaskList [ i ] , PlayerLevel ) )
continue ;
if ( c - > IsTaskActive ( TaskList [ i ] ) )
continue ;
if ( ! IsTaskRepeatable ( TaskList [ i ] ) & & c - > IsTaskCompleted ( TaskList [ i ] ) )
continue ;
2013-02-16 16:14:39 -08:00
ValidTasks + + ;
}
2018-07-02 22:01:17 -04:00
if ( ValidTasks = = 0 )
return ;
2013-02-16 16:14:39 -08:00
2018-07-02 22:01:17 -04:00
SerializeBuffer buf ( 50 * ValidTasks ) ;
2013-05-06 13:07:41 -04:00
2018-07-02 22:01:17 -04:00
buf . WriteUInt32 ( ValidTasks ) ; // TaskCount
buf . WriteUInt32 ( 2 ) ; // Type, valid values: 0-3. 0 = Task, 1 = Shared Task, 2 = Quest, 3 = ??? -- should fix maybe some day, but we let more than 1 type through :P
2018-06-07 18:14:09 -04:00
// so I guess an NPC can only offer one type of quests or we can only open a selection with one type :P (so quest call can tell us I guess)
// this is also sent in OP_TaskDescription
2018-07-02 22:01:17 -04:00
buf . WriteUInt32 ( mob - > GetID ( ) ) ; // TaskGiver
2013-02-16 16:14:39 -08:00
2018-07-02 22:01:17 -04:00
for ( int i = 0 ; i < TaskCount ; i + + ) { // max 40
if ( ! AppropriateLevel ( TaskList [ i ] , PlayerLevel ) )
continue ;
if ( c - > IsTaskActive ( TaskList [ i ] ) )
continue ;
if ( ! IsTaskRepeatable ( TaskList [ i ] ) & & c - > IsTaskCompleted ( TaskList [ i ] ) )
continue ;
2013-02-16 16:14:39 -08:00
2018-07-02 22:01:17 -04:00
buf . WriteUInt32 ( TaskList [ i ] ) ; // TaskID
buf . WriteFloat ( 1.0f ) ; // affects color, difficulty?
buf . WriteUInt32 ( Tasks [ TaskList [ i ] ] - > Duration ) ;
buf . WriteUInt32 ( static_cast < int > ( Tasks [ TaskList [ i ] ] - > dur_code ) ) ; // 1 = Short, 2 = Medium, 3 = Long, anything else Unlimited
2013-05-06 13:07:41 -04:00
2018-07-04 01:57:54 -04:00
buf . WriteString ( Tasks [ TaskList [ i ] ] - > Title ) ; // max 64 with null
buf . WriteString ( Tasks [ TaskList [ i ] ] - > Description ) ; // max 4000 with null
2013-02-16 16:14:39 -08:00
2018-07-02 22:01:17 -04:00
buf . WriteUInt8 ( 0 ) ; // Has reward set flag
buf . WriteUInt32 ( Tasks [ TaskList [ i ] ] - > ActivityCount ) ; // ActivityCount
2013-02-16 16:14:39 -08:00
2018-07-02 22:01:17 -04:00
for ( int j = 0 ; j < Tasks [ TaskList [ i ] ] - > ActivityCount ; + + j ) {
buf . WriteUInt32 ( j ) ; // ActivityNumber
auto & activity = Tasks [ TaskList [ i ] ] - > Activity [ j ] ;
buf . WriteUInt32 ( activity . Type ) ; // ActivityType
buf . WriteUInt32 ( 0 ) ; // solo, group, raid?
buf . WriteString ( activity . target_name ) ; // max length 64, "target name" so like loot x foo from bar (this is bar)
2018-06-05 22:16:53 -04:00
2018-07-02 22:01:17 -04:00
// this string is item names
2018-07-03 01:15:40 -04:00
buf . WriteLengthString ( activity . item_list ) ;
2018-06-05 22:16:53 -04:00
2018-07-02 22:01:17 -04:00
buf . WriteUInt32 ( activity . GoalCount ) ; // GoalCount
2018-06-05 22:16:53 -04:00
2018-07-02 22:01:17 -04:00
// this string is skill IDs? probably one of the "use on" tasks
2018-07-03 01:15:40 -04:00
buf . WriteLengthString ( activity . skill_list ) ;
2018-06-05 22:16:53 -04:00
2018-07-02 22:01:17 -04:00
// this string is spell IDs? probably one of the "use on" tasks
2018-07-03 01:15:40 -04:00
buf . WriteLengthString ( activity . spell_list ) ;
2018-06-05 22:16:53 -04:00
2018-07-02 22:01:17 -04:00
//buf.WriteString(itoa(Tasks[TaskList[i]]->Activity[ActivityID].ZoneID));
buf . WriteString ( activity . zones ) ; // Zone number in ascii max length 64, can be multiple with separated by ;
buf . WriteString ( activity . desc_override ) ; // max length 128 -- overrides the automatic descriptions
// this doesn't appear to be shown to the client at all and isn't the same as zones ... defaults to '0' though
buf . WriteString ( activity . zones ) ; // Zone number in ascii max length 64, probably can be separated by ; too, haven't found it used
}
2013-02-16 16:14:39 -08:00
}
2018-07-08 23:03:53 -04:00
auto outapp = new EQApplicationPacket ( OP_OpenNewTasksWindow , buf ) ;
2018-07-02 22:01:17 -04:00
2013-02-16 16:14:39 -08:00
c - > QueuePacket ( outapp ) ;
safe_delete ( outapp ) ;
}
int TaskManager : : GetActivityCount ( int TaskID ) {
// Return the total number of activities in a particular task.
2013-05-06 13:07:41 -04:00
if ( ( TaskID > 0 ) & & ( TaskID < MAXTASKS ) )
2013-02-16 16:14:39 -08:00
if ( Tasks [ TaskID ] ) return Tasks [ TaskID ] - > ActivityCount ;
return 0 ;
}
void TaskManager : : ExplainTask ( Client * c , int TaskID ) {
// TODO: This method is not finished (hardly started). It was intended to
// explain in English, what each activity did, conditions for step unlocking, etc.
//
return ;
if ( ! c ) return ;
if ( ( TaskID < = 0 ) | | ( TaskID > = MAXTASKS ) ) {
2019-08-11 00:27:04 -05:00
c - > Message ( Chat : : White , " TaskID out-of-range. " ) ;
2013-02-16 16:14:39 -08:00
return ;
}
2013-05-04 18:06:58 -07:00
if ( Tasks [ TaskID ] = = nullptr ) {
2019-08-11 00:27:04 -05:00
c - > Message ( Chat : : White , " Task does not exist. " ) ;
2013-02-16 16:14:39 -08:00
return ;
}
char Explanation [ 1000 ] , * ptr ;
2019-08-11 00:27:04 -05:00
c - > Message ( Chat : : White , " Task %4i: Title: %s " , TaskID , Tasks [ TaskID ] - > Description . c_str ( ) ) ;
c - > Message ( Chat : : White , " %3i Activities " , Tasks [ TaskID ] - > ActivityCount ) ;
2013-02-16 16:14:39 -08:00
ptr = Explanation ;
for ( int i = 0 ; i < Tasks [ TaskID ] - > ActivityCount ; i + + ) {
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
sprintf ( ptr , " Act: %3i: " , i ) ; ptr = ptr + strlen ( ptr ) ;
switch ( Tasks [ TaskID ] - > Activity [ i ] . Type ) {
case ActivityDeliver :
sprintf ( ptr , " Deliver " ) ;
break ;
}
}
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
ClientTaskState : : ClientTaskState ( ) {
ActiveTaskCount = 0 ;
LastCompletedTaskLoaded = 0 ;
CheckedTouchActivities = false ;
2018-06-24 23:21:35 -04:00
for ( int i = 0 ; i < MAXACTIVEQUESTS ; i + + ) {
ActiveQuests [ i ] . slot = i ;
2018-06-13 18:06:08 -04:00
ActiveQuests [ i ] . TaskID = TASKSLOTEMPTY ;
2018-06-24 23:21:35 -04:00
}
2018-06-16 23:52:25 -04:00
2018-06-24 23:21:35 -04:00
ActiveTask . slot = 0 ;
2018-06-16 23:52:25 -04:00
ActiveTask . TaskID = TASKSLOTEMPTY ;
2018-06-24 23:21:35 -04:00
// TODO: shared task
2013-02-16 16:14:39 -08:00
}
ClientTaskState : : ~ ClientTaskState ( ) {
}
int ClientTaskState : : GetActiveTaskID ( int index ) {
// Return the TaskID from the client's specified Active Task slot.
2018-06-13 17:51:35 -04:00
if ( ( index < 0 ) | | ( index > = MAXACTIVEQUESTS ) ) return 0 ;
2013-02-16 16:14:39 -08:00
2018-06-13 18:06:08 -04:00
return ActiveQuests [ index ] . TaskID ;
2013-02-16 16:14:39 -08:00
}
2014-10-08 13:22:10 -07:00
static void DeleteCompletedTaskFromDatabase ( int charID , int taskID ) {
2013-02-16 16:14:39 -08:00
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] DeleteCompletedTasksFromDatabase. CharID = %i, TaskID = %i " , charID , taskID ) ;
2013-02-16 16:14:39 -08:00
2014-10-08 13:22:10 -07:00
const std : : string query = StringFormat ( " DELETE FROM completed_tasks WHERE charid=%i AND taskid = %i " , charID , taskID ) ;
auto results = database . QueryDatabase ( query ) ;
if ( ! results . Success ( ) ) {
2013-02-16 16:14:39 -08:00
return ;
}
2014-10-08 13:22:10 -07:00
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Delete query %s " , query . c_str ( ) ) ;
2013-02-16 16:14:39 -08:00
}
2018-06-24 23:21:35 -04:00
bool ClientTaskState : : UnlockActivities ( int CharID , ClientTaskInformation & task_info )
{
2013-02-16 16:14:39 -08:00
bool AllActivitiesComplete = true ;
2018-06-24 23:21:35 -04:00
TaskInformation * Task = taskmanager - > Tasks [ task_info . TaskID ] ;
2013-02-16 16:14:39 -08:00
2018-06-24 23:21:35 -04:00
if ( Task = = nullptr )
return true ;
2013-02-16 16:14:39 -08:00
// On loading the client state, all activities that are not completed, are
2013-05-09 10:44:08 -04:00
// marked as hidden. For Sequential (non-stepped) mode, we mark the first
2013-02-16 16:14:39 -08:00
// activity as active if not complete.
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] CharID: %i Task: %i Sequence mode is %i " ,
2018-06-24 23:21:35 -04:00
CharID , task_info . TaskID , Task - > SequenceMode ) ;
2013-02-16 16:14:39 -08:00
2018-06-24 23:21:35 -04:00
if ( Task - > SequenceMode = = ActivitiesSequential ) {
if ( task_info . Activity [ 0 ] . State ! = ActivityCompleted )
task_info . Activity [ 0 ] . State = ActivityActive ;
2013-02-16 16:14:39 -08:00
// Enable the next Hidden task.
2018-06-24 23:21:35 -04:00
for ( int i = 0 ; i < Task - > ActivityCount ; i + + ) {
if ( ( task_info . Activity [ i ] . State = = ActivityActive ) & &
( ! Task - > Activity [ i ] . Optional ) ) {
2013-02-16 16:14:39 -08:00
AllActivitiesComplete = false ;
break ;
}
2018-06-24 23:21:35 -04:00
if ( task_info . Activity [ i ] . State = = ActivityHidden ) {
task_info . Activity [ i ] . State = ActivityActive ;
2013-02-16 16:14:39 -08:00
AllActivitiesComplete = false ;
break ;
}
}
2018-06-24 23:21:35 -04:00
if ( AllActivitiesComplete & & RuleB ( TaskSystem , RecordCompletedTasks ) ) {
if ( RuleB ( TasksSystem , KeepOneRecordPerCompletedTask ) ) {
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] KeepOneRecord enabled " ) ;
2016-05-25 16:10:28 -04:00
auto Iterator = CompletedTasks . begin ( ) ;
2013-02-16 16:14:39 -08:00
int ErasedElements = 0 ;
2018-06-24 23:21:35 -04:00
while ( Iterator ! = CompletedTasks . end ( ) ) {
2013-02-16 16:14:39 -08:00
int TaskID = ( * Iterator ) . TaskID ;
2018-06-24 23:21:35 -04:00
if ( TaskID = = task_info . TaskID ) {
2013-02-16 16:14:39 -08:00
Iterator = CompletedTasks . erase ( Iterator ) ;
ErasedElements + + ;
2018-06-24 23:21:35 -04:00
} else
2014-01-13 22:14:02 -05:00
+ + Iterator ;
2013-02-16 16:14:39 -08:00
}
2018-06-24 23:21:35 -04:00
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Erased Element count is %i " , ErasedElements ) ;
2018-06-24 23:21:35 -04:00
if ( ErasedElements ) {
2013-02-16 16:14:39 -08:00
LastCompletedTaskLoaded - = ErasedElements ;
2018-06-24 23:21:35 -04:00
DeleteCompletedTaskFromDatabase ( CharID , task_info . TaskID ) ;
2013-02-16 16:14:39 -08:00
}
}
CompletedTaskInformation cti ;
2018-06-24 23:21:35 -04:00
cti . TaskID = task_info . TaskID ;
2013-05-04 18:06:58 -07:00
cti . CompletedTime = time ( nullptr ) ;
2013-02-16 16:14:39 -08:00
2018-06-24 23:21:35 -04:00
for ( int i = 0 ; i < Task - > ActivityCount ; i + + )
cti . ActivityDone [ i ] = ( task_info . Activity [ i ] . State = = ActivityCompleted ) ;
2013-02-16 16:14:39 -08:00
CompletedTasks . push_back ( cti ) ;
}
2018-06-24 23:21:35 -04:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Returning sequential task, AllActivitiesComplete is %i " ,
AllActivitiesComplete ) ;
2013-02-16 16:14:39 -08:00
return AllActivitiesComplete ;
}
// Stepped Mode
// TODO: This code is probably more complex than it needs to be
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
bool CurrentStepComplete = true ;
2018-06-24 23:21:35 -04:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Current Step is %i, Last Step is %i " , task_info . CurrentStep ,
Task - > LastStep ) ;
2013-02-16 16:14:39 -08:00
// If CurrentStep is -1, this is the first call to this method since loading the
// client state. Unlock all activities with a step number of 0
2013-05-06 13:07:41 -04:00
2018-06-24 23:21:35 -04:00
if ( task_info . CurrentStep = = - 1 ) {
for ( int i = 0 ; i < Task - > ActivityCount ; i + + ) {
if ( Task - > Activity [ i ] . StepNumber = = 0 & & task_info . Activity [ i ] . State = = ActivityHidden ) {
task_info . Activity [ i ] . State = ActivityActive ;
// task_info.Activity[i].Updated=true;
2013-02-16 16:14:39 -08:00
}
}
2018-06-24 23:21:35 -04:00
task_info . CurrentStep = 0 ;
2013-02-16 16:14:39 -08:00
}
2018-06-24 23:21:35 -04:00
for ( int Step = task_info . CurrentStep ; Step < = Task - > LastStep ; Step + + ) {
for ( int Activity = 0 ; Activity < Task - > ActivityCount ; Activity + + ) {
if ( Task - > Activity [ Activity ] . StepNumber = = ( int ) task_info . CurrentStep ) {
if ( ( task_info . Activity [ Activity ] . State ! = ActivityCompleted ) & &
( ! Task - > Activity [ Activity ] . Optional ) ) {
2013-02-16 16:14:39 -08:00
CurrentStepComplete = false ;
AllActivitiesComplete = false ;
break ;
}
}
}
2018-06-24 23:21:35 -04:00
if ( ! CurrentStepComplete )
break ;
task_info . CurrentStep + + ;
2013-02-16 16:14:39 -08:00
}
2018-06-24 23:21:35 -04:00
if ( AllActivitiesComplete ) {
if ( RuleB ( TaskSystem , RecordCompletedTasks ) ) {
2013-02-16 16:14:39 -08:00
// If we are only keeping one completed record per task, and the player has done
// the same task again, erase the previous completed entry for this task.
2018-06-24 23:21:35 -04:00
if ( RuleB ( TasksSystem , KeepOneRecordPerCompletedTask ) ) {
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] KeepOneRecord enabled " ) ;
2016-05-25 16:10:28 -04:00
auto Iterator = CompletedTasks . begin ( ) ;
2013-02-16 16:14:39 -08:00
int ErasedElements = 0 ;
2018-06-24 23:21:35 -04:00
while ( Iterator ! = CompletedTasks . end ( ) ) {
2013-02-16 16:14:39 -08:00
int TaskID = ( * Iterator ) . TaskID ;
2018-06-24 23:21:35 -04:00
if ( TaskID = = task_info . TaskID ) {
2013-02-16 16:14:39 -08:00
Iterator = CompletedTasks . erase ( Iterator ) ;
ErasedElements + + ;
2018-06-24 23:21:35 -04:00
} else
2014-01-13 22:14:02 -05:00
+ + Iterator ;
2013-02-16 16:14:39 -08:00
}
2018-06-24 23:21:35 -04:00
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Erased Element count is %i " , ErasedElements ) ;
2018-06-24 23:21:35 -04:00
if ( ErasedElements ) {
2013-02-16 16:14:39 -08:00
LastCompletedTaskLoaded - = ErasedElements ;
2018-06-24 23:21:35 -04:00
DeleteCompletedTaskFromDatabase ( CharID , task_info . TaskID ) ;
2013-02-16 16:14:39 -08:00
}
}
2018-06-24 23:21:35 -04:00
2013-02-16 16:14:39 -08:00
CompletedTaskInformation cti ;
2018-06-24 23:21:35 -04:00
cti . TaskID = task_info . TaskID ;
2013-05-04 18:06:58 -07:00
cti . CompletedTime = time ( nullptr ) ;
2013-02-16 16:14:39 -08:00
2018-06-24 23:21:35 -04:00
for ( int i = 0 ; i < Task - > ActivityCount ; i + + )
cti . ActivityDone [ i ] = ( task_info . Activity [ i ] . State = = ActivityCompleted ) ;
2013-02-16 16:14:39 -08:00
CompletedTasks . push_back ( cti ) ;
}
return true ;
}
// Mark all non-completed tasks in the current step as active
//
2018-06-24 23:21:35 -04:00
for ( int Activity = 0 ; Activity < Task - > ActivityCount ; Activity + + ) {
if ( ( Task - > Activity [ Activity ] . StepNumber = = ( int ) task_info . CurrentStep ) & &
( task_info . Activity [ Activity ] . State = = ActivityHidden ) ) {
task_info . Activity [ Activity ] . State = ActivityActive ;
task_info . Activity [ Activity ] . Updated = true ;
2013-02-16 16:14:39 -08:00
}
}
return false ;
}
void ClientTaskState : : UpdateTasksOnKill ( Client * c , int NPCTypeID ) {
UpdateTasksByNPC ( c , ActivityKill , NPCTypeID ) ;
}
bool ClientTaskState : : UpdateTasksOnSpeakWith ( Client * c , int NPCTypeID ) {
return UpdateTasksByNPC ( c , ActivitySpeakWith , NPCTypeID ) ;
}
bool ClientTaskState : : UpdateTasksByNPC ( Client * c , int ActivityType , int NPCTypeID ) {
int Ret = false ;
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] ClientTaskState::UpdateTasks for NPCTypeID: %d " , NPCTypeID ) ;
2013-02-16 16:14:39 -08:00
// If the client has no tasks, there is nothing further to check.
2013-05-06 13:07:41 -04:00
2018-07-05 00:13:19 -04:00
if ( ! taskmanager | | ( ActiveTaskCount = = 0 & & ActiveTask . TaskID = = TASKSLOTEMPTY ) ) // could be better ...
return false ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
// loop over the union of tasks and quests
for ( int i = 0 ; i < MAXACTIVEQUESTS + 1 ; i + + ) {
auto cur_task = & ActiveTasks [ i ] ;
if ( cur_task - > TaskID = = TASKSLOTEMPTY )
continue ;
2013-02-16 16:14:39 -08:00
// Check if there are any active kill activities for this task
2018-07-05 00:13:19 -04:00
auto Task = taskmanager - > Tasks [ cur_task - > TaskID ] ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
if ( Task = = nullptr )
return false ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
for ( int j = 0 ; j < Task - > ActivityCount ; j + + ) {
2013-02-16 16:14:39 -08:00
// We are not interested in completed or hidden activities
2018-07-05 00:13:19 -04:00
if ( cur_task - > Activity [ j ] . State ! = ActivityActive )
continue ;
2013-02-16 16:14:39 -08:00
// We are only interested in Kill activities
2018-07-05 00:13:19 -04:00
if ( Task - > Activity [ j ] . Type ! = ActivityType )
continue ;
2013-02-16 16:14:39 -08:00
// Is there a zone restriction on the activity ?
2018-08-01 13:50:09 -04:00
if ( ! Task - > Activity [ j ] . CheckZone ( zone - > GetZoneID ( ) ) ) {
2018-07-05 00:13:19 -04:00
Log ( Logs : : General , Logs : : Tasks ,
" [UPDATE] Char: %s Task: %i, Activity %i, Activity type %i for NPC %i failed zone "
" check " ,
c - > GetName ( ) , cur_task - > TaskID , j , ActivityType , NPCTypeID ) ;
2013-02-16 16:14:39 -08:00
continue ;
}
// Is the activity to kill this type of NPC ?
2018-07-05 00:13:19 -04:00
switch ( Task - > Activity [ j ] . GoalMethod ) {
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
case METHODSINGLEID :
if ( Task - > Activity [ j ] . GoalID ! = NPCTypeID )
continue ;
break ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
case METHODLIST :
if ( ! taskmanager - > GoalListManager . IsInList ( Task - > Activity [ j ] . GoalID , NPCTypeID ) )
2013-02-16 16:14:39 -08:00
continue ;
2018-07-05 00:13:19 -04:00
break ;
default :
// If METHODQUEST, don't update the activity here
continue ;
2013-02-16 16:14:39 -08:00
}
// We found an active task to kill this type of NPC, so increment the done count
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Calling increment done count ByNPC " ) ;
2018-07-09 14:01:52 -04:00
IncrementDoneCount ( c , Task , cur_task - > slot , j ) ;
2013-02-16 16:14:39 -08:00
Ret = true ;
}
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
return Ret ;
}
int ClientTaskState : : ActiveSpeakTask ( int NPCTypeID ) {
// This method is to be used from Perl quests only and returns the TaskID of the first
// active task found which has an active SpeakWith activity for this NPC.
2018-07-05 00:13:19 -04:00
if ( ! taskmanager | | ( ActiveTaskCount = = 0 & & ActiveTask . TaskID = = TASKSLOTEMPTY ) ) // could be better ...
return 0 ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
// loop over the union of tasks and quests
for ( int i = 0 ; i < MAXACTIVEQUESTS + 1 ; i + + ) {
auto cur_task = & ActiveTasks [ i ] ;
if ( cur_task - > TaskID = = TASKSLOTEMPTY )
continue ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
TaskInformation * Task = taskmanager - > Tasks [ cur_task - > TaskID ] ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
if ( Task = = nullptr )
continue ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
for ( int j = 0 ; j < Task - > ActivityCount ; j + + ) {
2013-02-16 16:14:39 -08:00
// We are not interested in completed or hidden activities
2018-07-05 00:13:19 -04:00
if ( cur_task - > Activity [ j ] . State ! = ActivityActive )
continue ;
if ( Task - > Activity [ j ] . Type ! = ActivitySpeakWith )
continue ;
2013-02-16 16:14:39 -08:00
// Is there a zone restriction on the activity ?
2018-08-01 13:50:09 -04:00
if ( ! Task - > Activity [ j ] . CheckZone ( zone - > GetZoneID ( ) ) )
2013-02-16 16:14:39 -08:00
continue ;
// Is the activity to speak with this type of NPC ?
2018-07-05 00:13:19 -04:00
if ( Task - > Activity [ j ] . GoalMethod = = METHODQUEST & & Task - > Activity [ j ] . GoalID = = NPCTypeID )
return cur_task - > TaskID ;
2013-02-16 16:14:39 -08:00
}
}
return 0 ;
}
int ClientTaskState : : ActiveSpeakActivity ( int NPCTypeID , int TaskID ) {
// This method is to be used from Perl quests only and returns the ActivityID of the first
// active activity found in the specified task which is to SpeakWith this NPC.
2018-07-05 00:13:19 -04:00
if ( ! taskmanager | | ( ActiveTaskCount = = 0 & & ActiveTask . TaskID = = TASKSLOTEMPTY ) ) // could be better ...
return - 1 ;
if ( TaskID < = 0 | | TaskID > = MAXTASKS )
return - 1 ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
// loop over the union of tasks and quests
for ( int i = 0 ; i < MAXACTIVEQUESTS + 1 ; i + + ) {
auto cur_task = & ActiveTasks [ i ] ;
if ( cur_task - > TaskID ! = TaskID )
continue ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
TaskInformation * Task = taskmanager - > Tasks [ cur_task - > TaskID ] ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
if ( Task = = nullptr )
continue ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
for ( int j = 0 ; j < Task - > ActivityCount ; j + + ) {
2013-02-16 16:14:39 -08:00
// We are not interested in completed or hidden activities
2018-07-05 00:13:19 -04:00
if ( cur_task - > Activity [ j ] . State ! = ActivityActive )
continue ;
if ( Task - > Activity [ j ] . Type ! = ActivitySpeakWith )
continue ;
2013-02-16 16:14:39 -08:00
// Is there a zone restriction on the activity ?
2018-08-01 13:50:09 -04:00
if ( ! Task - > Activity [ j ] . CheckZone ( zone - > GetZoneID ( ) ) )
2013-02-16 16:14:39 -08:00
continue ;
// Is the activity to speak with this type of NPC ?
2018-07-05 00:13:19 -04:00
if ( Task - > Activity [ j ] . GoalMethod = = METHODQUEST & & Task - > Activity [ j ] . GoalID = = NPCTypeID )
return j ;
2013-02-16 16:14:39 -08:00
}
return 0 ;
}
return 0 ;
}
2018-07-09 14:01:52 -04:00
2013-02-16 16:14:39 -08:00
void ClientTaskState : : UpdateTasksForItem ( Client * c , ActivityType Type , int ItemID , int Count ) {
// This method updates the client's task activities of the specified type which relate
// to the specified item.
//
// Type should be one of ActivityLoot, ActivityTradeSkill, ActivityFish or ActivityForage
// If the client has no tasks, there is nothing further to check.
2013-05-06 13:07:41 -04:00
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] ClientTaskState::UpdateTasksForItem(%d,%d) " , Type , ItemID ) ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
if ( ! taskmanager | | ( ActiveTaskCount = = 0 & & ActiveTask . TaskID = = TASKSLOTEMPTY ) ) // could be better ...
return ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
// loop over the union of tasks and quests
for ( int i = 0 ; i < MAXACTIVEQUESTS + 1 ; i + + ) {
auto cur_task = & ActiveTasks [ i ] ;
if ( cur_task - > TaskID = = TASKSLOTEMPTY )
continue ;
2013-02-16 16:14:39 -08:00
// Check if there are any active loot activities for this task
2018-07-05 00:13:19 -04:00
TaskInformation * Task = taskmanager - > Tasks [ cur_task - > TaskID ] ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
if ( Task = = nullptr )
return ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
for ( int j = 0 ; j < Task - > ActivityCount ; j + + ) {
2013-02-16 16:14:39 -08:00
// We are not interested in completed or hidden activities
2018-07-05 00:13:19 -04:00
if ( cur_task - > Activity [ j ] . State ! = ActivityActive )
continue ;
2013-02-16 16:14:39 -08:00
// We are only interested in the ActivityType we were called with
2018-07-05 00:13:19 -04:00
if ( Task - > Activity [ j ] . Type ! = ( int ) Type )
continue ;
2013-02-16 16:14:39 -08:00
// Is there a zone restriction on the activity ?
2018-08-01 13:50:09 -04:00
if ( ! Task - > Activity [ j ] . CheckZone ( zone - > GetZoneID ( ) ) ) {
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Char: %s Activity type %i for Item %i failed zone check " ,
2013-05-09 10:44:08 -04:00
c - > GetName ( ) , Type , ItemID ) ;
2013-02-16 16:14:39 -08:00
continue ;
}
// Is the activity related to this item ?
//
switch ( Task - > Activity [ j ] . GoalMethod ) {
case METHODSINGLEID :
if ( Task - > Activity [ j ] . GoalID ! = ItemID ) continue ;
break ;
case METHODLIST :
if ( ! taskmanager - > GoalListManager . IsInList ( Task - > Activity [ j ] . GoalID , ItemID ) ) continue ;
break ;
default :
// If METHODQUEST, don't update the activity here
continue ;
}
// We found an active task related to this item, so increment the done count
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Calling increment done count ForItem " ) ;
2018-07-09 14:01:52 -04:00
IncrementDoneCount ( c , Task , cur_task - > slot , j , Count ) ;
2013-02-16 16:14:39 -08:00
}
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
return ;
}
2018-07-05 00:13:19 -04:00
void ClientTaskState : : UpdateTasksOnExplore ( Client * c , int ExploreID )
{
2013-02-16 16:14:39 -08:00
// If the client has no tasks, there is nothing further to check.
2013-05-06 13:07:41 -04:00
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] ClientTaskState::UpdateTasksOnExplore(%i) " , ExploreID ) ;
2018-07-05 00:13:19 -04:00
if ( ! taskmanager | | ( ActiveTaskCount = = 0 & & ActiveTask . TaskID = = TASKSLOTEMPTY ) ) // could be better ...
return ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
// loop over the union of tasks and quests
for ( int i = 0 ; i < MAXACTIVEQUESTS + 1 ; i + + ) {
auto cur_task = & ActiveTasks [ i ] ;
if ( cur_task - > TaskID = = TASKSLOTEMPTY )
continue ;
2013-02-16 16:14:39 -08:00
// Check if there are any active explore activities for this task
2018-07-05 00:13:19 -04:00
TaskInformation * Task = taskmanager - > Tasks [ cur_task - > TaskID ] ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
if ( Task = = nullptr )
return ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
for ( int j = 0 ; j < Task - > ActivityCount ; j + + ) {
2013-02-16 16:14:39 -08:00
// We are not interested in completed or hidden activities
2018-07-05 00:13:19 -04:00
if ( cur_task - > Activity [ j ] . State ! = ActivityActive )
continue ;
2013-02-16 16:14:39 -08:00
// We are only interested in explore activities
2018-07-05 00:13:19 -04:00
if ( Task - > Activity [ j ] . Type ! = ActivityExplore )
continue ;
2018-08-01 13:50:09 -04:00
if ( ! Task - > Activity [ j ] . CheckZone ( zone - > GetZoneID ( ) ) ) {
2018-07-05 00:13:19 -04:00
Log ( Logs : : General , Logs : : Tasks ,
" [UPDATE] Char: %s Explore exploreid %i failed zone check " , c - > GetName ( ) ,
ExploreID ) ;
2013-02-16 16:14:39 -08:00
continue ;
}
// Is the activity to explore this area id ?
2018-07-05 00:13:19 -04:00
switch ( Task - > Activity [ j ] . GoalMethod ) {
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
case METHODSINGLEID :
if ( Task - > Activity [ j ] . GoalID ! = ExploreID )
continue ;
break ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
case METHODLIST :
if ( ! taskmanager - > GoalListManager . IsInList ( Task - > Activity [ j ] . GoalID , ExploreID ) )
2013-02-16 16:14:39 -08:00
continue ;
2018-07-05 00:13:19 -04:00
break ;
default :
// If METHODQUEST, don't update the activity here
continue ;
2013-02-16 16:14:39 -08:00
}
// We found an active task to explore this area, so set done count to goal count
// (Only a goal count of 1 makes sense for explore activities?)
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Increment on explore " ) ;
2018-07-09 14:01:52 -04:00
IncrementDoneCount ( c , Task , cur_task - > slot , j ,
2018-07-05 00:13:19 -04:00
Task - > Activity [ j ] . GoalCount - cur_task - > Activity [ j ] . DoneCount ) ;
2013-02-16 16:14:39 -08:00
}
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
return ;
}
2020-05-17 18:36:06 -07:00
bool ClientTaskState : : UpdateTasksOnDeliver ( Client * c , std : : list < EQ : : ItemInstance * > & Items , int Cash , int NPCTypeID )
2018-07-05 00:13:19 -04:00
{
2013-02-16 16:14:39 -08:00
bool Ret = false ;
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] ClientTaskState::UpdateTasksForOnDeliver(%d) " , NPCTypeID ) ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
if ( ! taskmanager | | ( ActiveTaskCount = = 0 & & ActiveTask . TaskID = = TASKSLOTEMPTY ) ) // could be better ...
return false ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
// loop over the union of tasks and quests
for ( int i = 0 ; i < MAXACTIVEQUESTS + 1 ; i + + ) {
auto cur_task = & ActiveTasks [ i ] ;
if ( cur_task - > TaskID = = TASKSLOTEMPTY )
continue ;
2013-02-16 16:14:39 -08:00
// Check if there are any active deliver activities for this task
2018-07-05 00:13:19 -04:00
TaskInformation * Task = taskmanager - > Tasks [ cur_task - > TaskID ] ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
if ( Task = = nullptr )
return false ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
for ( int j = 0 ; j < Task - > ActivityCount ; j + + ) {
2013-02-16 16:14:39 -08:00
// We are not interested in completed or hidden activities
2018-07-05 00:13:19 -04:00
if ( cur_task - > Activity [ j ] . State ! = ActivityActive )
continue ;
2013-02-16 16:14:39 -08:00
// We are only interested in Deliver activities
2018-07-05 00:13:19 -04:00
if ( Task - > Activity [ j ] . Type ! = ActivityDeliver & & Task - > Activity [ j ] . Type ! = ActivityGiveCash )
continue ;
2013-02-16 16:14:39 -08:00
// Is there a zone restriction on the activity ?
2018-08-01 13:50:09 -04:00
if ( ! Task - > Activity [ j ] . CheckZone ( zone - > GetZoneID ( ) ) ) {
2018-07-05 00:13:19 -04:00
Log ( Logs : : General , Logs : : Tasks ,
" [UPDATE] Char: %s Deliver activity failed zone check (current zone %i, need zone "
2018-08-01 13:50:09 -04:00
" %s " ,
c - > GetName ( ) , zone - > GetZoneID ( ) , Task - > Activity [ j ] . zones . c_str ( ) ) ;
2013-02-16 16:14:39 -08:00
continue ;
}
// Is the activity to deliver to this NPCTypeID ?
2018-07-05 00:13:19 -04:00
if ( Task - > Activity [ j ] . DeliverToNPC ! = NPCTypeID )
continue ;
2013-02-16 16:14:39 -08:00
// Is the activity related to these items ?
//
2018-07-05 00:13:19 -04:00
if ( ( Task - > Activity [ j ] . Type = = ActivityGiveCash ) & & Cash ) {
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Increment on GiveCash " ) ;
2013-02-16 16:14:39 -08:00
IncrementDoneCount ( c , Task , i , j , Cash ) ;
Ret = true ;
2018-07-05 00:13:19 -04:00
} else {
for ( auto & k : Items ) {
switch ( Task - > Activity [ j ] . GoalMethod ) {
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
case METHODSINGLEID :
if ( Task - > Activity [ j ] . GoalID ! = k - > GetID ( ) )
continue ;
break ;
2013-05-06 13:07:41 -04:00
2018-07-05 00:13:19 -04:00
case METHODLIST :
if ( ! taskmanager - > GoalListManager . IsInList ( Task - > Activity [ j ] . GoalID ,
k - > GetID ( ) ) )
2013-02-16 16:14:39 -08:00
continue ;
2018-07-05 00:13:19 -04:00
break ;
default :
// If METHODQUEST, don't update the activity here
continue ;
2013-02-16 16:14:39 -08:00
}
// We found an active task related to this item, so increment the done count
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Increment on GiveItem " ) ;
2018-07-09 14:01:52 -04:00
IncrementDoneCount ( c , Task , cur_task - > slot , j , k - > GetCharges ( ) < = 0 ? 1 : k - > GetCharges ( ) ) ;
2013-02-16 16:14:39 -08:00
Ret = true ;
}
}
}
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
return Ret ;
}
2018-07-05 00:13:19 -04:00
void ClientTaskState : : UpdateTasksOnTouch ( Client * c , int ZoneID )
{
2013-02-16 16:14:39 -08:00
// If the client has no tasks, there is nothing further to check.
2013-05-06 13:07:41 -04:00
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] ClientTaskState::UpdateTasksOnTouch(%i) " , ZoneID ) ;
2018-07-05 00:13:19 -04:00
if ( ! taskmanager | | ( ActiveTaskCount = = 0 & & ActiveTask . TaskID = = TASKSLOTEMPTY ) ) // could be better ...
return ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
// loop over the union of tasks and quests
for ( int i = 0 ; i < MAXACTIVEQUESTS + 1 ; i + + ) {
auto cur_task = & ActiveTasks [ i ] ;
if ( cur_task - > TaskID = = TASKSLOTEMPTY )
continue ;
2013-02-16 16:14:39 -08:00
// Check if there are any active explore activities for this task
2018-07-05 00:13:19 -04:00
TaskInformation * Task = taskmanager - > Tasks [ cur_task - > TaskID ] ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
if ( Task = = nullptr )
return ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
for ( int j = 0 ; j < Task - > ActivityCount ; j + + ) {
2013-02-16 16:14:39 -08:00
// We are not interested in completed or hidden activities
2018-07-05 00:13:19 -04:00
if ( cur_task - > Activity [ j ] . State ! = ActivityActive )
continue ;
2013-02-16 16:14:39 -08:00
// We are only interested in touch activities
2018-07-05 00:13:19 -04:00
if ( Task - > Activity [ j ] . Type ! = ActivityTouch )
continue ;
if ( Task - > Activity [ j ] . GoalMethod ! = METHODSINGLEID )
continue ;
2018-08-01 13:50:09 -04:00
if ( ! Task - > Activity [ j ] . CheckZone ( ZoneID ) ) {
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Char: %s Touch activity failed zone check " ,
2018-07-05 00:13:19 -04:00
c - > GetName ( ) ) ;
2013-02-16 16:14:39 -08:00
continue ;
}
// We found an active task to zone into this zone, so set done count to goal count
// (Only a goal count of 1 makes sense for touch activities?)
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Increment on Touch " ) ;
2018-07-09 14:01:52 -04:00
IncrementDoneCount ( c , Task , cur_task - > slot , j ,
2018-07-05 00:13:19 -04:00
Task - > Activity [ j ] . GoalCount - cur_task - > Activity [ j ] . DoneCount ) ;
2013-02-16 16:14:39 -08:00
}
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
return ;
}
2018-07-05 00:13:19 -04:00
void ClientTaskState : : IncrementDoneCount ( Client * c , TaskInformation * Task , int TaskIndex , int ActivityID , int Count ,
bool ignore_quest_update )
{
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] IncrementDoneCount " ) ;
2013-05-06 13:07:41 -04:00
2018-07-05 00:13:19 -04:00
auto info = GetClientTaskInfo ( Task - > type , TaskIndex ) ;
if ( info = = nullptr )
return ;
info - > Activity [ ActivityID ] . DoneCount + = Count ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
if ( info - > Activity [ ActivityID ] . DoneCount > Task - > Activity [ ActivityID ] . GoalCount )
info - > Activity [ ActivityID ] . DoneCount = Task - > Activity [ ActivityID ] . GoalCount ;
2013-02-16 16:14:39 -08:00
2014-12-29 05:35:50 -06:00
if ( ! ignore_quest_update ) {
char buf [ 24 ] ;
2018-07-05 00:13:19 -04:00
snprintf ( buf , 23 , " %d %d %d " , info - > Activity [ ActivityID ] . DoneCount , info - > Activity [ ActivityID ] . ActivityID , info - > TaskID ) ;
2014-12-29 05:35:50 -06:00
buf [ 23 ] = ' \0 ' ;
parse - > EventPlayer ( EVENT_TASK_UPDATE , c , buf , 0 ) ;
}
2013-02-16 16:14:39 -08:00
2018-07-05 00:13:19 -04:00
info - > Activity [ ActivityID ] . Updated = true ;
2013-02-16 16:14:39 -08:00
// Have we reached the goal count for this activity ?
2018-07-05 00:13:19 -04:00
if ( info - > Activity [ ActivityID ] . DoneCount > = Task - > Activity [ ActivityID ] . GoalCount ) {
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Done (%i) = Goal (%i) for Activity %i " ,
2018-07-05 00:13:19 -04:00
info - > Activity [ ActivityID ] . DoneCount ,
2013-05-09 10:44:08 -04:00
Task - > Activity [ ActivityID ] . GoalCount ,
ActivityID ) ;
2013-02-16 16:14:39 -08:00
// Flag the activity as complete
2018-07-05 00:13:19 -04:00
info - > Activity [ ActivityID ] . State = ActivityCompleted ;
2013-02-16 16:14:39 -08:00
// Unlock subsequent activities for this task
2018-07-05 00:13:19 -04:00
bool TaskComplete = UnlockActivities ( c - > CharacterID ( ) , * info ) ;
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] TaskCompleted is %i " , TaskComplete ) ;
2013-02-16 16:14:39 -08:00
// and by the 'Task Stage Completed' message
2018-07-05 00:13:19 -04:00
c - > SendTaskActivityComplete ( info - > TaskID , ActivityID , TaskIndex , Task - > type ) ;
2013-02-16 16:14:39 -08:00
// Send the updated task/activity list to the client
2018-07-08 00:29:46 -04:00
taskmanager - > SendSingleActiveTaskToClient ( c , * info , TaskComplete , false ) ;
2013-02-16 16:14:39 -08:00
// Inform the client the task has been updated, both by a chat message
2019-08-11 00:27:04 -05:00
c - > Message ( Chat : : White , " Your task '%s' has been updated. " , Task - > Title . c_str ( ) ) ;
2014-10-07 11:36:42 -07: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
if ( Task - > Activity [ ActivityID ] . GoalMethod ! = METHODQUEST ) {
2014-12-29 05:35:50 -06:00
if ( ! ignore_quest_update ) {
char buf [ 24 ] ;
2018-07-05 00:13:19 -04:00
snprintf ( buf , 23 , " %d %d " , info - > TaskID , info - > Activity [ ActivityID ] . ActivityID ) ;
2014-12-29 05:35:50 -06:00
buf [ 23 ] = ' \0 ' ;
parse - > EventPlayer ( EVENT_TASK_STAGE_COMPLETE , c , buf , 0 ) ;
}
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: PlayerLogTaskUpdates :: Update */
if ( RuleB ( QueryServ , PlayerLogTaskUpdates ) ) {
2018-07-05 00:13:19 -04:00
std : : string event_desc = StringFormat ( " Task Stage Complete :: taskid:%i activityid:%i donecount:%i in zoneid:%i instid:%i " , info - > TaskID , info - > Activity [ ActivityID ] . ActivityID , info - > Activity [ ActivityID ] . DoneCount , c - > GetZoneID ( ) , c - > GetInstanceID ( ) ) ;
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
QServ - > PlayerLogEvent ( Player_Log_Task_Updates , c - > CharacterID ( ) , event_desc ) ;
}
2014-03-31 22:03:11 -05:00
}
2014-10-07 11:36:42 -07:00
2013-02-16 16:14:39 -08:00
// If this task is now complete, the Completed tasks will have been
// updated in UnlockActivities. Send the completed task list to the
// client. This is the same sequence the packets are sent on live.
if ( TaskComplete ) {
char buf [ 24 ] ;
2018-07-05 00:13:19 -04:00
snprintf ( buf , 23 , " %d %d %d " , info - > Activity [ ActivityID ] . DoneCount , info - > Activity [ ActivityID ] . ActivityID , info - > TaskID ) ;
2013-02-16 16:14:39 -08:00
buf [ 23 ] = ' \0 ' ;
parse - > EventPlayer ( EVENT_TASK_COMPLETE , c , buf , 0 ) ;
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: PlayerLogTaskUpdates :: Complete */
if ( RuleB ( QueryServ , PlayerLogTaskUpdates ) ) {
2018-07-05 00:13:19 -04:00
std : : string event_desc = StringFormat ( " Task Complete :: taskid:%i activityid:%i donecount:%i in zoneid:%i instid:%i " , info - > TaskID , info - > Activity [ ActivityID ] . ActivityID , info - > Activity [ ActivityID ] . DoneCount , c - > GetZoneID ( ) , c - > GetInstanceID ( ) ) ;
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
QServ - > PlayerLogEvent ( Player_Log_Task_Updates , c - > CharacterID ( ) , event_desc ) ;
}
2013-02-16 16:14:39 -08:00
taskmanager - > SendCompletedTasksToClient ( c , this ) ;
2018-07-05 00:13:19 -04:00
c - > SendTaskActivityComplete ( info - > TaskID , 0 , TaskIndex , Task - > type , 0 ) ;
2013-02-16 16:14:39 -08:00
taskmanager - > SaveClientState ( c , this ) ;
//c->SendTaskComplete(TaskIndex);
2018-07-05 00:13:19 -04:00
c - > CancelTask ( TaskIndex , Task - > type ) ;
2013-02-16 16:14:39 -08:00
//if(Task->RewardMethod != METHODQUEST) RewardTask(c, Task);
// If Experience and/or cash rewards are set, reward them from the task even if RewardMethod is METHODQUEST
RewardTask ( c , Task ) ;
//RemoveTask(c, TaskIndex);
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
}
2018-07-08 00:29:46 -04:00
} else {
2013-02-16 16:14:39 -08:00
// Send an update packet for this single activity
2018-07-08 00:29:46 -04:00
taskmanager - > SendTaskActivityLong ( c , info - > TaskID , ActivityID , TaskIndex ,
Task - > Activity [ ActivityID ] . Optional ) ;
}
2013-02-16 16:14:39 -08:00
}
void ClientTaskState : : RewardTask ( Client * c , TaskInformation * Task ) {
if ( ! Task | | ! c ) return ;
2020-05-17 18:36:06 -07:00
const EQ : : ItemData * Item ;
2013-05-22 16:17:19 -04:00
std : : vector < int > RewardList ;
2013-02-16 16:14:39 -08:00
switch ( Task - > RewardMethod ) {
case METHODSINGLEID :
{
if ( Task - > RewardID ) {
c - > SummonItem ( Task - > RewardID ) ;
Item = database . GetItem ( Task - > RewardID ) ;
if ( Item )
2019-08-11 00:00:55 -05:00
c - > Message ( Chat : : Yellow , " You receive %s as a reward. " , Item - > Name ) ;
2013-02-16 16:14:39 -08:00
}
break ;
}
2013-05-06 13:07:41 -04:00
case METHODLIST :
2013-02-16 16:14:39 -08:00
{
RewardList = taskmanager - > GoalListManager . GetListContents ( Task - > RewardID ) ;
for ( unsigned int i = 0 ; i < RewardList . size ( ) ; i + + ) {
c - > SummonItem ( RewardList [ i ] ) ;
Item = database . GetItem ( RewardList [ i ] ) ;
if ( Item )
2019-08-11 00:00:55 -05:00
c - > Message ( Chat : : Yellow , " You receive %s as a reward. " , Item - > Name ) ;
2013-02-16 16:14:39 -08:00
}
break ;
}
default :
{
// Nothing special done for METHODQUEST
break ;
}
}
2018-07-08 21:40:04 -04:00
2018-07-20 14:06:07 -04:00
if ( ! Task - > completion_emote . empty ( ) )
2019-08-11 00:00:55 -05:00
c - > SendColoredText ( Chat : : Yellow , Task - > completion_emote ) ; // unsure if they use this packet or color, should work
2018-07-20 14:06:07 -04:00
2018-07-08 21:40:04 -04:00
// just use normal NPC faction ID stuff
if ( Task - > faction_reward )
c - > SetFactionLevel ( c - > CharacterID ( ) , Task - > faction_reward , c - > GetBaseClass ( ) , c - > GetBaseRace ( ) , c - > GetDeity ( ) ) ;
2013-02-16 16:14:39 -08:00
if ( Task - > CashReward ) {
int Plat , Gold , Silver , Copper ;
Copper = Task - > CashReward ;
c - > AddMoneyToPP ( Copper , true ) ;
Plat = Copper / 1000 ;
Copper = Copper - ( Plat * 1000 ) ;
Gold = Copper / 100 ;
Copper = Copper - ( Gold * 100 ) ;
Silver = Copper / 10 ;
Copper = Copper - ( Silver * 10 ) ;
2013-05-22 16:17:19 -04:00
std : : string CashMessage ;
2013-02-16 16:14:39 -08:00
if ( Plat > 0 ) {
CashMessage = " You receive " ;
CashMessage + = itoa ( Plat ) ;
CashMessage + = " platinum " ;
}
if ( Gold > 0 ) {
if ( CashMessage . length ( ) = = 0 ) {
CashMessage = " You receive " ;
}
else {
CashMessage + = " , " ;
}
CashMessage + = itoa ( Gold ) ;
CashMessage + = " gold " ;
}
if ( Silver > 0 ) {
if ( CashMessage . length ( ) = = 0 ) {
CashMessage = " You receive " ;
}
else {
CashMessage + = " , " ;
}
CashMessage + = itoa ( Silver ) ;
CashMessage + = " silver " ;
}
if ( Copper > 0 ) {
if ( CashMessage . length ( ) = = 0 ) {
CashMessage = " You receive " ;
}
else {
CashMessage + = " , " ;
}
CashMessage + = itoa ( Copper ) ;
CashMessage + = " copper " ;
}
CashMessage + = " pieces. " ;
2019-08-11 00:00:55 -05:00
c - > Message ( Chat : : Yellow , CashMessage . c_str ( ) ) ;
2013-02-16 16:14:39 -08:00
}
int32 EXPReward = Task - > XPReward ;
if ( EXPReward > 0 ) {
c - > AddEXP ( EXPReward ) ;
}
if ( EXPReward < 0 ) {
uint32 PosReward = EXPReward * - 1 ;
// Minimal Level Based Exp Reward Setting is 101 (1% exp at level 1)
if ( PosReward > 100 & & PosReward < 25700 ) {
uint8 MaxLevel = PosReward / 100 ;
uint8 ExpPercent = PosReward - ( MaxLevel * 100 ) ;
c - > AddLevelBasedExp ( ExpPercent , MaxLevel ) ;
}
}
c - > SendSound ( ) ;
}
2018-06-16 22:42:26 -04:00
bool ClientTaskState : : IsTaskActive ( int TaskID )
{
2018-07-08 00:29:46 -04:00
if ( ActiveTask . TaskID = = TaskID )
return true ;
2018-06-16 22:42:26 -04:00
if ( ActiveTaskCount = = 0 | | TaskID = = 0 )
return false ;
2013-02-16 16:14:39 -08:00
2018-06-16 22:42:26 -04:00
for ( int i = 0 ; i < MAXACTIVEQUESTS ; i + + ) {
if ( ActiveQuests [ i ] . TaskID = = TaskID )
return true ;
2013-02-16 16:14:39 -08:00
}
return false ;
}
2018-07-05 00:45:51 -04:00
void ClientTaskState : : FailTask ( Client * c , int TaskID )
{
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] FailTask %i, ActiveTaskCount is %i " , TaskID , ActiveTaskCount ) ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:45:51 -04:00
if ( ActiveTask . TaskID = = TaskID ) {
c - > SendTaskFailed ( TaskID , 0 , TaskType : : Task ) ;
// Remove the task from the client
c - > CancelTask ( 0 , TaskType : : Task ) ;
return ;
}
2013-02-16 16:14:39 -08:00
2018-07-05 00:45:51 -04:00
// TODO: shared tasks
if ( ActiveTaskCount = = 0 )
return ;
for ( int i = 0 ; i < MAXACTIVEQUESTS ; i + + ) {
if ( ActiveQuests [ i ] . TaskID = = TaskID ) {
c - > SendTaskFailed ( ActiveQuests [ i ] . TaskID , i , TaskType : : Quest ) ;
2013-02-16 16:14:39 -08:00
// Remove the task from the client
2018-07-05 00:45:51 -04:00
c - > CancelTask ( i , TaskType : : Quest ) ;
2013-02-16 16:14:39 -08:00
return ;
}
}
}
2018-07-08 00:29:46 -04:00
// TODO: Shared tasks
bool ClientTaskState : : IsTaskActivityActive ( int TaskID , int ActivityID )
{
2013-02-16 16:14:39 -08:00
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] ClientTaskState IsTaskActivityActive(%i, %i). " , TaskID , ActivityID ) ;
2013-02-16 16:14:39 -08:00
// Quick sanity check
2018-07-08 00:29:46 -04:00
if ( ActivityID < 0 )
return false ;
if ( ActiveTaskCount = = 0 & & ActiveTask . TaskID = = TASKSLOTEMPTY )
return false ;
2013-02-16 16:14:39 -08:00
int ActiveTaskIndex = - 1 ;
2018-07-08 00:29:46 -04:00
auto type = TaskType : : Task ;
2013-02-16 16:14:39 -08:00
2018-07-08 00:29:46 -04:00
if ( ActiveTask . TaskID = = TaskID )
ActiveTaskIndex = 0 ;
if ( ActiveTaskIndex = = - 1 ) {
for ( int i = 0 ; i < MAXACTIVEQUESTS ; i + + ) {
if ( ActiveQuests [ i ] . TaskID = = TaskID ) {
ActiveTaskIndex = i ;
type = TaskType : : Quest ;
break ;
}
2013-02-16 16:14:39 -08:00
}
}
// The client does not have this task
2018-07-08 00:29:46 -04:00
if ( ActiveTaskIndex = = - 1 )
return false ;
2013-02-16 16:14:39 -08:00
2018-07-08 00:29:46 -04:00
auto info = GetClientTaskInfo ( type , ActiveTaskIndex ) ;
2013-02-16 16:14:39 -08:00
2018-07-08 00:29:46 -04:00
if ( info = = nullptr )
return false ;
2013-02-16 16:14:39 -08:00
2018-07-08 00:29:46 -04:00
TaskInformation * Task = taskmanager - > Tasks [ info - > TaskID ] ;
2013-02-16 16:14:39 -08:00
2018-07-08 00:29:46 -04:00
// The task is invalid
if ( Task = = nullptr )
return false ;
2013-02-16 16:14:39 -08:00
2018-07-08 00:29:46 -04:00
// The ActivityID is out of range
if ( ActivityID > = Task - > ActivityCount )
return false ;
2013-02-16 16:14:39 -08:00
2018-07-08 00:29:46 -04:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] ClientTaskState IsTaskActivityActive(%i, %i). State is %i " , TaskID ,
ActivityID , info - > Activity [ ActivityID ] . State ) ;
2013-02-16 16:14:39 -08:00
2018-07-08 00:29:46 -04:00
return ( info - > Activity [ ActivityID ] . State = = ActivityActive ) ;
2013-02-16 16:14:39 -08:00
}
2014-12-29 05:35:50 -06:00
void ClientTaskState : : UpdateTaskActivity ( Client * c , int TaskID , int ActivityID , int Count , bool ignore_quest_update /*= false*/ )
{
2013-02-16 16:14:39 -08:00
2018-07-08 00:29:46 -04:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] ClientTaskState UpdateTaskActivity(%i, %i, %i). " , TaskID , ActivityID ,
Count ) ;
2013-02-16 16:14:39 -08:00
// Quick sanity check
2018-07-08 00:29:46 -04:00
if ( ActivityID < 0 | | ( ActiveTaskCount = = 0 & & ActiveTask . TaskID = = TASKSLOTEMPTY ) )
return ;
2013-02-16 16:14:39 -08:00
int ActiveTaskIndex = - 1 ;
2018-07-08 00:29:46 -04:00
auto type = TaskType : : Task ;
2013-02-16 16:14:39 -08:00
2018-07-08 00:29:46 -04:00
if ( ActiveTask . TaskID = = TaskID )
ActiveTaskIndex = 0 ;
if ( ActiveTaskIndex = = - 1 ) {
for ( int i = 0 ; i < MAXACTIVEQUESTS ; i + + ) {
if ( ActiveQuests [ i ] . TaskID = = TaskID ) {
ActiveTaskIndex = i ;
type = TaskType : : Quest ;
break ;
}
2013-02-16 16:14:39 -08:00
}
}
// The client does not have this task
2018-07-08 00:29:46 -04:00
if ( ActiveTaskIndex = = - 1 )
return ;
auto info = GetClientTaskInfo ( type , ActiveTaskIndex ) ;
if ( info = = nullptr )
return ;
2013-02-16 16:14:39 -08:00
2018-07-08 00:29:46 -04:00
TaskInformation * Task = taskmanager - > Tasks [ info - > TaskID ] ;
2013-02-16 16:14:39 -08:00
// The task is invalid
2018-07-08 00:29:46 -04:00
if ( Task = = nullptr )
return ;
2013-02-16 16:14:39 -08:00
// The ActivityID is out of range
2018-07-08 00:29:46 -04:00
if ( ActivityID > = Task - > ActivityCount )
return ;
2013-02-16 16:14:39 -08:00
// The Activity is not currently active
2018-07-08 00:29:46 -04:00
if ( info - > Activity [ ActivityID ] . State ! = ActivityActive )
return ;
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Increment done count on UpdateTaskActivity " ) ;
2014-12-29 05:35:50 -06:00
IncrementDoneCount ( c , Task , ActiveTaskIndex , ActivityID , Count , ignore_quest_update ) ;
2013-02-16 16:14:39 -08:00
}
2018-07-08 00:29:46 -04:00
void ClientTaskState : : ResetTaskActivity ( Client * c , int TaskID , int ActivityID )
{
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] ClientTaskState UpdateTaskActivity(%i, %i, 0). " , TaskID , ActivityID ) ;
2013-02-16 16:14:39 -08:00
// Quick sanity check
2018-07-08 00:29:46 -04:00
if ( ActivityID < 0 | | ( ActiveTaskCount = = 0 & & ActiveTask . TaskID = = TASKSLOTEMPTY ) )
return ;
2013-02-16 16:14:39 -08:00
int ActiveTaskIndex = - 1 ;
2018-07-08 00:29:46 -04:00
auto type = TaskType : : Task ;
2013-02-16 16:14:39 -08:00
2018-07-08 00:29:46 -04:00
if ( ActiveTask . TaskID = = TaskID )
ActiveTaskIndex = 0 ;
if ( ActiveTaskIndex = = - 1 ) {
for ( int i = 0 ; i < MAXACTIVEQUESTS ; i + + ) {
if ( ActiveQuests [ i ] . TaskID = = TaskID ) {
ActiveTaskIndex = i ;
type = TaskType : : Quest ;
break ;
}
2013-02-16 16:14:39 -08:00
}
}
// The client does not have this task
2018-07-08 00:29:46 -04:00
if ( ActiveTaskIndex = = - 1 )
return ;
2013-02-16 16:14:39 -08:00
2018-07-08 00:29:46 -04:00
auto info = GetClientTaskInfo ( type , ActiveTaskIndex ) ;
if ( info = = nullptr )
return ;
TaskInformation * Task = taskmanager - > Tasks [ info - > TaskID ] ;
2013-02-16 16:14:39 -08:00
// The task is invalid
2018-07-08 00:29:46 -04:00
if ( Task = = nullptr )
return ;
2013-02-16 16:14:39 -08:00
// The ActivityID is out of range
2018-07-08 00:29:46 -04:00
if ( ActivityID > = Task - > ActivityCount )
return ;
2013-02-16 16:14:39 -08:00
// The Activity is not currently active
2018-07-08 00:29:46 -04:00
if ( info - > Activity [ ActivityID ] . State ! = ActivityActive )
return ;
2013-02-16 16:14:39 -08:00
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] ResetTaskActivityCount " ) ;
2013-02-16 16:14:39 -08:00
2018-07-08 00:29:46 -04:00
info - > Activity [ ActivityID ] . DoneCount = 0 ;
2013-02-16 16:14:39 -08:00
2018-07-08 00:29:46 -04:00
info - > Activity [ ActivityID ] . Updated = true ;
2013-02-16 16:14:39 -08:00
// Send an update packet for this single activity
2018-07-08 00:29:46 -04:00
taskmanager - > SendTaskActivityLong ( c , info - > TaskID , ActivityID , ActiveTaskIndex ,
Task - > Activity [ ActivityID ] . Optional ) ;
2013-02-16 16:14:39 -08:00
}
2018-07-08 23:32:30 -04:00
void ClientTaskState : : ShowClientTasks ( Client * c )
{
2019-08-11 00:27:04 -05:00
c - > Message ( Chat : : White , " Task Information: " ) ;
2018-07-08 23:32:30 -04:00
if ( ActiveTask . TaskID ! = TASKSLOTEMPTY ) {
2019-08-11 00:27:04 -05:00
c - > Message ( Chat : : White , " Task: %i %s " , ActiveTask . TaskID , taskmanager - > Tasks [ ActiveTask . TaskID ] - > Title . c_str ( ) ) ;
c - > Message ( Chat : : White , " Description: [%s] \n " , taskmanager - > Tasks [ ActiveTask . TaskID ] - > Description . c_str ( ) ) ;
2018-07-08 23:32:30 -04:00
for ( int j = 0 ; j < taskmanager - > GetActivityCount ( ActiveTask . TaskID ) ; j + + ) {
2019-08-11 00:27:04 -05:00
c - > Message ( Chat : : White , " Activity: %2d, DoneCount: %2d, Status: %d (0=Hidden, 1=Active, 2=Complete) " ,
2018-07-08 23:32:30 -04:00
ActiveTask . Activity [ j ] . ActivityID , ActiveTask . Activity [ j ] . DoneCount ,
ActiveTask . Activity [ j ] . State ) ;
}
}
for ( int i = 0 ; i < MAXACTIVEQUESTS ; i + + ) {
if ( ActiveQuests [ i ] . TaskID = = TASKSLOTEMPTY )
2013-02-16 16:14:39 -08:00
continue ;
2019-08-11 00:27:04 -05:00
c - > Message ( Chat : : White , " Quest: %i %s " , ActiveQuests [ i ] . TaskID ,
2018-07-08 23:32:30 -04:00
taskmanager - > Tasks [ ActiveQuests [ i ] . TaskID ] - > Title . c_str ( ) ) ;
2019-08-11 00:27:04 -05:00
c - > Message ( Chat : : White , " Description: [%s] \n " , taskmanager - > Tasks [ ActiveQuests [ i ] . TaskID ] - > Description . c_str ( ) ) ;
2018-07-08 23:32:30 -04:00
for ( int j = 0 ; j < taskmanager - > GetActivityCount ( ActiveQuests [ i ] . TaskID ) ; j + + ) {
2019-08-11 00:27:04 -05:00
c - > Message ( Chat : : White , " Activity: %2d, DoneCount: %2d, Status: %d (0=Hidden, 1=Active, 2=Complete) " ,
2018-07-08 23:32:30 -04:00
ActiveQuests [ i ] . Activity [ j ] . ActivityID , ActiveQuests [ i ] . Activity [ j ] . DoneCount ,
ActiveQuests [ i ] . Activity [ j ] . State ) ;
2013-02-16 16:14:39 -08:00
}
}
}
2018-07-08 16:36:00 -04:00
// TODO: Shared Task
int ClientTaskState : : TaskTimeLeft ( int TaskID )
{
if ( ActiveTask . TaskID = = TaskID ) {
int Now = time ( nullptr ) ;
2013-02-16 16:14:39 -08:00
2018-07-08 16:36:00 -04:00
TaskInformation * Task = taskmanager - > Tasks [ TaskID ] ;
2013-02-16 16:14:39 -08:00
2018-07-08 16:36:00 -04:00
if ( Task = = nullptr )
return - 1 ;
if ( ! Task - > Duration )
return - 1 ;
int TimeLeft = ( ActiveTask . AcceptedTime + Task - > Duration - Now ) ;
return ( TimeLeft > 0 ? TimeLeft : 0 ) ;
}
if ( ActiveTaskCount = = 0 )
return - 1 ;
2013-02-16 16:14:39 -08:00
2018-07-08 16:36:00 -04:00
for ( int i = 0 ; i < MAXACTIVEQUESTS ; i + + ) {
if ( ActiveQuests [ i ] . TaskID ! = TaskID )
continue ;
2013-02-16 16:14:39 -08:00
2013-05-04 18:06:58 -07:00
int Now = time ( nullptr ) ;
2013-02-16 16:14:39 -08:00
2018-07-08 16:36:00 -04:00
TaskInformation * Task = taskmanager - > Tasks [ ActiveQuests [ i ] . TaskID ] ;
2013-02-16 16:14:39 -08:00
2018-07-08 16:36:00 -04:00
if ( Task = = nullptr )
return - 1 ;
2013-02-16 16:14:39 -08:00
2018-07-08 16:36:00 -04:00
if ( ! Task - > Duration )
return - 1 ;
2013-02-16 16:14:39 -08:00
2018-06-13 18:06:08 -04:00
int TimeLeft = ( ActiveQuests [ i ] . AcceptedTime + Task - > Duration - Now ) ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// If Timeleft is negative, return 0, else return the number of seconds left
2013-05-06 13:07:41 -04:00
2018-07-08 16:36:00 -04:00
return ( TimeLeft > 0 ? TimeLeft : 0 ) ;
2013-02-16 16:14:39 -08:00
}
return - 1 ;
}
int ClientTaskState : : IsTaskCompleted ( int TaskID ) {
// Returns: -1 if RecordCompletedTasks is not true
2013-05-09 10:44:08 -04:00
// +1 if the task has been completed
// 0 if the task has not been completed
2013-02-16 16:14:39 -08:00
if ( ! ( RuleB ( TaskSystem , RecordCompletedTasks ) ) ) return - 1 ;
for ( unsigned int i = 0 ; i < CompletedTasks . size ( ) ; i + + ) {
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Comparing completed task %i with %i " , CompletedTasks [ i ] . TaskID , TaskID ) ;
2013-02-16 16:14:39 -08:00
if ( CompletedTasks [ i ] . TaskID = = TaskID ) return 1 ;
}
return 0 ;
}
2013-05-09 10:44:08 -04:00
bool TaskManager : : IsTaskRepeatable ( int TaskID ) {
2013-02-16 16:14:39 -08:00
if ( ( TaskID < = 0 ) | | ( TaskID > = MAXTASKS ) ) return false ;
TaskInformation * Task = taskmanager - > Tasks [ TaskID ] ;
2013-05-04 18:06:58 -07:00
if ( Task = = nullptr ) return false ;
2013-02-16 16:14:39 -08:00
return Task - > Repeatable ;
}
2018-07-05 00:45:51 -04:00
bool ClientTaskState : : TaskOutOfTime ( TaskType type , int Index )
{
2013-02-16 16:14:39 -08:00
// Returns true if the Task in the specified slot has a time limit that has been exceeded.
2018-07-05 00:45:51 -04:00
auto info = GetClientTaskInfo ( type , Index ) ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:45:51 -04:00
if ( info = = nullptr )
return false ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:45:51 -04:00
// make sure the TaskID is at least maybe in our array
if ( info - > TaskID < = 0 | | info - > TaskID > = MAXTASKS )
return false ;
2013-02-16 16:14:39 -08:00
2013-05-04 18:06:58 -07:00
int Now = time ( nullptr ) ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:45:51 -04:00
TaskInformation * Task = taskmanager - > Tasks [ info - > TaskID ] ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:45:51 -04:00
if ( Task = = nullptr )
return false ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:45:51 -04:00
return ( Task - > Duration & & ( info - > AcceptedTime + Task - > Duration < = Now ) ) ;
2013-02-16 16:14:39 -08:00
}
2018-07-05 00:45:51 -04:00
void ClientTaskState : : TaskPeriodicChecks ( Client * c )
{
2018-07-08 16:36:00 -04:00
if ( ActiveTask . TaskID ! = TASKSLOTEMPTY ) {
2018-07-05 00:45:51 -04:00
if ( TaskOutOfTime ( TaskType : : Task , 0 ) ) {
// Send Red Task Failed Message
c - > SendTaskFailed ( ActiveTask . TaskID , 0 , TaskType : : Task ) ;
// Remove the task from the client
c - > CancelTask ( 0 , TaskType : : Task ) ;
// It is a conscious decision to only fail one task per call to this method,
// otherwise the player will not see all the failed messages where multiple
// tasks fail at the same time.
return ;
}
}
// TODO: shared tasks -- although that will probably be manager in world checking and telling zones to fail us
2013-02-16 16:14:39 -08:00
2018-07-05 00:45:51 -04:00
if ( ActiveTaskCount = = 0 )
return ;
2013-02-16 16:14:39 -08:00
// Check for tasks that have failed because they have not been completed in the specified time
//
2018-07-05 00:45:51 -04:00
for ( int i = 0 ; i < MAXACTIVEQUESTS ; i + + ) {
if ( ActiveQuests [ i ] . TaskID = = TASKSLOTEMPTY )
continue ;
2013-02-16 16:14:39 -08:00
2018-07-05 00:45:51 -04:00
if ( TaskOutOfTime ( TaskType : : Quest , i ) ) {
2013-02-16 16:14:39 -08:00
// Send Red Task Failed Message
2018-07-05 00:45:51 -04:00
c - > SendTaskFailed ( ActiveQuests [ i ] . TaskID , i , TaskType : : Quest ) ;
2013-02-16 16:14:39 -08:00
// Remove the task from the client
2018-07-05 00:45:51 -04:00
c - > CancelTask ( i , TaskType : : Quest ) ;
2013-02-16 16:14:39 -08:00
// It is a conscious decision to only fail one task per call to this method,
// otherwise the player will not see all the failed messages where multiple
2013-05-06 13:07:41 -04:00
// tasks fail at the same time.
2013-02-16 16:14:39 -08:00
break ;
}
}
// Check for activities that require zoning into a specific zone.
// This is done in this method because it gives an extra few seconds for the client screen to display
// the zone before we send the 'Task Activity Completed' message.
//
2018-07-05 00:45:51 -04:00
if ( ! CheckedTouchActivities ) {
2013-02-16 16:14:39 -08:00
UpdateTasksOnTouch ( c , zone - > GetZoneID ( ) ) ;
CheckedTouchActivities = true ;
}
}
#if 0
void Client : : SendTaskComplete ( int TaskIndex ) {
// 0x4c8c
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
TaskComplete_Struct * tcs ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
EQApplicationPacket * outapp = new EQApplicationPacket ( OP_TaskComplete , sizeof ( TaskComplete_Struct ) ) ;
tcs = ( TaskComplete_Struct * ) outapp - > pBuffer ;
// I have seen unknown0 as non-zero. It always seems to match the value in the first word of the
// Task Activity Complete packet sent immediately prior to it.
2013-05-06 13:07:41 -04:00
//tcs->unknown00 = 0x00000000;
2013-02-16 16:14:39 -08:00
tcs - > unknown00 = TaskIndex ;
// I have only seen 0x00000002 in the next field. This is a common 'unknown' value in the task packets.
// I suspect this is the type field to indicate this is a quest task, as opposed to other types.
2013-05-06 13:07:41 -04:00
tcs - > unknown04 = 0x00000002 ;
2013-02-16 16:14:39 -08:00
2015-01-18 02:20:16 -06:00
Log . LogDebugType ( Logs : : Detail , Logs : : Tasks , " SendTasksComplete " ) ;
2013-02-16 16:14:39 -08:00
DumpPacket ( outapp ) ; fflush ( stdout ) ;
QueuePacket ( outapp ) ;
safe_delete ( outapp ) ;
}
# endif
void ClientTaskState : : SendTaskHistory ( Client * c , int TaskIndex ) {
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Task History Requested for Completed Task Index %i " , TaskIndex ) ;
2013-02-16 16:14:39 -08:00
// We only sent the most recent 50 completed tasks, so we need to offset the Index the client sent to us.
int AdjustedTaskIndex = TaskIndex ;
if ( CompletedTasks . size ( ) > 50 )
AdjustedTaskIndex + = ( CompletedTasks . size ( ) - 50 ) ;
if ( ( AdjustedTaskIndex < 0 ) | | ( AdjustedTaskIndex > = ( int ) CompletedTasks . size ( ) ) ) return ;
int TaskID = CompletedTasks [ AdjustedTaskIndex ] . TaskID ;
if ( ( TaskID < 0 ) | | ( TaskID > MAXTASKS ) ) return ;
TaskInformation * Task = taskmanager - > Tasks [ TaskID ] ;
2013-05-04 18:06:58 -07:00
if ( Task = = nullptr ) return ;
2013-02-16 16:14:39 -08:00
TaskHistoryReplyHeader_Struct * ths ;
TaskHistoryReplyData1_Struct * thd1 ;
TaskHistoryReplyData2_Struct * thd2 ;
char * Ptr ;
int CompletedActivityCount = 0 ; ;
int PacketLength = sizeof ( TaskHistoryReplyHeader_Struct ) ;
for ( int i = 0 ; i < Task - > ActivityCount ; i + + ) {
if ( CompletedTasks [ AdjustedTaskIndex ] . ActivityDone [ i ] ) {
CompletedActivityCount + + ;
PacketLength = PacketLength + sizeof ( TaskHistoryReplyData1_Struct ) +
2018-07-08 16:55:08 -04:00
Task - > Activity [ i ] . target_name . size ( ) + 1 +
Task - > Activity [ i ] . item_list . size ( ) + 1 +
2013-05-09 10:44:08 -04:00
sizeof ( TaskHistoryReplyData2_Struct ) +
2018-07-08 16:55:08 -04:00
Task - > Activity [ i ] . desc_override . size ( ) + 1 ;
2013-02-16 16:14:39 -08:00
}
}
2013-05-06 13:07:41 -04:00
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket ( OP_TaskHistoryReply , PacketLength ) ;
2013-02-16 16:14:39 -08:00
ths = ( TaskHistoryReplyHeader_Struct * ) outapp - > pBuffer ;
// We use the TaskIndex the client sent in the request
ths - > TaskID = TaskIndex ;
ths - > ActivityCount = CompletedActivityCount ;
Ptr = ( char * ) ths + sizeof ( TaskHistoryReplyHeader_Struct ) ;
for ( int i = 0 ; i < Task - > ActivityCount ; i + + ) {
if ( CompletedTasks [ AdjustedTaskIndex ] . ActivityDone [ i ] ) {
thd1 = ( TaskHistoryReplyData1_Struct * ) Ptr ;
thd1 - > ActivityType = Task - > Activity [ i ] . Type ;
Ptr = ( char * ) thd1 + sizeof ( TaskHistoryReplyData1_Struct ) ;
2018-07-08 16:55:08 -04:00
VARSTRUCT_ENCODE_STRING ( Ptr , Task - > Activity [ i ] . target_name . c_str ( ) ) ;
VARSTRUCT_ENCODE_STRING ( Ptr , Task - > Activity [ i ] . item_list . c_str ( ) ) ;
2013-02-16 16:14:39 -08:00
thd2 = ( TaskHistoryReplyData2_Struct * ) Ptr ;
thd2 - > GoalCount = Task - > Activity [ i ] . GoalCount ;
thd2 - > unknown04 = 0xffffffff ;
thd2 - > unknown08 = 0xffffffff ;
2018-08-01 13:50:09 -04:00
thd2 - > ZoneID = Task - > Activity [ i ] . ZoneIDs . empty ( ) ? 0 : Task - > Activity [ i ] . ZoneIDs . front ( ) ;
2013-02-16 16:14:39 -08:00
thd2 - > unknown16 = 0x00000000 ;
Ptr = ( char * ) thd2 + sizeof ( TaskHistoryReplyData2_Struct ) ;
2018-07-08 16:55:08 -04:00
VARSTRUCT_ENCODE_STRING ( Ptr , Task - > Activity [ i ] . desc_override . c_str ( ) ) ;
2013-02-16 16:14:39 -08:00
}
}
c - > QueuePacket ( outapp ) ;
safe_delete ( outapp ) ;
}
2018-07-05 00:13:19 -04:00
void Client : : SendTaskActivityComplete ( int TaskID , int ActivityID , int TaskIndex , TaskType type , int TaskIncomplete )
{
2013-02-16 16:14:39 -08:00
// 0x54eb
TaskActivityComplete_Struct * tac ;
2013-05-06 13:07:41 -04:00
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket ( OP_TaskActivityComplete , sizeof ( TaskActivityComplete_Struct ) ) ;
2013-02-16 16:14:39 -08:00
tac = ( TaskActivityComplete_Struct * ) outapp - > pBuffer ;
tac - > TaskIndex = TaskIndex ;
2018-07-05 00:13:19 -04:00
tac - > TaskType = static_cast < uint32 > ( type ) ;
tac - > TaskID = TaskID ;
2013-02-16 16:14:39 -08:00
tac - > ActivityID = ActivityID ;
2018-07-05 00:13:19 -04:00
tac - > task_completed = 0x00000001 ;
tac - > stage_complete = TaskIncomplete ;
2013-02-16 16:14:39 -08:00
QueuePacket ( outapp ) ;
safe_delete ( outapp ) ;
}
2018-07-05 00:13:19 -04:00
void Client : : SendTaskFailed ( int TaskID , int TaskIndex , TaskType type )
{
2013-02-16 16:14:39 -08:00
// 0x54eb
char buf [ 24 ] ;
snprintf ( buf , 23 , " %d " , TaskID ) ;
buf [ 23 ] = ' \0 ' ;
parse - > EventPlayer ( EVENT_TASK_FAIL , this , buf , 0 ) ;
TaskActivityComplete_Struct * tac ;
2013-05-06 13:07:41 -04:00
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket ( OP_TaskActivityComplete , sizeof ( TaskActivityComplete_Struct ) ) ;
2013-02-16 16:14:39 -08:00
tac = ( TaskActivityComplete_Struct * ) outapp - > pBuffer ;
tac - > TaskIndex = TaskIndex ;
2018-07-05 00:13:19 -04:00
tac - > TaskType = static_cast < uint32 > ( type ) ;
tac - > TaskID = TaskID ;
2013-02-16 16:14:39 -08:00
tac - > ActivityID = 0 ;
2018-07-05 00:13:19 -04:00
tac - > task_completed = 0 ; //Fail
tac - > stage_complete = 0 ; // 0 for task complete or failed.
2013-02-16 16:14:39 -08:00
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] TaskFailed " ) ;
2013-02-16 16:14:39 -08:00
QueuePacket ( outapp ) ;
safe_delete ( outapp ) ;
}
void TaskManager : : SendCompletedTasksToClient ( Client * c , ClientTaskState * State ) {
int PacketLength = 4 ;
//vector<CompletedTaskInformation>::const_iterator iterator;
// The client only display the first 50 Completed Tasks send, so send the 50 most recent
int FirstTaskToSend = 0 ;
int LastTaskToSend = State - > CompletedTasks . size ( ) ;
2013-05-06 13:07:41 -04:00
if ( State - > CompletedTasks . size ( ) > 50 )
2013-02-16 16:14:39 -08:00
FirstTaskToSend = State - > CompletedTasks . size ( ) - 50 ;
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Completed Task Count: %i, First Task to send is %i, Last is %i " ,
2013-05-09 10:44:08 -04:00
State - > CompletedTasks . size ( ) , FirstTaskToSend , LastTaskToSend ) ;
2013-02-16 16:14:39 -08:00
/*
for ( iterator = State - > CompletedTasks . begin ( ) ; iterator ! = State - > CompletedTasks . end ( ) ; iterator + + ) {
int TaskID = ( * iterator ) . TaskID ;
2013-05-04 18:06:58 -07:00
if ( Tasks [ TaskID ] = = nullptr ) continue ;
2013-02-16 16:14:39 -08:00
PacketLength = PacketLength + 8 + strlen ( Tasks [ TaskID ] - > Title ) + 1 ;
}
*/
for ( int i = FirstTaskToSend ; i < LastTaskToSend ; i + + ) {
int TaskID = State - > CompletedTasks [ i ] . TaskID ;
2013-05-04 18:06:58 -07:00
if ( Tasks [ TaskID ] = = nullptr ) continue ;
2018-06-12 13:56:17 -04:00
PacketLength = PacketLength + 8 + Tasks [ TaskID ] - > Title . size ( ) + 1 ;
2013-02-16 16:14:39 -08:00
}
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket ( OP_CompletedTasks , PacketLength ) ;
2013-02-16 16:14:39 -08:00
char * buf = ( char * ) outapp - > pBuffer ;
//*(uint32 *)buf = State->CompletedTasks.size();
* ( uint32 * ) buf = LastTaskToSend - FirstTaskToSend ;
buf = buf + 4 ;
//for(iterator=State->CompletedTasks.begin(); iterator!=State->CompletedTasks.end(); iterator++) {
// int TaskID = (*iterator).TaskID;
for ( int i = FirstTaskToSend ; i < LastTaskToSend ; i + + ) {
int TaskID = State - > CompletedTasks [ i ] . TaskID ;
2013-05-04 18:06:58 -07:00
if ( Tasks [ TaskID ] = = nullptr ) continue ;
2013-02-16 16:14:39 -08:00
* ( uint32 * ) buf = TaskID ;
buf = buf + 4 ;
2018-06-12 13:56:17 -04:00
sprintf ( buf , " %s " , Tasks [ TaskID ] - > Title . c_str ( ) ) ;
2013-02-16 16:14:39 -08:00
buf = buf + strlen ( buf ) + 1 ;
//*(uint32 *)buf = (*iterator).CompletedTime;
* ( uint32 * ) buf = State - > CompletedTasks [ i ] . CompletedTime ;
buf = buf + 4 ;
}
c - > QueuePacket ( outapp ) ;
safe_delete ( outapp ) ;
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
void TaskManager : : SendTaskActivityShort ( Client * c , int TaskID , int ActivityID , int ClientTaskIndex )
{
// This Activity Packet is sent for activities that have not yet been unlocked and appear as ???
// in the client.
TaskActivityShort_Struct * tass ;
2020-05-17 18:36:06 -07:00
if ( c - > ClientVersionBit ( ) & EQ : : versions : : maskRoFAndLater )
2013-02-16 16:14:39 -08:00
{
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket ( OP_TaskActivity , 25 ) ;
2013-02-16 16:14:39 -08:00
outapp - > WriteUInt32 ( ClientTaskIndex ) ;
2018-06-24 23:21:35 -04:00
outapp - > WriteUInt32 ( static_cast < uint32 > ( Tasks [ TaskID ] - > type ) ) ;
2013-02-16 16:14:39 -08:00
outapp - > WriteUInt32 ( TaskID ) ;
outapp - > WriteUInt32 ( ActivityID ) ;
outapp - > WriteUInt32 ( 0 ) ;
outapp - > WriteUInt32 ( 0xffffffff ) ;
outapp - > WriteUInt8 ( 0 ) ;
c - > FastQueuePacket ( & outapp ) ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
return ;
}
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket ( OP_TaskActivity , sizeof ( TaskActivityShort_Struct ) ) ;
2013-02-16 16:14:39 -08:00
tass = ( TaskActivityShort_Struct * ) outapp - > pBuffer ;
tass - > TaskSequenceNumber = ClientTaskIndex ;
2018-06-24 23:21:35 -04:00
tass - > unknown2 = static_cast < uint32 > ( Tasks [ TaskID ] - > type ) ;
2013-02-16 16:14:39 -08:00
tass - > TaskID = TaskID ;
tass - > ActivityID = ActivityID ;
tass - > unknown3 = 0x000000 ;
tass - > ActivityType = 0xffffffff ;
tass - > unknown4 = 0x00000000 ;
c - > QueuePacket ( outapp ) ;
safe_delete ( outapp ) ;
}
void TaskManager : : SendTaskActivityLong ( Client * c , int TaskID , int ActivityID , int ClientTaskIndex , bool Optional , bool TaskComplete ) {
2020-05-17 18:36:06 -07:00
if ( c - > ClientVersion ( ) > = EQ : : versions : : ClientVersion : : RoF )
2013-02-16 16:14:39 -08:00
{
SendTaskActivityNew ( c , TaskID , ActivityID , ClientTaskIndex , Optional , TaskComplete ) ;
return ;
}
2018-07-04 13:30:24 -04:00
SerializeBuffer buf ( 100 ) ;
2013-02-16 16:14:39 -08:00
2018-07-04 13:30:24 -04:00
buf . WriteUInt32 ( ClientTaskIndex ) ;
buf . WriteUInt32 ( static_cast < uint32 > ( Tasks [ TaskID ] - > type ) ) ;
buf . WriteUInt32 ( TaskID ) ;
buf . WriteUInt32 ( ActivityID ) ;
buf . WriteUInt32 ( 0 ) ; // unknown3
2013-02-16 16:14:39 -08:00
2018-07-02 22:01:17 -04:00
// We send our 'internal' types as ActivityCastOn. text3 should be set to the activity description, so it makes
2013-02-16 16:14:39 -08:00
// no difference to the client. All activity updates will be done based on our interal activity types.
2013-05-09 10:44:08 -04:00
if ( ( Tasks [ TaskID ] - > Activity [ ActivityID ] . Type > 0 ) & & Tasks [ TaskID ] - > Activity [ ActivityID ] . Type < 100 )
2018-07-04 13:30:24 -04:00
buf . WriteUInt32 ( Tasks [ TaskID ] - > Activity [ ActivityID ] . Type ) ;
2013-02-16 16:14:39 -08:00
else
2018-07-04 13:30:24 -04:00
buf . WriteUInt32 ( ActivityCastOn ) ; // w/e!
2013-02-16 16:14:39 -08:00
2018-07-04 14:36:18 -04:00
buf . WriteUInt32 ( Optional ) ;
2018-07-04 13:30:24 -04:00
buf . WriteUInt32 ( 0 ) ; // solo, group, raid
2013-02-16 16:14:39 -08:00
2018-07-04 13:30:24 -04:00
buf . WriteString ( Tasks [ TaskID ] - > Activity [ ActivityID ] . target_name ) ; // target name string
buf . WriteString ( Tasks [ TaskID ] - > Activity [ ActivityID ] . item_list ) ; // item name list
2013-02-16 16:14:39 -08:00
if ( Tasks [ TaskID ] - > Activity [ ActivityID ] . Type ! = ActivityGiveCash )
2018-07-04 13:30:24 -04:00
buf . WriteUInt32 ( Tasks [ TaskID ] - > Activity [ ActivityID ] . GoalCount ) ;
2013-02-16 16:14:39 -08:00
else
// For our internal type GiveCash, where the goal count has the amount of cash that must be given,
// we don't want the donecount and goalcount fields cluttered up with potentially large numbers, so we just
// send a goalcount of 1, and a bit further down, a donecount of 1 if the activity is complete, 0 otherwise.
// The text3 field should decribe the exact activity goal, e.g. give 3500gp to Hasten Bootstrutter.
2018-07-04 13:30:24 -04:00
buf . WriteUInt32 ( 1 ) ;
2013-02-16 16:14:39 -08:00
2018-07-04 13:30:24 -04:00
buf . WriteUInt32 ( Tasks [ TaskID ] - > Activity [ ActivityID ] . skill_id ) ;
buf . WriteUInt32 ( Tasks [ TaskID ] - > Activity [ ActivityID ] . spell_id ) ;
2018-08-01 13:50:09 -04:00
buf . WriteUInt32 ( Tasks [ TaskID ] - > Activity [ ActivityID ] . ZoneIDs . empty ( ) ? 0 : Tasks [ TaskID ] - > Activity [ ActivityID ] . ZoneIDs . front ( ) ) ;
2018-07-04 13:30:24 -04:00
buf . WriteUInt32 ( 0 ) ;
2013-02-16 16:14:39 -08:00
2018-07-04 13:30:24 -04:00
buf . WriteString ( Tasks [ TaskID ] - > Activity [ ActivityID ] . desc_override ) ;
2013-02-16 16:14:39 -08:00
if ( Tasks [ TaskID ] - > Activity [ ActivityID ] . Type ! = ActivityGiveCash )
2018-07-05 14:58:01 -04:00
buf . WriteUInt32 ( c - > GetTaskActivityDoneCount ( Tasks [ TaskID ] - > type , ClientTaskIndex , ActivityID ) ) ;
2013-02-16 16:14:39 -08:00
else
// For internal activity types, DoneCount is either 1 if the activity is complete, 0 otherwise.
2018-07-05 14:58:01 -04:00
buf . WriteUInt32 ( ( c - > GetTaskActivityDoneCount ( Tasks [ TaskID ] - > type , ClientTaskIndex , ActivityID ) > = Tasks [ TaskID ] - > Activity [ ActivityID ] . GoalCount ) ) ;
2013-02-16 16:14:39 -08:00
2018-07-04 13:30:24 -04:00
buf . WriteUInt32 ( 1 ) ; // unknown
2013-02-16 16:14:39 -08:00
2018-07-08 23:03:53 -04:00
auto outapp = new EQApplicationPacket ( OP_TaskActivity , buf ) ;
2013-02-16 16:14:39 -08:00
c - > QueuePacket ( outapp ) ;
safe_delete ( outapp ) ;
}
// Used only by RoF+ Clients
2018-07-02 22:01:17 -04:00
void TaskManager : : SendTaskActivityNew ( Client * c , int TaskID , int ActivityID , int ClientTaskIndex , bool Optional , bool TaskComplete )
{
SerializeBuffer buf ( 100 ) ;
2013-02-16 16:14:39 -08:00
2018-07-02 22:01:17 -04:00
buf . WriteUInt32 ( ClientTaskIndex ) ; // TaskSequenceNumber
buf . WriteUInt32 ( static_cast < uint32 > ( Tasks [ TaskID ] - > type ) ) ; // task type
buf . WriteUInt32 ( TaskID ) ;
buf . WriteUInt32 ( ActivityID ) ;
buf . WriteUInt32 ( 0 ) ; // unknown3
2013-02-16 16:14:39 -08:00
2018-07-02 22:01:17 -04:00
// We send our 'internal' types as ActivityCastOn. text3 should be set to the activity description, so it makes
2013-02-16 16:14:39 -08:00
// no difference to the client. All activity updates will be done based on our interal activity types.
2013-05-09 10:44:08 -04:00
if ( ( Tasks [ TaskID ] - > Activity [ ActivityID ] . Type > 0 ) & & Tasks [ TaskID ] - > Activity [ ActivityID ] . Type < 100 )
2018-07-02 22:01:17 -04:00
buf . WriteUInt32 ( Tasks [ TaskID ] - > Activity [ ActivityID ] . Type ) ;
2013-02-16 16:14:39 -08:00
else
2018-07-02 22:01:17 -04:00
buf . WriteUInt32 ( ActivityCastOn ) ; // w/e!
2013-02-16 16:14:39 -08:00
2018-07-02 22:01:17 -04:00
buf . WriteUInt8 ( Optional ) ;
buf . WriteUInt32 ( 0 ) ; // solo, group, raid
2013-02-16 16:14:39 -08:00
// One of these unknown fields maybe related to the 'Use On' activity types
2018-07-02 22:01:17 -04:00
buf . WriteString ( Tasks [ TaskID ] - > Activity [ ActivityID ] . target_name ) ; // target name string
2013-02-16 16:14:39 -08:00
2018-07-03 01:15:40 -04:00
buf . WriteLengthString ( Tasks [ TaskID ] - > Activity [ ActivityID ] . item_list ) ; // item name list
2013-02-16 16:14:39 -08:00
// Goal Count
if ( Tasks [ TaskID ] - > Activity [ ActivityID ] . Type ! = ActivityGiveCash )
2018-07-02 22:01:17 -04:00
buf . WriteUInt32 ( Tasks [ TaskID ] - > Activity [ ActivityID ] . GoalCount ) ;
2013-02-16 16:14:39 -08:00
else
2018-07-02 22:01:17 -04:00
buf . WriteUInt32 ( 1 ) ; // GoalCount
2013-05-06 13:07:41 -04:00
2018-07-02 22:01:17 -04:00
// skill ID list ; separated
2018-07-03 01:15:40 -04:00
buf . WriteLengthString ( Tasks [ TaskID ] - > Activity [ ActivityID ] . skill_list ) ;
2013-02-16 16:14:39 -08:00
2018-07-02 22:01:17 -04:00
// spelll ID list ; separated -- unsure wtf we're doing here
2018-07-03 01:15:40 -04:00
buf . WriteLengthString ( Tasks [ TaskID ] - > Activity [ ActivityID ] . spell_list ) ;
2013-05-06 13:07:41 -04:00
2018-07-02 22:01:17 -04:00
buf . WriteString ( Tasks [ TaskID ] - > Activity [ ActivityID ] . zones ) ;
buf . WriteUInt32 ( 0 ) ; // unknown7
2013-02-16 16:14:39 -08:00
2018-07-02 22:01:17 -04:00
buf . WriteString ( Tasks [ TaskID ] - > Activity [ ActivityID ] . desc_override ) ; // description override
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if ( Tasks [ TaskID ] - > Activity [ ActivityID ] . Type ! = ActivityGiveCash )
2018-07-05 14:58:01 -04:00
buf . WriteUInt32 ( c - > GetTaskActivityDoneCount ( Tasks [ TaskID ] - > type , ClientTaskIndex , ActivityID ) ) ; // DoneCount
2013-02-16 16:14:39 -08:00
else
// For internal activity types, DoneCount is either 1 if the activity is complete, 0 otherwise.
2018-07-05 14:58:01 -04:00
buf . WriteUInt32 ( ( c - > GetTaskActivityDoneCount ( Tasks [ TaskID ] - > type , ClientTaskIndex , ActivityID ) > = Tasks [ TaskID ] - > Activity [ ActivityID ] . GoalCount ) ) ;
2013-02-16 16:14:39 -08:00
2018-07-02 22:01:17 -04:00
buf . WriteUInt8 ( 1 ) ; // unknown9
2013-05-06 13:07:41 -04:00
2018-07-02 22:01:17 -04:00
buf . WriteString ( Tasks [ TaskID ] - > Activity [ ActivityID ] . zones ) ;
2013-02-16 16:14:39 -08:00
2018-07-08 23:03:53 -04:00
auto outapp = new EQApplicationPacket ( OP_TaskActivity , buf ) ;
2013-02-16 16:14:39 -08:00
c - > QueuePacket ( outapp ) ;
safe_delete ( outapp ) ;
}
2018-06-24 23:21:35 -04:00
void TaskManager : : SendActiveTasksToClient ( Client * c , bool TaskComplete )
{
auto state = c - > GetTaskState ( ) ;
if ( ! state )
return ;
2013-02-16 16:14:39 -08:00
2018-07-08 16:55:08 -04:00
for ( int TaskIndex = 0 ; TaskIndex < MAXACTIVEQUESTS + 1 ; TaskIndex + + ) {
int TaskID = state - > ActiveTasks [ TaskIndex ] . TaskID ;
if ( ( TaskID = = 0 ) | | ( Tasks [ TaskID ] = = 0 ) )
continue ;
int StartTime = state - > ActiveTasks [ TaskIndex ] . AcceptedTime ;
2013-05-06 13:07:41 -04:00
2018-07-08 16:55:08 -04:00
SendActiveTaskDescription ( c , TaskID , state - > ActiveTasks [ TaskIndex ] , StartTime , Tasks [ TaskID ] - > Duration ,
false ) ;
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] SendActiveTasksToClient: Task %i, Activities: %i " , TaskID ,
GetActivityCount ( TaskID ) ) ;
2013-02-16 16:14:39 -08:00
int Sequence = 0 ;
2018-07-09 14:01:52 -04:00
int fixed_index = Tasks [ TaskID ] - > type = = TaskType : : Task ? 0 : TaskIndex - 1 ; // hmmm fuck
2018-07-08 16:55:08 -04:00
for ( int Activity = 0 ; Activity < GetActivityCount ( TaskID ) ; Activity + + ) {
2018-07-09 14:01:52 -04:00
if ( c - > GetTaskActivityState ( Tasks [ TaskID ] - > type , fixed_index , Activity ) ! = ActivityHidden ) {
2018-07-08 16:55:08 -04:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Long: %i, %i, %i Complete=%i " , TaskID ,
2018-07-09 14:01:52 -04:00
Activity , fixed_index , TaskComplete ) ;
2018-07-08 16:55:08 -04:00
if ( Activity = = GetActivityCount ( TaskID ) - 1 )
2018-07-09 14:01:52 -04:00
SendTaskActivityLong ( c , TaskID , Activity , fixed_index ,
2018-07-08 16:55:08 -04:00
Tasks [ TaskID ] - > Activity [ Activity ] . Optional , TaskComplete ) ;
2013-02-16 16:14:39 -08:00
else
2018-07-09 14:01:52 -04:00
SendTaskActivityLong ( c , TaskID , Activity , fixed_index ,
2018-07-08 16:55:08 -04:00
Tasks [ TaskID ] - > Activity [ Activity ] . Optional , 0 ) ;
} else {
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Short: %i, %i, %i " , TaskID , Activity ,
2018-07-09 14:01:52 -04:00
fixed_index ) ;
SendTaskActivityShort ( c , TaskID , Activity , fixed_index ) ;
2013-02-16 16:14:39 -08:00
}
Sequence + + ;
}
}
}
2018-06-24 23:21:35 -04:00
void TaskManager : : SendSingleActiveTaskToClient ( Client * c , ClientTaskInformation & task_info , bool TaskComplete ,
bool BringUpTaskJournal )
{
int TaskID = task_info . TaskID ;
2013-02-16 16:14:39 -08:00
2018-06-24 23:21:35 -04:00
if ( TaskID = = 0 | | Tasks [ TaskID ] = = nullptr )
return ;
2013-02-16 16:14:39 -08:00
2018-06-24 23:21:35 -04:00
int StartTime = task_info . AcceptedTime ;
SendActiveTaskDescription ( c , TaskID , task_info , StartTime , Tasks [ TaskID ] - > Duration , BringUpTaskJournal ) ;
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] SendSingleActiveTasksToClient: Task %i, Activities: %i " , TaskID , GetActivityCount ( TaskID ) ) ;
2013-02-16 16:14:39 -08:00
2018-06-24 23:21:35 -04:00
for ( int Activity = 0 ; Activity < GetActivityCount ( TaskID ) ; Activity + + ) {
if ( task_info . Activity [ Activity ] . State ! = ActivityHidden ) {
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Long: %i, %i Complete=%i " , TaskID , Activity , TaskComplete ) ;
if ( Activity = = GetActivityCount ( TaskID ) - 1 )
SendTaskActivityLong ( c , TaskID , Activity , task_info . slot ,
2013-05-09 10:44:08 -04:00
Tasks [ TaskID ] - > Activity [ Activity ] . Optional , TaskComplete ) ;
2013-02-16 16:14:39 -08:00
else
2018-06-24 23:21:35 -04:00
SendTaskActivityLong ( c , TaskID , Activity , task_info . slot ,
2013-05-09 10:44:08 -04:00
Tasks [ TaskID ] - > Activity [ Activity ] . Optional , 0 ) ;
2018-06-24 23:21:35 -04:00
} else {
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] Short: %i, %i " , TaskID , Activity ) ;
SendTaskActivityShort ( c , TaskID , Activity , task_info . slot ) ;
2013-02-16 16:14:39 -08:00
}
}
}
2018-06-24 23:21:35 -04:00
void TaskManager : : SendActiveTaskDescription ( Client * c , int TaskID , ClientTaskInformation & task_info , int StartTime , int Duration , bool BringUpTaskJournal )
2014-12-25 11:04:01 -05:00
{
if ( ( TaskID < 1 ) | | ( TaskID > = MAXTASKS ) | | ! Tasks [ TaskID ] )
return ;
2013-02-16 16:14:39 -08:00
2018-06-24 23:21:35 -04:00
int PacketLength = sizeof ( TaskDescriptionHeader_Struct ) + Tasks [ TaskID ] - > Title . length ( ) + 1
+ sizeof ( TaskDescriptionData1_Struct ) + Tasks [ TaskID ] - > Description . length ( ) + 1
2013-05-09 10:44:08 -04:00
+ sizeof ( TaskDescriptionData2_Struct ) + 1 + sizeof ( TaskDescriptionTrailer_Struct ) ;
2013-02-16 16:14:39 -08:00
// If there is an item make the Reward text into a link to the item (only the first item if a list
// is specified). I have been unable to get multiple item links to work.
//
2018-06-24 23:21:35 -04:00
if ( Tasks [ TaskID ] - > RewardID & & Tasks [ TaskID ] - > item_link . empty ( ) ) {
int ItemID = 0 ;
2013-02-16 16:14:39 -08:00
// If the reward is a list of items, and the first entry on the list is valid
2018-06-24 23:21:35 -04:00
if ( Tasks [ TaskID ] - > RewardMethod = = METHODSINGLEID ) {
2013-02-16 16:14:39 -08:00
ItemID = Tasks [ TaskID ] - > RewardID ;
2018-06-24 23:21:35 -04:00
} else if ( Tasks [ TaskID ] - > RewardMethod = = METHODLIST ) {
2013-02-16 16:14:39 -08:00
ItemID = GoalListManager . GetFirstEntry ( Tasks [ TaskID ] - > RewardID ) ;
2018-06-24 23:21:35 -04:00
if ( ItemID < 0 )
2013-02-16 16:14:39 -08:00
ItemID = 0 ;
}
2014-12-25 11:04:01 -05:00
2013-02-16 16:14:39 -08:00
if ( ItemID ) {
2020-05-17 18:36:06 -07:00
const EQ : : ItemData * reward_item = database . GetItem ( ItemID ) ;
2014-12-28 12:49:14 -05:00
2020-05-17 18:36:06 -07:00
EQ : : SayLinkEngine linker ;
linker . SetLinkType ( EQ : : saylink : : SayLinkItemData ) ;
2014-12-28 12:49:14 -05:00
linker . SetItemData ( reward_item ) ;
linker . SetTaskUse ( ) ;
2018-06-24 23:21:35 -04:00
Tasks [ TaskID ] - > item_link = linker . GenerateLink ( ) ;
2013-02-16 16:14:39 -08:00
}
}
2018-06-24 23:21:35 -04:00
PacketLength + = Tasks [ TaskID ] - > Reward . length ( ) + 1 + Tasks [ TaskID ] - > item_link . length ( ) + 1 ;
2013-02-16 16:14:39 -08:00
char * Ptr ;
TaskDescriptionHeader_Struct * tdh ;
TaskDescriptionData1_Struct * tdd1 ;
TaskDescriptionData2_Struct * tdd2 ;
TaskDescriptionTrailer_Struct * tdt ;
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket ( OP_TaskDescription , PacketLength ) ;
2013-02-16 16:14:39 -08:00
tdh = ( TaskDescriptionHeader_Struct * ) outapp - > pBuffer ;
2018-06-24 23:21:35 -04:00
tdh - > SequenceNumber = task_info . slot ;
2013-02-16 16:14:39 -08:00
tdh - > TaskID = TaskID ;
2018-06-24 23:21:35 -04:00
tdh - > open_window = BringUpTaskJournal ;
tdh - > task_type = static_cast < uint32 > ( Tasks [ TaskID ] - > type ) ;
tdh - > reward_type = 0 ; // TODO: 4 says Radiant Crystals else Ebon Crystals when shared task
2013-02-16 16:14:39 -08:00
Ptr = ( char * ) tdh + sizeof ( TaskDescriptionHeader_Struct ) ;
2018-06-12 13:56:17 -04:00
sprintf ( Ptr , " %s " , Tasks [ TaskID ] - > Title . c_str ( ) ) ;
2018-06-24 23:21:35 -04:00
Ptr + = Tasks [ TaskID ] - > Title . length ( ) + 1 ;
2013-02-16 16:14:39 -08:00
tdd1 = ( TaskDescriptionData1_Struct * ) Ptr ;
tdd1 - > Duration = Duration ;
2018-06-24 23:21:35 -04:00
tdd1 - > dur_code = static_cast < uint32 > ( Tasks [ TaskID ] - > dur_code ) ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
tdd1 - > StartTime = StartTime ;
Ptr = ( char * ) tdd1 + sizeof ( TaskDescriptionData1_Struct ) ;
2018-06-12 13:56:17 -04:00
sprintf ( Ptr , " %s " , Tasks [ TaskID ] - > Description . c_str ( ) ) ;
2018-06-24 23:21:35 -04:00
Ptr + = Tasks [ TaskID ] - > Description . length ( ) + 1 ;
2013-02-16 16:14:39 -08:00
tdd2 = ( TaskDescriptionData2_Struct * ) Ptr ;
2018-06-24 23:21:35 -04:00
// we have this reward stuff!
// if we ever don't hardcode this, TaskDescriptionTrailer_Struct will need to be fixed since
// "has_reward_selection" is after this bool! Smaller packet when this is 0
tdd2 - > has_rewards = 1 ;
2013-05-06 13:07:41 -04:00
2018-06-24 23:21:35 -04:00
tdd2 - > coin_reward = Tasks [ TaskID ] - > CashReward ;
tdd2 - > xp_reward = Tasks [ TaskID ] - > XPReward ? 1 : 0 ; // just booled
2018-07-08 21:40:04 -04:00
tdd2 - > faction_reward = Tasks [ TaskID ] - > faction_reward ? 1 : 0 ; // faction booled
2013-02-16 16:14:39 -08:00
Ptr = ( char * ) tdd2 + sizeof ( TaskDescriptionData2_Struct ) ;
2018-06-24 23:21:35 -04:00
// we actually have 2 strings here. One is max length 96 and not parsed for item links
// We actually skipped past that string incorrectly before, so TODO: fix item link string
sprintf ( Ptr , " %s " , Tasks [ TaskID ] - > Reward . c_str ( ) ) ;
Ptr + = Tasks [ TaskID ] - > Reward . length ( ) + 1 ;
2013-02-16 16:14:39 -08:00
2018-06-24 23:21:35 -04:00
// second string is parsed for item links
sprintf ( Ptr , " %s " , Tasks [ TaskID ] - > item_link . c_str ( ) ) ;
Ptr + = Tasks [ TaskID ] - > item_link . length ( ) + 1 ;
2013-02-16 16:14:39 -08:00
2018-06-24 23:21:35 -04:00
tdt = ( TaskDescriptionTrailer_Struct * ) Ptr ;
tdt - > Points = 0x00000000 ; // Points Count TODO: this does have a visible affect on the client ...
tdt - > has_reward_selection = 0 ; // TODO: new rewards window
2013-02-16 16:14:39 -08:00
c - > QueuePacket ( outapp ) ;
safe_delete ( outapp ) ;
}
2018-07-05 14:58:01 -04:00
bool ClientTaskState : : IsTaskActivityCompleted ( TaskType type , int index , int ActivityID )
{
switch ( type ) {
case TaskType : : Task :
if ( index ! = 0 )
return false ;
return ActiveTask . Activity [ ActivityID ] . State = = ActivityCompleted ;
case TaskType : : Shared :
return false ; // TODO: shared tasks
case TaskType : : Quest :
if ( index < MAXACTIVEQUESTS )
return ActiveQuests [ index ] . Activity [ ActivityID ] . State = = ActivityCompleted ;
default :
return false ;
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
}
2018-07-05 14:58:01 -04:00
// should we be defaulting to hidden?
ActivityState ClientTaskState : : GetTaskActivityState ( TaskType type , int index , int ActivityID )
{
switch ( type ) {
case TaskType : : Task :
if ( index ! = 0 )
return ActivityHidden ;
return ActiveTask . Activity [ ActivityID ] . State ;
case TaskType : : Shared :
return ActivityHidden ; // TODO: shared tasks
case TaskType : : Quest :
if ( index < MAXACTIVEQUESTS )
return ActiveQuests [ index ] . Activity [ ActivityID ] . State ;
default :
return ActivityHidden ;
}
2013-02-16 16:14:39 -08:00
}
2018-07-05 14:58:01 -04:00
int ClientTaskState : : GetTaskActivityDoneCount ( TaskType type , int index , int ActivityID )
{
switch ( type ) {
case TaskType : : Task :
if ( index ! = 0 )
return 0 ;
return ActiveTask . Activity [ ActivityID ] . DoneCount ;
case TaskType : : Shared :
return 0 ; // TODO: shared tasks
case TaskType : : Quest :
if ( index < MAXACTIVEQUESTS )
return ActiveQuests [ index ] . Activity [ ActivityID ] . DoneCount ;
default :
return 0 ;
}
2013-02-16 16:14:39 -08:00
}
2018-07-06 00:59:19 -04:00
int ClientTaskState : : GetTaskActivityDoneCountFromTaskID ( int TaskID , int ActivityID )
{
if ( ActiveTask . TaskID = = TaskID )
return ActiveTask . Activity [ ActivityID ] . DoneCount ;
// TODO: shared tasks
2013-02-16 16:14:39 -08:00
int ActiveTaskIndex = - 1 ;
2018-06-13 17:51:35 -04:00
for ( int i = 0 ; i < MAXACTIVEQUESTS ; i + + ) {
2018-06-13 18:06:08 -04:00
if ( ActiveQuests [ i ] . TaskID = = TaskID ) {
2013-02-16 16:14:39 -08:00
ActiveTaskIndex = i ;
break ;
}
}
2015-02-05 05:43:36 -06:00
2015-02-12 01:54:41 -06:00
if ( ActiveTaskIndex = = - 1 )
return 0 ;
2018-06-13 18:06:08 -04:00
if ( ActiveQuests [ ActiveTaskIndex ] . Activity [ ActivityID ] . DoneCount ) {
return ActiveQuests [ ActiveTaskIndex ] . Activity [ ActivityID ] . DoneCount ;
2015-02-05 05:43:36 -06:00
}
else {
return 0 ;
}
2013-02-16 16:14:39 -08:00
}
2018-07-06 00:59:19 -04:00
int ClientTaskState : : GetTaskStartTime ( TaskType type , int index )
{
switch ( type ) {
case TaskType : : Task :
return ActiveTask . AcceptedTime ;
case TaskType : : Quest :
return ActiveQuests [ index ] . AcceptedTime ;
case TaskType : : Shared : // TODO
default :
return - 1 ;
}
2013-02-16 16:14:39 -08:00
}
void ClientTaskState : : CancelAllTasks ( Client * c ) {
// This method exists solely to be called during #task reloadall
2013-05-06 13:07:41 -04:00
// It removes tasks from the in-game client state ready for them to be
2013-02-16 16:14:39 -08:00
// resent to the client, in case an updated task fails to load
2018-06-24 23:21:35 -04:00
CancelTask ( c , 0 , TaskType : : Task , false ) ;
ActiveTask . TaskID = TASKSLOTEMPTY ;
2018-06-13 17:51:35 -04:00
for ( int i = 0 ; i < MAXACTIVEQUESTS ; i + + )
2018-06-13 18:06:08 -04:00
if ( ActiveQuests [ i ] . TaskID ! = TASKSLOTEMPTY ) {
2018-06-24 23:21:35 -04:00
CancelTask ( c , i , TaskType : : Quest , false ) ;
2018-06-13 18:06:08 -04:00
ActiveQuests [ i ] . TaskID = TASKSLOTEMPTY ;
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
2018-06-24 23:21:35 -04:00
// TODO: shared
2013-02-16 16:14:39 -08:00
}
2018-06-24 23:21:35 -04:00
void ClientTaskState : : CancelTask ( Client * c , int SequenceNumber , TaskType type , bool RemoveFromDB )
{
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket ( OP_CancelTask , sizeof ( CancelTask_Struct ) ) ;
2013-02-16 16:14:39 -08:00
CancelTask_Struct * cts = ( CancelTask_Struct * ) outapp - > pBuffer ;
cts - > SequenceNumber = SequenceNumber ;
2018-06-24 23:21:35 -04:00
cts - > type = static_cast < uint32 > ( type ) ;
2013-02-16 16:14:39 -08:00
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] CancelTask " ) ;
2013-02-16 16:14:39 -08:00
c - > QueuePacket ( outapp ) ;
safe_delete ( outapp ) ;
if ( RemoveFromDB )
2018-06-24 23:21:35 -04:00
RemoveTask ( c , SequenceNumber , type ) ;
2013-02-16 16:14:39 -08:00
}
2018-06-24 23:21:35 -04:00
void ClientTaskState : : RemoveTask ( Client * c , int sequenceNumber , TaskType type )
{
2014-10-08 13:33:22 -07:00
int characterID = c - > CharacterID ( ) ;
2018-07-08 16:55:08 -04:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] ClientTaskState Cancel Task %i " , sequenceNumber ) ;
2013-02-16 16:14:39 -08:00
2018-07-08 16:55:08 -04:00
int task_id = - 1 ;
switch ( type ) {
case TaskType : : Task :
if ( sequenceNumber = = 0 )
task_id = ActiveTask . TaskID ;
break ;
case TaskType : : Quest :
if ( sequenceNumber < MAXACTIVEQUESTS )
task_id = ActiveQuests [ sequenceNumber ] . TaskID ;
break ;
case TaskType : : Shared : // TODO:
default :
break ;
}
std : : string query = StringFormat ( " DELETE FROM character_activities WHERE charid=%i AND taskid = %i " ,
characterID , task_id ) ;
auto results = database . QueryDatabase ( query ) ;
if ( ! results . Success ( ) ) {
2019-09-02 03:10:03 -05:00
LogError ( " [TASKS] Error in CientTaskState::CancelTask [{}] " ,
2018-07-08 16:55:08 -04:00
results . ErrorMessage ( ) . c_str ( ) ) ;
2013-02-16 16:14:39 -08:00
return ;
}
2018-07-08 16:55:08 -04:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] CancelTask: %s " , query . c_str ( ) ) ;
2013-02-16 16:14:39 -08:00
2018-07-08 16:55:08 -04:00
query = StringFormat ( " DELETE FROM character_tasks WHERE charid=%i AND taskid = %i AND type=%i " , characterID ,
task_id , static_cast < int > ( type ) ) ;
2014-10-08 13:33:22 -07:00
results = database . QueryDatabase ( query ) ;
2018-07-08 16:55:08 -04:00
if ( ! results . Success ( ) )
2019-09-02 03:10:03 -05:00
LogError ( " [TASKS] Error in CientTaskState::CancelTask [{}] " ,
2018-07-08 16:55:08 -04:00
results . ErrorMessage ( ) . c_str ( ) ) ;
2013-02-16 16:14:39 -08:00
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] CancelTask: %s " , query . c_str ( ) ) ;
2013-02-16 16:14:39 -08:00
2018-06-24 23:21:35 -04:00
switch ( type ) {
case TaskType : : Task :
ActiveTask . TaskID = TASKSLOTEMPTY ;
break ;
case TaskType : : Shared :
break ; // TODO: shared tasks
case TaskType : : Quest :
ActiveQuests [ sequenceNumber ] . TaskID = TASKSLOTEMPTY ;
ActiveTaskCount - - ;
break ;
default :
break ;
}
2013-02-16 16:14:39 -08:00
}
2018-06-24 23:21:35 -04:00
void ClientTaskState : : AcceptNewTask ( Client * c , int TaskID , int NPCID , bool enforce_level_requirement )
{
if ( ! taskmanager | | TaskID < 0 | | TaskID > = MAXTASKS ) {
2019-08-11 00:00:55 -05:00
c - > Message ( Chat : : Red , " Task system not functioning, or TaskID %i out of range. " , TaskID ) ;
2013-02-16 16:14:39 -08:00
return ;
}
2018-06-24 23:21:35 -04:00
auto task = taskmanager - > Tasks [ TaskID ] ;
if ( task = = nullptr ) {
2019-08-11 00:00:55 -05:00
c - > Message ( Chat : : Red , " Invalid TaskID %i " , TaskID ) ;
2013-02-16 16:14:39 -08:00
return ;
}
2018-06-24 23:21:35 -04:00
bool max_tasks = false ;
switch ( task - > type ) {
case TaskType : : Task :
if ( ActiveTask . TaskID ! = TASKSLOTEMPTY )
max_tasks = true ;
break ;
case TaskType : : Shared : // TODO: shared tasks
// if (something)
max_tasks = true ;
break ;
case TaskType : : Quest :
if ( ActiveTaskCount = = MAXACTIVEQUESTS )
max_tasks = true ;
break ;
default :
break ;
}
if ( max_tasks ) {
2019-08-11 00:00:55 -05:00
c - > Message ( Chat : : Red , " You already have the maximum allowable number of active tasks (%i) " , MAXACTIVEQUESTS ) ;
2013-02-16 16:14:39 -08:00
return ;
}
2018-06-24 23:21:35 -04:00
// only Quests can have more than one, so don't need to check others
if ( task - > type = = TaskType : : Quest ) {
for ( int i = 0 ; i < MAXACTIVEQUESTS ; i + + ) {
if ( ActiveQuests [ i ] . TaskID = = TaskID ) {
2019-08-11 00:00:55 -05:00
c - > Message ( Chat : : Red , " You have already been assigned this task. " ) ;
2018-06-24 23:21:35 -04:00
return ;
}
2013-02-16 16:14:39 -08:00
}
}
2018-06-24 23:21:35 -04:00
if ( enforce_level_requirement & & ! taskmanager - > AppropriateLevel ( TaskID , c - > GetLevel ( ) ) ) {
2019-08-11 00:00:55 -05:00
c - > Message ( Chat : : Red , " You are outside the level range of this task. " ) ;
2015-07-15 19:16:51 -04:00
return ;
}
2018-06-24 23:21:35 -04:00
if ( ! taskmanager - > IsTaskRepeatable ( TaskID ) & & IsTaskCompleted ( TaskID ) )
return ;
2013-02-16 16:14:39 -08:00
// We do it this way, because when the Client cancels a task, it retains the sequence number of the remaining
// tasks in it's window, until something causes the TaskDescription packets to be sent again. We could just
// resend all the active task data to the client when it cancels a task, but that could be construed as a
// waste of bandwidth.
//
2018-06-24 23:21:35 -04:00
ClientTaskInformation * active_slot = nullptr ;
switch ( task - > type ) {
case TaskType : : Task :
active_slot = & ActiveTask ;
break ;
case TaskType : : Shared : // TODO: shared
active_slot = nullptr ;
break ;
case TaskType : : Quest :
for ( int i = 0 ; i < MAXACTIVEQUESTS ; i + + ) {
Log ( Logs : : General , Logs : : Tasks ,
" [UPDATE] ClientTaskState Looking for free slot in slot %i, found TaskID of %i " , i ,
ActiveQuests [ i ] . TaskID ) ;
if ( ActiveQuests [ i ] . TaskID = = 0 ) {
active_slot = & ActiveQuests [ i ] ;
break ;
}
2013-02-16 16:14:39 -08:00
}
2018-06-24 23:21:35 -04:00
break ;
default :
break ;
2013-02-16 16:14:39 -08:00
}
// This shouldn't happen unless there is a bug in the handling of ActiveTaskCount somewhere
2018-06-24 23:21:35 -04:00
if ( active_slot = = nullptr ) {
2019-08-11 00:00:55 -05:00
c - > Message ( Chat : : Red , " You already have the maximum allowable number of active tasks (%i) " , MAXACTIVEQUESTS ) ;
2013-02-16 16:14:39 -08:00
return ;
}
2013-05-06 13:07:41 -04:00
2018-06-24 23:21:35 -04:00
active_slot - > TaskID = TaskID ;
active_slot - > AcceptedTime = time ( nullptr ) ;
active_slot - > Updated = true ;
active_slot - > CurrentStep = - 1 ;
2013-02-16 16:14:39 -08:00
2018-06-24 23:21:35 -04:00
for ( int i = 0 ; i < taskmanager - > Tasks [ TaskID ] - > ActivityCount ; i + + ) {
active_slot - > Activity [ i ] . ActivityID = i ;
active_slot - > Activity [ i ] . DoneCount = 0 ;
active_slot - > Activity [ i ] . State = ActivityHidden ;
active_slot - > Activity [ i ] . Updated = true ;
2013-02-16 16:14:39 -08:00
}
2018-06-24 23:21:35 -04:00
UnlockActivities ( c - > CharacterID ( ) , * active_slot ) ;
if ( task - > type = = TaskType : : Quest )
ActiveTaskCount + + ;
taskmanager - > SendSingleActiveTaskToClient ( c , * active_slot , false , true ) ;
2019-08-11 00:27:04 -05:00
c - > Message ( Chat : : White , " You have been assigned the task '%s'. " , taskmanager - > Tasks [ TaskID ] - > Title . c_str ( ) ) ;
2013-02-16 16:14:39 -08:00
2018-06-24 23:21:35 -04:00
std : : string buf = std : : to_string ( TaskID ) ;
2013-02-16 16:14:39 -08:00
NPC * npc = entity_list . GetID ( NPCID ) - > CastToNPC ( ) ;
if ( ! npc ) {
2019-08-11 00:00:55 -05:00
c - > Message ( Chat : : Yellow , " Task Giver ID is %i " , NPCID ) ;
c - > Message ( Chat : : Red , " Unable to find NPC to send EVENT_TASKACCEPTED to. Report this bug. " ) ;
2013-02-16 16:14:39 -08:00
return ;
}
2018-06-24 23:21:35 -04:00
taskmanager - > SaveClientState ( c , this ) ;
parse - > EventNPC ( EVENT_TASK_ACCEPTED , npc , c , buf . c_str ( ) , 0 ) ;
2013-02-16 16:14:39 -08:00
}
void ClientTaskState : : ProcessTaskProximities ( Client * c , float X , float Y , float Z ) {
float LastX = c - > ProximityX ( ) ;
float LastY = c - > ProximityY ( ) ;
float LastZ = c - > ProximityZ ( ) ;
if ( ( LastX = = X ) & & ( LastY = = Y ) & & ( LastZ = = Z ) ) return ;
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [PROXIMITY] Checking proximities for Position %8.3f, %8.3f, %8.3f " , X , Y , Z ) ;
2013-02-16 16:14:39 -08:00
int ExploreID = taskmanager - > ProximityManager . CheckProximities ( X , Y , Z ) ;
2015-01-20 03:46:27 -06:00
if ( ExploreID > 0 ) {
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [PROXIMITY] Position %8.3f, %8.3f, %8.3f is within proximity %i " , X , Y , Z , ExploreID ) ;
2013-02-16 16:14:39 -08:00
UpdateTasksOnExplore ( c , ExploreID ) ;
}
}
2018-06-11 21:58:11 -04:00
TaskGoalListManager : : TaskGoalListManager ( )
{
2013-02-16 16:14:39 -08:00
NumberOfLists = 0 ;
}
2018-06-11 21:58:11 -04:00
TaskGoalListManager : : ~ TaskGoalListManager ( ) { }
2013-02-16 16:14:39 -08:00
2018-06-11 21:49:32 -04:00
bool TaskGoalListManager : : LoadLists ( )
{
2013-02-16 16:14:39 -08:00
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [GLOBALLOAD] TaskGoalListManager::LoadLists Called " ) ;
2013-02-16 16:14:39 -08:00
2018-06-11 21:49:32 -04:00
TaskGoalLists . clear ( ) ;
2013-02-16 16:14:39 -08:00
2018-06-11 21:49:32 -04:00
const char * ERR_MYSQLERROR = " Error in TaskGoalListManager::LoadLists: %s %s " ;
2013-02-16 16:14:39 -08:00
2014-10-08 14:11:35 -07:00
NumberOfLists = 0 ;
2013-02-16 16:14:39 -08:00
2018-06-11 21:49:32 -04:00
std : : string query = " SELECT `listid`, COUNT(`entry`) "
" FROM `goallists` GROUP by `listid` "
" ORDER BY `listid` " ;
auto results = database . QueryDatabase ( query ) ;
if ( ! results . Success ( ) ) {
2014-10-08 14:11:35 -07:00
return false ;
2018-06-11 21:49:32 -04:00
}
2013-02-16 16:14:39 -08:00
2018-06-11 21:49:32 -04:00
NumberOfLists = results . RowCount ( ) ;
Log ( Logs : : General , Logs : : Tasks , " [GLOBALLOAD] Database returned a count of %i lists " , NumberOfLists ) ;
2013-05-06 13:07:41 -04:00
2018-06-11 21:49:32 -04:00
TaskGoalLists . reserve ( NumberOfLists ) ;
2013-02-16 16:14:39 -08:00
2018-06-11 21:49:32 -04:00
int listIndex = 0 ;
2013-02-16 16:14:39 -08:00
2018-06-11 21:49:32 -04:00
for ( auto row = results . begin ( ) ; row ! = results . end ( ) ; + + row ) {
int listID = atoi ( row [ 0 ] ) ;
int listSize = atoi ( row [ 1 ] ) ;
2018-06-11 22:49:20 -04:00
TaskGoalLists . push_back ( { listID , 0 , 0 } ) ;
2013-02-16 16:14:39 -08:00
2018-06-11 21:58:11 -04:00
TaskGoalLists [ listIndex ] . GoalItemEntries . reserve ( listSize ) ;
2013-02-16 16:14:39 -08:00
2018-06-11 21:49:32 -04:00
listIndex + + ;
}
2013-02-16 16:14:39 -08:00
2018-06-11 22:25:44 -04:00
for ( int listIndex = 0 ; listIndex < NumberOfLists ; listIndex + + ) {
2013-02-16 16:14:39 -08:00
2014-10-08 14:11:35 -07:00
int listID = TaskGoalLists [ listIndex ] . ListID ;
2018-06-11 22:49:20 -04:00
auto size = TaskGoalLists [ listIndex ] . GoalItemEntries . capacity ( ) ; // this was only done for manual memory management, shouldn't need to do this
2018-06-11 22:25:44 -04:00
query = StringFormat ( " SELECT `entry` from `goallists` "
" WHERE `listid` = %i "
" ORDER BY `entry` ASC LIMIT %i " ,
listID , size ) ;
results = database . QueryDatabase ( query ) ;
if ( ! results . Success ( ) ) {
2014-10-08 14:11:35 -07:00
continue ;
2018-06-11 22:25:44 -04:00
}
2013-02-16 16:14:39 -08:00
2018-06-11 23:54:42 -04:00
for ( auto row = results . begin ( ) ; row ! = results . end ( ) ; + + row ) {
2013-02-16 16:14:39 -08:00
2018-06-11 22:25:44 -04:00
int entry = atoi ( row [ 0 ] ) ;
2013-02-16 16:14:39 -08:00
2018-06-11 22:25:44 -04:00
if ( entry < TaskGoalLists [ listIndex ] . Min )
TaskGoalLists [ listIndex ] . Min = entry ;
2013-02-16 16:14:39 -08:00
2018-06-11 22:25:44 -04:00
if ( entry > TaskGoalLists [ listIndex ] . Max )
TaskGoalLists [ listIndex ] . Max = entry ;
2013-02-16 16:14:39 -08:00
2018-06-11 23:54:42 -04:00
TaskGoalLists [ listIndex ] . GoalItemEntries . push_back ( entry ) ;
2018-06-11 22:25:44 -04:00
}
2013-02-16 16:14:39 -08:00
}
2014-10-08 14:11:35 -07:00
2013-02-16 16:14:39 -08:00
return true ;
}
int TaskGoalListManager : : GetListByID ( int ListID ) {
// Find the list with the specified ListID and return the index
2018-06-11 22:25:44 -04:00
auto it = std : : find_if ( TaskGoalLists . begin ( ) , TaskGoalLists . end ( ) ,
[ ListID ] ( const TaskGoalList_Struct & t ) { return t . ListID = = ListID ; } ) ;
2013-05-06 13:07:41 -04:00
2018-06-11 22:25:44 -04:00
if ( it = = TaskGoalLists . end ( ) )
return - 1 ;
2013-02-16 16:14:39 -08:00
2018-06-11 22:25:44 -04:00
return std : : distance ( TaskGoalLists . begin ( ) , it ) ;
2013-02-16 16:14:39 -08:00
}
int TaskGoalListManager : : GetFirstEntry ( int ListID ) {
int ListIndex = GetListByID ( ListID ) ;
if ( ( ListIndex < 0 ) | | ( ListIndex > = NumberOfLists ) ) return - 1 ;
2018-06-11 22:49:20 -04:00
if ( TaskGoalLists [ ListIndex ] . GoalItemEntries . empty ( ) ) return - 1 ;
2013-02-16 16:14:39 -08:00
return TaskGoalLists [ ListIndex ] . GoalItemEntries [ 0 ] ;
}
2013-05-22 16:17:19 -04:00
std : : vector < int > TaskGoalListManager : : GetListContents ( int ListID ) {
2013-02-16 16:14:39 -08:00
2013-05-22 16:17:19 -04:00
std : : vector < int > ListContents ;
2013-02-16 16:14:39 -08:00
int ListIndex = GetListByID ( ListID ) ;
if ( ( ListIndex < 0 ) | | ( ListIndex > = NumberOfLists ) ) return ListContents ;
2018-06-11 22:49:20 -04:00
ListContents = TaskGoalLists [ ListIndex ] . GoalItemEntries ;
2013-02-16 16:14:39 -08:00
return ListContents ;
}
2018-06-11 22:49:20 -04:00
bool TaskGoalListManager : : IsInList ( int ListID , int Entry )
{
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] TaskGoalListManager::IsInList(%i, %i) " , ListID , Entry ) ;
2013-02-16 16:14:39 -08:00
int ListIndex = GetListByID ( ListID ) ;
2018-06-11 22:49:20 -04:00
if ( ( ListIndex < 0 ) | | ( ListIndex > = NumberOfLists ) )
return false ;
2013-02-16 16:14:39 -08:00
2018-06-11 22:49:20 -04:00
if ( ( Entry < TaskGoalLists [ ListIndex ] . Min ) | | ( Entry > TaskGoalLists [ ListIndex ] . Max ) )
2013-05-09 10:44:08 -04:00
return false ;
2013-02-16 16:14:39 -08:00
int FirstEntry = 0 ;
2018-06-11 22:49:20 -04:00
auto & task = TaskGoalLists [ ListIndex ] ;
2013-02-16 16:14:39 -08:00
2018-06-11 22:49:20 -04:00
auto it = std : : find ( task . GoalItemEntries . begin ( ) , task . GoalItemEntries . end ( ) , Entry ) ;
2013-02-16 16:14:39 -08:00
2018-06-11 22:49:20 -04:00
if ( it = = task . GoalItemEntries . end ( ) )
return false ;
2013-02-16 16:14:39 -08:00
2018-06-11 22:49:20 -04:00
Log ( Logs : : General , Logs : : Tasks , " [UPDATE] TaskGoalListManager::IsInList(%i, %i) returning true " , ListIndex ,
Entry ) ;
return true ;
2013-02-16 16:14:39 -08:00
}
TaskProximityManager : : TaskProximityManager ( ) {
}
TaskProximityManager : : ~ TaskProximityManager ( ) {
}
2014-10-08 14:15:43 -07:00
bool TaskProximityManager : : LoadProximities ( int zoneID ) {
TaskProximity proximity ;
2013-02-16 16:14:39 -08:00
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [GLOBALLOAD] TaskProximityManager::LoadProximities Called for zone %i " , zoneID ) ;
2013-02-16 16:14:39 -08:00
TaskProximities . clear ( ) ;
2014-10-08 14:15:43 -07:00
std : : string query = StringFormat ( " SELECT `exploreid`, `minx`, `maxx`, "
" `miny`, `maxy`, `minz`, `maxz` "
" FROM `proximities` WHERE `zoneid` = %i "
" ORDER BY `zoneid` ASC " , zoneID ) ;
auto results = database . QueryDatabase ( query ) ;
if ( ! results . Success ( ) ) {
return false ;
}
2013-02-16 16:14:39 -08:00
2014-10-08 14:15:43 -07:00
for ( auto row = results . begin ( ) ; row ! = results . end ( ) ; + + row ) {
proximity . ExploreID = atoi ( row [ 0 ] ) ;
proximity . MinX = atof ( row [ 1 ] ) ;
proximity . MaxX = atof ( row [ 2 ] ) ;
proximity . MinY = atof ( row [ 3 ] ) ;
proximity . MaxY = atof ( row [ 4 ] ) ;
proximity . MinZ = atof ( row [ 5 ] ) ;
proximity . MaxZ = atof ( row [ 6 ] ) ;
2013-02-16 16:14:39 -08:00
2014-10-08 14:15:43 -07:00
TaskProximities . push_back ( proximity ) ;
}
2013-02-16 16:14:39 -08:00
return true ;
}
int TaskProximityManager : : CheckProximities ( float X , float Y , float Z ) {
for ( unsigned int i = 0 ; i < TaskProximities . size ( ) ; i + + ) {
TaskProximity * P = & TaskProximities [ i ] ;
2017-04-01 03:51:46 -05:00
Log ( Logs : : General , Logs : : Tasks , " [PROXIMITY] Checking %8.3f, %8.3f, %8.3f against %8.3f, %8.3f, %8.3f, %8.3f, %8.3f, %8.3f " ,
2013-05-09 10:44:08 -04:00
X , Y , Z , P - > MinX , P - > MaxX , P - > MinY , P - > MaxY , P - > MinZ , P - > MaxZ ) ;
2013-02-16 16:14:39 -08:00
if ( X < P - > MinX | | X > P - > MaxX | | Y < P - > MinY | | Y > P - > MaxY | |
2013-05-09 10:44:08 -04:00
Z < P - > MinZ | | Z > P - > MaxZ ) continue ;
2013-02-16 16:14:39 -08:00
return P - > ExploreID ;
}
return 0 ;
}