diff options
Diffstat (limited to 'src/char/int_party.c')
-rw-r--r-- | src/char/int_party.c | 506 |
1 files changed, 300 insertions, 206 deletions
diff --git a/src/char/int_party.c b/src/char/int_party.c index 8ee03ecc5..c16eea34e 100644 --- a/src/char/int_party.c +++ b/src/char/int_party.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -42,83 +42,139 @@ static struct inter_party_interface inter_party_s; struct inter_party_interface *inter_party; -//Updates party's level range and unsets even share if broken. +/** + * Updates party's level range and disables even share if requirements are not fulfilled. + * + * @param p The party. + * @return 0 on failure, 1 on success. + * + **/ static int inter_party_check_lv(struct party_data *p) { - int i; - unsigned int lv; nullpo_ret(p); - p->min_lv = UINT_MAX; - p->max_lv = 0; - for(i=0;i<MAX_PARTY;i++){ - /** - * - If not online OR if it's a family party and this is the child (doesn't affect exp range) - **/ - if(!p->party.member[i].online || p->party.member[i].char_id == p->family ) - continue; - lv=p->party.member[i].lv; - if (lv < p->min_lv) p->min_lv = lv; - if (lv > p->max_lv) p->max_lv = lv; + p->min_lv = MAX_LEVEL; + p->max_lv = 1; + + for (int i = 0; i < MAX_PARTY; i++) { + if (p->party.member[i].online == 0 || p->party.member[i].char_id == p->family) + continue; /// If not online OR if it's a family party and this is the child, don't affect exp range. + + p->min_lv = min(p->min_lv, p->party.member[i].lv); + p->max_lv = max(p->max_lv, p->party.member[i].lv); } - if (p->party.exp && !inter_party->check_exp_share(p)) { + if (p->party.exp == 1 && inter_party->check_exp_share(p) == 0) { p->party.exp = 0; mapif->party_optionchanged(0, &p->party, 0, 0); return 0; } + return 1; } -//Calculates the state of a party. + +/** + * Checks if a party is a family state party. (Family share feature.) + * Conditions for a family state party: + * - All party members have to belong to the same family. Not even offline strangers are allowed. + * So only parties with 2 or 3 members come in question. + * - At least one parent has to be on the same map with the child. + * - Parents within the party have to be level 70 or higher, even when offline. + * + * @param p The party. + * @return The child's char ID on success, otherwise 0. + * + **/ +static int inter_party_is_family_party(struct party_data *p) +{ + nullpo_ret(p); + + if (p->size < 2 || p->size > 3 || p->party.count < 2) + return 0; + + int child_id = 0; + + for (int i = 0; i < MAX_PARTY - 1; i++) { + if (p->party.member[i].online == 0) + continue; + + struct mmo_charstatus *char_i = idb_get(chr->char_db_, p->party.member[i].char_id); + + if (char_i == NULL) + continue; + + for (int j = i + 1; j < MAX_PARTY; j++) { + if (p->party.member[j].online == 0) + continue; + + struct mmo_charstatus *char_j = idb_get(chr->char_db_, p->party.member[j].char_id); + + if (char_j == NULL) + continue; + + if (p->party.member[i].map != p->party.member[j].map) + continue; + + if (char_i->char_id == char_j->child && char_j->base_level >= 70) + child_id = char_i->char_id; + + if (char_j->char_id == char_i->child && char_i->base_level >= 70) + child_id = char_j->char_id; + + if (child_id != 0) + break; + } + + if (child_id != 0) + break; + } + + if (child_id != 0 && p->size > 2) { + for (int i = 0; i < MAX_PARTY; i++) { + struct mmo_charstatus *party_member = idb_get(chr->char_db_, p->party.member[i].char_id); + + /// Check if there is a stranger within the party. + if (party_member != NULL && party_member->char_id != child_id && party_member->child != child_id) { + child_id = 0; /// Stranger detected. + break; + } + + /// Check if there is a parents with level lower than 70 within the party. + if (party_member != NULL && party_member->child == child_id && party_member->base_level < 70) { + child_id = 0; /// Parent with level lower than 70 detected. + break; + } + } + } + + return child_id; +} + +/** + * Calculates the state of a party. + * + * @param p The party. + * + **/ static void inter_party_calc_state(struct party_data *p) { - int i; nullpo_retv(p); - p->min_lv = UINT_MAX; - p->max_lv = 0; - p->party.count = - p->size = - p->family = 0; - - //Check party size - for(i=0;i<MAX_PARTY;i++){ - if (!p->party.member[i].lv) continue; + + p->party.count = 0; + p->size = 0; + + for (int i = 0; i < MAX_PARTY; i++) { + if (p->party.member[i].lv == 0) /// Is this even possible? [Kenpachi] + continue; + p->size++; - if(p->party.member[i].online) + + if (p->party.member[i].online == 1) p->party.count++; } - // FIXME[Haru]: What if the occupied positions aren't the first three? It can happen if some party members leave. This is the reason why family sharing some times stops working until you recreate your party - if( p->size == 2 && ( chr->char_child(p->party.member[0].char_id,p->party.member[1].char_id) || chr->char_child(p->party.member[1].char_id,p->party.member[0].char_id) ) ) { - //Child should be able to share with either of their parents [RoM] - if (p->party.member[0].class >= JOB_BABY && p->party.member[0].class <= JOB_SUPER_BABY) //first slot is the child? - p->family = p->party.member[0].char_id; - else - p->family = p->party.member[1].char_id; - } else if( p->size == 3 ) { - //Check Family State. - p->family = chr->char_family( - p->party.member[0].char_id, - p->party.member[1].char_id, - p->party.member[2].char_id - ); - } - //max/min levels. - for (i = 0; i < MAX_PARTY; i++) { - unsigned int lv = p->party.member[i].lv; - if (!lv) continue; - if (p->party.member[i].online - && p->party.member[i].char_id != p->family /* In families, the kid is not counted towards exp share rules. */ - ) { - if( lv < p->min_lv ) p->min_lv=lv; - if( p->max_lv < lv ) p->max_lv=lv; - } - } - if (p->party.exp && !inter_party->check_exp_share(p)) { - p->party.exp = 0; //Set off even share. - mapif->party_optionchanged(0, &p->party, 0, 0); - } - return; + p->family = inter_party->is_family_party(p); + inter_party->check_lv(p); } // Save party to mysql @@ -196,6 +252,39 @@ static int inter_party_tosql(struct party *p, int flag, int index) return 1; } +/** + * Updates the `char`.`party_id` column and removes party data from memory. + * Sets the party ID of all characters whose party ID matches the passed one to 0. + * Calls idb_remove() to remove party data from memory. + * + * @param party_id The party ID. + * @return 0 on failure, 1 on success. + * + **/ +static int inter_party_del_nonexistent_party(int party_id) +{ + struct SqlStmt *stmt = SQL->StmtMalloc(inter->sql_handle); + + if (stmt == NULL) { + SqlStmt_ShowDebug(stmt); + return 0; + } + + const char *query = "UPDATE `%s` SET `party_id`='0' WHERE `party_id`=?"; + + if (SQL_ERROR == SQL->StmtPrepare(stmt, query, char_db) + || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_UINT32, &party_id, sizeof(party_id)) + || SQL_ERROR == SQL->StmtExecute(stmt)) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + return 0; + } + + idb_remove(inter_party->db, party_id); + + return 1; +} + // Read party from mysql static struct party_data *inter_party_fromsql(int party_id) { @@ -314,11 +403,18 @@ static struct party_data *inter_party_search_partyname(const char *const str) return p; } -// Returns whether this party can keep having exp share or not. +/** + * Checks if a party fulfills the requirements to share EXP. + * + * @param p The party. + * @return 1 if party can share EXP, otherwise 0. + * + **/ static int inter_party_check_exp_share(struct party_data *const p) { nullpo_ret(p); - return (p->party.count < 2 || p->max_lv - p->min_lv <= party_share_level); + + return (p->party.count < 2 || p->family != 0 || p->max_lv - p->min_lv <= party_share_level); } // Is there any member in the party? @@ -389,36 +485,38 @@ static struct party_data *inter_party_create(const char *name, int item, int ite return p; } -// Add a player to party request +/** + * Add a player to party request. + * + * @param party_id The ID of the party. + * @param member The member to add. + * @return true on success, otherwise false. + * + **/ static bool inter_party_add_member(int party_id, const struct party_member *member) { - struct party_data *p; - int i; + nullpo_retr(false, member); - nullpo_ret(member); - p = inter_party->fromsql(party_id); - if( p == NULL || p->size == MAX_PARTY ) { + if (party_id < 1) /// Invalid party ID. return false; - } - ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == 0 ); - if (i == MAX_PARTY) { - // Party full + struct party_data *p = inter_party->fromsql(party_id); + + if (p == NULL) { /// Party does not exist. + inter_party->del_nonexistent_party(party_id); return false; } + int i; + + ARR_FIND(0, MAX_PARTY, i, p->party.member[i].account_id == 0); + + if (i == MAX_PARTY) /// Party is full. + return false; + memcpy(&p->party.member[i], member, sizeof(struct party_member)); p->party.member[i].leader = 0; - if (p->party.member[i].online) p->party.count++; - p->size++; - if (p->size == 2 || p->size == 3) // Check family state. And also accept either of their Parents. [RoM] - inter_party->calc_state(p); - else //Check even share range. - if (member->lv < p->min_lv || member->lv > p->max_lv || p->family) { - if (p->family) p->family = 0; //Family state broken. - inter_party->check_lv(p); - } - + inter_party->calc_state(p); /// Count online/offline members and check family state and even share range. mapif->party_info(-1, &p->party, 0); inter_party->tosql(&p->party, PS_ADDMEMBER, i); @@ -446,103 +544,92 @@ static bool inter_party_change_option(int party_id, int account_id, int exp, int return true; } -//Request leave party +/** + * Leave party request. + * + * @param party_id The ID of the party. + * @param account_id The account ID of the leaving character. + * @param char_id The char ID of the leaving character. + * @return true on success, otherwise false. + * + **/ static bool inter_party_leave(int party_id, int account_id, int char_id) { - struct party_data *p; - int i,j; + if (party_id < 1) /// Invalid party ID. + return false; - p = inter_party->fromsql(party_id); - if( p == NULL ) - {// Party does not exists? - if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `party_id`='0' WHERE `party_id`='%d'", char_db, party_id) ) - Sql_ShowDebug(inter->sql_handle); + struct party_data *p = inter_party->fromsql(party_id); + + if (p == NULL) { /// Party does not exist. + inter_party->del_nonexistent_party(party_id); return false; } - for (i = 0; i < MAX_PARTY; i++) { - if(p->party.member[i].account_id == account_id && - p->party.member[i].char_id == char_id) { - break; - } - } - if (i >= MAX_PARTY) - return false; //Member not found? + int i; + + ARR_FIND(0, MAX_PARTY, i, p->party.member[i].account_id == account_id && p->party.member[i].char_id == char_id); + + if (i == MAX_PARTY) /// Character not found in party. + return false; mapif->party_withdraw(party_id, account_id, char_id); - j = p->party.member[i].lv; - if (p->party.member[i].online > 0) - p->party.count--; + if (p->party.member[i].online == 1) + p->party.member[i].online = 0; + memset(&p->party.member[i], 0, sizeof(struct party_member)); - p->size--; - if (j == p->min_lv || j == p->max_lv || p->family) { - if(p->family) p->family = 0; //Family state broken. - inter_party->check_lv(p); - } + inter_party->calc_state(p); /// Count online/offline members and check family state and even share range. + inter_party->tosql(&p->party, PS_DELMEMBER, i); - if (inter_party->check_empty(p) == 0) { - inter_party->tosql(&p->party, PS_DELMEMBER, i); + if (inter_party->check_empty(p) == 0) mapif->party_info(-1, &p->party, 0); - } + return true; } -// When member goes to other map or levels up. -static bool inter_party_change_map(int party_id, int account_id, int char_id, unsigned short map, int online, unsigned int lv) +/** + * Updates party data if a member changes map or levels up. + * + * @param party_id The ID of the party. + * @param account_id The character's account ID. + * @param char_id The character's char ID. + * @param map The character's map index. + * @param online The character's online state. + * @param lv The character's level. + * @return true on success, otherwise false. + * + **/ +static bool inter_party_change_map(int party_id, int account_id, int char_id, unsigned short map, int online, int lv) { - struct party_data *p; - int i; + if (party_id < 1) /// Invalid party ID. + return false; - p = inter_party->fromsql(party_id); - if (p == NULL) + struct party_data *p = inter_party->fromsql(party_id); + + if (p == NULL) { /// Party does not exist. + inter_party->del_nonexistent_party(party_id); return false; + } + + int i; - for(i = 0; i < MAX_PARTY && - (p->party.member[i].account_id != account_id || - p->party.member[i].char_id != char_id); i++); + ARR_FIND(0, MAX_PARTY, i, p->party.member[i].account_id == account_id && p->party.member[i].char_id == char_id); - if (i == MAX_PARTY) + if (i == MAX_PARTY) /// Character not found in party. return false; if (p->party.member[i].online != online) - { p->party.member[i].online = online; - if (online) - p->party.count++; - else - p->party.count--; - // Even share check situations: Family state (always breaks) - // character logging on/off is max/min level (update level range) - // or character logging on/off has a different level (update level range using new level) - if (p->family || - (p->party.member[i].lv <= p->min_lv || p->party.member[i].lv >= p->max_lv) || - (p->party.member[i].lv != lv && (lv <= p->min_lv || lv >= p->max_lv)) - ) - { - p->party.member[i].lv = lv; - inter_party->check_lv(p); - } - //Send online/offline update. - mapif->party_membermoved(&p->party, i); - } - if (p->party.member[i].lv != lv) { - if(p->party.member[i].lv == p->min_lv || - p->party.member[i].lv == p->max_lv) - { - p->party.member[i].lv = lv; - inter_party->check_lv(p); - } else - p->party.member[i].lv = lv; - //There is no need to send level update to map servers - //since they do nothing with it. - } + if (p->party.member[i].lv != lv) + p->party.member[i].lv = lv; - if (p->party.member[i].map != map) { + if (p->party.member[i].map != map) p->party.member[i].map = map; - mapif->party_membermoved(&p->party, i); - } + + inter_party->calc_state(p); /// Count online/offline members and check family state and even share range. + mapif->party_membermoved(&p->party, i); /// Send online/offline update. + return true; } @@ -599,7 +686,6 @@ static int inter_party_parse_frommap(int fd) case 0x3024: mapif->parse_PartyLeave(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; case 0x3025: mapif->parse_PartyChangeMap(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOW(fd,14), RFIFOB(fd,16), RFIFOW(fd,17)); break; case 0x3026: mapif->parse_BreakParty(fd, RFIFOL(fd,2)); break; - case 0x3027: mapif->parse_PartyMessage(fd, RFIFOL(fd,4), RFIFOL(fd,8), RFIFOP(fd,12), RFIFOW(fd,2)-12); break; case 0x3029: mapif->parse_PartyLeaderChange(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; default: return 0; @@ -607,98 +693,104 @@ static int inter_party_parse_frommap(int fd) return 1; } +/** + * Sets the online state of a charcter within a party to online. + * + * @param char_id The character's char ID. + * @param party_id The ID of the party. + * @return 1 on success, otherwise 0. + * + **/ static int inter_party_CharOnline(int char_id, int party_id) { - struct party_data* p; - int i; - - if( party_id == -1 ) - {// Get party_id from the database + if (party_id == INDEX_NOT_FOUND) { /// Get party_id from the database. char* data; - if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT party_id FROM `%s` WHERE char_id='%d'", char_db, char_id) ) - { + if (SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT party_id FROM `%s` WHERE char_id='%d'", char_db, char_id)) { Sql_ShowDebug(inter->sql_handle); return 0; } - if( SQL_SUCCESS != SQL->NextRow(inter->sql_handle) ) - return 0; //Eh? No party? + if (SQL_SUCCESS != SQL->NextRow(inter->sql_handle)) + return 0; SQL->GetData(inter->sql_handle, 0, &data, NULL); party_id = atoi(data); SQL->FreeResult(inter->sql_handle); } - if (party_id == 0) - return 0; //No party... - p = inter_party->fromsql(party_id); - if(!p) { - ShowError("Character %d's party %d not found!\n", char_id, party_id); + if (party_id == 0) /// Character isn't member of a party. return 0; - } - //Set member online - for(i=0; i<MAX_PARTY; i++) { - if (p->party.member[i].char_id == char_id) { - if (!p->party.member[i].online) { - p->party.member[i].online = 1; - p->party.count++; - if (p->party.member[i].lv < p->min_lv || - p->party.member[i].lv > p->max_lv) - inter_party->check_lv(p); - } - break; - } + struct party_data *p = inter_party->fromsql(party_id); + + if (p == NULL) { /// Party does not exist. + inter_party->del_nonexistent_party(party_id); + return 0; } + + int i; + + ARR_FIND(0, MAX_PARTY, i, p->party.member[i].char_id == char_id); + + if (i == MAX_PARTY) /// Character not found in party. + return 0; + + p->party.member[i].online = 1; /// Set member online. + inter_party->calc_state(p); /// Count online/offline members and check family state and even share range. + return 1; } +/** + * Sets the online state of a charcter within a party to offline. + * + * @param char_id The character's char ID. + * @param party_id The ID of the party. + * @return 1 on success, otherwise 0. + * + **/ static int inter_party_CharOffline(int char_id, int party_id) { - struct party_data *p=NULL; - int i; - - if( party_id == -1 ) - {// Get guild_id from the database + if (party_id == INDEX_NOT_FOUND) { /// Get party_id from the database. char* data; - if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT party_id FROM `%s` WHERE char_id='%d'", char_db, char_id) ) - { + if (SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT party_id FROM `%s` WHERE char_id='%d'", char_db, char_id)) { Sql_ShowDebug(inter->sql_handle); return 0; } - if( SQL_SUCCESS != SQL->NextRow(inter->sql_handle) ) - return 0; //Eh? No party? + if (SQL_SUCCESS != SQL->NextRow(inter->sql_handle)) + return 0; SQL->GetData(inter->sql_handle, 0, &data, NULL); party_id = atoi(data); SQL->FreeResult(inter->sql_handle); } - if (party_id == 0) - return 0; //No party... - //Character has a party, set character offline and check if they were the only member online - if ((p = inter_party->fromsql(party_id)) == NULL) + if (party_id == 0) /// Character isn't member of a party. return 0; - //Set member offline - for(i=0; i< MAX_PARTY; i++) { - if(p->party.member[i].char_id == char_id) - { - p->party.member[i].online = 0; - p->party.count--; - if(p->party.member[i].lv == p->min_lv || - p->party.member[i].lv == p->max_lv) - inter_party->check_lv(p); - break; - } + struct party_data *p = inter_party->fromsql(party_id); + + if (p == NULL) { /// Party does not exist. + inter_party->del_nonexistent_party(party_id); + return 0; } - if(!p->party.count) - //Parties don't have any data that needs be saved at this point... so just remove it from memory. + int i; + + ARR_FIND(0, MAX_PARTY, i, p->party.member[i].char_id == char_id); + + if (i == MAX_PARTY) /// Character not found in party. + return 0; + + p->party.member[i].online = 0; /// Set member offline. + inter_party->calc_state(p); /// Count online/offline members and check family state and even share range. + + if (p->party.count == 0) /// Parties don't have any data that needs be saved at this point... so just remove it from memory. idb_remove(inter_party->db, party_id); + return 1; } @@ -712,8 +804,10 @@ void inter_party_defaults(void) inter_party->sql_init = inter_party_sql_init; inter_party->sql_final = inter_party_sql_final; inter_party->check_lv = inter_party_check_lv; + inter_party->is_family_party = inter_party_is_family_party; inter_party->calc_state = inter_party_calc_state; inter_party->tosql = inter_party_tosql; + inter_party->del_nonexistent_party = inter_party_del_nonexistent_party; inter_party->fromsql = inter_party_fromsql; inter_party->search_partyname = inter_party_search_partyname; inter_party->check_exp_share = inter_party_check_exp_share; |