Converted Quest Database to YAML (#4064)
* Changes TimeLimit format to be human readable. * Converts title to string. * Converts objectives and drop items to vectors. * General clean ups and optimizations. * Got rid of dummy_quest. * Added TXT to YAML converter. Thanks to @Atemo and @Normynator! Co-authored-by: atemo <capucrath@gmail.com>
This commit is contained in:
parent
e07d2b9ec2
commit
2f326bf04a
@ -1,7 +0,0 @@
|
|||||||
// Quest Database
|
|
||||||
//
|
|
||||||
// Structure of Database:
|
|
||||||
// Quest ID,Time Limit,Target1,Val1,Target2,Val2,Target3,Val3,MobID1,NameID1,Rate1,MobID2,NameID2,Rate2,MobID3,NameID3,Rate3,Quest Title
|
|
||||||
//
|
|
||||||
// The MobID*, NameID*, and Rate* reflect special values for quests that can drop an item at given rate from given mob.
|
|
||||||
// If no MobID* is given, then any mob has a chance to drop the given ItemID*.
|
|
44
db/import-tmpl/quest_db.yml
Normal file
44
db/import-tmpl/quest_db.yml
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# This file is a part of rAthena.
|
||||||
|
# Copyright(C) 2019 rAthena Development Team
|
||||||
|
# https://rathena.org - https://github.com/rathena
|
||||||
|
#
|
||||||
|
# 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, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
###########################################################################
|
||||||
|
# Quest Database
|
||||||
|
###########################################################################
|
||||||
|
#
|
||||||
|
# Quest Settings
|
||||||
|
#
|
||||||
|
###########################################################################
|
||||||
|
# - Id Quest ID.
|
||||||
|
# Title Quest title.
|
||||||
|
# TimeLimit Amount of time before the quest expires. (Default: 0)
|
||||||
|
# Use a number following by "d" for day(s), "h" for hour(s), "mn" for minute(s), and "s" for second(s).
|
||||||
|
# Specify with "+" for how long until the quest expires.
|
||||||
|
# Specify without "+" for the exact time the quest expires using "d" (optionnal), [0-23]"h" (required), [0-59]"mn" (optionnal), [0-59]"s" (optionnal) format.
|
||||||
|
# Please note the number before "d" only shift the exact timer to the given day(s).
|
||||||
|
# Targets: Quest objective target. (Default: null)
|
||||||
|
# - Mob Monster to kill.
|
||||||
|
# Count Amount of monsters to kill.
|
||||||
|
# Drops: Quest item drop targets. (Default: null)
|
||||||
|
# - Mob Monster to kill. 0 will apply to all monsters. (Default: 0)
|
||||||
|
# Item Item to drop.
|
||||||
|
# Count Amount of items that will drop. Non-stackable items default to 1. (Default: 1)
|
||||||
|
# Rate Item drop rate. (10000 = 100%)
|
||||||
|
###########################################################################
|
||||||
|
|
||||||
|
Header:
|
||||||
|
Type: QUEST_DB
|
||||||
|
Version: 1
|
File diff suppressed because it is too large
Load Diff
7098
db/pre-re/quest_db.yml
Normal file
7098
db/pre-re/quest_db.yml
Normal file
File diff suppressed because it is too large
Load Diff
52
db/quest_db.yml
Normal file
52
db/quest_db.yml
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# This file is a part of rAthena.
|
||||||
|
# Copyright(C) 2019 rAthena Development Team
|
||||||
|
# https://rathena.org - https://github.com/rathena
|
||||||
|
#
|
||||||
|
# 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, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
###########################################################################
|
||||||
|
# Quest Database
|
||||||
|
###########################################################################
|
||||||
|
#
|
||||||
|
# Quest Settings
|
||||||
|
#
|
||||||
|
###########################################################################
|
||||||
|
# - Id Quest ID.
|
||||||
|
# Title Quest title.
|
||||||
|
# TimeLimit Amount of time before the quest expires. (Default: 0)
|
||||||
|
# Use a number following by "d" for day(s), "h" for hour(s), "mn" for minute(s), and "s" for second(s).
|
||||||
|
# Specify with "+" for how long until the quest expires.
|
||||||
|
# Specify without "+" for the exact time the quest expires using "d" (optionnal), [0-23]"h" (required), [0-59]"mn" (optionnal), [0-59]"s" (optionnal) format.
|
||||||
|
# Please note the number before "d" only shift the exact timer to the given day(s).
|
||||||
|
# Targets: Quest objective target. (Default: null)
|
||||||
|
# - Mob Monster to kill.
|
||||||
|
# Count Amount of monsters to kill.
|
||||||
|
# Drops: Quest item drop targets. (Default: null)
|
||||||
|
# - Mob Monster to kill. 0 will apply to all monsters. (Default: 0)
|
||||||
|
# Item Item to drop.
|
||||||
|
# Count Amount of items that will drop. Non-stackable items default to 1. (Default: 1)
|
||||||
|
# Rate Item drop rate. (10000 = 100%)
|
||||||
|
###########################################################################
|
||||||
|
|
||||||
|
Header:
|
||||||
|
Type: QUEST_DB
|
||||||
|
Version: 1
|
||||||
|
|
||||||
|
Footer:
|
||||||
|
Imports:
|
||||||
|
- Path: db/pre-re/quest_db.yml
|
||||||
|
Mode: Prerenewal
|
||||||
|
- Path: db/re/quest_db.yml
|
||||||
|
Mode: Renewal
|
||||||
|
- Path: db/import/quest_db.yml
|
4053
db/re/quest_db.txt
4053
db/re/quest_db.txt
File diff suppressed because it is too large
Load Diff
9573
db/re/quest_db.yml
Normal file
9573
db/re/quest_db.yml
Normal file
File diff suppressed because it is too large
Load Diff
23
doc/yaml/db/quest_db.yml
Normal file
23
doc/yaml/db/quest_db.yml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
###########################################################################
|
||||||
|
# Quest Database
|
||||||
|
###########################################################################
|
||||||
|
#
|
||||||
|
# Quest Settings
|
||||||
|
#
|
||||||
|
###########################################################################
|
||||||
|
# - Id Quest ID.
|
||||||
|
# Title Quest title.
|
||||||
|
# TimeLimit Amount of time before the quest expires. (Default: 0)
|
||||||
|
# Use a number following by "d" for day(s), "h" for hour(s), "mn" for minute(s), and "s" for second(s).
|
||||||
|
# Specify with "+" for how long until the quest expires.
|
||||||
|
# Specify without "+" for the exact time the quest expires using "d" (optionnal), [0-23]"h" (required), [0-59]"mn" (optionnal), [0-59]"s" (optionnal) format.
|
||||||
|
# Please note the number before "d" only shift the exact timer to the given day(s).
|
||||||
|
# Targets: Quest objective target. (Default: null)
|
||||||
|
# - Mob Monster to kill.
|
||||||
|
# Count Amount of monsters to kill.
|
||||||
|
# Drops: Quest item drop targets. (Default: null)
|
||||||
|
# - Mob Monster to kill. 0 will apply to all monsters. (Default: 0)
|
||||||
|
# Item Item to drop.
|
||||||
|
# Count Amount of items that will drop. Non-stackable items default to 1. (Default: 1)
|
||||||
|
# Rate Item drop rate. (10000 = 100%)
|
||||||
|
###########################################################################
|
@ -102,7 +102,7 @@ bool mapif_quest_delete(uint32 char_id, int quest_id) {
|
|||||||
* @return false in case of errors, true otherwise
|
* @return false in case of errors, true otherwise
|
||||||
*/
|
*/
|
||||||
bool mapif_quest_add(uint32 char_id, struct quest qd) {
|
bool mapif_quest_add(uint32 char_id, struct quest qd) {
|
||||||
if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s`(`quest_id`, `char_id`, `state`, `time`, `count1`, `count2`, `count3`) VALUES ('%d', '%d', '%d','%d', '%d', '%d', '%d')", schema_config.quest_db, qd.quest_id, char_id, qd.state, qd.time, qd.count[0], qd.count[1], qd.count[2]) )
|
if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s`(`quest_id`, `char_id`, `state`, `time`, `count1`, `count2`, `count3`) VALUES ('%d', '%d', '%d', '%u', '%d', '%d', '%d')", schema_config.quest_db, qd.quest_id, char_id, qd.state, qd.time, qd.count[0], qd.count[1], qd.count[2]) )
|
||||||
{
|
{
|
||||||
Sql_ShowDebug(sql_handle);
|
Sql_ShowDebug(sql_handle);
|
||||||
return false;
|
return false;
|
||||||
|
@ -69,7 +69,6 @@
|
|||||||
#define MAX_GUILDLEVEL 50 ///Max Guild level
|
#define MAX_GUILDLEVEL 50 ///Max Guild level
|
||||||
#define MAX_GUARDIANS 8 ///Local max per castle. If this value is increased, need to add more fields on MySQL `guild_castle` table [Skotlex]
|
#define MAX_GUARDIANS 8 ///Local max per castle. If this value is increased, need to add more fields on MySQL `guild_castle` table [Skotlex]
|
||||||
#define MAX_QUEST_OBJECTIVES 3 ///Max quest objectives for a quest
|
#define MAX_QUEST_OBJECTIVES 3 ///Max quest objectives for a quest
|
||||||
#define MAX_QUEST_DROPS 3 ///Max quest drops for a quest
|
|
||||||
#define MAX_PC_BONUS_SCRIPT 50 ///Max bonus script can be fetched from `bonus_script` table on player load [Cydh]
|
#define MAX_PC_BONUS_SCRIPT 50 ///Max bonus script can be fetched from `bonus_script` table on player load [Cydh]
|
||||||
#define MAX_ITEM_RDM_OPT 5 /// Max item random option [Napster]
|
#define MAX_ITEM_RDM_OPT 5 /// Max item random option [Napster]
|
||||||
#define DB_NAME_LEN 256 //max len of dbs
|
#define DB_NAME_LEN 256 //max len of dbs
|
||||||
@ -212,7 +211,7 @@ enum e_mode {
|
|||||||
#define CL_MASK 0xF000000
|
#define CL_MASK 0xF000000
|
||||||
|
|
||||||
// Questlog states
|
// Questlog states
|
||||||
enum quest_state {
|
enum e_quest_state : uint8 {
|
||||||
Q_INACTIVE, ///< Inactive quest (the user can toggle between active and inactive quests)
|
Q_INACTIVE, ///< Inactive quest (the user can toggle between active and inactive quests)
|
||||||
Q_ACTIVE, ///< Active quest
|
Q_ACTIVE, ///< Active quest
|
||||||
Q_COMPLETE, ///< Completed quest
|
Q_COMPLETE, ///< Completed quest
|
||||||
@ -221,9 +220,9 @@ enum quest_state {
|
|||||||
/// Questlog entry
|
/// Questlog entry
|
||||||
struct quest {
|
struct quest {
|
||||||
int quest_id; ///< Quest ID
|
int quest_id; ///< Quest ID
|
||||||
unsigned int time; ///< Expiration time
|
uint32 time; ///< Expiration time
|
||||||
int count[MAX_QUEST_OBJECTIVES]; ///< Kill counters of each quest objective
|
int count[MAX_QUEST_OBJECTIVES]; ///< Kill counters of each quest objective
|
||||||
enum quest_state state; ///< Current quest state
|
e_quest_state state; ///< Current quest state
|
||||||
};
|
};
|
||||||
|
|
||||||
struct s_item_randomoption {
|
struct s_item_randomoption {
|
||||||
|
@ -4027,7 +4027,7 @@ ACMD_FUNC(reload) {
|
|||||||
map_msg_reload();
|
map_msg_reload();
|
||||||
clif_displaymessage(fd, msg_txt(sd,463)); // Message configuration has been reloaded.
|
clif_displaymessage(fd, msg_txt(sd,463)); // Message configuration has been reloaded.
|
||||||
} else if (strstr(command, "questdb") || strncmp(message, "questdb", 3) == 0) {
|
} else if (strstr(command, "questdb") || strncmp(message, "questdb", 3) == 0) {
|
||||||
do_reload_quest();
|
if (quest_db.reload())
|
||||||
clif_displaymessage(fd, msg_txt(sd,1377)); // Quest database has been reloaded.
|
clif_displaymessage(fd, msg_txt(sd,1377)); // Quest database has been reloaded.
|
||||||
} else if (strstr(command, "instancedb") || strncmp(message, "instancedb", 4) == 0) {
|
} else if (strstr(command, "instancedb") || strncmp(message, "instancedb", 4) == 0) {
|
||||||
instance_reload();
|
instance_reload();
|
||||||
|
@ -16929,25 +16929,24 @@ void clif_quest_send_list(struct map_session_data *sd)
|
|||||||
WFIFOL(fd, 4) = limit;
|
WFIFOL(fd, 4) = limit;
|
||||||
|
|
||||||
for (i = 0; i < limit; i++) {
|
for (i = 0; i < limit; i++) {
|
||||||
struct quest_db *qi = quest_search(sd->quest_log[i].quest_id);
|
std::shared_ptr<s_quest_db> qi = quest_search(sd->quest_log[i].quest_id);
|
||||||
|
|
||||||
WFIFOL(fd, offset) = sd->quest_log[i].quest_id;
|
WFIFOL(fd, offset) = sd->quest_log[i].quest_id;
|
||||||
offset += 4;
|
offset += 4;
|
||||||
WFIFOB(fd, offset) = sd->quest_log[i].state;
|
WFIFOB(fd, offset) = sd->quest_log[i].state;
|
||||||
offset++;
|
offset++;
|
||||||
WFIFOL(fd, offset) = sd->quest_log[i].time - qi->time;
|
WFIFOL(fd, offset) = static_cast<uint32>(sd->quest_log[i].time - qi->time);
|
||||||
offset += 4;
|
offset += 4;
|
||||||
WFIFOL(fd, offset) = sd->quest_log[i].time;
|
WFIFOL(fd, offset) = static_cast<uint32>(sd->quest_log[i].time);
|
||||||
offset += 4;
|
offset += 4;
|
||||||
WFIFOW(fd, offset) = qi->objectives_count;
|
WFIFOW(fd, offset) = static_cast<uint16>(qi->objectives.size());
|
||||||
offset += 2;
|
offset += 2;
|
||||||
|
|
||||||
if( qi->objectives_count > 0 ){
|
if (!qi->objectives.empty()) {
|
||||||
int j;
|
|
||||||
struct mob_db *mob;
|
struct mob_db *mob;
|
||||||
|
|
||||||
for( j = 0; j < qi->objectives_count; j++ ){
|
for (int j = 0; j < qi->objectives.size(); j++) {
|
||||||
mob = mob_db(qi->objectives[j].mob);
|
mob = mob_db(qi->objectives[j]->mob_id);
|
||||||
|
|
||||||
#if PACKETVER >= 20150513
|
#if PACKETVER >= 20150513
|
||||||
WFIFOL(fd, offset) = sd->quest_log[i].quest_id * 1000 + j;
|
WFIFOL(fd, offset) = sd->quest_log[i].quest_id * 1000 + j;
|
||||||
@ -16955,7 +16954,7 @@ void clif_quest_send_list(struct map_session_data *sd)
|
|||||||
WFIFOL(fd, offset) = 0; // TODO: Find info - mobType
|
WFIFOL(fd, offset) = 0; // TODO: Find info - mobType
|
||||||
offset += 4;
|
offset += 4;
|
||||||
#endif
|
#endif
|
||||||
WFIFOL(fd, offset) = qi->objectives[j].mob;
|
WFIFOL(fd, offset) = qi->objectives[j]->mob_id;
|
||||||
offset += 4;
|
offset += 4;
|
||||||
#if PACKETVER >= 20150513
|
#if PACKETVER >= 20150513
|
||||||
WFIFOW(fd, offset) = 0; // TODO: Find info - levelMin
|
WFIFOW(fd, offset) = 0; // TODO: Find info - levelMin
|
||||||
@ -16965,7 +16964,7 @@ void clif_quest_send_list(struct map_session_data *sd)
|
|||||||
#endif
|
#endif
|
||||||
WFIFOW(fd, offset) = sd->quest_log[i].count[j];
|
WFIFOW(fd, offset) = sd->quest_log[i].count[j];
|
||||||
offset += 2;
|
offset += 2;
|
||||||
WFIFOW(fd, offset) = qi->objectives[j].count;
|
WFIFOW(fd, offset) = qi->objectives[j]->count;
|
||||||
offset += 2;
|
offset += 2;
|
||||||
safestrncpy((char*)WFIFOP(fd, offset), mob->jname, NAME_LENGTH);
|
safestrncpy((char*)WFIFOP(fd, offset), mob->jname, NAME_LENGTH);
|
||||||
offset += NAME_LENGTH;
|
offset += NAME_LENGTH;
|
||||||
@ -16998,7 +16997,7 @@ void clif_quest_send_list(struct map_session_data *sd)
|
|||||||
void clif_quest_send_mission(struct map_session_data *sd)
|
void clif_quest_send_mission(struct map_session_data *sd)
|
||||||
{
|
{
|
||||||
int fd = sd->fd;
|
int fd = sd->fd;
|
||||||
int i, j, limit = 0;
|
int limit = 0;
|
||||||
int len = sd->avail_quests*104+8;
|
int len = sd->avail_quests*104+8;
|
||||||
struct mob_db *mob;
|
struct mob_db *mob;
|
||||||
|
|
||||||
@ -17008,18 +17007,18 @@ void clif_quest_send_mission(struct map_session_data *sd)
|
|||||||
WFIFOW(fd, 2) = len;
|
WFIFOW(fd, 2) = len;
|
||||||
WFIFOL(fd, 4) = limit;
|
WFIFOL(fd, 4) = limit;
|
||||||
|
|
||||||
for (i = 0; i < limit; i++) {
|
for (int i = 0; i < limit; i++) {
|
||||||
struct quest_db *qi = quest_search(sd->quest_log[i].quest_id);
|
std::shared_ptr<s_quest_db> qi = quest_search(sd->quest_log[i].quest_id);
|
||||||
|
|
||||||
WFIFOL(fd, i*104+8) = sd->quest_log[i].quest_id;
|
WFIFOL(fd, i*104+8) = sd->quest_log[i].quest_id;
|
||||||
WFIFOL(fd, i*104+12) = sd->quest_log[i].time - qi->time;
|
WFIFOL(fd, i*104+12) = static_cast<uint32>(sd->quest_log[i].time - qi->time);
|
||||||
WFIFOL(fd, i*104+16) = sd->quest_log[i].time;
|
WFIFOL(fd, i*104+16) = static_cast<uint32>(sd->quest_log[i].time);
|
||||||
WFIFOW(fd, i*104+20) = qi->objectives_count;
|
WFIFOW(fd, i*104+20) = static_cast<uint16>(qi->objectives.size());
|
||||||
|
|
||||||
for (j = 0 ; j < qi->objectives_count; j++) {
|
for (int j = 0 ; j < qi->objectives.size(); j++) {
|
||||||
WFIFOL(fd, i*104+22+j*30) = qi->objectives[j].mob;
|
WFIFOL(fd, i*104+22+j*30) = qi->objectives[j]->mob_id;
|
||||||
WFIFOW(fd, i*104+26+j*30) = sd->quest_log[i].count[j];
|
WFIFOW(fd, i*104+26+j*30) = sd->quest_log[i].count[j];
|
||||||
mob = mob_db(qi->objectives[j].mob);
|
mob = mob_db(qi->objectives[j]->mob_id);
|
||||||
safestrncpy(WFIFOCP(fd, i*104+28+j*30), mob->jname, NAME_LENGTH);
|
safestrncpy(WFIFOCP(fd, i*104+28+j*30), mob->jname, NAME_LENGTH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -17035,8 +17034,7 @@ void clif_quest_send_mission(struct map_session_data *sd)
|
|||||||
void clif_quest_add(struct map_session_data *sd, struct quest *qd)
|
void clif_quest_add(struct map_session_data *sd, struct quest *qd)
|
||||||
{
|
{
|
||||||
int fd = sd->fd;
|
int fd = sd->fd;
|
||||||
int i, offset;
|
std::shared_ptr<s_quest_db> qi = quest_search(qd->quest_id);
|
||||||
struct quest_db *qi = quest_search(qd->quest_id);
|
|
||||||
#if PACKETVER >= 20150513
|
#if PACKETVER >= 20150513
|
||||||
int cmd = 0x9f9;
|
int cmd = 0x9f9;
|
||||||
#else
|
#else
|
||||||
@ -17047,11 +17045,11 @@ void clif_quest_add(struct map_session_data *sd, struct quest *qd)
|
|||||||
WFIFOW(fd, 0) = cmd;
|
WFIFOW(fd, 0) = cmd;
|
||||||
WFIFOL(fd, 2) = qd->quest_id;
|
WFIFOL(fd, 2) = qd->quest_id;
|
||||||
WFIFOB(fd, 6) = qd->state;
|
WFIFOB(fd, 6) = qd->state;
|
||||||
WFIFOB(fd, 7) = qd->time - qi->time;
|
WFIFOB(fd, 7) = static_cast<uint8>(qd->time - qi->time);
|
||||||
WFIFOL(fd, 11) = qd->time;
|
WFIFOL(fd, 11) = static_cast<uint32>(qd->time);
|
||||||
WFIFOW(fd, 15) = qi->objectives_count;
|
WFIFOW(fd, 15) = static_cast<uint16>(qi->objectives.size());
|
||||||
|
|
||||||
for (i = 0, offset = 17; i < qi->objectives_count; i++) {
|
for (int i = 0, offset = 17; i < qi->objectives.size(); i++) {
|
||||||
struct mob_db *mob;
|
struct mob_db *mob;
|
||||||
#if PACKETVER >= 20150513
|
#if PACKETVER >= 20150513
|
||||||
WFIFOL(fd, offset) = qd->quest_id * 1000 + i;
|
WFIFOL(fd, offset) = qd->quest_id * 1000 + i;
|
||||||
@ -17059,7 +17057,7 @@ void clif_quest_add(struct map_session_data *sd, struct quest *qd)
|
|||||||
WFIFOL(fd, offset) = 0; // TODO: Find info - mobType
|
WFIFOL(fd, offset) = 0; // TODO: Find info - mobType
|
||||||
offset += 4;
|
offset += 4;
|
||||||
#endif
|
#endif
|
||||||
WFIFOL(fd, offset) = qi->objectives[i].mob;
|
WFIFOL(fd, offset) = qi->objectives[i]->mob_id;
|
||||||
offset += 4;
|
offset += 4;
|
||||||
#if PACKETVER >= 20150513
|
#if PACKETVER >= 20150513
|
||||||
WFIFOW(fd, offset) = 0; // TODO: Find info - levelMin
|
WFIFOW(fd, offset) = 0; // TODO: Find info - levelMin
|
||||||
@ -17069,7 +17067,7 @@ void clif_quest_add(struct map_session_data *sd, struct quest *qd)
|
|||||||
#endif
|
#endif
|
||||||
WFIFOW(fd, offset) = qd->count[i];
|
WFIFOW(fd, offset) = qd->count[i];
|
||||||
offset += 2;
|
offset += 2;
|
||||||
mob = mob_db(qi->objectives[i].mob);
|
mob = mob_db(qi->objectives[i]->mob_id);
|
||||||
safestrncpy(WFIFOCP(fd, offset), mob->jname, NAME_LENGTH);
|
safestrncpy(WFIFOCP(fd, offset), mob->jname, NAME_LENGTH);
|
||||||
offset += NAME_LENGTH;
|
offset += NAME_LENGTH;
|
||||||
}
|
}
|
||||||
@ -17077,16 +17075,16 @@ void clif_quest_add(struct map_session_data *sd, struct quest *qd)
|
|||||||
WFIFOSET(fd, packet_len(cmd));
|
WFIFOSET(fd, packet_len(cmd));
|
||||||
|
|
||||||
#if PACKETVER >= 20150513
|
#if PACKETVER >= 20150513
|
||||||
int len = 4 + qi->objectives_count * 12;
|
int len = 4 + qi->objectives.size() * 12;
|
||||||
|
|
||||||
WFIFOHEAD(fd, len);
|
WFIFOHEAD(fd, len);
|
||||||
WFIFOW(fd, 0) = 0x8fe;
|
WFIFOW(fd, 0) = 0x8fe;
|
||||||
WFIFOW(fd, 2) = len;
|
WFIFOW(fd, 2) = len;
|
||||||
|
|
||||||
for( i = 0, offset = 4; i < qi->objectives_count; i++, offset += 12 ){
|
for (int i = 0, offset = 4; i < qi->objectives.size(); i++, offset += 12) {
|
||||||
WFIFOL(fd, offset) = qd->quest_id * 1000 + i;
|
WFIFOL(fd, offset) = qd->quest_id * 1000 + i;
|
||||||
WFIFOL(fd, offset+4) = qi->objectives[i].mob;
|
WFIFOL(fd, offset+4) = qi->objectives[i]->mob_id;
|
||||||
WFIFOW(fd, offset + 10) = qi->objectives[i].count;
|
WFIFOW(fd, offset + 10) = qi->objectives[i]->count;
|
||||||
WFIFOW(fd, offset + 12) = qd->count[i];
|
WFIFOW(fd, offset + 12) = qd->count[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17115,9 +17113,9 @@ void clif_quest_delete(struct map_session_data *sd, int quest_id)
|
|||||||
void clif_quest_update_objective(struct map_session_data *sd, struct quest *qd, int mobid)
|
void clif_quest_update_objective(struct map_session_data *sd, struct quest *qd, int mobid)
|
||||||
{
|
{
|
||||||
int fd = sd->fd;
|
int fd = sd->fd;
|
||||||
int i, offset;
|
int offset = 6;
|
||||||
struct quest_db *qi = quest_search(qd->quest_id);
|
std::shared_ptr<s_quest_db> qi = quest_search(qd->quest_id);
|
||||||
int len = qi->objectives_count * 12 + 6;
|
int len = qi->objectives.size() * 12 + 6;
|
||||||
#if PACKETVER >= 20150513
|
#if PACKETVER >= 20150513
|
||||||
int cmd = 0x9fa;
|
int cmd = 0x9fa;
|
||||||
#else
|
#else
|
||||||
@ -17126,20 +17124,20 @@ void clif_quest_update_objective(struct map_session_data *sd, struct quest *qd,
|
|||||||
|
|
||||||
WFIFOHEAD(fd, len);
|
WFIFOHEAD(fd, len);
|
||||||
WFIFOW(fd, 0) = cmd;
|
WFIFOW(fd, 0) = cmd;
|
||||||
WFIFOW(fd, 4) = qi->objectives_count;
|
WFIFOW(fd, 4) = static_cast<uint16>(qi->objectives.size());
|
||||||
|
|
||||||
for (i = 0, offset = 6; i < qi->objectives_count; i++) {
|
for (int i = 0; i < qi->objectives.size(); i++) {
|
||||||
if (mobid == 0 || mobid == qi->objectives[i].mob) {
|
if (mobid == 0 || mobid == qi->objectives[i]->mob_id) {
|
||||||
WFIFOL(fd, offset) = qd->quest_id;
|
WFIFOL(fd, offset) = qd->quest_id;
|
||||||
offset += 4;
|
offset += 4;
|
||||||
#if PACKETVER >= 20150513
|
#if PACKETVER >= 20150513
|
||||||
WFIFOL(fd, offset) = qd->quest_id * 1000 + i;
|
WFIFOL(fd, offset) = qd->quest_id * 1000 + i;
|
||||||
offset += 4;
|
offset += 4;
|
||||||
#else
|
#else
|
||||||
WFIFOL(fd, offset) = qi->objectives[i].mob;
|
WFIFOL(fd, offset) = qi->objectives[i].mob_id;
|
||||||
offset += 4;
|
offset += 4;
|
||||||
#endif
|
#endif
|
||||||
WFIFOW(fd, offset) = qi->objectives[i].count;
|
WFIFOW(fd, offset) = qi->objectives[i]->count;
|
||||||
offset += 2;
|
offset += 2;
|
||||||
WFIFOW(fd, offset) = qd->count[i];
|
WFIFOW(fd, offset) = qd->count[i];
|
||||||
offset += 2;
|
offset += 2;
|
||||||
|
@ -2041,15 +2041,15 @@ void intif_parse_questlog(int fd)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
struct quest *received = (struct quest *)RFIFOP(fd,8);
|
struct quest *received = (struct quest *)RFIFOP(fd,8);
|
||||||
int i, k = num_received;
|
int k = num_received;
|
||||||
|
|
||||||
if(sd->quest_log)
|
if(sd->quest_log)
|
||||||
RECREATE(sd->quest_log, struct quest, num_received);
|
RECREATE(sd->quest_log, struct quest, num_received);
|
||||||
else
|
else
|
||||||
CREATE(sd->quest_log, struct quest, num_received);
|
CREATE(sd->quest_log, struct quest, num_received);
|
||||||
|
|
||||||
for(i = 0; i < num_received; i++) {
|
for(int i = 0; i < num_received; i++) {
|
||||||
if(quest_search(received[i].quest_id) == &quest_dummy) {
|
if(!quest_search(received[i].quest_id)) {
|
||||||
ShowError("intif_parse_QuestLog: quest %d not found in DB.\n", received[i].quest_id);
|
ShowError("intif_parse_QuestLog: quest %d not found in DB.\n", received[i].quest_id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -357,7 +357,7 @@
|
|||||||
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\mob_skill_db.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\mob_skill_db.txt')" />
|
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\mob_skill_db.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\mob_skill_db.txt')" />
|
||||||
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\pet_db.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\pet_db.yml')" />
|
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\pet_db.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\pet_db.yml')" />
|
||||||
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\produce_db.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\produce_db.txt')" />
|
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\produce_db.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\produce_db.txt')" />
|
||||||
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\quest_db.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\quest_db.txt')" />
|
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\quest_db.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\quest_db.yml')" />
|
||||||
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\refine_db.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\refine_db.yml')" />
|
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\refine_db.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\refine_db.yml')" />
|
||||||
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\size_fix.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\size_fix.yml')" />
|
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\size_fix.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\size_fix.yml')" />
|
||||||
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\skill_changematerial_db.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\skill_changematerial_db.txt')" />
|
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\skill_changematerial_db.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\skill_changematerial_db.txt')" />
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
#include "../common/showmsg.hpp"
|
#include "../common/showmsg.hpp"
|
||||||
#include "../common/socket.hpp"
|
#include "../common/socket.hpp"
|
||||||
#include "../common/strlib.hpp"
|
#include "../common/strlib.hpp"
|
||||||
|
#include "../common/utilities.hpp"
|
||||||
|
#include "../common/utils.hpp"
|
||||||
|
|
||||||
#include "battle.hpp"
|
#include "battle.hpp"
|
||||||
#include "chrif.hpp"
|
#include "chrif.hpp"
|
||||||
@ -24,20 +26,296 @@
|
|||||||
#include "party.hpp"
|
#include "party.hpp"
|
||||||
#include "pc.hpp"
|
#include "pc.hpp"
|
||||||
|
|
||||||
static DBMap *questdb;
|
static int split_exact_quest_time(char* modif_p, int* day, int* hour, int* minute, int *second);
|
||||||
static void questdb_free_sub(struct quest_db *quest, bool free);
|
|
||||||
struct quest_db quest_dummy;
|
const std::string QuestDatabase::getDefaultLocation() {
|
||||||
|
return std::string(db_path) + "/quest_db.yml";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads and parses an entry from the quest_db.
|
||||||
|
* @param node: YAML node containing the entry.
|
||||||
|
* @return count of successfully parsed rows
|
||||||
|
*/
|
||||||
|
uint64 QuestDatabase::parseBodyNode(const YAML::Node &node) {
|
||||||
|
uint32 quest_id;
|
||||||
|
|
||||||
|
if (!this->asUInt32(node, "Id", quest_id))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
std::shared_ptr<s_quest_db> quest = this->find(quest_id);
|
||||||
|
bool exists = quest != nullptr;
|
||||||
|
|
||||||
|
if (!exists) {
|
||||||
|
if (!this->nodesExist(node, { "Title" }))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
quest = std::make_shared<s_quest_db>();
|
||||||
|
quest->id = quest_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->nodeExists(node, "Title")) {
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
if (!this->asString(node, "Title", name))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
quest->name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->nodeExists(node, "TimeLimit")) {
|
||||||
|
std::string time;
|
||||||
|
|
||||||
|
if (!this->asString(node, "TimeLimit", time))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (time.find("+") != std::string::npos) {
|
||||||
|
double timediff = solve_time(const_cast<char *>(time.c_str()));
|
||||||
|
|
||||||
|
if (timediff <= 0) {
|
||||||
|
this->invalidWarning(node["TimeLimit"], "Incorrect TimeLimit format %s given, skipping.\n", time.c_str());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
quest->time = static_cast<time_t>(timediff);
|
||||||
|
}
|
||||||
|
else {// '+' not found, set to specific time
|
||||||
|
int32 day, hour, minute, second;
|
||||||
|
|
||||||
|
if (split_exact_quest_time(const_cast<char *>(time.c_str()), &day, &hour, &minute, &second) == 0) {
|
||||||
|
this->invalidWarning(node["TimeLimit"], "Incorrect TimeLimit format %s given, skipping.\n", time.c_str());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
quest->time = day * 86400 + hour * 3600 + minute * 60 + second;
|
||||||
|
quest->time_at = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (!exists) {
|
||||||
|
quest->time = 0;
|
||||||
|
quest->time_at = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->nodeExists(node, "Targets")) {
|
||||||
|
const YAML::Node &targets = node["Targets"];
|
||||||
|
|
||||||
|
for (const YAML::Node &targetNode : targets) {
|
||||||
|
if (quest->objectives.size() >= MAX_QUEST_OBJECTIVES) {
|
||||||
|
this->invalidWarning(targetNode, "Targets list exceeds the maximum of %d, skipping.\n", MAX_QUEST_OBJECTIVES);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->nodeExists(targetNode, "Mob"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string mob_name;
|
||||||
|
|
||||||
|
if (!this->asString(targetNode, "Mob", mob_name))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
struct mob_db *mob = mobdb_search_aegisname(mob_name.c_str());
|
||||||
|
|
||||||
|
if (!mob) {
|
||||||
|
this->invalidWarning(targetNode["Mob"], "Mob %s does not exist, skipping.\n", mob_name.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//std::shared_ptr<s_quest_objective> target = util::vector_find(quest->objectives, mob->vd.class_);
|
||||||
|
std::shared_ptr<s_quest_objective> target;
|
||||||
|
std::vector<std::shared_ptr<s_quest_objective>>::iterator it = std::find_if(quest->objectives.begin(), quest->objectives.end(), [&](std::shared_ptr<s_quest_objective> const &v) {
|
||||||
|
return (*v).mob_id == mob->vd.class_;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (it != quest->objectives.end())
|
||||||
|
target = (*it);
|
||||||
|
else
|
||||||
|
target = nullptr;
|
||||||
|
|
||||||
|
bool targetExists = target != nullptr;
|
||||||
|
|
||||||
|
if (!targetExists) {
|
||||||
|
if (!this->nodeExists(targetNode, "Count")) {
|
||||||
|
this->invalidWarning(targetNode["Count"], "Targets has no Count value specified, skipping.\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
target = std::make_shared<s_quest_objective>();
|
||||||
|
target->mob_id = mob->vd.class_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->nodeExists(targetNode, "Count")) {
|
||||||
|
uint16 count;
|
||||||
|
|
||||||
|
if (!this->asUInt16(targetNode, "Count", count))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
target->count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
quest->objectives.push_back(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->nodeExists(node, "Drops")) {
|
||||||
|
const YAML::Node &drops = node["Drops"];
|
||||||
|
|
||||||
|
for (const YAML::Node &dropNode : drops) {
|
||||||
|
if (quest->objectives.size() >= MAX_QUEST_OBJECTIVES) {
|
||||||
|
this->invalidWarning(dropNode, "Drops list exceeds the maximum of %d, skipping.\n", MAX_QUEST_OBJECTIVES);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 mob_id = 0; // Can be 0 which means all monsters
|
||||||
|
|
||||||
|
if (this->nodeExists(dropNode, "Mob")) {
|
||||||
|
std::string mob_name;
|
||||||
|
|
||||||
|
if (!this->asString(dropNode, "Mob", mob_name))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
struct mob_db *mob = mobdb_search_aegisname(mob_name.c_str());
|
||||||
|
|
||||||
|
if (!mob) {
|
||||||
|
this->invalidWarning(dropNode["Mob"], "Mob %s does not exist, skipping.\n", mob_name.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
mob_id = mob->vd.class_;
|
||||||
|
}
|
||||||
|
|
||||||
|
//std::shared_ptr<s_quest_dropitem> target = util::vector_find(quest->dropitem, mob_id);
|
||||||
|
std::shared_ptr<s_quest_dropitem> target;
|
||||||
|
std::vector<std::shared_ptr<s_quest_dropitem>>::iterator it = std::find_if(quest->dropitem.begin(), quest->dropitem.end(), [&](std::shared_ptr<s_quest_dropitem> const &v) {
|
||||||
|
return (*v).mob_id == mob_id;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (it != quest->dropitem.end())
|
||||||
|
target = (*it);
|
||||||
|
else
|
||||||
|
target = nullptr;
|
||||||
|
|
||||||
|
bool targetExists = target != nullptr;
|
||||||
|
|
||||||
|
if (!targetExists) {
|
||||||
|
if (!this->nodeExists(dropNode, "Item")) {
|
||||||
|
this->invalidWarning(dropNode["Item"], "Drops has no Item value specified, skipping.\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->nodeExists(dropNode, "Rate")) {
|
||||||
|
this->invalidWarning(dropNode["Item"], "Drops has no Rate value specified, skipping.\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
target = std::make_shared<s_quest_dropitem>();
|
||||||
|
target->mob_id = mob_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->nodeExists(dropNode, "Item")) {
|
||||||
|
std::string item_name;
|
||||||
|
|
||||||
|
if (!this->asString(dropNode, "Item", item_name))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
struct item_data *item = itemdb_search_aegisname(item_name.c_str());
|
||||||
|
|
||||||
|
if (!item) {
|
||||||
|
this->invalidWarning(dropNode["Item"], "Item %s does not exist, skipping.\n", item_name.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
target->nameid = item->nameid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->nodeExists(dropNode, "Count")) {
|
||||||
|
uint16 count;
|
||||||
|
|
||||||
|
if (!this->asUInt16(dropNode, "Count", count))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!itemdb_isstackable(target->nameid)) {
|
||||||
|
this->invalidWarning(dropNode["Count"], "Item %s is not stackable, capping to 1.\n", itemdb_name(target->nameid));
|
||||||
|
count = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
target->count = count;
|
||||||
|
} else {
|
||||||
|
if (!targetExists)
|
||||||
|
target->count = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->nodeExists(dropNode, "Rate")) {
|
||||||
|
uint16 rate;
|
||||||
|
|
||||||
|
if (!this->asUInt16(dropNode, "Rate", rate))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
target->rate = rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
quest->dropitem.push_back(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!exists)
|
||||||
|
this->put(quest_id, quest);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int split_exact_quest_time(char* modif_p, int* day, int* hour, int* minute, int *second) {
|
||||||
|
int d = -1, h = -1, mn = -1, s = -1;
|
||||||
|
|
||||||
|
nullpo_retr(0, modif_p);
|
||||||
|
|
||||||
|
while (modif_p[0] != '\0') {
|
||||||
|
int value = atoi(modif_p);
|
||||||
|
|
||||||
|
if (modif_p[0] == '-' || modif_p[0] == '+')
|
||||||
|
modif_p++;
|
||||||
|
while (modif_p[0] >= '0' && modif_p[0] <= '9')
|
||||||
|
modif_p++;
|
||||||
|
if (modif_p[0] == 's') {
|
||||||
|
s = value;
|
||||||
|
modif_p++;
|
||||||
|
} else if (modif_p[0] == 'm' && modif_p[1] == 'n') {
|
||||||
|
mn = value;
|
||||||
|
modif_p = modif_p + 2;
|
||||||
|
} else if (modif_p[0] == 'h') {
|
||||||
|
h = value;
|
||||||
|
modif_p++;
|
||||||
|
} else if (modif_p[0] == 'd' || modif_p[0] == 'j') {
|
||||||
|
d = value;
|
||||||
|
modif_p++;
|
||||||
|
} else if (modif_p[0] != '\0') {
|
||||||
|
modif_p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (h < 0 || h > 23 || mn > 59 || s > 59) // hour is required
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
*day = max(0,d);
|
||||||
|
*hour = h;
|
||||||
|
*minute = max(0,mn);
|
||||||
|
*second = max(0,s);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches a quest by ID.
|
* Searches a quest by ID.
|
||||||
* @param quest_id : ID to lookup
|
* @param quest_id : ID to lookup
|
||||||
* @return Quest entry (equals to &quest_dummy if the ID is invalid)
|
* @return Quest entry or nullptr on failure
|
||||||
*/
|
*/
|
||||||
struct quest_db *quest_search(int quest_id)
|
std::shared_ptr<s_quest_db> quest_search(int quest_id)
|
||||||
{
|
{
|
||||||
struct quest_db *quest = (struct quest_db *)idb_get(questdb, quest_id);
|
auto quest = quest_db.find(quest_id);
|
||||||
|
|
||||||
if (!quest)
|
if (!quest)
|
||||||
return &quest_dummy;
|
return nullptr;
|
||||||
|
|
||||||
return quest;
|
return quest;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,13 +324,9 @@ struct quest_db *quest_search(int quest_id)
|
|||||||
* @param sd : Player's data
|
* @param sd : Player's data
|
||||||
* @return 0 in case of success, nonzero otherwise (i.e. the player has no quests)
|
* @return 0 in case of success, nonzero otherwise (i.e. the player has no quests)
|
||||||
*/
|
*/
|
||||||
int quest_pc_login(TBL_PC *sd)
|
int quest_pc_login(struct map_session_data *sd)
|
||||||
{
|
{
|
||||||
#if PACKETVER < 20141022
|
if (!sd->avail_quests)
|
||||||
int i;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if( sd->avail_quests == 0 )
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
clif_quest_send_list(sd);
|
clif_quest_send_list(sd);
|
||||||
@ -61,13 +335,39 @@ int quest_pc_login(TBL_PC *sd)
|
|||||||
clif_quest_send_mission(sd);
|
clif_quest_send_mission(sd);
|
||||||
|
|
||||||
//@TODO[Haru]: Is this necessary? Does quest_send_mission not take care of this?
|
//@TODO[Haru]: Is this necessary? Does quest_send_mission not take care of this?
|
||||||
for( i = 0; i < sd->avail_quests; i++ )
|
for (int i = 0; i < sd->avail_quests; i++)
|
||||||
clif_quest_update_objective(sd, &sd->quest_log[i], 0);
|
clif_quest_update_objective(sd, &sd->quest_log[i], 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine a quest's time limit.
|
||||||
|
* @param qi: Quest data
|
||||||
|
* @return Time limit value
|
||||||
|
*/
|
||||||
|
static time_t quest_time(std::shared_ptr<s_quest_db> qi)
|
||||||
|
{
|
||||||
|
if (!qi || qi->time < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!qi->time_at && qi->time > 0)
|
||||||
|
return time(nullptr) + qi->time;
|
||||||
|
else if (qi->time_at) {
|
||||||
|
time_t t = time(nullptr);
|
||||||
|
struct tm *lt = localtime(&t);
|
||||||
|
uint32 time_today = lt->tm_hour * 3600 + lt->tm_min * 60 + lt->tm_sec;
|
||||||
|
|
||||||
|
if (time_today < (qi->time % 86400))
|
||||||
|
return static_cast<time_t>(t + qi->time - time_today);
|
||||||
|
else // Carry over to the next day
|
||||||
|
return static_cast<time_t>(t + 86400 + qi->time - time_today);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a quest to the player's list.
|
* Adds a quest to the player's list.
|
||||||
* New quest will be added as Q_ACTIVE.
|
* New quest will be added as Q_ACTIVE.
|
||||||
@ -75,53 +375,34 @@ int quest_pc_login(TBL_PC *sd)
|
|||||||
* @param quest_id : ID of the quest to add.
|
* @param quest_id : ID of the quest to add.
|
||||||
* @return 0 in case of success, nonzero otherwise
|
* @return 0 in case of success, nonzero otherwise
|
||||||
*/
|
*/
|
||||||
int quest_add(TBL_PC *sd, int quest_id)
|
int quest_add(struct map_session_data *sd, int quest_id)
|
||||||
{
|
{
|
||||||
int n;
|
std::shared_ptr<s_quest_db> qi = quest_search(quest_id);
|
||||||
struct quest_db *qi = quest_search(quest_id);
|
|
||||||
|
|
||||||
if( qi == &quest_dummy ) {
|
if (!qi) {
|
||||||
ShowError("quest_add: quest %d not found in DB.\n", quest_id);
|
ShowError("quest_add: quest %d not found in DB.\n", quest_id);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( quest_check(sd, quest_id, HAVEQUEST) >= 0 ) {
|
if (quest_check(sd, quest_id, HAVEQUEST) >= 0) {
|
||||||
ShowError("quest_add: Character %d already has quest %d.\n", sd->status.char_id, quest_id);
|
ShowError("quest_add: Character %d already has quest %d.\n", sd->status.char_id, quest_id);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
n = sd->avail_quests; //Insertion point
|
int n = sd->avail_quests; //Insertion point
|
||||||
|
|
||||||
sd->num_quests++;
|
sd->num_quests++;
|
||||||
sd->avail_quests++;
|
sd->avail_quests++;
|
||||||
RECREATE(sd->quest_log, struct quest, sd->num_quests);
|
RECREATE(sd->quest_log, struct quest, sd->num_quests);
|
||||||
|
|
||||||
//The character has some completed quests, make room before them so that they will stay at the end of the array
|
//The character has some completed quests, make room before them so that they will stay at the end of the array
|
||||||
if( sd->avail_quests != sd->num_quests )
|
if (sd->avail_quests != sd->num_quests)
|
||||||
memmove(&sd->quest_log[n + 1], &sd->quest_log[n], sizeof(struct quest) * (sd->num_quests-sd->avail_quests));
|
memmove(&sd->quest_log[n + 1], &sd->quest_log[n], sizeof(struct quest) * (sd->num_quests-sd->avail_quests));
|
||||||
|
|
||||||
memset(&sd->quest_log[n], 0, sizeof(struct quest));
|
sd->quest_log[n] = {};
|
||||||
|
|
||||||
sd->quest_log[n].quest_id = qi->id;
|
sd->quest_log[n].quest_id = qi->id;
|
||||||
if (qi->time) {
|
sd->quest_log[n].time = (uint32)quest_time(qi);
|
||||||
if (qi->time_type == 0)
|
|
||||||
sd->quest_log[n].time = (unsigned int)(time(NULL) + qi->time);
|
|
||||||
else { // quest time limit at HH:MM
|
|
||||||
int time_today;
|
|
||||||
time_t t;
|
|
||||||
struct tm * lt;
|
|
||||||
|
|
||||||
t = time(NULL);
|
|
||||||
lt = localtime(&t);
|
|
||||||
time_today = (lt->tm_hour) * 3600 + (lt->tm_min) * 60 + (lt->tm_sec);
|
|
||||||
if (time_today < qi->time)
|
|
||||||
sd->quest_log[n].time = (unsigned int)(time(NULL) + qi->time - time_today);
|
|
||||||
else // next day
|
|
||||||
sd->quest_log[n].time = (unsigned int)(time(NULL) + 86400 + qi->time - time_today);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sd->quest_log[n].state = Q_ACTIVE;
|
sd->quest_log[n].state = Q_ACTIVE;
|
||||||
|
|
||||||
sd->save_quest = true;
|
sd->save_quest = true;
|
||||||
|
|
||||||
clif_quest_add(sd, &sd->quest_log[n]);
|
clif_quest_add(sd, &sd->quest_log[n]);
|
||||||
@ -140,55 +421,37 @@ int quest_add(TBL_PC *sd, int quest_id)
|
|||||||
* @param qid2 : New quest to add
|
* @param qid2 : New quest to add
|
||||||
* @return 0 in case of success, nonzero otherwise
|
* @return 0 in case of success, nonzero otherwise
|
||||||
*/
|
*/
|
||||||
int quest_change(TBL_PC *sd, int qid1, int qid2)
|
int quest_change(struct map_session_data *sd, int qid1, int qid2)
|
||||||
{
|
{
|
||||||
int i;
|
std::shared_ptr<s_quest_db> qi = quest_search(qid2);
|
||||||
struct quest_db *qi = quest_search(qid2);
|
|
||||||
|
|
||||||
if( qi == &quest_dummy ) {
|
if (!qi) {
|
||||||
ShowError("quest_change: quest %d not found in DB.\n", qid2);
|
ShowError("quest_change: quest %d not found in DB.\n", qid2);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( quest_check(sd, qid2, HAVEQUEST) >= 0 ) {
|
if (quest_check(sd, qid2, HAVEQUEST) >= 0) {
|
||||||
ShowError("quest_change: Character %d already has quest %d.\n", sd->status.char_id, qid2);
|
ShowError("quest_change: Character %d already has quest %d.\n", sd->status.char_id, qid2);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( quest_check(sd, qid1, HAVEQUEST) < 0 ) {
|
if (quest_check(sd, qid1, HAVEQUEST) < 0) {
|
||||||
ShowError("quest_change: Character %d doesn't have quest %d.\n", sd->status.char_id, qid1);
|
ShowError("quest_change: Character %d doesn't have quest %d.\n", sd->status.char_id, qid1);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
ARR_FIND(0, sd->avail_quests, i, sd->quest_log[i].quest_id == qid1);
|
ARR_FIND(0, sd->avail_quests, i, sd->quest_log[i].quest_id == qid1);
|
||||||
if( i == sd->avail_quests ) {
|
if (i == sd->avail_quests) {
|
||||||
ShowError("quest_change: Character %d has completed quest %d.\n", sd->status.char_id, qid1);
|
ShowError("quest_change: Character %d has completed quest %d.\n", sd->status.char_id, qid1);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&sd->quest_log[i], 0, sizeof(struct quest));
|
sd->quest_log[i] = {};
|
||||||
sd->quest_log[i].quest_id = qi->id;
|
sd->quest_log[i].quest_id = qi->id;
|
||||||
|
sd->quest_log[i].time = (uint32)quest_time(qi);
|
||||||
if (qi->time) {
|
|
||||||
if (qi->time_type == 0)
|
|
||||||
sd->quest_log[i].time = (unsigned int)(time(NULL) + qi->time);
|
|
||||||
else { // quest time limit at HH:MM
|
|
||||||
int time_today;
|
|
||||||
time_t t;
|
|
||||||
struct tm * lt;
|
|
||||||
|
|
||||||
t = time(NULL);
|
|
||||||
lt = localtime(&t);
|
|
||||||
time_today = (lt->tm_hour) * 3600 + (lt->tm_min) * 60 + (lt->tm_sec);
|
|
||||||
if (time_today < qi->time)
|
|
||||||
sd->quest_log[i].time = (unsigned int)(time(NULL) + qi->time - time_today);
|
|
||||||
else // next day
|
|
||||||
sd->quest_log[i].time = (unsigned int)(time(NULL) + 86400 + qi->time - time_today);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sd->quest_log[i].state = Q_ACTIVE;
|
sd->quest_log[i].state = Q_ACTIVE;
|
||||||
|
|
||||||
sd->save_quest = true;
|
sd->save_quest = true;
|
||||||
|
|
||||||
clif_quest_delete(sd, qid1);
|
clif_quest_delete(sd, qid1);
|
||||||
@ -207,24 +470,24 @@ int quest_change(TBL_PC *sd, int qid1, int qid2)
|
|||||||
* @param quest_id : ID of the quest to remove
|
* @param quest_id : ID of the quest to remove
|
||||||
* @return 0 in case of success, nonzero otherwise
|
* @return 0 in case of success, nonzero otherwise
|
||||||
*/
|
*/
|
||||||
int quest_delete(TBL_PC *sd, int quest_id)
|
int quest_delete(struct map_session_data *sd, int quest_id)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
//Search for quest
|
//Search for quest
|
||||||
ARR_FIND(0, sd->num_quests, i, sd->quest_log[i].quest_id == quest_id);
|
ARR_FIND(0, sd->num_quests, i, sd->quest_log[i].quest_id == quest_id);
|
||||||
if( i == sd->num_quests ) {
|
if (i == sd->num_quests) {
|
||||||
ShowError("quest_delete: Character %d doesn't have quest %d.\n", sd->status.char_id, quest_id);
|
ShowError("quest_delete: Character %d doesn't have quest %d.\n", sd->status.char_id, quest_id);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( sd->quest_log[i].state != Q_COMPLETE )
|
if (sd->quest_log[i].state != Q_COMPLETE)
|
||||||
sd->avail_quests--;
|
sd->avail_quests--;
|
||||||
|
|
||||||
if( i < --sd->num_quests ) //Compact the array
|
if (i < --sd->num_quests) //Compact the array
|
||||||
memmove(&sd->quest_log[i], &sd->quest_log[i + 1], sizeof(struct quest) * (sd->num_quests - i));
|
memmove(&sd->quest_log[i], &sd->quest_log[i + 1], sizeof(struct quest) * (sd->num_quests - i));
|
||||||
|
|
||||||
if( sd->num_quests == 0 ) {
|
if (sd->num_quests == 0) {
|
||||||
aFree(sd->quest_log);
|
aFree(sd->quest_log);
|
||||||
sd->quest_log = NULL;
|
sd->quest_log = NULL;
|
||||||
} else
|
} else
|
||||||
@ -273,53 +536,49 @@ int quest_update_objective_sub(struct block_list *bl, va_list ap)
|
|||||||
* @param sd : Character's data
|
* @param sd : Character's data
|
||||||
* @param mob_id : Monster ID
|
* @param mob_id : Monster ID
|
||||||
*/
|
*/
|
||||||
void quest_update_objective(TBL_PC *sd, int mob_id)
|
void quest_update_objective(struct map_session_data *sd, int mob_id)
|
||||||
{
|
{
|
||||||
int i, j;
|
for (int i = 0; i < sd->avail_quests; i++) {
|
||||||
|
if (sd->quest_log[i].state == Q_COMPLETE) // Skip complete quests
|
||||||
for( i = 0; i < sd->avail_quests; i++ ) {
|
|
||||||
struct quest_db *qi = NULL;
|
|
||||||
|
|
||||||
if( sd->quest_log[i].state == Q_COMPLETE ) // Skip complete quests
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
qi = quest_search(sd->quest_log[i].quest_id);
|
std::shared_ptr<s_quest_db> qi = quest_search(sd->quest_log[i].quest_id);
|
||||||
|
|
||||||
for( j = 0; j < qi->objectives_count; j++ ) {
|
// Process quest objectives
|
||||||
if( qi->objectives[j].mob == mob_id && sd->quest_log[i].count[j] < qi->objectives[j].count ) {
|
for (int j = 0; j < qi->objectives.size(); j++) {
|
||||||
|
if (qi->objectives[j]->mob_id == mob_id && sd->quest_log[i].count[j] < qi->objectives[j]->count) {
|
||||||
sd->quest_log[i].count[j]++;
|
sd->quest_log[i].count[j]++;
|
||||||
sd->save_quest = true;
|
sd->save_quest = true;
|
||||||
clif_quest_update_objective(sd, &sd->quest_log[i], mob_id);
|
clif_quest_update_objective(sd, &sd->quest_log[i], mob_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// process quest-granted extra drop bonuses
|
// Process quest-granted extra drop bonuses
|
||||||
for (j = 0; j < qi->dropitem_count; j++) {
|
for (const auto &it : qi->dropitem) {
|
||||||
struct quest_dropitem *dropitem = &qi->dropitem[j];
|
if (it->mob_id != 0 && it->mob_id != mob_id)
|
||||||
struct item item;
|
|
||||||
int temp;
|
|
||||||
|
|
||||||
if (dropitem->mob_id != 0 && dropitem->mob_id != mob_id)
|
|
||||||
continue;
|
continue;
|
||||||
// TODO: Should this be affected by server rates?
|
if (it->rate < 10000 && rnd()%10000 >= it->rate)
|
||||||
if (dropitem->rate < 10000 && rnd()%10000 >= dropitem->rate)
|
continue; // TODO: Should this be affected by server rates?
|
||||||
continue;
|
if (!itemdb_exists(it->nameid))
|
||||||
if (!itemdb_exists(dropitem->nameid))
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
memset(&item,0,sizeof(item));
|
struct item item = {};
|
||||||
item.nameid = dropitem->nameid;
|
|
||||||
item.identify = itemdb_isidentified(dropitem->nameid);
|
item.nameid = it->nameid;
|
||||||
item.amount = dropitem->count;
|
item.identify = itemdb_isidentified(it->nameid);
|
||||||
|
item.amount = it->count;
|
||||||
//#ifdef BOUND_ITEMS
|
//#ifdef BOUND_ITEMS
|
||||||
// item.bound = dropitem->bound;
|
// item.bound = it.bound;
|
||||||
//#endif
|
//#endif
|
||||||
// if (dropitem->isGUID)
|
// if (it.isGUID)
|
||||||
// item.unique_id = pc_generate_unique_id(sd);
|
// item.unique_id = pc_generate_unique_id(sd);
|
||||||
if ((temp = pc_additem(sd, &item, 1, LOG_TYPE_QUEST)) != 0) // Failed to obtain the item
|
|
||||||
|
char temp;
|
||||||
|
|
||||||
|
if ((temp = pc_additem(sd, &item, 1, LOG_TYPE_QUEST)) != ADDITEM_SUCCESS) // Failed to obtain the item
|
||||||
clif_additem(sd, 0, 0, temp);
|
clif_additem(sd, 0, 0, temp);
|
||||||
// else if (dropitem->isAnnounced || itemdb_exists(dropitem->nameid)->flag.broadcast)
|
// else if (it.isAnnounced || itemdb_exists(it.nameid)->flag.broadcast)
|
||||||
// intif_broadcast_obtain_special_item(sd, dropitem->nameid, dropitem->mob_id, ITEMOBTAIN_TYPE_MONSTER_ITEM);
|
// intif_broadcast_obtain_special_item(sd, it.nameid, it.mob_id, ITEMOBTAIN_TYPE_MONSTER_ITEM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pc_show_questinfo(sd);
|
pc_show_questinfo(sd);
|
||||||
@ -334,12 +593,12 @@ void quest_update_objective(TBL_PC *sd, int mob_id)
|
|||||||
* @return 0 in case of success, nonzero otherwise
|
* @return 0 in case of success, nonzero otherwise
|
||||||
* @author [Inkfish]
|
* @author [Inkfish]
|
||||||
*/
|
*/
|
||||||
int quest_update_status(TBL_PC *sd, int quest_id, enum quest_state status)
|
int quest_update_status(struct map_session_data *sd, int quest_id, e_quest_state status)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
ARR_FIND(0, sd->avail_quests, i, sd->quest_log[i].quest_id == quest_id);
|
ARR_FIND(0, sd->avail_quests, i, sd->quest_log[i].quest_id == quest_id);
|
||||||
if( i == sd->avail_quests ) {
|
if (i == sd->avail_quests) {
|
||||||
ShowError("quest_update_status: Character %d doesn't have quest %d.\n", sd->status.char_id, quest_id);
|
ShowError("quest_update_status: Character %d doesn't have quest %d.\n", sd->status.char_id, quest_id);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -347,13 +606,13 @@ int quest_update_status(TBL_PC *sd, int quest_id, enum quest_state status)
|
|||||||
sd->quest_log[i].state = status;
|
sd->quest_log[i].state = status;
|
||||||
sd->save_quest = true;
|
sd->save_quest = true;
|
||||||
|
|
||||||
if( status < Q_COMPLETE ) {
|
if (status < Q_COMPLETE) {
|
||||||
clif_quest_update_status(sd, quest_id, status == Q_ACTIVE ? true : false);
|
clif_quest_update_status(sd, quest_id, status == Q_ACTIVE ? true : false);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The quest is complete, so it needs to be moved to the completed quests block at the end of the array.
|
// The quest is complete, so it needs to be moved to the completed quests block at the end of the array.
|
||||||
if( i < (--sd->avail_quests) ) {
|
if (i < (--sd->avail_quests)) {
|
||||||
struct quest tmp_quest;
|
struct quest tmp_quest;
|
||||||
|
|
||||||
memcpy(&tmp_quest, &sd->quest_log[i], sizeof(struct quest));
|
memcpy(&tmp_quest, &sd->quest_log[i], sizeof(struct quest));
|
||||||
@ -363,7 +622,7 @@ int quest_update_status(TBL_PC *sd, int quest_id, enum quest_state status)
|
|||||||
|
|
||||||
clif_quest_delete(sd, quest_id);
|
clif_quest_delete(sd, quest_id);
|
||||||
|
|
||||||
if( save_settings&CHARSAVE_QUEST )
|
if (save_settings&CHARSAVE_QUEST)
|
||||||
chrif_save(sd, CSAVE_NORMAL);
|
chrif_save(sd, CSAVE_NORMAL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -383,30 +642,30 @@ int quest_update_status(TBL_PC *sd, int quest_id, enum quest_state status)
|
|||||||
* 1 if the quest's timeout has expired
|
* 1 if the quest's timeout has expired
|
||||||
* 0 otherwise
|
* 0 otherwise
|
||||||
*/
|
*/
|
||||||
int quest_check(TBL_PC *sd, int quest_id, enum quest_check_type type)
|
int quest_check(struct map_session_data *sd, int quest_id, e_quest_check_type type)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
ARR_FIND(0, sd->num_quests, i, sd->quest_log[i].quest_id == quest_id);
|
ARR_FIND(0, sd->num_quests, i, sd->quest_log[i].quest_id == quest_id);
|
||||||
if( i == sd->num_quests )
|
if (i == sd->num_quests)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
switch( type ) {
|
switch (type) {
|
||||||
case HAVEQUEST:
|
case HAVEQUEST:
|
||||||
if (sd->quest_log[i].state == Q_INACTIVE) // Player has the quest but it's in the inactive state; send it as Q_ACTIVE.
|
if (sd->quest_log[i].state == Q_INACTIVE) // Player has the quest but it's in the inactive state; send it as Q_ACTIVE.
|
||||||
return 1;
|
return 1;
|
||||||
return sd->quest_log[i].state;
|
return sd->quest_log[i].state;
|
||||||
case PLAYTIME:
|
case PLAYTIME:
|
||||||
return (sd->quest_log[i].time < (unsigned int)time(NULL) ? 2 : sd->quest_log[i].state == Q_COMPLETE ? 1 : 0);
|
return (sd->quest_log[i].time < (unsigned int)time(nullptr) ? 2 : sd->quest_log[i].state == Q_COMPLETE ? 1 : 0);
|
||||||
case HUNTING:
|
case HUNTING:
|
||||||
if( sd->quest_log[i].state == Q_INACTIVE || sd->quest_log[i].state == Q_ACTIVE ) {
|
if (sd->quest_log[i].state == Q_INACTIVE || sd->quest_log[i].state == Q_ACTIVE) {
|
||||||
int j;
|
int j;
|
||||||
struct quest_db *qi = quest_search(sd->quest_log[i].quest_id);
|
std::shared_ptr<s_quest_db> qi = quest_search(sd->quest_log[i].quest_id);
|
||||||
|
|
||||||
ARR_FIND(0, qi->objectives_count, j, sd->quest_log[i].count[j] < qi->objectives[j].count);
|
ARR_FIND(0, qi->objectives.size(), j, sd->quest_log[i].count[j] < qi->objectives[j]->count);
|
||||||
if( j == qi->objectives_count )
|
if (j == qi->objectives.size())
|
||||||
return 2;
|
return 2;
|
||||||
if( sd->quest_log[i].time < (unsigned int)time(NULL) )
|
if (sd->quest_log[i].time < (unsigned int)time(nullptr))
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -418,153 +677,6 @@ int quest_check(TBL_PC *sd, int quest_id, enum quest_check_type type)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads quests from the quest db.txt
|
|
||||||
* @return Number of loaded quests, or -1 if the file couldn't be read.
|
|
||||||
*/
|
|
||||||
void quest_read_txtdb(void)
|
|
||||||
{
|
|
||||||
const char* dbsubpath[] = {
|
|
||||||
DBPATH,
|
|
||||||
DBIMPORT"/",
|
|
||||||
};
|
|
||||||
uint8 f;
|
|
||||||
|
|
||||||
for (f = 0; f < ARRAYLENGTH(dbsubpath); f++) {
|
|
||||||
FILE *fp;
|
|
||||||
char line[1024];
|
|
||||||
uint32 ln = 0, count = 0;
|
|
||||||
char filename[256];
|
|
||||||
|
|
||||||
sprintf(filename, "%s/%s%s", db_path, dbsubpath[f], "quest_db.txt");
|
|
||||||
if ((fp = fopen(filename, "r")) == NULL) {
|
|
||||||
if (f == 0)
|
|
||||||
ShowError("Can't read %s\n", filename);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while(fgets(line, sizeof(line), fp)) {
|
|
||||||
struct quest_db *quest = NULL;
|
|
||||||
char *str[19], *p;
|
|
||||||
int quest_id = 0;
|
|
||||||
uint8 i;
|
|
||||||
|
|
||||||
++ln;
|
|
||||||
if (line[0] == '\0' || (line[0] == '/' && line[1] == '/'))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
p = trim(line);
|
|
||||||
|
|
||||||
if (*p == '\0')
|
|
||||||
continue; // empty line
|
|
||||||
|
|
||||||
memset(str, 0, sizeof(str));
|
|
||||||
for(i = 0, p = line; i < 18 && p; i++) {
|
|
||||||
str[i] = p;
|
|
||||||
p = strchr(p,',');
|
|
||||||
if (p)
|
|
||||||
*p++ = 0;
|
|
||||||
}
|
|
||||||
if (str[0] == NULL)
|
|
||||||
continue;
|
|
||||||
if (i < 18) {
|
|
||||||
ShowError("quest_read_txtdb: Insufficient columns in '%s' line %d (%d of %d)\n", filename, ln, i, 18);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
quest_id = atoi(str[0]);
|
|
||||||
|
|
||||||
if (quest_id < 0 || quest_id >= INT_MAX) {
|
|
||||||
ShowError("quest_read_txtdb: Invalid quest ID '%d' in '%s' line '%d' (min: 0, max: %d.)\n", quest_id, filename, ln, INT_MAX);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(quest = (struct quest_db *)idb_get(questdb, quest_id)))
|
|
||||||
CREATE(quest, struct quest_db, 1);
|
|
||||||
else {
|
|
||||||
if (quest->objectives) {
|
|
||||||
aFree(quest->objectives);
|
|
||||||
quest->objectives = NULL;
|
|
||||||
quest->objectives_count = 0;
|
|
||||||
}
|
|
||||||
if (quest->dropitem) {
|
|
||||||
aFree(quest->dropitem);
|
|
||||||
quest->dropitem = NULL;
|
|
||||||
quest->dropitem_count = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strchr(str[1],':') == NULL) {
|
|
||||||
quest->time = atoi(str[1]);
|
|
||||||
quest->time_type = 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
unsigned char hour, min;
|
|
||||||
|
|
||||||
hour = atoi(str[1]);
|
|
||||||
str[1] = strchr(str[1],':');
|
|
||||||
*str[1] ++= 0;
|
|
||||||
min = atoi(str[1]);
|
|
||||||
|
|
||||||
quest->time = hour * 3600 + min * 60;
|
|
||||||
quest->time_type = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(i = 0; i < MAX_QUEST_OBJECTIVES; i++) {
|
|
||||||
uint16 mob_id = (uint16)atoi(str[2 * i + 2]);
|
|
||||||
|
|
||||||
if (!mob_id)
|
|
||||||
continue;
|
|
||||||
if (mob_db(mob_id) == NULL) {
|
|
||||||
ShowWarning("quest_read_txtdb: Invalid monster as objective '%d' in line %d.\n", mob_id, ln);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
RECREATE(quest->objectives, struct quest_objective, quest->objectives_count+1);
|
|
||||||
quest->objectives[quest->objectives_count].mob = mob_id;
|
|
||||||
quest->objectives[quest->objectives_count].count = (uint16)atoi(str[2 * i + 3]);
|
|
||||||
quest->objectives_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(i = 0; i < MAX_QUEST_DROPS; i++) {
|
|
||||||
uint16 mob_id = (uint16)atoi(str[3 * i + (2 * MAX_QUEST_OBJECTIVES + 2)]), nameid = (uint16)atoi(str[3 * i + (2 * MAX_QUEST_OBJECTIVES + 3)]);
|
|
||||||
|
|
||||||
if (!nameid)
|
|
||||||
continue;
|
|
||||||
if (!itemdb_exists(nameid) || (mob_id && mob_db(mob_id) == NULL)) {
|
|
||||||
ShowWarning("quest_read_txtdb: Invalid item reward '%d' (mob %d, optional) in line %d.\n", nameid, mob_id, ln);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
RECREATE(quest->dropitem, struct quest_dropitem, quest->dropitem_count+1);
|
|
||||||
quest->dropitem[quest->dropitem_count].mob_id = mob_id;
|
|
||||||
quest->dropitem[quest->dropitem_count].nameid = nameid;
|
|
||||||
quest->dropitem[quest->dropitem_count].count = 1;
|
|
||||||
quest->dropitem[quest->dropitem_count].rate = atoi(str[3 * i + (2 * MAX_QUEST_OBJECTIVES + 4)]);
|
|
||||||
quest->dropitem_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
//StringBuf_Init(&entry.name);
|
|
||||||
//StringBuf_Printf(&entry.name, "%s", str[17]);
|
|
||||||
|
|
||||||
if (!quest->id) {
|
|
||||||
quest->id = quest_id;
|
|
||||||
idb_put(questdb, quest->id, quest);
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
ShowStatus("Done reading '" CL_WHITE "%d" CL_RESET "' entries in '" CL_WHITE "%s" CL_RESET "'.\n", count, filename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads Quest DB
|
|
||||||
*/
|
|
||||||
static void quest_read_db(void)
|
|
||||||
{
|
|
||||||
quest_read_txtdb();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map iterator to ensures a player has no invalid quest log entries.
|
* Map iterator to ensures a player has no invalid quest log entries.
|
||||||
* Any entries that are no longer in the db are removed.
|
* Any entries that are no longer in the db are removed.
|
||||||
@ -572,23 +684,22 @@ static void quest_read_db(void)
|
|||||||
* @param sd : Character's data
|
* @param sd : Character's data
|
||||||
* @param ap : Ignored
|
* @param ap : Ignored
|
||||||
*/
|
*/
|
||||||
int quest_reload_check_sub(struct map_session_data *sd, va_list ap)
|
static int quest_reload_check_sub(struct map_session_data *sd, va_list ap)
|
||||||
{
|
{
|
||||||
int i, j;
|
|
||||||
|
|
||||||
nullpo_ret(sd);
|
nullpo_ret(sd);
|
||||||
|
|
||||||
j = 0;
|
int i, j = 0;
|
||||||
for( i = 0; i < sd->num_quests; i++ ) {
|
|
||||||
struct quest_db *qi = quest_search(sd->quest_log[i].quest_id);
|
|
||||||
|
|
||||||
if( qi == &quest_dummy ) { //Remove no longer existing entries
|
for (i = 0; i < sd->num_quests; i++) {
|
||||||
if( sd->quest_log[i].state != Q_COMPLETE ) //And inform the client if necessary
|
std::shared_ptr<s_quest_db> qi = quest_search(sd->quest_log[i].quest_id);
|
||||||
|
|
||||||
|
if (!qi) { //Remove no longer existing entries
|
||||||
|
if (sd->quest_log[i].state != Q_COMPLETE) //And inform the client if necessary
|
||||||
clif_quest_delete(sd, sd->quest_log[i].quest_id);
|
clif_quest_delete(sd, sd->quest_log[i].quest_id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( i != j ) {
|
if (i != j) {
|
||||||
//Move entries if there's a gap to fill
|
//Move entries if there's a gap to fill
|
||||||
memcpy(&sd->quest_log[j], &sd->quest_log[i], sizeof(struct quest));
|
memcpy(&sd->quest_log[j], &sd->quest_log[i], sizeof(struct quest));
|
||||||
}
|
}
|
||||||
@ -603,50 +714,23 @@ int quest_reload_check_sub(struct map_session_data *sd, va_list ap)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
bool QuestDatabase::reload() {
|
||||||
* Clear quest single entry
|
if (!TypesafeYamlDatabase::reload())
|
||||||
* @param quest
|
return false;
|
||||||
* @param free Will free quest from memory
|
|
||||||
**/
|
// Update quest data for players, to ensure no entries about removed quests are left over.
|
||||||
static void questdb_free_sub(struct quest_db *quest, bool free)
|
map_foreachpc(&quest_reload_check_sub);
|
||||||
{
|
return true;
|
||||||
if (quest->objectives) {
|
|
||||||
aFree(quest->objectives);
|
|
||||||
quest->objectives = NULL;
|
|
||||||
quest->objectives_count = 0;
|
|
||||||
}
|
|
||||||
if (quest->dropitem) {
|
|
||||||
aFree(quest->dropitem);
|
|
||||||
quest->dropitem = NULL;
|
|
||||||
quest->dropitem_count = 0;
|
|
||||||
}
|
|
||||||
if (&quest->name)
|
|
||||||
StringBuf_Destroy(&quest->name);
|
|
||||||
if (free)
|
|
||||||
aFree(quest);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
QuestDatabase quest_db;
|
||||||
* Clears the quest database for shutdown or reload.
|
|
||||||
*/
|
|
||||||
static int questdb_free(DBKey key, DBData *data, va_list ap)
|
|
||||||
{
|
|
||||||
struct quest_db *quest = (struct quest_db *)db_data2ptr(data);
|
|
||||||
|
|
||||||
if (!quest)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
questdb_free_sub(quest, true);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the quest interface.
|
* Initializes the quest interface.
|
||||||
*/
|
*/
|
||||||
void do_init_quest(void)
|
void do_init_quest(void)
|
||||||
{
|
{
|
||||||
questdb = idb_alloc(DB_OPT_BASE);
|
quest_db.load();
|
||||||
quest_read_db();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -654,20 +738,4 @@ void do_init_quest(void)
|
|||||||
*/
|
*/
|
||||||
void do_final_quest(void)
|
void do_final_quest(void)
|
||||||
{
|
{
|
||||||
memset(&quest_dummy, 0, sizeof(quest_dummy));
|
|
||||||
questdb->destroy(questdb, questdb_free);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reloads the quest database.
|
|
||||||
*/
|
|
||||||
void do_reload_quest(void)
|
|
||||||
{
|
|
||||||
memset(&quest_dummy, 0, sizeof(quest_dummy));
|
|
||||||
questdb->clear(questdb, questdb_free);
|
|
||||||
|
|
||||||
quest_read_db();
|
|
||||||
|
|
||||||
//Update quest data for players, to ensure no entries about removed quests are left over.
|
|
||||||
map_foreachpc(&quest_reload_check_sub);
|
|
||||||
}
|
}
|
||||||
|
@ -4,14 +4,17 @@
|
|||||||
#ifndef QUEST_HPP
|
#ifndef QUEST_HPP
|
||||||
#define QUEST_HPP
|
#define QUEST_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "../common/cbasetypes.hpp"
|
#include "../common/cbasetypes.hpp"
|
||||||
|
#include "../common/database.hpp"
|
||||||
#include "../common/strlib.hpp"
|
#include "../common/strlib.hpp"
|
||||||
|
|
||||||
#include "map.hpp"
|
#include "map.hpp"
|
||||||
|
|
||||||
struct map_session_data;
|
struct map_session_data;
|
||||||
|
|
||||||
struct quest_dropitem {
|
struct s_quest_dropitem {
|
||||||
uint16 nameid;
|
uint16 nameid;
|
||||||
uint16 count;
|
uint16 count;
|
||||||
uint16 rate;
|
uint16 rate;
|
||||||
@ -21,46 +24,53 @@ struct quest_dropitem {
|
|||||||
//bool isGUID;
|
//bool isGUID;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct quest_objective {
|
struct s_quest_objective {
|
||||||
uint16 mob;
|
uint16 mob_id;
|
||||||
uint16 count;
|
uint16 count;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct quest_db {
|
struct s_quest_db {
|
||||||
// TODO: find out if signed or unsigned in client
|
int32 id;
|
||||||
int id;
|
time_t time;
|
||||||
unsigned int time;
|
bool time_at;
|
||||||
bool time_type;
|
std::vector<std::shared_ptr<s_quest_objective>> objectives;
|
||||||
uint8 objectives_count;
|
std::vector<std::shared_ptr<s_quest_dropitem>> dropitem;
|
||||||
struct quest_objective *objectives;
|
std::string name;
|
||||||
uint8 dropitem_count;
|
|
||||||
struct quest_dropitem *dropitem;
|
|
||||||
StringBuf name;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct quest_db quest_dummy; ///< Dummy entry for invalid quest lookups
|
|
||||||
|
|
||||||
// Questlog check types
|
// Questlog check types
|
||||||
enum quest_check_type {
|
enum e_quest_check_type : uint8 {
|
||||||
HAVEQUEST, ///< Query the state of the given quest
|
HAVEQUEST, ///< Query the state of the given quest
|
||||||
PLAYTIME, ///< Check if the given quest has been completed or has yet to expire
|
PLAYTIME, ///< Check if the given quest has been completed or has yet to expire
|
||||||
HUNTING, ///< Check if the given hunting quest's requirements have been met
|
HUNTING, ///< Check if the given hunting quest's requirements have been met
|
||||||
};
|
};
|
||||||
|
|
||||||
int quest_pc_login(TBL_PC *sd);
|
class QuestDatabase : public TypesafeYamlDatabase<uint32, s_quest_db> {
|
||||||
|
public:
|
||||||
|
QuestDatabase() : TypesafeYamlDatabase("QUEST_DB", 1) {
|
||||||
|
|
||||||
int quest_add(TBL_PC * sd, int quest_id);
|
}
|
||||||
int quest_delete(TBL_PC * sd, int quest_id);
|
|
||||||
int quest_change(TBL_PC * sd, int qid1, int qid2);
|
const std::string getDefaultLocation();
|
||||||
|
uint64 parseBodyNode(const YAML::Node& node);
|
||||||
|
bool reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
extern QuestDatabase quest_db;
|
||||||
|
|
||||||
|
int quest_pc_login(struct map_session_data *sd);
|
||||||
|
|
||||||
|
int quest_add(struct map_session_data *sd, int quest_id);
|
||||||
|
int quest_delete(struct map_session_data *sd, int quest_id);
|
||||||
|
int quest_change(struct map_session_data *sd, int qid1, int qid2);
|
||||||
int quest_update_objective_sub(struct block_list *bl, va_list ap);
|
int quest_update_objective_sub(struct block_list *bl, va_list ap);
|
||||||
void quest_update_objective(TBL_PC * sd, int mob_id);
|
void quest_update_objective(struct map_session_data *sd, int mob_id);
|
||||||
int quest_update_status(TBL_PC * sd, int quest_id, enum quest_state status);
|
int quest_update_status(struct map_session_data *sd, int quest_id, e_quest_state status);
|
||||||
int quest_check(TBL_PC * sd, int quest_id, enum quest_check_type type);
|
int quest_check(struct map_session_data *sd, int quest_id, e_quest_check_type type);
|
||||||
|
|
||||||
struct quest_db *quest_search(int quest_id);
|
std::shared_ptr<s_quest_db> quest_search(int quest_id);
|
||||||
|
|
||||||
void do_init_quest(void);
|
void do_init_quest(void);
|
||||||
void do_final_quest(void);
|
void do_final_quest(void);
|
||||||
void do_reload_quest(void);
|
|
||||||
|
|
||||||
#endif /* QUEST_HPP */
|
#endif /* QUEST_HPP */
|
||||||
|
@ -19587,10 +19587,10 @@ BUILDIN_FUNC(changequest)
|
|||||||
BUILDIN_FUNC(checkquest)
|
BUILDIN_FUNC(checkquest)
|
||||||
{
|
{
|
||||||
struct map_session_data *sd;
|
struct map_session_data *sd;
|
||||||
enum quest_check_type type = HAVEQUEST;
|
e_quest_check_type type = HAVEQUEST;
|
||||||
|
|
||||||
if( script_hasdata(st, 3) )
|
if( script_hasdata(st, 3) )
|
||||||
type = (enum quest_check_type)script_getnum(st, 3);
|
type = (e_quest_check_type)script_getnum(st, 3);
|
||||||
|
|
||||||
if (!script_charid2sd(4,sd))
|
if (!script_charid2sd(4,sd))
|
||||||
return SCRIPT_CMD_FAILURE;
|
return SCRIPT_CMD_FAILURE;
|
||||||
@ -19606,12 +19606,11 @@ BUILDIN_FUNC(checkquest)
|
|||||||
BUILDIN_FUNC(isbegin_quest)
|
BUILDIN_FUNC(isbegin_quest)
|
||||||
{
|
{
|
||||||
struct map_session_data *sd;
|
struct map_session_data *sd;
|
||||||
int i;
|
|
||||||
|
|
||||||
if (!script_charid2sd(3,sd))
|
if (!script_charid2sd(3,sd))
|
||||||
return SCRIPT_CMD_FAILURE;
|
return SCRIPT_CMD_FAILURE;
|
||||||
|
|
||||||
i = quest_check(sd, script_getnum(st, 2), (enum quest_check_type) HAVEQUEST);
|
int i = quest_check(sd, script_getnum(st, 2), HAVEQUEST);
|
||||||
script_pushint(st, i + (i < 1));
|
script_pushint(st, i + (i < 1));
|
||||||
|
|
||||||
return SCRIPT_CMD_SUCCESS;
|
return SCRIPT_CMD_SUCCESS;
|
||||||
|
@ -70,6 +70,7 @@ int getch( void ){
|
|||||||
#define MAX_SKILL_ITEM_REQUIRE 10
|
#define MAX_SKILL_ITEM_REQUIRE 10
|
||||||
#define MAX_SKILL_STATUS_REQUIRE 3
|
#define MAX_SKILL_STATUS_REQUIRE 3
|
||||||
#define MAX_SKILL_EQUIP_REQUIRE 10
|
#define MAX_SKILL_EQUIP_REQUIRE 10
|
||||||
|
#define MAX_QUEST_DROPS 3
|
||||||
|
|
||||||
struct s_skill_unit_csv : s_skill_db {
|
struct s_skill_unit_csv : s_skill_db {
|
||||||
std::string target_str;
|
std::string target_str;
|
||||||
@ -98,6 +99,7 @@ static bool skill_parse_row_unitdb(char* split[], int columns, int current);
|
|||||||
static bool skill_parse_row_copyabledb(char* split[], int columns, int current);
|
static bool skill_parse_row_copyabledb(char* split[], int columns, int current);
|
||||||
static bool skill_parse_row_nonearnpcrangedb(char* split[], int columns, int current);
|
static bool skill_parse_row_nonearnpcrangedb(char* split[], int columns, int current);
|
||||||
static bool skill_parse_row_skilldb(char* split[], int columns, int current);
|
static bool skill_parse_row_skilldb(char* split[], int columns, int current);
|
||||||
|
static bool quest_read_db(char *split[], int columns, int current);
|
||||||
|
|
||||||
// Constants for conversion
|
// Constants for conversion
|
||||||
std::unordered_map<uint16, std::string> aegis_itemnames;
|
std::unordered_map<uint16, std::string> aegis_itemnames;
|
||||||
@ -352,6 +354,12 @@ int do_init( int argc, char** argv ){
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!process("QUEST_DB", 1, root_paths, "quest_db", [](const std::string &path, const std::string &name_ext) -> bool {
|
||||||
|
return sv_readdb(path.c_str(), name_ext.c_str(), ',', 3 + MAX_QUEST_OBJECTIVES * 2 + MAX_QUEST_DROPS * 3, 100, -1, &quest_read_db, false);
|
||||||
|
})) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: add implementations ;-)
|
// TODO: add implementations ;-)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -2390,3 +2398,138 @@ static bool skill_parse_row_skilldb(char* split[], int columns, int current) {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copied and adjusted from quest.cpp
|
||||||
|
static bool quest_read_db(char *split[], int columns, int current) {
|
||||||
|
int quest_id = atoi(split[0]);
|
||||||
|
|
||||||
|
if (quest_id < 0 || quest_id >= INT_MAX) {
|
||||||
|
ShowError("quest_read_db: Invalid quest ID '%d'.\n", quest_id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
body << YAML::BeginMap;
|
||||||
|
body << YAML::Key << "Id" << YAML::Value << quest_id;
|
||||||
|
|
||||||
|
std::string title = split[17];
|
||||||
|
|
||||||
|
if (columns > 18) { // If the title has a comma in it, concatenate
|
||||||
|
int col = 18;
|
||||||
|
|
||||||
|
while (col < columns) {
|
||||||
|
title += ',' + std::string(split[col]);
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
title.erase(std::remove(title.begin(), title.end(), '"'), title.end()); // Strip double quotes out
|
||||||
|
body << YAML::Key << "Title" << YAML::Value << title;
|
||||||
|
|
||||||
|
if (strchr(split[1], ':') == NULL) {
|
||||||
|
uint32 time = atoi(split[1]);
|
||||||
|
|
||||||
|
if (time > 0) {
|
||||||
|
int day = time / 86400;
|
||||||
|
|
||||||
|
time %= (24 * 3600);
|
||||||
|
int hour = time / 3600;
|
||||||
|
|
||||||
|
time %= 3600;
|
||||||
|
int minute = time / 60;
|
||||||
|
|
||||||
|
time %= 60;
|
||||||
|
int second = time;
|
||||||
|
|
||||||
|
std::string output = "+";
|
||||||
|
|
||||||
|
if (day > 0)
|
||||||
|
output += std::to_string(day) + "d";
|
||||||
|
if (hour > 0)
|
||||||
|
output += std::to_string(hour) + "h";
|
||||||
|
if (minute > 0)
|
||||||
|
output += std::to_string(minute) + "mn";
|
||||||
|
if (second > 0)
|
||||||
|
output += std::to_string(second) + "s";
|
||||||
|
|
||||||
|
body << YAML::Key << "TimeLimit" << YAML::Value << output;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (*split[1]) {
|
||||||
|
std::string time_str(split[1]), hour = time_str.substr(0, time_str.find(':')), output = {};
|
||||||
|
|
||||||
|
time_str.erase(0, 3); // Remove "HH:"
|
||||||
|
|
||||||
|
if (std::stoi(hour) > 0)
|
||||||
|
output = std::to_string(std::stoi(hour)) + "h";
|
||||||
|
if (std::stoi(time_str) > 0)
|
||||||
|
output += std::to_string(std::stoi(time_str)) + "mn";
|
||||||
|
|
||||||
|
body << YAML::Key << "TimeLimit" << YAML::Value << output; // No quests in TXT format had days, default to 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (atoi(split[2]) > 0) {
|
||||||
|
body << YAML::Key << "Targets";
|
||||||
|
body << YAML::BeginSeq;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < MAX_QUEST_OBJECTIVES; i++) {
|
||||||
|
int32 mob_id = (int32)atoi(split[i * 2 + 2]), count = atoi(split[i * 2 + 3]);
|
||||||
|
|
||||||
|
if (!mob_id || !count)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string *mob_name = util::umap_find(aegis_mobnames, static_cast<uint16>(mob_id));
|
||||||
|
|
||||||
|
if (!mob_name) {
|
||||||
|
ShowError("quest_read_db: Invalid mob-class %hu, target not read.\n", mob_id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
body << YAML::BeginMap;
|
||||||
|
body << YAML::Key << "Mob" << YAML::Value << *mob_name;
|
||||||
|
body << YAML::Key << "Count" << YAML::Value << count;
|
||||||
|
body << YAML::EndMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
body << YAML::EndSeq;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (atoi(split[2 * MAX_QUEST_OBJECTIVES + 2]) > 0) {
|
||||||
|
body << YAML::Key << "Drops";
|
||||||
|
body << YAML::BeginSeq;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < MAX_QUEST_DROPS; i++) {
|
||||||
|
int32 mob_id = (int32)atoi(split[3 * i + (2 * MAX_QUEST_OBJECTIVES + 2)]), nameid = (uint16)atoi(split[3 * i + (2 * MAX_QUEST_OBJECTIVES + 3)]);
|
||||||
|
|
||||||
|
if (!mob_id || !nameid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string *mob_name = util::umap_find(aegis_mobnames, static_cast<uint16>(mob_id));
|
||||||
|
|
||||||
|
if (!mob_name) {
|
||||||
|
ShowError("quest_read_db: Invalid mob-class %hu, drop not read.\n", mob_id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string *item_name = util::umap_find(aegis_itemnames, static_cast<uint16>(nameid));
|
||||||
|
|
||||||
|
if (!item_name) {
|
||||||
|
ShowError("quest_read_db: Invalid item name %hu, drop not read.\n", nameid);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
body << YAML::BeginMap;
|
||||||
|
body << YAML::Key << "Mob" << YAML::Value << *mob_name;
|
||||||
|
body << YAML::Key << "Item" << YAML::Value << *item_name;
|
||||||
|
//body << YAML::Key << "Count" << YAML::Value << 1; // Default is 1
|
||||||
|
body << YAML::Key << "Rate" << YAML::Value << atoi(split[3 * i + (2 * MAX_QUEST_OBJECTIVES + 4)]);
|
||||||
|
body << YAML::EndMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
body << YAML::EndSeq;
|
||||||
|
}
|
||||||
|
|
||||||
|
body << YAML::EndMap;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user