|
@@ -105,26 +105,49 @@ uint64 QuestDatabase::parseBodyNode(const YAML::Node &node) {
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
- if (!this->nodeExists(targetNode, "Mob"))
|
|
|
|
- continue;
|
|
|
|
|
|
+ if (!this->nodeExists(targetNode, "Mob") && !this->nodeExists(targetNode, "Id")) {
|
|
|
|
+ this->invalidWarning(targetNode, "Missing Target 'Mob' or 'Id', skipping.\n");
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
|
|
- std::string mob_name;
|
|
|
|
|
|
+ std::shared_ptr<s_quest_objective> target;
|
|
|
|
+ std::vector<std::shared_ptr<s_quest_objective>>::iterator it;
|
|
|
|
+ uint16 index = 0, mob_id = 0;
|
|
|
|
|
|
- if (!this->asString(targetNode, "Mob", mob_name))
|
|
|
|
- return 0;
|
|
|
|
|
|
+ if (this->nodeExists(targetNode, "Mob")) {
|
|
|
|
|
|
- struct mob_db *mob = mobdb_search_aegisname(mob_name.c_str());
|
|
|
|
|
|
+ std::string mob_name;
|
|
|
|
|
|
- if (!mob) {
|
|
|
|
- this->invalidWarning(targetNode["Mob"], "Mob %s does not exist, skipping.\n", mob_name.c_str());
|
|
|
|
- continue;
|
|
|
|
|
|
+ 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());
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ mob_id = mob->vd.class_;
|
|
|
|
+
|
|
|
|
+ it = std::find_if(quest->objectives.begin(), quest->objectives.end(), [&](std::shared_ptr<s_quest_objective> const &v) {
|
|
|
|
+ return (*v).mob_id == mob_id;
|
|
|
|
+ });
|
|
}
|
|
}
|
|
|
|
+ else {
|
|
|
|
+ if (!this->asUInt16(targetNode, "Id", index)) {
|
|
|
|
+ this->invalidWarning(targetNode, "Missing 'Id', skipping.\n");
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ if (index == 0) {
|
|
|
|
+ this->invalidWarning(targetNode["Id"], "'Id' can't be 0, skipping.\n");
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
|
|
- //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_;
|
|
|
|
- });
|
|
|
|
|
|
+ it = std::find_if(quest->objectives.begin(), quest->objectives.end(), [&](std::shared_ptr<s_quest_objective> const &v) {
|
|
|
|
+ return (*v).index == index;
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
|
|
if (it != quest->objectives.end())
|
|
if (it != quest->objectives.end())
|
|
target = (*it);
|
|
target = (*it);
|
|
@@ -136,11 +159,118 @@ uint64 QuestDatabase::parseBodyNode(const YAML::Node &node) {
|
|
if (!targetExists) {
|
|
if (!targetExists) {
|
|
if (!this->nodeExists(targetNode, "Count")) {
|
|
if (!this->nodeExists(targetNode, "Count")) {
|
|
this->invalidWarning(targetNode["Count"], "Targets has no Count value specified, skipping.\n");
|
|
this->invalidWarning(targetNode["Count"], "Targets has no Count value specified, skipping.\n");
|
|
- continue;
|
|
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!this->nodeExists(targetNode, "Mob") && !this->nodeExists(targetNode, "MinLevel") && !this->nodeExists(targetNode, "MaxLevel") &&
|
|
|
|
+ !this->nodeExists(targetNode, "Race") && !this->nodeExists(targetNode, "Size") && !this->nodeExists(targetNode, "Element")) {
|
|
|
|
+ this->invalidWarning(targetNode, "Targets is missing required field, skipping.\n");
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
target = std::make_shared<s_quest_objective>();
|
|
target = std::make_shared<s_quest_objective>();
|
|
- target->mob_id = mob->vd.class_;
|
|
|
|
|
|
+ target->index = index;
|
|
|
|
+ target->mob_id = mob_id;
|
|
|
|
+ target->min_level = 0;
|
|
|
|
+ target->max_level = 0;
|
|
|
|
+ target->race = RC_ALL;
|
|
|
|
+ target->size = SZ_ALL;
|
|
|
|
+ target->element = ELE_ALL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!this->nodeExists(targetNode, "Mob")) {
|
|
|
|
+ if (this->nodeExists(targetNode, "MinLevel")) {
|
|
|
|
+ uint16 level;
|
|
|
|
+
|
|
|
|
+ if (!this->asUInt16(targetNode, "MinLevel", level))
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ target->min_level = level;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (this->nodeExists(targetNode, "MaxLevel")) {
|
|
|
|
+ uint16 level;
|
|
|
|
+
|
|
|
|
+ if (!this->asUInt16(targetNode, "MaxLevel", level))
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ if (target->min_level > level) {
|
|
|
|
+ this->invalidWarning(targetNode["MaxLevel"], "%d's MinLevel is greater than MaxLevel. Defaulting MaxLevel to %d.\n", target->min_level, MAX_LEVEL);
|
|
|
|
+ level = MAX_LEVEL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ target->max_level = level;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (this->nodeExists(targetNode, "Race")) {
|
|
|
|
+ std::string race;
|
|
|
|
+
|
|
|
|
+ if (!this->asString(targetNode, "Race", race))
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ std::string race_constant = "RC_" + race;
|
|
|
|
+ int64 constant;
|
|
|
|
+
|
|
|
|
+ if (!script_get_constant(race_constant.c_str(), &constant)) {
|
|
|
|
+ this->invalidWarning(targetNode["Race"], "Invalid race %s, skipping.\n", race.c_str());
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (constant < RC_FORMLESS || constant > RC_ALL || constant == RC_PLAYER_HUMAN || constant == RC_PLAYER_DORAM) {
|
|
|
|
+ this->invalidWarning(targetNode["Race"], "Unsupported race %s, skipping.\n", race.c_str());
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ target->race = static_cast<e_race>(constant);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (this->nodeExists(targetNode, "Size")) {
|
|
|
|
+ std::string size_;
|
|
|
|
+
|
|
|
|
+ if (!this->asString(targetNode, "Size", size_))
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ std::string size_constant = "Size_" + size_;
|
|
|
|
+ int64 constant;
|
|
|
|
+
|
|
|
|
+ if (!script_get_constant(size_constant.c_str(), &constant)) {
|
|
|
|
+ this->invalidWarning(targetNode["Size"], "Invalid size type %s, skipping.\n", size_.c_str());
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (constant < SZ_SMALL || constant > SZ_ALL) {
|
|
|
|
+ this->invalidWarning(targetNode["size"], "Unsupported size %s, skipping.\n", size_.c_str());
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ target->size = static_cast<e_size>(constant);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (this->nodeExists(targetNode, "Element")) {
|
|
|
|
+ std::string element;
|
|
|
|
+
|
|
|
|
+ if (!this->asString(targetNode, "Element", element))
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ std::string element_constant = "Ele_" + element;
|
|
|
|
+ int64 constant;
|
|
|
|
+
|
|
|
|
+ if (!script_get_constant(element_constant.c_str(), &constant)) {
|
|
|
|
+ this->invalidWarning(targetNode["Element"], "Invalid element %s, skipping.\n", element.c_str());
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (constant < ELE_NEUTRAL || constant > ELE_ALL) {
|
|
|
|
+ this->invalidWarning(targetNode["Element"], "Unsupported element %s, skipping.\n", element.c_str());
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ target->element = static_cast<e_element>(constant);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // if max_level is set, min_level is 1
|
|
|
|
+ if (target->min_level == 0 && target->max_level > 0)
|
|
|
|
+ target->min_level = 1;
|
|
}
|
|
}
|
|
|
|
|
|
if (this->nodeExists(targetNode, "Count")) {
|
|
if (this->nodeExists(targetNode, "Count")) {
|
|
@@ -336,7 +466,7 @@ int quest_pc_login(struct map_session_data *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 (int 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]);
|
|
#endif
|
|
#endif
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
@@ -406,7 +536,7 @@ int quest_add(struct map_session_data *sd, int quest_id)
|
|
sd->save_quest = true;
|
|
sd->save_quest = true;
|
|
|
|
|
|
clif_quest_add(sd, &sd->quest_log[n]);
|
|
clif_quest_add(sd, &sd->quest_log[n]);
|
|
- clif_quest_update_objective(sd, &sd->quest_log[n], 0);
|
|
|
|
|
|
+ clif_quest_update_objective(sd, &sd->quest_log[n]);
|
|
|
|
|
|
if( save_settings&CHARSAVE_QUEST )
|
|
if( save_settings&CHARSAVE_QUEST )
|
|
chrif_save(sd, CSAVE_NORMAL);
|
|
chrif_save(sd, CSAVE_NORMAL);
|
|
@@ -456,7 +586,7 @@ int quest_change(struct map_session_data *sd, int qid1, int qid2)
|
|
|
|
|
|
clif_quest_delete(sd, qid1);
|
|
clif_quest_delete(sd, qid1);
|
|
clif_quest_add(sd, &sd->quest_log[i]);
|
|
clif_quest_add(sd, &sd->quest_log[i]);
|
|
- clif_quest_update_objective(sd, &sd->quest_log[i], 0);
|
|
|
|
|
|
+ clif_quest_update_objective(sd, &sd->quest_log[i]);
|
|
|
|
|
|
if( save_settings&CHARSAVE_QUEST )
|
|
if( save_settings&CHARSAVE_QUEST )
|
|
chrif_save(sd, CSAVE_NORMAL);
|
|
chrif_save(sd, CSAVE_NORMAL);
|
|
@@ -509,47 +639,81 @@ int quest_delete(struct map_session_data *sd, int quest_id)
|
|
* @param ap : Argument list, expecting:
|
|
* @param ap : Argument list, expecting:
|
|
* int Party ID
|
|
* int Party ID
|
|
* int Mob ID
|
|
* int Mob ID
|
|
|
|
+ * int Mob Level
|
|
|
|
+ * int Mob Race
|
|
|
|
+ * int Mob Size
|
|
|
|
+ * int Mob Element
|
|
*/
|
|
*/
|
|
int quest_update_objective_sub(struct block_list *bl, va_list ap)
|
|
int quest_update_objective_sub(struct block_list *bl, va_list ap)
|
|
{
|
|
{
|
|
|
|
+ nullpo_ret(bl);
|
|
|
|
+
|
|
struct map_session_data *sd;
|
|
struct map_session_data *sd;
|
|
- int mob_id, party_id;
|
|
|
|
|
|
|
|
- nullpo_ret(bl);
|
|
|
|
nullpo_ret(sd = (struct map_session_data *)bl);
|
|
nullpo_ret(sd = (struct map_session_data *)bl);
|
|
|
|
|
|
- party_id = va_arg(ap,int);
|
|
|
|
- mob_id = va_arg(ap,int);
|
|
|
|
-
|
|
|
|
if( !sd->avail_quests )
|
|
if( !sd->avail_quests )
|
|
return 0;
|
|
return 0;
|
|
|
|
+
|
|
|
|
+ int party_id = va_arg(ap,int);
|
|
|
|
+ int mob_id = va_arg(ap, int);
|
|
|
|
+ int mob_level = va_arg(ap, int);
|
|
|
|
+ e_race mob_race = static_cast<e_race>(va_arg(ap, int));
|
|
|
|
+ e_size mob_size = static_cast<e_size>(va_arg(ap, int));
|
|
|
|
+ e_element mob_element = static_cast<e_element>(va_arg(ap, int));
|
|
|
|
+
|
|
if( sd->status.party_id != party_id )
|
|
if( sd->status.party_id != party_id )
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
- quest_update_objective(sd, mob_id);
|
|
|
|
|
|
+ quest_update_objective(sd, mob_id, mob_level, mob_race, mob_size, mob_element);
|
|
|
|
|
|
return 1;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Updates the quest objectives for a character after killing a monster, including the handling of quest-granted drops.
|
|
* Updates the quest objectives for a character after killing a monster, including the handling of quest-granted drops.
|
|
- * @param sd : Character's data
|
|
|
|
- * @param mob_id : Monster ID
|
|
|
|
|
|
+ * @param sd: Character's data
|
|
|
|
+ * @param mob_id: Monster ID
|
|
|
|
+ * @param mob_level: Monster Level
|
|
|
|
+ * @param mob_race: Monster Race
|
|
|
|
+ * @param mob_size: Monster Size
|
|
|
|
+ * @param mob_element: Monster Element
|
|
*/
|
|
*/
|
|
-void quest_update_objective(struct map_session_data *sd, int mob_id)
|
|
|
|
|
|
+void quest_update_objective(struct map_session_data *sd, int mob_id, int mob_level, e_race mob_race, e_size mob_size, e_element mob_element)
|
|
{
|
|
{
|
|
|
|
+ nullpo_retv(sd);
|
|
|
|
+
|
|
for (int i = 0; i < sd->avail_quests; i++) {
|
|
for (int i = 0; i < sd->avail_quests; i++) {
|
|
if (sd->quest_log[i].state == Q_COMPLETE) // Skip complete quests
|
|
if (sd->quest_log[i].state == Q_COMPLETE) // Skip complete quests
|
|
continue;
|
|
continue;
|
|
|
|
|
|
std::shared_ptr<s_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);
|
|
|
|
+ if (!qi)
|
|
|
|
+ continue;
|
|
|
|
|
|
// Process quest objectives
|
|
// Process quest objectives
|
|
for (int j = 0; j < qi->objectives.size(); j++) {
|
|
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) {
|
|
|
|
|
|
+ uint8 objective_check = 0; // Must pass all 5 checks
|
|
|
|
+
|
|
|
|
+ if (qi->objectives[j]->mob_id == mob_id)
|
|
|
|
+ objective_check = 5;
|
|
|
|
+ else if (qi->objectives[j]->mob_id == 0) {
|
|
|
|
+ if (qi->objectives[j]->min_level == 0 || qi->objectives[j]->min_level <= mob_level)
|
|
|
|
+ objective_check++;
|
|
|
|
+ if (qi->objectives[j]->max_level == 0 || qi->objectives[j]->max_level >= mob_level)
|
|
|
|
+ objective_check++;
|
|
|
|
+ if (qi->objectives[j]->race == RC_ALL || qi->objectives[j]->race == mob_race)
|
|
|
|
+ objective_check++;
|
|
|
|
+ if (qi->objectives[j]->size == SZ_ALL || qi->objectives[j]->size == mob_size)
|
|
|
|
+ objective_check++;
|
|
|
|
+ if (qi->objectives[j]->element == ELE_ALL || qi->objectives[j]->element == mob_element)
|
|
|
|
+ objective_check++;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (objective_check == 5 && 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]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -562,21 +726,21 @@ void quest_update_objective(struct map_session_data *sd, int mob_id)
|
|
if (!itemdb_exists(it->nameid))
|
|
if (!itemdb_exists(it->nameid))
|
|
continue;
|
|
continue;
|
|
|
|
|
|
- struct item item = {};
|
|
|
|
|
|
+ struct item entry = {};
|
|
|
|
|
|
- item.nameid = it->nameid;
|
|
|
|
- item.identify = itemdb_isidentified(it->nameid);
|
|
|
|
- item.amount = it->count;
|
|
|
|
|
|
+ entry.nameid = it->nameid;
|
|
|
|
+ entry.identify = itemdb_isidentified(it->nameid);
|
|
|
|
+ entry.amount = it->count;
|
|
//#ifdef BOUND_ITEMS
|
|
//#ifdef BOUND_ITEMS
|
|
-// item.bound = it.bound;
|
|
|
|
|
|
+// entry.bound = it->bound;
|
|
//#endif
|
|
//#endif
|
|
// if (it.isGUID)
|
|
// if (it.isGUID)
|
|
// item.unique_id = pc_generate_unique_id(sd);
|
|
// item.unique_id = pc_generate_unique_id(sd);
|
|
|
|
|
|
- char temp;
|
|
|
|
|
|
+ e_additem_result result;
|
|
|
|
|
|
- if ((temp = pc_additem(sd, &item, 1, LOG_TYPE_QUEST)) != ADDITEM_SUCCESS) // Failed to obtain the item
|
|
|
|
- clif_additem(sd, 0, 0, temp);
|
|
|
|
|
|
+ if ((result = pc_additem(sd, &entry, 1, LOG_TYPE_QUEST)) != ADDITEM_SUCCESS) // Failed to obtain the item
|
|
|
|
+ clif_additem(sd, 0, 0, result);
|
|
// else if (it.isAnnounced || itemdb_exists(it.nameid)->flag.broadcast)
|
|
// 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);
|
|
// intif_broadcast_obtain_special_item(sd, it.nameid, it.mob_id, ITEMOBTAIN_TYPE_MONSTER_ITEM);
|
|
}
|
|
}
|