From dc587df982562b804cc6a37e50025dd2b2b13b44 Mon Sep 17 00:00:00 2001 From: Aleos Date: Fri, 6 Mar 2020 09:16:18 -0500 Subject: [PATCH] Updates to the Adoption System * Part of #3074. * Adds script command unadopt and atcommand unadopt to allow babies to leave their parents. * Includes an independence flag which converts the baby to a normal class. * Adds support for any class base level 50 or less to be adopted (in renewal mode). --- conf/atcommands.yml | 6 +++ conf/msg_conf/map_msg.conf | 7 ++++ doc/script_commands.txt | 20 ++++++++- src/map/atcommand.cpp | 54 +++++++++++++++++++++++-- src/map/pc.cpp | 83 ++++++++++++++++++++++++++++++++++++-- src/map/pc.hpp | 30 +++++++++----- src/map/script.cpp | 70 +++++++++++++++++++++++++++++--- 7 files changed, 245 insertions(+), 25 deletions(-) diff --git a/conf/atcommands.yml b/conf/atcommands.yml index b50eaad76e..594a54cb7d 100644 --- a/conf/atcommands.yml +++ b/conf/atcommands.yml @@ -964,6 +964,12 @@ Body: - Command: reloadnpcfile Aliases: - reloadnpc + - Command: adopt + Help: | + Adopt a character. + - Command: unadopt + Help: | + Disown a character. Independence flag will make the baby a normal class. Footer: Imports: diff --git a/conf/msg_conf/map_msg.conf b/conf/msg_conf/map_msg.conf index da093f137e..3e72f68148 100644 --- a/conf/msg_conf/map_msg.conf +++ b/conf/msg_conf/map_msg.conf @@ -1706,5 +1706,12 @@ 1503: You've entered a PK Zone. 1504: You've entered a PK Zone (safe until level %d). +// More Adoption +1505: The Baby is not level 50 or lower. +1506: Baby has been disowned. +1507: Baby is not adopted. +1508: A Parent is not the father or mother of the Baby. +1509: Usage: + //Custom translations import: conf/msg_conf/import/map_msg_eng_conf.txt diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 005cc8a4d8..da9a6b36ca 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -6095,12 +6095,30 @@ Return values: ADOPT_ALREADY_ADOPTED - Character is already adopted. ADOPT_MARRIED_AND_PARTY - Parents need to be married and in a party with the baby. ADOPT_EQUIP_RINGS - Parents need wedding rings equipped. - ADOPT_NOT_NOVICE - Baby is not a Novice. + ADOPT_NOT_NOVICE - Baby is not a Novice. In RENEWAL this has been changed to any class less that is base level 50 or lower. ADOPT_CHARACTER_NOT_FOUND - A parent or Baby was not found. ADOPT_MORE_CHILDREN - You cannot adopt more than 1 child. (client message) ADOPT_LEVEL_70 - Parents need to be at least level 70 in order to adopt someone. (client message) ADOPT_MARRIED - You cannot adopt a married person. (client message) +--------------------------------------- + +*unadopt("",""{,}); +*unadopt(,{,}); + +This function will remove an adoption to the specified baby character. The parent +value can be either parent. Both parents and the baby need to be online in order +for adoption removal to work. + +The option allows the baby to be converted to a normal class. This option +is false by default. + +Return values: + UNADOPT_ALLOWED - Baby is no longer adopted. + UNADOPT_CHARACTER_NOT_FOUND - A parent or Baby was not found. + UNADOPT_NOT_ADOPTED - The Baby is not adopted. + UNADOPT_NOT_CHILD - A Parent is not the father or mother of the Baby. + --------------------------------------- // 4,3.- End of marriage-related commands diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp index 266f375268..50df80aa38 100644 --- a/src/map/atcommand.cpp +++ b/src/map/atcommand.cpp @@ -10051,7 +10051,6 @@ ACMD_FUNC(clonestat) { ACMD_FUNC(adopt) { TBL_PC *b_sd; - enum adopt_responses response; nullpo_retr(-1, sd); @@ -10064,12 +10063,12 @@ ACMD_FUNC(adopt) return -1; } - if ((b_sd = map_nick2sd(atcmd_player_name,false)) == NULL) { + if ((b_sd = map_nick2sd(atcmd_player_name,false)) == nullptr) { clif_displaymessage(fd, msg_txt(sd, 3)); // Character not found. return -1; } - response = pc_try_adopt(sd, map_charid2sd(sd->status.partner_id), b_sd); + e_adopt_responses response = pc_try_adopt(sd, map_charid2sd(sd->status.partner_id), b_sd); if (response == ADOPT_ALLOWED) { TBL_PC *p_sd = map_charid2sd(sd->status.partner_id); @@ -10079,8 +10078,54 @@ ACMD_FUNC(adopt) return 0; } - if (response < ADOPT_MORE_CHILDREN) // No displaymessage for client-type responses + if (response < ADOPT_MORE_CHILDREN) { // No displaymessage for client-type responses +#ifdef RENEWAL + if (response == ADOPT_NOT_NOVICE) + clif_displaymessage(fd, msg_txt(sd, 1505)); + else +#endif clif_displaymessage(fd, msg_txt(sd, 744 + response - 1)); + } + return -1; +} + +/** + * Remove an adopted character. + * Usage: @unadopt + */ +ACMD_FUNC(unadopt) +{ + TBL_PC *b_sd; + + nullpo_retr(-1, sd); + + memset(atcmd_output, '\0', sizeof(atcmd_output)); + memset(atcmd_player_name, '\0', sizeof(atcmd_player_name)); + + uint8 independent_baby = 0; + + if (!message || !*message || sscanf(message, "%23s %1hhu", atcmd_player_name, &independent_baby) < 1) { + sprintf(atcmd_output, msg_txt(sd, 1509), command); // Usage: + clif_displaymessage(fd, atcmd_output); + return -1; + } + + if ((b_sd = map_nick2sd(atcmd_player_name,false)) == nullptr) { + clif_displaymessage(fd, msg_txt(sd, 3)); // Character not found. + return -1; + } + + e_unadopt_responses response = pc_unadopt(sd, map_charid2sd(sd->status.partner_id), b_sd, independent_baby != 0); + + if (response == UNADOPT_ALLOWED) { + clif_displaymessage(fd, msg_txt(sd, 1506)); // Baby has been disowned. + return 0; + } + + if (response == UNADOPT_CHARACTER_NOT_FOUND) + clif_displaymessage(fd, msg_txt(sd, 748)); // A Parent or Baby was not found. + else + clif_displaymessage(fd, msg_txt(sd, 1507 + response - 1)); return -1; } @@ -10427,6 +10472,7 @@ void atcommand_basecommands(void) { ACMD_DEF(clonestat), ACMD_DEF(bodystyle), ACMD_DEF(adopt), + ACMD_DEF2("unadopt", adopt), ACMD_DEF(agitstart3), ACMD_DEF(agitend3), ACMD_DEFR(limitedsale, ATCMD_NOCONSOLE|ATCMD_NOAUTOTRADE), diff --git a/src/map/pc.cpp b/src/map/pc.cpp index 8f609f5059..17eb223fd0 100755 --- a/src/map/pc.cpp +++ b/src/map/pc.cpp @@ -1049,7 +1049,7 @@ bool pc_isequipped(struct map_session_data *sd, unsigned short nameid) * ADOPT_LEVEL_70 - Parents need to be level 70+ (client message) * ADOPT_MARRIED - Cannot adopt a married person (client message) */ -enum adopt_responses pc_try_adopt(struct map_session_data *p1_sd, struct map_session_data *p2_sd, struct map_session_data *b_sd) +e_adopt_responses pc_try_adopt(struct map_session_data *p1_sd, struct map_session_data *p2_sd, struct map_session_data *b_sd) { if( !p1_sd || !p2_sd || !b_sd ) return ADOPT_CHARACTER_NOT_FOUND; @@ -1090,7 +1090,11 @@ enum adopt_responses pc_try_adopt(struct map_session_data *p1_sd, struct map_ses return ADOPT_MARRIED; } - if( !( ( b_sd->status.class_ >= JOB_NOVICE && b_sd->status.class_ <= JOB_THIEF ) || b_sd->status.class_ == JOB_SUPER_NOVICE || b_sd->status.class_ == JOB_SUPER_NOVICE_E ) ) +#ifdef RENEWAL + if (b_sd->status.base_level < 51) +#else + if (!((b_sd->status.class_ >= JOB_NOVICE && b_sd->status.class_ <= JOB_THIEF) || b_sd->status.class_ == JOB_SUPER_NOVICE || b_sd->status.class_ == JOB_SUPER_NOVICE_E)) +#endif return ADOPT_NOT_NOVICE; return ADOPT_ALLOWED; @@ -1147,7 +1151,80 @@ bool pc_adoption(struct map_session_data *p1_sd, struct map_session_data *p2_sd, return false; // Job Change Fail } - + +/** + * Check unadoption rules and remove adoption + * @param p1_sd: Player 1 + * @param p2_sd: Player 2 + * @param b_sd: Player that will be unadopted + * @param independent_baby: If the baby becomes a normal class + * @return see pc.hpp::e_unadopt_responses + */ +e_unadopt_responses pc_unadopt(struct map_session_data *p1_sd, struct map_session_data *p2_sd, struct map_session_data *b_sd, bool independent_baby) { + if (!p1_sd || !p2_sd || !b_sd) + return UNADOPT_CHARACTER_NOT_FOUND; + + if (!b_sd->status.father || !b_sd->status.mother) + return UNADOPT_NOT_ADOPTED; + + if (p1_sd->status.child != b_sd->status.char_id && p2_sd->status.child != b_sd->status.char_id) + return UNADOPT_NOT_CHILD; + + // Baby is separated from parents by independence option (Becomes a normal class) + if (independent_baby) { + int32 joblevel = b_sd->status.job_level, job = pc_mapid2jobid(b_sd->class_ | ~JOBL_BABY, b_sd->status.sex); + uint32 jobexp = b_sd->status.job_exp; + + if (job == -1 || !pc_jobchange(b_sd, job, 0)) + return UNADOPT_CHARACTER_NOT_FOUND; + } + + // Remove Baby/Parent status + p1_sd->status.child = 0; + p2_sd->status.child = 0; + b_sd->status.father = 0; + b_sd->status.mother = 0; + + uint16 sk_idx; + + // Remove Baby Skills + if ((sk_idx = skill_get_index(WE_BABY)) > 0 && pc_checkskill(b_sd, WE_BABY) > 0) { + b_sd->status.skill[sk_idx].lv = 0; + b_sd->status.skill[sk_idx].flag = SKILL_FLAG_TEMPORARY; + clif_deleteskill(b_sd, WE_BABY); + } + if ((sk_idx = skill_get_index(WE_CALLPARENT)) > 0 && pc_checkskill(b_sd, WE_CALLPARENT) > 0) { + b_sd->status.skill[sk_idx].lv = 0; + b_sd->status.skill[sk_idx].flag = SKILL_FLAG_TEMPORARY; + clif_deleteskill(b_sd, WE_CALLPARENT); + } + if ((sk_idx = skill_get_index(WE_CHEERUP)) > 0 && pc_checkskill(b_sd, WE_CHEERUP) > 0) { + b_sd->status.skill[sk_idx].lv = 0; + b_sd->status.skill[sk_idx].flag = SKILL_FLAG_TEMPORARY; + clif_deleteskill(b_sd, WE_CHEERUP); + } + + // Remove Parents Skills + if ((sk_idx = skill_get_index(WE_CALLBABY)) > 0) { + if (pc_checkskill(p1_sd, WE_CALLBABY) > 0) { + p1_sd->status.skill[sk_idx].lv = 0; + p1_sd->status.skill[sk_idx].flag = SKILL_FLAG_TEMPORARY; + clif_deleteskill(b_sd, WE_CALLBABY); + } + if (pc_checkskill(p2_sd, WE_CALLBABY) > 0) { + p1_sd->status.skill[sk_idx].lv = 0; + p1_sd->status.skill[sk_idx].flag = SKILL_FLAG_TEMPORARY; + clif_deleteskill(b_sd, WE_CALLBABY); + } + } + + chrif_save(p1_sd, CSAVE_NORMAL); + chrif_save(p2_sd, CSAVE_NORMAL); + chrif_save(b_sd, CSAVE_NORMAL); + + return UNADOPT_ALLOWED; +} + /*========================================== * Check if player can use/equip selected item. Used by pc_isUseitem and pc_isequip Returns: diff --git a/src/map/pc.hpp b/src/map/pc.hpp index 60505ba55c..784476250b 100644 --- a/src/map/pc.hpp +++ b/src/map/pc.hpp @@ -836,16 +836,23 @@ enum idletime_option { IDLE_ATCOMMAND = 0x200, }; -enum adopt_responses { - ADOPT_ALLOWED = 0, - ADOPT_ALREADY_ADOPTED, - ADOPT_MARRIED_AND_PARTY, - ADOPT_EQUIP_RINGS, - ADOPT_NOT_NOVICE, - ADOPT_CHARACTER_NOT_FOUND, - ADOPT_MORE_CHILDREN, - ADOPT_LEVEL_70, - ADOPT_MARRIED, +enum e_adopt_responses : uint8 { + ADOPT_ALLOWED = 0, ///< Baby can be adopted + ADOPT_ALREADY_ADOPTED, ///< Player is already adopted + ADOPT_MARRIED_AND_PARTY, ///< Parents need to be married and in a party + ADOPT_EQUIP_RINGS, ///< Parents need their wedding rings equipped + ADOPT_NOT_NOVICE, ///< Baby is not a Novice + ADOPT_CHARACTER_NOT_FOUND, ///< Baby or Parent not found + ADOPT_MORE_CHILDREN, ///< Cannot adopt more than 1 baby (client message) + ADOPT_LEVEL_70, ///< Parents need to be level 70 or higher (client message) + ADOPT_MARRIED, ///< Parents need to be married (client message) +}; + +enum e_unadopt_responses : uint8 { + UNADOPT_ALLOWED = 0, ///< Baby can be unadopted + UNADOPT_CHARACTER_NOT_FOUND, ///< Baby or Parent not found + UNADOPT_NOT_ADOPTED, ///< Baby is not adopted + UNADOPT_NOT_CHILD, ///< Neither parent is the father/mother }; enum item_check { @@ -1120,8 +1127,9 @@ bool pc_takeitem(struct map_session_data *sd,struct flooritem_data *fitem); bool pc_dropitem(struct map_session_data *sd,int n,int amount); bool pc_isequipped(struct map_session_data *sd, unsigned short nameid); -enum adopt_responses pc_try_adopt(struct map_session_data *p1_sd, struct map_session_data *p2_sd, struct map_session_data *b_sd); +e_adopt_responses pc_try_adopt(struct map_session_data *p1_sd, struct map_session_data *p2_sd, struct map_session_data *b_sd); bool pc_adoption(struct map_session_data *p1_sd, struct map_session_data *p2_sd, struct map_session_data *b_sd); +e_unadopt_responses pc_unadopt(struct map_session_data *p1_sd, struct map_session_data *p2_sd, struct map_session_data *b_sd, bool independent_baby); void pc_updateweightstatus(struct map_session_data *sd); diff --git a/src/map/script.cpp b/src/map/script.cpp index 5af99c5a5f..b9c61e8aaa 100644 --- a/src/map/script.cpp +++ b/src/map/script.cpp @@ -22729,13 +22729,12 @@ BUILDIN_FUNC(navigateto){ BUILDIN_FUNC(adopt) { TBL_PC *sd, *b_sd; - enum adopt_responses response; if (script_isstring(st, 2)) { const char *name = script_getstr(st, 2); sd = map_nick2sd(name,false); - if (sd == NULL) { + if (sd == nullptr) { ShowError("buildin_adopt: Non-existant parent character %s requested.\n", name); return SCRIPT_CMD_FAILURE; } @@ -22743,7 +22742,7 @@ BUILDIN_FUNC(adopt) uint32 char_id = script_getnum(st, 2); sd = map_charid2sd(char_id); - if (sd == NULL) { + if (sd == nullptr) { ShowError("buildin_adopt: Non-existant parent character %d requested.\n", char_id); return SCRIPT_CMD_FAILURE; } @@ -22753,7 +22752,7 @@ BUILDIN_FUNC(adopt) const char *name = script_getstr(st, 3); b_sd = map_nick2sd(name,false); - if (b_sd == NULL) { + if (b_sd == nullptr) { ShowError("buildin_adopt: Non-existant baby character %s requested.\n", name); return SCRIPT_CMD_FAILURE; } @@ -22761,13 +22760,13 @@ BUILDIN_FUNC(adopt) uint32 char_id = script_getnum(st, 3); b_sd = map_charid2sd(char_id); - if (b_sd == NULL) { + if (b_sd == nullptr) { ShowError("buildin_adopt: Non-existant baby character %d requested.\n", char_id); return SCRIPT_CMD_FAILURE; } } - response = pc_try_adopt(sd, map_charid2sd(sd->status.partner_id), b_sd); + e_adopt_responses response = pc_try_adopt(sd, map_charid2sd(sd->status.partner_id), b_sd); if (response == ADOPT_ALLOWED) { TBL_PC *p_sd = map_charid2sd(sd->status.partner_id); @@ -22782,6 +22781,64 @@ BUILDIN_FUNC(adopt) return SCRIPT_CMD_FAILURE; } +/** + * unadopt("",""{,}); + * unadopt(,{,}); + */ +BUILDIN_FUNC(unadopt) +{ + TBL_PC *sd, *b_sd; + + if (script_isstring(st, 2)) { + const char *name = script_getstr(st, 2); + + sd = map_nick2sd(name,false); + if (sd == nullptr) { + ShowError("buildin_unadopt: Non-existant parent character %s requested.\n", name); + return SCRIPT_CMD_FAILURE; + } + } else { + uint32 char_id = script_getnum(st, 2); + + sd = map_charid2sd(char_id); + if (sd == nullptr) { + ShowError("buildin_unadopt: Non-existant parent character %d requested.\n", char_id); + return SCRIPT_CMD_FAILURE; + } + } + + if (script_isstring(st, 3)) { + const char *name = script_getstr(st, 3); + + b_sd = map_nick2sd(name,false); + if (b_sd == nullptr) { + ShowError("buildin_unadopt: Non-existant baby character %s requested.\n", name); + return SCRIPT_CMD_FAILURE; + } + } else { + uint32 char_id = script_getnum(st, 3); + + b_sd = map_charid2sd(char_id); + if (b_sd == nullptr) { + ShowError("buildin_unadopt: Non-existant baby character %d requested.\n", char_id); + return SCRIPT_CMD_FAILURE; + } + } + + bool independent_baby = false; + + if (script_hasdata(st, 4)) + independent_baby = script_getnum(st, 4) != 0; + + e_unadopt_responses response = pc_unadopt(sd, map_charid2sd(sd->status.partner_id), b_sd, independent_baby); + + script_pushint(st, response); + if (response == UNADOPT_ALLOWED) + return SCRIPT_CMD_SUCCESS; + else + return SCRIPT_CMD_FAILURE; +} + /** * Returns the minimum or maximum of all the given parameters for integer variables. * @@ -25217,6 +25274,7 @@ struct script_function buildin_func[] = { BUILDIN_DEF(navigateto,"s???????"), BUILDIN_DEF(getguildalliance,"ii"), BUILDIN_DEF(adopt,"vv"), + BUILDIN_DEF(unadopt, "vv?"), BUILDIN_DEF(getexp2,"ii?"), BUILDIN_DEF(recalculatestat,""), BUILDIN_DEF(hateffect,"ii"),