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
|
||||
*/
|
||||
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);
|
||||
return false;
|
||||
|
@ -69,7 +69,6 @@
|
||||
#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_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_ITEM_RDM_OPT 5 /// Max item random option [Napster]
|
||||
#define DB_NAME_LEN 256 //max len of dbs
|
||||
@ -212,7 +211,7 @@ enum e_mode {
|
||||
#define CL_MASK 0xF000000
|
||||
|
||||
// Questlog states
|
||||
enum quest_state {
|
||||
enum e_quest_state : uint8 {
|
||||
Q_INACTIVE, ///< Inactive quest (the user can toggle between active and inactive quests)
|
||||
Q_ACTIVE, ///< Active quest
|
||||
Q_COMPLETE, ///< Completed quest
|
||||
@ -221,9 +220,9 @@ enum quest_state {
|
||||
/// Questlog entry
|
||||
struct quest {
|
||||
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
|
||||
enum quest_state state; ///< Current quest state
|
||||
e_quest_state state; ///< Current quest state
|
||||
};
|
||||
|
||||
struct s_item_randomoption {
|
||||
|
@ -4027,8 +4027,8 @@ ACMD_FUNC(reload) {
|
||||
map_msg_reload();
|
||||
clif_displaymessage(fd, msg_txt(sd,463)); // Message configuration has been reloaded.
|
||||
} else if (strstr(command, "questdb") || strncmp(message, "questdb", 3) == 0) {
|
||||
do_reload_quest();
|
||||
clif_displaymessage(fd, msg_txt(sd,1377)); // Quest database has been reloaded.
|
||||
if (quest_db.reload())
|
||||
clif_displaymessage(fd, msg_txt(sd,1377)); // Quest database has been reloaded.
|
||||
} else if (strstr(command, "instancedb") || strncmp(message, "instancedb", 4) == 0) {
|
||||
instance_reload();
|
||||
clif_displaymessage(fd, msg_txt(sd,516)); // Instance database has been reloaded.
|
||||
|
@ -16929,25 +16929,24 @@ void clif_quest_send_list(struct map_session_data *sd)
|
||||
WFIFOL(fd, 4) = limit;
|
||||
|
||||
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;
|
||||
offset += 4;
|
||||
WFIFOB(fd, offset) = sd->quest_log[i].state;
|
||||
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;
|
||||
WFIFOL(fd, offset) = sd->quest_log[i].time;
|
||||
WFIFOL(fd, offset) = static_cast<uint32>(sd->quest_log[i].time);
|
||||
offset += 4;
|
||||
WFIFOW(fd, offset) = qi->objectives_count;
|
||||
WFIFOW(fd, offset) = static_cast<uint16>(qi->objectives.size());
|
||||
offset += 2;
|
||||
|
||||
if( qi->objectives_count > 0 ){
|
||||
int j;
|
||||
if (!qi->objectives.empty()) {
|
||||
struct mob_db *mob;
|
||||
|
||||
for( j = 0; j < qi->objectives_count; j++ ){
|
||||
mob = mob_db(qi->objectives[j].mob);
|
||||
for (int j = 0; j < qi->objectives.size(); j++) {
|
||||
mob = mob_db(qi->objectives[j]->mob_id);
|
||||
|
||||
#if PACKETVER >= 20150513
|
||||
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
|
||||
offset += 4;
|
||||
#endif
|
||||
WFIFOL(fd, offset) = qi->objectives[j].mob;
|
||||
WFIFOL(fd, offset) = qi->objectives[j]->mob_id;
|
||||
offset += 4;
|
||||
#if PACKETVER >= 20150513
|
||||
WFIFOW(fd, offset) = 0; // TODO: Find info - levelMin
|
||||
@ -16965,7 +16964,7 @@ void clif_quest_send_list(struct map_session_data *sd)
|
||||
#endif
|
||||
WFIFOW(fd, offset) = sd->quest_log[i].count[j];
|
||||
offset += 2;
|
||||
WFIFOW(fd, offset) = qi->objectives[j].count;
|
||||
WFIFOW(fd, offset) = qi->objectives[j]->count;
|
||||
offset += 2;
|
||||
safestrncpy((char*)WFIFOP(fd, offset), mob->jname, 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)
|
||||
{
|
||||
int fd = sd->fd;
|
||||
int i, j, limit = 0;
|
||||
int limit = 0;
|
||||
int len = sd->avail_quests*104+8;
|
||||
struct mob_db *mob;
|
||||
|
||||
@ -17008,18 +17007,18 @@ void clif_quest_send_mission(struct map_session_data *sd)
|
||||
WFIFOW(fd, 2) = len;
|
||||
WFIFOL(fd, 4) = limit;
|
||||
|
||||
for (i = 0; i < limit; i++) {
|
||||
struct quest_db *qi = quest_search(sd->quest_log[i].quest_id);
|
||||
for (int i = 0; i < limit; i++) {
|
||||
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+12) = sd->quest_log[i].time - qi->time;
|
||||
WFIFOL(fd, i*104+16) = sd->quest_log[i].time;
|
||||
WFIFOW(fd, i*104+20) = qi->objectives_count;
|
||||
WFIFOL(fd, i*104+12) = static_cast<uint32>(sd->quest_log[i].time - qi->time);
|
||||
WFIFOL(fd, i*104+16) = static_cast<uint32>(sd->quest_log[i].time);
|
||||
WFIFOW(fd, i*104+20) = static_cast<uint16>(qi->objectives.size());
|
||||
|
||||
for (j = 0 ; j < qi->objectives_count; j++) {
|
||||
WFIFOL(fd, i*104+22+j*30) = qi->objectives[j].mob;
|
||||
for (int j = 0 ; j < qi->objectives.size(); j++) {
|
||||
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];
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
{
|
||||
int fd = sd->fd;
|
||||
int i, offset;
|
||||
struct quest_db *qi = quest_search(qd->quest_id);
|
||||
std::shared_ptr<s_quest_db> qi = quest_search(qd->quest_id);
|
||||
#if PACKETVER >= 20150513
|
||||
int cmd = 0x9f9;
|
||||
#else
|
||||
@ -17047,11 +17045,11 @@ void clif_quest_add(struct map_session_data *sd, struct quest *qd)
|
||||
WFIFOW(fd, 0) = cmd;
|
||||
WFIFOL(fd, 2) = qd->quest_id;
|
||||
WFIFOB(fd, 6) = qd->state;
|
||||
WFIFOB(fd, 7) = qd->time - qi->time;
|
||||
WFIFOL(fd, 11) = qd->time;
|
||||
WFIFOW(fd, 15) = qi->objectives_count;
|
||||
WFIFOB(fd, 7) = static_cast<uint8>(qd->time - qi->time);
|
||||
WFIFOL(fd, 11) = static_cast<uint32>(qd->time);
|
||||
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;
|
||||
#if PACKETVER >= 20150513
|
||||
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
|
||||
offset += 4;
|
||||
#endif
|
||||
WFIFOL(fd, offset) = qi->objectives[i].mob;
|
||||
WFIFOL(fd, offset) = qi->objectives[i]->mob_id;
|
||||
offset += 4;
|
||||
#if PACKETVER >= 20150513
|
||||
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
|
||||
WFIFOW(fd, offset) = qd->count[i];
|
||||
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);
|
||||
offset += NAME_LENGTH;
|
||||
}
|
||||
@ -17077,16 +17075,16 @@ void clif_quest_add(struct map_session_data *sd, struct quest *qd)
|
||||
WFIFOSET(fd, packet_len(cmd));
|
||||
|
||||
#if PACKETVER >= 20150513
|
||||
int len = 4 + qi->objectives_count * 12;
|
||||
int len = 4 + qi->objectives.size() * 12;
|
||||
|
||||
WFIFOHEAD(fd, len);
|
||||
WFIFOW(fd, 0) = 0x8fe;
|
||||
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+4) = qi->objectives[i].mob;
|
||||
WFIFOW(fd, offset + 10) = qi->objectives[i].count;
|
||||
WFIFOL(fd, offset+4) = qi->objectives[i]->mob_id;
|
||||
WFIFOW(fd, offset + 10) = qi->objectives[i]->count;
|
||||
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)
|
||||
{
|
||||
int fd = sd->fd;
|
||||
int i, offset;
|
||||
struct quest_db *qi = quest_search(qd->quest_id);
|
||||
int len = qi->objectives_count * 12 + 6;
|
||||
int offset = 6;
|
||||
std::shared_ptr<s_quest_db> qi = quest_search(qd->quest_id);
|
||||
int len = qi->objectives.size() * 12 + 6;
|
||||
#if PACKETVER >= 20150513
|
||||
int cmd = 0x9fa;
|
||||
#else
|
||||
@ -17126,20 +17124,20 @@ void clif_quest_update_objective(struct map_session_data *sd, struct quest *qd,
|
||||
|
||||
WFIFOHEAD(fd, len);
|
||||
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++) {
|
||||
if (mobid == 0 || mobid == qi->objectives[i].mob) {
|
||||
for (int i = 0; i < qi->objectives.size(); i++) {
|
||||
if (mobid == 0 || mobid == qi->objectives[i]->mob_id) {
|
||||
WFIFOL(fd, offset) = qd->quest_id;
|
||||
offset += 4;
|
||||
#if PACKETVER >= 20150513
|
||||
WFIFOL(fd, offset) = qd->quest_id * 1000 + i;
|
||||
offset += 4;
|
||||
#else
|
||||
WFIFOL(fd, offset) = qi->objectives[i].mob;
|
||||
WFIFOL(fd, offset) = qi->objectives[i].mob_id;
|
||||
offset += 4;
|
||||
#endif
|
||||
WFIFOW(fd, offset) = qi->objectives[i].count;
|
||||
WFIFOW(fd, offset) = qi->objectives[i]->count;
|
||||
offset += 2;
|
||||
WFIFOW(fd, offset) = qd->count[i];
|
||||
offset += 2;
|
||||
|
@ -2041,15 +2041,15 @@ void intif_parse_questlog(int fd)
|
||||
}
|
||||
} else {
|
||||
struct quest *received = (struct quest *)RFIFOP(fd,8);
|
||||
int i, k = num_received;
|
||||
int k = num_received;
|
||||
|
||||
if(sd->quest_log)
|
||||
RECREATE(sd->quest_log, struct quest, num_received);
|
||||
else
|
||||
CREATE(sd->quest_log, struct quest, num_received);
|
||||
|
||||
for(i = 0; i < num_received; i++) {
|
||||
if(quest_search(received[i].quest_id) == &quest_dummy) {
|
||||
for(int i = 0; i < num_received; i++) {
|
||||
if(!quest_search(received[i].quest_id)) {
|
||||
ShowError("intif_parse_QuestLog: quest %d not found in DB.\n", received[i].quest_id);
|
||||
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\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\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\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')" />
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include "../common/showmsg.hpp"
|
||||
#include "../common/socket.hpp"
|
||||
#include "../common/strlib.hpp"
|
||||
#include "../common/utilities.hpp"
|
||||
#include "../common/utils.hpp"
|
||||
|
||||
#include "battle.hpp"
|
||||
#include "chrif.hpp"
|
||||
@ -24,20 +26,296 @@
|
||||
#include "party.hpp"
|
||||
#include "pc.hpp"
|
||||
|
||||
static DBMap *questdb;
|
||||
static void questdb_free_sub(struct quest_db *quest, bool free);
|
||||
struct quest_db quest_dummy;
|
||||
static int split_exact_quest_time(char* modif_p, int* day, int* hour, int* minute, int *second);
|
||||
|
||||
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.
|
||||
* @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)
|
||||
return &quest_dummy;
|
||||
return nullptr;
|
||||
|
||||
return quest;
|
||||
}
|
||||
|
||||
@ -46,13 +324,9 @@ struct quest_db *quest_search(int quest_id)
|
||||
* @param sd : Player's data
|
||||
* @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
|
||||
int i;
|
||||
#endif
|
||||
|
||||
if( sd->avail_quests == 0 )
|
||||
if (!sd->avail_quests)
|
||||
return 1;
|
||||
|
||||
clif_quest_send_list(sd);
|
||||
@ -61,13 +335,39 @@ int quest_pc_login(TBL_PC *sd)
|
||||
clif_quest_send_mission(sd);
|
||||
|
||||
//@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);
|
||||
#endif
|
||||
|
||||
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.
|
||||
* 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.
|
||||
* @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;
|
||||
struct quest_db *qi = quest_search(quest_id);
|
||||
std::shared_ptr<s_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);
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = sd->avail_quests; //Insertion point
|
||||
int n = sd->avail_quests; //Insertion point
|
||||
|
||||
sd->num_quests++;
|
||||
sd->avail_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
|
||||
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));
|
||||
|
||||
memset(&sd->quest_log[n], 0, sizeof(struct quest));
|
||||
|
||||
sd->quest_log[n] = {};
|
||||
sd->quest_log[n].quest_id = qi->id;
|
||||
if (qi->time) {
|
||||
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].time = (uint32)quest_time(qi);
|
||||
sd->quest_log[n].state = Q_ACTIVE;
|
||||
|
||||
sd->save_quest = true;
|
||||
|
||||
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
|
||||
* @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;
|
||||
struct quest_db *qi = quest_search(qid2);
|
||||
std::shared_ptr<s_quest_db> qi = quest_search(qid2);
|
||||
|
||||
if( qi == &quest_dummy ) {
|
||||
if (!qi) {
|
||||
ShowError("quest_change: quest %d not found in DB.\n", qid2);
|
||||
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);
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int i;
|
||||
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&sd->quest_log[i], 0, sizeof(struct quest));
|
||||
sd->quest_log[i] = {};
|
||||
sd->quest_log[i].quest_id = qi->id;
|
||||
|
||||
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].time = (uint32)quest_time(qi);
|
||||
sd->quest_log[i].state = Q_ACTIVE;
|
||||
|
||||
sd->save_quest = true;
|
||||
|
||||
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
|
||||
* @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;
|
||||
|
||||
//Search for quest
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( sd->quest_log[i].state != Q_COMPLETE )
|
||||
if (sd->quest_log[i].state != Q_COMPLETE)
|
||||
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));
|
||||
|
||||
if( sd->num_quests == 0 ) {
|
||||
if (sd->num_quests == 0) {
|
||||
aFree(sd->quest_log);
|
||||
sd->quest_log = NULL;
|
||||
} else
|
||||
@ -273,53 +536,49 @@ int quest_update_objective_sub(struct block_list *bl, va_list ap)
|
||||
* @param sd : Character's data
|
||||
* @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( i = 0; i < sd->avail_quests; i++ ) {
|
||||
struct quest_db *qi = NULL;
|
||||
|
||||
if( sd->quest_log[i].state == Q_COMPLETE ) // Skip complete quests
|
||||
for (int i = 0; i < sd->avail_quests; i++) {
|
||||
if (sd->quest_log[i].state == Q_COMPLETE) // Skip complete quests
|
||||
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++ ) {
|
||||
if( qi->objectives[j].mob == mob_id && sd->quest_log[i].count[j] < qi->objectives[j].count ) {
|
||||
// Process quest objectives
|
||||
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->save_quest = true;
|
||||
clif_quest_update_objective(sd, &sd->quest_log[i], mob_id);
|
||||
}
|
||||
}
|
||||
|
||||
// process quest-granted extra drop bonuses
|
||||
for (j = 0; j < qi->dropitem_count; j++) {
|
||||
struct quest_dropitem *dropitem = &qi->dropitem[j];
|
||||
struct item item;
|
||||
int temp;
|
||||
|
||||
if (dropitem->mob_id != 0 && dropitem->mob_id != mob_id)
|
||||
// Process quest-granted extra drop bonuses
|
||||
for (const auto &it : qi->dropitem) {
|
||||
if (it->mob_id != 0 && it->mob_id != mob_id)
|
||||
continue;
|
||||
// TODO: Should this be affected by server rates?
|
||||
if (dropitem->rate < 10000 && rnd()%10000 >= dropitem->rate)
|
||||
continue;
|
||||
if (!itemdb_exists(dropitem->nameid))
|
||||
if (it->rate < 10000 && rnd()%10000 >= it->rate)
|
||||
continue; // TODO: Should this be affected by server rates?
|
||||
if (!itemdb_exists(it->nameid))
|
||||
continue;
|
||||
|
||||
memset(&item,0,sizeof(item));
|
||||
item.nameid = dropitem->nameid;
|
||||
item.identify = itemdb_isidentified(dropitem->nameid);
|
||||
item.amount = dropitem->count;
|
||||
struct item item = {};
|
||||
|
||||
item.nameid = it->nameid;
|
||||
item.identify = itemdb_isidentified(it->nameid);
|
||||
item.amount = it->count;
|
||||
//#ifdef BOUND_ITEMS
|
||||
// item.bound = dropitem->bound;
|
||||
// item.bound = it.bound;
|
||||
//#endif
|
||||
// if (dropitem->isGUID)
|
||||
// if (it.isGUID)
|
||||
// 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);
|
||||
// else if (dropitem->isAnnounced || itemdb_exists(dropitem->nameid)->flag.broadcast)
|
||||
// intif_broadcast_obtain_special_item(sd, dropitem->nameid, dropitem->mob_id, ITEMOBTAIN_TYPE_MONSTER_ITEM);
|
||||
// else if (it.isAnnounced || itemdb_exists(it.nameid)->flag.broadcast)
|
||||
// intif_broadcast_obtain_special_item(sd, it.nameid, it.mob_id, ITEMOBTAIN_TYPE_MONSTER_ITEM);
|
||||
}
|
||||
}
|
||||
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
|
||||
* @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;
|
||||
|
||||
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);
|
||||
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->save_quest = true;
|
||||
|
||||
if( status < Q_COMPLETE ) {
|
||||
if (status < Q_COMPLETE) {
|
||||
clif_quest_update_status(sd, quest_id, status == Q_ACTIVE ? true : false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
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);
|
||||
|
||||
if( save_settings&CHARSAVE_QUEST )
|
||||
if (save_settings&CHARSAVE_QUEST)
|
||||
chrif_save(sd, CSAVE_NORMAL);
|
||||
|
||||
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
|
||||
* 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;
|
||||
|
||||
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;
|
||||
|
||||
switch( type ) {
|
||||
switch (type) {
|
||||
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.
|
||||
return 1;
|
||||
return sd->quest_log[i].state;
|
||||
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:
|
||||
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;
|
||||
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);
|
||||
if( j == qi->objectives_count )
|
||||
ARR_FIND(0, qi->objectives.size(), j, sd->quest_log[i].count[j] < qi->objectives[j]->count);
|
||||
if (j == qi->objectives.size())
|
||||
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 0;
|
||||
@ -418,153 +677,6 @@ int quest_check(TBL_PC *sd, int quest_id, enum quest_check_type type)
|
||||
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.
|
||||
* 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 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);
|
||||
|
||||
j = 0;
|
||||
for( i = 0; i < sd->num_quests; i++ ) {
|
||||
struct quest_db *qi = quest_search(sd->quest_log[i].quest_id);
|
||||
int i, j = 0;
|
||||
|
||||
if( qi == &quest_dummy ) { //Remove no longer existing entries
|
||||
if( sd->quest_log[i].state != Q_COMPLETE ) //And inform the client if necessary
|
||||
for (i = 0; i < sd->num_quests; i++) {
|
||||
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);
|
||||
continue;
|
||||
}
|
||||
|
||||
if( i != j ) {
|
||||
if (i != j) {
|
||||
//Move entries if there's a gap to fill
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear quest single entry
|
||||
* @param quest
|
||||
* @param free Will free quest from memory
|
||||
**/
|
||||
static void questdb_free_sub(struct quest_db *quest, bool free)
|
||||
{
|
||||
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);
|
||||
bool QuestDatabase::reload() {
|
||||
if (!TypesafeYamlDatabase::reload())
|
||||
return false;
|
||||
|
||||
// Update quest data for players, to ensure no entries about removed quests are left over.
|
||||
map_foreachpc(&quest_reload_check_sub);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
QuestDatabase quest_db;
|
||||
|
||||
/**
|
||||
* Initializes the quest interface.
|
||||
*/
|
||||
void do_init_quest(void)
|
||||
{
|
||||
questdb = idb_alloc(DB_OPT_BASE);
|
||||
quest_read_db();
|
||||
quest_db.load();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -654,20 +738,4 @@ void do_init_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
|
||||
#define QUEST_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "../common/cbasetypes.hpp"
|
||||
#include "../common/database.hpp"
|
||||
#include "../common/strlib.hpp"
|
||||
|
||||
#include "map.hpp"
|
||||
|
||||
struct map_session_data;
|
||||
|
||||
struct quest_dropitem {
|
||||
struct s_quest_dropitem {
|
||||
uint16 nameid;
|
||||
uint16 count;
|
||||
uint16 rate;
|
||||
@ -21,46 +24,53 @@ struct quest_dropitem {
|
||||
//bool isGUID;
|
||||
};
|
||||
|
||||
struct quest_objective {
|
||||
uint16 mob;
|
||||
struct s_quest_objective {
|
||||
uint16 mob_id;
|
||||
uint16 count;
|
||||
};
|
||||
|
||||
struct quest_db {
|
||||
// TODO: find out if signed or unsigned in client
|
||||
int id;
|
||||
unsigned int time;
|
||||
bool time_type;
|
||||
uint8 objectives_count;
|
||||
struct quest_objective *objectives;
|
||||
uint8 dropitem_count;
|
||||
struct quest_dropitem *dropitem;
|
||||
StringBuf name;
|
||||
struct s_quest_db {
|
||||
int32 id;
|
||||
time_t time;
|
||||
bool time_at;
|
||||
std::vector<std::shared_ptr<s_quest_objective>> objectives;
|
||||
std::vector<std::shared_ptr<s_quest_dropitem>> dropitem;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
extern struct quest_db quest_dummy; ///< Dummy entry for invalid quest lookups
|
||||
|
||||
// Questlog check types
|
||||
enum quest_check_type {
|
||||
enum e_quest_check_type : uint8 {
|
||||
HAVEQUEST, ///< Query the state of the given quest
|
||||
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
|
||||
};
|
||||
|
||||
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);
|
||||
void quest_update_objective(TBL_PC * sd, int mob_id);
|
||||
int quest_update_status(TBL_PC * sd, int quest_id, enum quest_state status);
|
||||
int quest_check(TBL_PC * sd, int quest_id, enum quest_check_type type);
|
||||
void quest_update_objective(struct map_session_data *sd, int mob_id);
|
||||
int quest_update_status(struct map_session_data *sd, int quest_id, e_quest_state status);
|
||||
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_final_quest(void);
|
||||
void do_reload_quest(void);
|
||||
|
||||
#endif /* QUEST_HPP */
|
||||
|
@ -19587,10 +19587,10 @@ BUILDIN_FUNC(changequest)
|
||||
BUILDIN_FUNC(checkquest)
|
||||
{
|
||||
struct map_session_data *sd;
|
||||
enum quest_check_type type = HAVEQUEST;
|
||||
e_quest_check_type type = HAVEQUEST;
|
||||
|
||||
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))
|
||||
return SCRIPT_CMD_FAILURE;
|
||||
@ -19606,12 +19606,11 @@ BUILDIN_FUNC(checkquest)
|
||||
BUILDIN_FUNC(isbegin_quest)
|
||||
{
|
||||
struct map_session_data *sd;
|
||||
int i;
|
||||
|
||||
if (!script_charid2sd(3,sd))
|
||||
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));
|
||||
|
||||
return SCRIPT_CMD_SUCCESS;
|
||||
|
@ -70,6 +70,7 @@ int getch( void ){
|
||||
#define MAX_SKILL_ITEM_REQUIRE 10
|
||||
#define MAX_SKILL_STATUS_REQUIRE 3
|
||||
#define MAX_SKILL_EQUIP_REQUIRE 10
|
||||
#define MAX_QUEST_DROPS 3
|
||||
|
||||
struct s_skill_unit_csv : s_skill_db {
|
||||
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_nonearnpcrangedb(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
|
||||
std::unordered_map<uint16, std::string> aegis_itemnames;
|
||||
@ -352,6 +354,12 @@ int do_init( int argc, char** argv ){
|
||||
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 ;-)
|
||||
|
||||
return 0;
|
||||
@ -2390,3 +2398,138 @@ static bool skill_parse_row_skilldb(char* split[], int columns, int current) {
|
||||
|
||||
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