diff options
Diffstat (limited to 'src/map')
-rw-r--r-- | src/map/atcommand.c | 21 | ||||
-rw-r--r-- | src/map/battle.c | 13 | ||||
-rw-r--r-- | src/map/battle.h | 7 | ||||
-rw-r--r-- | src/map/buyingstore.c | 13 | ||||
-rw-r--r-- | src/map/clif.c | 601 | ||||
-rw-r--r-- | src/map/clif.h | 9 | ||||
-rw-r--r-- | src/map/guild.c | 13 | ||||
-rw-r--r-- | src/map/instance.c | 4 | ||||
-rw-r--r-- | src/map/itemdb.h | 1 | ||||
-rw-r--r-- | src/map/mail.c | 4 | ||||
-rw-r--r-- | src/map/map.c | 10 | ||||
-rw-r--r-- | src/map/packets_struct.h | 18 | ||||
-rw-r--r-- | src/map/party.c | 39 | ||||
-rw-r--r-- | src/map/party.h | 2 | ||||
-rw-r--r-- | src/map/pc.c | 26 | ||||
-rw-r--r-- | src/map/pc.h | 12 | ||||
-rw-r--r-- | src/map/pet.c | 131 | ||||
-rw-r--r-- | src/map/pet.h | 2 | ||||
-rw-r--r-- | src/map/rodex.c | 3 | ||||
-rw-r--r-- | src/map/script.c | 11 | ||||
-rw-r--r-- | src/map/script.h | 2 | ||||
-rw-r--r-- | src/map/skill.c | 68 | ||||
-rw-r--r-- | src/map/skill.h | 1 | ||||
-rw-r--r-- | src/map/trade.c | 9 | ||||
-rw-r--r-- | src/map/vending.c | 6 |
25 files changed, 751 insertions, 275 deletions
diff --git a/src/map/atcommand.c b/src/map/atcommand.c index 76448b237..9d5c601bf 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -2810,10 +2810,8 @@ ACMD(petfriendly) return false; } - if (friendly != pd->pet.intimate) { // No need to update the pet's status if intimacy value won't change. + if (friendly != pd->pet.intimate) // No need to update the pet's status if intimacy value won't change. pet->set_intimate(pd, friendly); - clif->send_petstatus(sd); - } clif->message(fd, msg_fd(fd, 182)); // Pet intimacy changed. (Send message regardless of value has changed or not.) @@ -2854,10 +2852,8 @@ ACMD(pethungry) return false; } - if (hungry != pd->pet.hungry) { // No need to update the pet's status if hunger value won't change. - pd->pet.hungry = hungry; - clif->send_petstatus(sd); - } + if (hungry != pd->pet.hungry) // No need to update the pet's status if hunger value won't change. + pet->set_hunger(pd, hungry); clif->message(fd, msg_fd(fd, 185)); // Pet hunger changed. (Send message regardless of value has changed or not.) @@ -2881,6 +2877,15 @@ ACMD(petrename) } pd->pet.rename_flag = 0; + + int i; + + ARR_FIND(0, sd->status.inventorySize, i, sd->status.inventory[i].card[0] == CARD0_PET + && pd->pet.pet_id == MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2])); + + if (i != sd->status.inventorySize) + sd->status.inventory[i].card[3] = pet->get_card4_value(pd->pet.rename_flag, pd->pet.intimate); + intif->save_petdata(sd->status.account_id, &pd->pet); clif->send_petstatus(sd); clif->message(fd, msg_fd(fd,187)); // You can now rename your pet. @@ -8562,7 +8567,7 @@ ACMD(itemlist) if( it->card[0] == CARD0_PET ) { // pet egg - if (it->card[3]) + if ((it->card[3] & 1) != 0) StrBuf->Printf(&buf, msg_fd(fd,1348), (unsigned int)MakeDWord(it->card[1], it->card[2])); // -> (pet egg, pet id: %u, named) else StrBuf->Printf(&buf, msg_fd(fd,1349), (unsigned int)MakeDWord(it->card[1], it->card[2])); // -> (pet egg, pet id: %u, unnamed) diff --git a/src/map/battle.c b/src/map/battle.c index 611154953..b5c0cf4fa 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -7101,6 +7101,7 @@ static const struct battle_data { { "pet_max_atk1", &battle_config.pet_max_atk1, 750, 0, INT_MAX, }, { "pet_max_atk2", &battle_config.pet_max_atk2, 1000, 0, INT_MAX, }, { "pet_disable_in_gvg", &battle_config.pet_no_gvg, 0, 0, 1, }, + { "pet_remove_immediately", &battle_config.pet_remove_immediately, 1, 0, 1, }, { "skill_min_damage", &battle_config.skill_min_damage, 2|4, 0, 1|2|4, }, { "finger_offensive_type", &battle_config.finger_offensive_type, 0, 0, 1, }, { "heal_exp", &battle_config.heal_exp, 0, 0, INT_MAX, }, @@ -7345,6 +7346,7 @@ static const struct battle_data { { "searchstore_querydelay", &battle_config.searchstore_querydelay, 10, 0, INT_MAX, }, { "searchstore_maxresults", &battle_config.searchstore_maxresults, 30, 1, INT_MAX, }, { "display_party_name", &battle_config.display_party_name, 0, 0, 1, }, + { "send_party_options", &battle_config.send_party_options, 0x31F9, 0, 0x1FFFF, }, { "cashshop_show_points", &battle_config.cashshop_show_points, 0, 0, 1, }, { "mail_show_status", &battle_config.mail_show_status, 0, 0, 2, }, { "client_limit_unit_lv", &battle_config.client_limit_unit_lv, 0, 0, BL_ALL, }, @@ -7378,12 +7380,12 @@ static const struct battle_data { { "item_restricted_consumption_type", &battle_config.item_restricted_consumption_type,1, 0, 1, }, { "unequip_restricted_equipment", &battle_config.unequip_restricted_equipment, 0, 0, 3, }, { "max_walk_path", &battle_config.max_walk_path, 17, 1, MAX_WALKPATH, }, - { "item_enabled_npc", &battle_config.item_enabled_npc, 1, 0, INT_MAX, }, + { "item_enabled_npc", &battle_config.item_enabled_npc, 1, 0, 3, }, { "gm_ignore_warpable_area", &battle_config.gm_ignore_warpable_area, 0, 2, 100, }, { "packet_obfuscation", &battle_config.packet_obfuscation, 1, 0, 3, }, { "client_accept_chatdori", &battle_config.client_accept_chatdori, 0, 0, INT_MAX, }, { "snovice_call_type", &battle_config.snovice_call_type, 0, 0, 1, }, - { "guild_notice_changemap", &battle_config.guild_notice_changemap, 2, 0, 2, }, + { "guild_notice_changemap", &battle_config.guild_notice_changemap, 7, 0, 7, }, { "features/banking", &battle_config.feature_banking, 1, 0, 1, }, { "features/auction", &battle_config.feature_auction, 0, 0, 2, }, { "idletime_criteria", &battle_config.idletime_criteria, 0x25, 1, INT_MAX, }, @@ -7431,6 +7433,10 @@ static const struct battle_data { { "ping_time", &battle_config.ping_time, 20, 0, 99999999, }, { "option_drop_max_loop", &battle_config.option_drop_max_loop, 10, 1, 100000, }, { "drop_connection_on_quit", &battle_config.drop_connection_on_quit, 0, 0, 1, }, + { "display_rate_messages", &battle_config.display_rate_messages, 1, 0, 7, }, + { "display_config_messages", &battle_config.display_config_messages, 0x1F1, 0, 0x1F7, }, + { "display_overweight_messages", &battle_config.display_overweight_messages, 3, 0, 3, }, + { "show_tip_window", &battle_config.show_tip_window, 1, 0, 1, }, { "features/enable_refinery_ui", &battle_config.enable_refinery_ui, 1, 0, 1, }, { "features/replace_refine_npcs", &battle_config.replace_refine_npcs, 1, 0, 1, }, { "batk_min_limit", &battle_config.batk_min, 0, 0, INT_MAX, }, @@ -7449,6 +7455,7 @@ static const struct battle_data { { "hit_max_limit", &battle_config.hit_max, SHRT_MAX, 1, INT_MAX, }, { "autoloot_adjust", &battle_config.autoloot_adjust, 0, 0, 1, }, { "hom_bonus_exp_from_master", &battle_config.hom_bonus_exp_from_master, 10, 0, 100, }, + { "allowed_actions_when_dead", &battle_config.allowed_actions_when_dead, 0, 0, 3, }, }; static bool battle_set_value_sub(int index, int value) @@ -7681,6 +7688,8 @@ static void do_init_battle(bool minimal) static void do_final_battle(void) { + if (map->minimal) + return; ers_destroy(battle->delay_damage_ers); } diff --git a/src/map/battle.h b/src/map/battle.h index 55ee32445..f648718ff 100644 --- a/src/map/battle.h +++ b/src/map/battle.h @@ -230,6 +230,7 @@ struct Battle_Config { int pet_max_atk2; //[Skotlex] int pet_no_gvg; //Disables pets in gvg. [Skotlex] int pet_equip_required; + int pet_remove_immediately; int skill_min_damage; int finger_offensive_type; @@ -471,6 +472,7 @@ struct Battle_Config { int searchstore_querydelay; int searchstore_maxresults; int display_party_name; + int send_party_options; int cashshop_show_points; int mail_show_status; int client_limit_unit_lv; @@ -590,6 +592,10 @@ struct Battle_Config { int option_drop_max_loop; int drop_connection_on_quit; + int display_rate_messages; + int display_config_messages; + int display_overweight_messages; + int show_tip_window; int enable_refinery_ui; int replace_refine_npcs; @@ -609,6 +615,7 @@ struct Battle_Config { int hit_max; int autoloot_adjust; + int allowed_actions_when_dead; }; /* criteria for battle_config.idletime_critera */ diff --git a/src/map/buyingstore.c b/src/map/buyingstore.c index 2c2fc13ae..fd6e6fd6e 100644 --- a/src/map/buyingstore.c +++ b/src/map/buyingstore.c @@ -91,8 +91,9 @@ static void buyingstore_create(struct map_session_data *sd, int zenylimit, unsig return; } - if( !battle_config.feature_buying_store || pc_istrading(sd) || sd->state.prevend || sd->buyingstore.slots == 0 || count > sd->buyingstore.slots || zenylimit <= 0 || zenylimit > sd->status.zeny || !storename[0] ) - {// disabled or invalid input + if (battle_config.feature_buying_store == 0 || pc_istrading_except_npc(sd) || sd->state.prevend != 0 + || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || sd->buyingstore.slots == 0 + || count > sd->buyingstore.slots || zenylimit <= 0 || zenylimit > sd->status.zeny || *storename == '\0') { // Disabled or invalid input. sd->buyingstore.slots = 0; clif->buyingstore_open_failed(sd, BUYINGSTORE_CREATE, 0); return; @@ -218,8 +219,8 @@ static void buyingstore_open(struct map_session_data *sd, int account_id) struct map_session_data* pl_sd; nullpo_retv(sd); - if (!battle_config.feature_buying_store || pc_istrading(sd) || sd->state.prevend) - {// not allowed to sell + if (battle_config.feature_buying_store == 0 || pc_istrading_except_npc(sd) || sd->state.prevend != 0 + || (sd->npc_id != 0 && sd->state.using_megaphone == 0)) { // Not allowed to sell. return; } @@ -255,8 +256,8 @@ static void buyingstore_trade(struct map_session_data* sd, int account_id, unsig return; } - if (!battle_config.feature_buying_store || pc_istrading(sd) || sd->state.prevend) - {// not allowed to sell + if (battle_config.feature_buying_store == 0 || pc_istrading_except_npc(sd) || sd->state.prevend != 0 + || (sd->npc_id != 0 && sd->state.using_megaphone == 0)) { // Not allowed to sell. clif->buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, 0); return; } diff --git a/src/map/clif.c b/src/map/clif.c index e0c30368f..8e94686c4 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -2431,23 +2431,26 @@ static void clif_scriptinput(struct map_session_data *sd, int npcid) /// - close inputstr window static void clif_scriptinputstr(struct map_session_data *sd, int npcid) { - int fd; - struct block_list *bl = NULL; - nullpo_retv(sd); - if (!sd->state.using_fake_npc && (npcid == npc->fake_nd->bl.id || ((bl = map->id2bl(npcid)) != NULL && (bl->m!=sd->bl.m || - bl->x<sd->bl.x-AREA_SIZE-1 || bl->x>sd->bl.x+AREA_SIZE+1 || - bl->y<sd->bl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1)))) + struct block_list *bl = map->id2bl(npcid); + int x1 = sd->bl.x - AREA_SIZE - 1; + int x2 = sd->bl.x + AREA_SIZE + 1; + int y1 = sd->bl.y - AREA_SIZE - 1; + int y2 = sd->bl.y + AREA_SIZE + 1; + bool out_of_sight = (bl != NULL && (bl->m != sd->bl.m || bl->x < x1 || bl->x > x2 || bl->y < y1 || bl->y > y2)); + + if (sd->state.using_fake_npc == 0 && sd->state.using_megaphone == 0 + && (npcid == npc->fake_nd->bl.id || out_of_sight)) { clif->sendfakenpc(sd, npcid); + } pc->update_idle_time(sd, BCIDLE_SCRIPT); - fd=sd->fd; - WFIFOHEAD(fd, packet_len(0x1d4)); - WFIFOW(fd,0)=0x1d4; - WFIFOL(fd,2)=npcid; - WFIFOSET(fd,packet_len(0x1d4)); + WFIFOHEAD(sd->fd, packet_len(0x1d4)); + WFIFOW(sd->fd, 0) = 0x1d4; + WFIFOL(sd->fd, 2) = (sd->state.using_megaphone == 0) ? npcid : 0; + WFIFOSET(sd->fd, packet_len(0x1d4)); } /// Marks a position on client's minimap (ZC_COMPASS). @@ -2519,8 +2522,8 @@ static void clif_addcards(struct EQUIPSLOTINFO *buf, struct item *item) if (item->card[0] == CARD0_PET) { //pet eggs buf->card[0] = 0; buf->card[1] = 0; - buf->card[2] = 0; - buf->card[3] = item->card[3]; //Pet renamed flag. + buf->card[2] = (item->card[3] >> 1); // Pet intimacy level. + buf->card[3] = (item->card[3] & 1); // Pet renamed flag. return; } if (item->card[0] == CARD0_FORGE || item->card[0] == CARD0_CREATE) { //Forged/created items @@ -7292,46 +7295,101 @@ static void clif_party_inviteack(struct map_session_data *sd, const char *nick, #endif } -/// Updates party settings. -/// 0101 <exp option>.L (ZC_GROUPINFO_CHANGE) -/// 07d8 <exp option>.L <item pick rule>.B <item share rule>.B (ZC_REQ_GROUPINFO_CHANGE_V2) -/// exp option: -/// 0 = exp sharing disabled -/// 1 = exp sharing enabled -/// 2 = cannot change exp sharing -/// -/// flag: -/// 0 = send to party -/// 1 = send to sd +/** + * Sends party settings to the client. + * + * 0101 <exp option>.L (ZC_GROUPINFO_CHANGE) + * 07d8 <exp option>.L <item pick rule>.B <item share rule>.B (ZC_REQ_GROUPINFO_CHANGE_V2) + * <exp option>: + * 0 = EXP sharing disabled. + * 1 = EXP sharing enabled. + * 2 = Cannot change EXP sharing. + * + * @param p The related party. + * @param sd The related character. + * @param flag Reason for sending. + * @parblock + * Possible flags: + * 0x01 = Cannot change EXP sharing. (Only set when tried to change options manually.) + * 0x02 = Options changed manually. + * 0x04 = Options changed automatically. + * 0x08 = Member added. + * 0x10 = Member removed. + * 0x20 = Character logged in. + * 0x40 = Character changed map. + * 0x80 = Character teleported. + * @endparblock + * + **/ static void clif_party_option(struct party_data *p, struct map_session_data *sd, int flag) { - unsigned char buf[16]; + nullpo_retv(p); + + if (sd == NULL && (flag & 0x01) == 0) { + for (int i = 0; i < MAX_PARTY; i++) { + if (p->data[i].sd != NULL) { + sd = p->data[i].sd; + break; + } + } + } + + if (sd == NULL) + return; + + int conf = battle_config.send_party_options; + + if (((flag & 0x01) != 0 && (conf & 0x10) == 0) + || ((flag & 0x02) != 0 && (conf & 0x08) == 0) + || ((flag & 0x04) != 0 && (conf & 0x20) == 0) + || ((flag & 0x08) != 0 && (conf & 0x40) == 0) + || ((flag & 0x10) != 0 && (conf & 0x80) == 0) + || ((flag & 0x20) != 0 && (conf & 0x01) == 0) + || ((flag & 0x40) != 0 && (conf & 0x02) == 0) + || ((flag & 0x80) != 0 && (conf & 0x04) == 0)) { + return; + } + + enum send_target target = SELF; + + if (((flag & 0x01) != 0 && (conf & 0x100) != 0) + || ((flag & 0x01) == 0 && (flag & 0x02) != 0) + || (flag & 0x04) != 0) { + target = PARTY; + } + + int cmd = 0x101; + + if (((flag & 0x01) != 0 && (conf & 0x02000) != 0) + || ((flag & 0x02) != 0 && (conf & 0x01000) != 0) + || ((flag & 0x04) != 0 && (conf & 0x04000) != 0) + || ((flag & 0x08) != 0 && (conf & 0x08000) != 0) + || ((flag & 0x10) != 0 && (conf & 0x10000) != 0) + || ((flag & 0x20) != 0 && (conf & 0x00200) != 0) + || ((flag & 0x40) != 0 && (conf & 0x00400) != 0) + || ((flag & 0x80) != 0 && (conf & 0x00800) != 0)) { + cmd = 0x7d8; + } + #if PACKETVER < 20090603 - const int cmd = 0x101; -#else - const int cmd = 0x7d8; + if (cmd == 0x7d8) + cmd = 0x101; #endif - nullpo_retv(p); + unsigned char buf[16]; - if(!sd && flag==0){ - int i; - for(i=0;i<MAX_PARTY && !p->data[i].sd;i++) - ; - if (i < MAX_PARTY) - sd = p->data[i].sd; + WBUFW(buf, 0) = cmd; + WBUFL(buf, 2) = ((flag & 0x10) != 0) ? 0 : (((flag & 0x01) != 0) ? 2 : p->party.exp); + + if (cmd == 0x7d8) { + WBUFB(buf, 6) = ((flag & 0x10) != 0) ? 0 : (((p->party.item & 1) != 0) ? 1 : 0); + WBUFB(buf, 7) = ((flag & 0x10) != 0) ? 0 : (((p->party.item & 2) != 0) ? 1 : 0); } - if(!sd) return; - WBUFW(buf,0)=cmd; - WBUFL(buf,2)=((flag&0x01)?2:p->party.exp); -#if PACKETVER >= 20090603 - WBUFB(buf,6)=(p->party.item&1)?1:0; - WBUFB(buf,7)=(p->party.item&2)?1:0; -#endif - if(flag==0) - clif->send(buf,packet_len(cmd),&sd->bl,PARTY); - else - clif->send(buf,packet_len(cmd),&sd->bl,SELF); + + clif->send(buf, packet_len(cmd), &sd->bl, target); + + if ((flag & 0x04) != 0) + p->state.option_auto_changed = 0; } /// 0105 <account id>.L <char name>.24B <result>.B (ZC_DELETE_MEMBER_FROM_GROUP). @@ -10613,6 +10671,97 @@ static void clif_parse_WantToConnection(int fd, struct map_session_data *sd) } /** + * Displays the common server messages upon login, chaning maps or teleporting to a character. + * + * @param sd The character who should receive the messages. + * @param connect_new Whether the character is logging in. + * @param change_map Whether the character is changing maps. + * + **/ +static void clif_load_end_ack_sub_messages(struct map_session_data *sd, bool connect_new, bool change_map) +{ + nullpo_retv(sd); + + /** Display overweight messages. **/ + if (((battle_config.display_overweight_messages & 0x1) != 0 && connect_new) + || ((battle_config.display_overweight_messages & 0x2) != 0 && !connect_new && change_map)) { + // Send the character's weight to the client. (With displaying overweight messages.) + clif->updatestatus(sd, SP_MAXWEIGHT); + clif->updatestatus(sd, SP_WEIGHT); + } else { + // Send the character's weight to the client. (Without displaying overweight messages.) + clif->updatestatus(sd, SP_WEIGHT); + clif->updatestatus(sd, SP_MAXWEIGHT); + } + + /** Display configuration messages. **/ + if (((battle_config.display_config_messages & 0x1) != 0 && connect_new) + || ((battle_config.display_config_messages & 0x2) != 0 && !connect_new && change_map) + || (battle_config.display_config_messages & 0x4) != 0) { +#if PACKETVER >= 20070918 + if ((battle_config.display_config_messages & 0x10) != 0) + clif->partyinvitationstate(sd); + + if ((battle_config.display_config_messages & 0x20) != 0) + clif->equpcheckbox(sd); +#endif + +#if PACKETVER_MAIN_NUM >= 20171025 || PACKETVER_RE_NUM >= 20170920 + if ((battle_config.display_config_messages & 0x40) != 0) + clif->zc_config(sd, CZ_CONFIG_CALL, sd->status.allow_call); + + if ((battle_config.display_config_messages & 0x80) != 0) { + if (sd->pd != NULL) + clif->zc_config(sd, CZ_CONFIG_PET_AUTOFEEDING, sd->pd->pet.autofeed); + else + clif->zc_config(sd, CZ_CONFIG_PET_AUTOFEEDING, false); + } + + if ((battle_config.display_config_messages & 0x100) != 0) { + if (sd->hd != NULL) + clif->zc_config(sd, CZ_CONFIG_HOMUNCULUS_AUTOFEEDING, sd->hd->homunculus.autofeed); + else + clif->zc_config(sd, CZ_CONFIG_HOMUNCULUS_AUTOFEEDING, false); + } +#endif + } + + /** Display party options. **/ + struct party_data *p = NULL; + + if (sd->status.party_id != 0 && (p = party->search(sd->status.party_id)) != NULL) { + int flag; + + if (p->state.option_auto_changed != 0) + flag = 0x04; + else if (connect_new) + flag = 0x20; + else if (change_map) + flag = 0x40; + else + flag = 0x80; + + clif->party_option(p, sd, flag); + } + + /** Display rate modifier messages. **/ + if (((battle_config.display_rate_messages & 0x1) != 0 && connect_new) + || ((battle_config.display_rate_messages & 0x2) != 0 && !connect_new && change_map) + || (battle_config.display_rate_messages & 0x4) != 0) { + clif->show_modifiers(sd); + } + + /** Display guild notice. **/ + if (sd->guild != NULL) { + if (((battle_config.guild_notice_changemap & 0x1) != 0 && connect_new) + || ((battle_config.guild_notice_changemap & 0x2) != 0 && !connect_new && change_map) + || (battle_config.guild_notice_changemap & 0x4) != 0) { + clif->guild_notice(sd, sd->guild); + } + } +} + +/** * Notification from the client, that it has finished map loading and is about to display player's character. (CZ_NOTIFY_ACTORINIT) * * @code @@ -10626,6 +10775,9 @@ static void clif_parse_WantToConnection(int fd, struct map_session_data *sd) static void clif_parse_LoadEndAck(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); static void clif_parse_LoadEndAck(int fd, struct map_session_data *sd) { + if (sd->state.using_megaphone != 0) + sd->state.using_megaphone = 0; + if (sd->bl.prev != NULL) return; @@ -10732,10 +10884,6 @@ static void clif_parse_LoadEndAck(int fd, struct map_session_data *sd) // Check for and delete unavailable/disabled items. pc->checkitem(sd); - // Send the character's weight to the client. - clif->updatestatus(sd, SP_WEIGHT); - clif->updatestatus(sd, SP_MAXWEIGHT); - // Send character's guild info to the client. Call this before clif->spawn() to show guild emblems correctly. if (sd->status.guild_id != 0) guild->send_memberinfoshort(sd, 1); @@ -10764,9 +10912,18 @@ static void clif_parse_LoadEndAck(int fd, struct map_session_data *sd) map->addblock(&sd->bl); // Add the character to the map. clif->spawn(&sd->bl); // Spawn character client side. + clif_load_end_ack_sub_messages(sd, (sd->state.connect_new != 0), (sd->state.changemap != 0)); + + struct party_data *p = NULL; + + if (sd->status.party_id != 0) + p = party->search(sd->status.party_id); + // Send character's party info to the client. Call this after clif->spawn() to show HP bars correctly. - if (sd->status.party_id != 0) { - party->send_movemap(sd); + if (p != NULL) { + if (sd->state.connect_new == 0) // Login is handled in party_member_joined(). + party->send_movemap(sd); + clif->party_hp(sd); // Show HP after displacement. [LuzZza] } @@ -10806,10 +10963,7 @@ static void clif_parse_LoadEndAck(int fd, struct map_session_data *sd) clif->message(sd->fd, msg_sd(sd, 866)); // "Pets are not allowed in Guild Wars." pet->menu(sd, 3); // Option 3 is return to egg. } else { - map->addblock(&sd->pd->bl); - clif->spawn(&sd->pd->bl); - clif->send_petdata(sd,sd->pd, 0, 0); - clif->send_petstatus(sd); + pet->spawn(sd, false); } } @@ -10917,6 +11071,11 @@ static void clif_parse_LoadEndAck(int fd, struct map_session_data *sd) // Notify everyone that this character logged in. [Skotlex] map->foreachpc(clif->friendslist_toggle_sub, sd->status.account_id, sd->status.char_id, 1); +#if PACKETVER >= 20171122 + if (battle_config.show_tip_window != 0) + clif->open_ui_send(sd, ZC_TIPBOX_UI); +#endif + // Run OnPCLoginEvent labels. npc->script_event(sd, NPCE_LOGIN); } else { @@ -10938,26 +11097,9 @@ static void clif_parse_LoadEndAck(int fd, struct map_session_data *sd) } else { sd->state.warp_clean = 1; } - - if (sd->guild != NULL && ((battle_config.guild_notice_changemap == 1 && sd->state.changemap != 0) - || battle_config.guild_notice_changemap == 2)) { - clif->guild_notice(sd, sd->guild); - } } if (sd->state.changemap != 0) { // Restore information that gets lost on map-change. -#if PACKETVER >= 20070918 - clif->partyinvitationstate(sd); - clif->equpcheckbox(sd); -#endif - -#if PACKETVER_MAIN_NUM >= 20171025 || PACKETVER_RE_NUM >= 20170920 - if (sd->hd != NULL) - clif->zc_config(sd, CZ_CONFIG_HOMUNCULUS_AUTOFEEDING, sd->hd->homunculus.autofeed); - else - clif->zc_config(sd, CZ_CONFIG_HOMUNCULUS_AUTOFEEDING, false); -#endif - bool flee_penalty = (battle_config.bg_flee_penalty != 100 || battle_config.gvg_flee_penalty != 100); bool is_gvg = (map_flag_gvg2(sd->state.pmap) || map_flag_gvg2(sd->bl.m)); bool is_bg = (map->list[sd->state.pmap].flag.battleground != 0 || map->list[sd->bl.m].flag.battleground != 0); @@ -11006,12 +11148,14 @@ static void clif_parse_LoadEndAck(int fd, struct map_session_data *sd) mail->clear(sd); clif->maptypeproperty2(&sd->bl, SELF); - // Init guild aura. - if (sd->state.gmaster_flag != 0) { - guild->aura_refresh(sd, GD_LEADERSHIP, guild->checkskill(sd->guild, GD_LEADERSHIP)); - guild->aura_refresh(sd, GD_GLORYWOUNDS, guild->checkskill(sd->guild, GD_GLORYWOUNDS)); - guild->aura_refresh(sd, GD_SOULCOLD, guild->checkskill(sd->guild, GD_SOULCOLD)); - guild->aura_refresh(sd, GD_HAWKEYES, guild->checkskill(sd->guild, GD_HAWKEYES)); + if (sd->guild != NULL) { + // Init guild aura. + if (sd->state.gmaster_flag != 0) { + guild->aura_refresh(sd, GD_LEADERSHIP, guild->checkskill(sd->guild, GD_LEADERSHIP)); + guild->aura_refresh(sd, GD_GLORYWOUNDS, guild->checkskill(sd->guild, GD_GLORYWOUNDS)); + guild->aura_refresh(sd, GD_SOULCOLD, guild->checkskill(sd->guild, GD_SOULCOLD)); + guild->aura_refresh(sd, GD_HAWKEYES, guild->checkskill(sd->guild, GD_HAWKEYES)); + } } if (sd->state.vending != 0) { // Character is vending. @@ -11036,10 +11180,6 @@ static void clif_parse_LoadEndAck(int fd, struct map_session_data *sd) clif->weather_check(sd); - // This should be displayed last. - if (sd->guild != NULL && first_time) - clif->guild_notice(sd, sd->guild); - // For automatic triggering of NPCs after map loading. (So you don't need to walk 1 step first.) if (map->getcell(sd->bl.m, &sd->bl, sd->bl.x, sd->bl.y, CELL_CHKNPC) != 0) npc->touch_areanpc(sd, sd->bl.m, sd->bl.x, sd->bl.y); @@ -11067,6 +11207,19 @@ static void clif_parse_LoadEndAck(int fd, struct map_session_data *sd) #if PACKETVER >= 20090218 quest->questinfo_refresh(sd); // NPC quest/event icon check. [Kisuka] #endif + + if (first_time) { + int i; + + ARR_FIND(0, instance->instances, i, instance->list[i].owner_type == IOT_CHAR && instance->list[i].owner_id == sd->status.account_id); + + if (i < instance->instances) { + sd->instances = 1; + CREATE(sd->instance, short, 1); + sd->instance[0] = instance->list[i].id; + clif->instance_join(sd->fd, instance->list[i].id); + } + } } /// Server's tick (ZC_NOTIFY_TIME). @@ -11288,7 +11441,7 @@ static void clif_parse_WalkToXY(int fd, struct map_session_data *sd) ; //You CAN walk on this OPT1 value. /*else if( sd->progressbar.npc_id ) clif->progressbar_abort(sd);*/ - else if (pc_cant_act(sd) || pc_isvending(sd)) + else if (pc_cant_act_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; if(sd->sc.data[SC_RUN] || sd->sc.data[SC_WUGDASH]) @@ -11650,8 +11803,10 @@ static void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action return; } - if (pc_cant_act(sd) || pc_issit(sd) || sd->sc.option&OPTION_HIDE || pc_isvending(sd)) + if (pc_cant_act_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) + || pc_issit(sd) || (sd->sc.option & OPTION_HIDE) != 0 || pc_isvending(sd)) { return; + } if (sd->sc.option & OPTION_COSTUME) return; @@ -11951,7 +12106,7 @@ static void clif_parse_TakeItem(int fd, struct map_session_data *sd) ) ) break; - if (pc_cant_act(sd)) + if (pc_cant_act_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0)) break; if (!pc->takeitem(sd, fitem)) @@ -11977,7 +12132,7 @@ static void clif_parse_DropItem(int fd, struct map_session_data *sd) if (pc_isdead(sd)) break; - if ( pc_cant_act2(sd) || sd->state.vending ) + if (pc_cant_act_except_npc_chat(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0)) break; if (sd->sc.count && ( @@ -12050,7 +12205,7 @@ static void clif_parse_EquipItem(int fd, struct map_session_data *sd) return; //Out of bounds check. if( sd->npc_id ) { - if ((sd->npc_item_flag & ITEMENABLEDNPC_EQUIP) == 0) + if ((sd->npc_item_flag & ITEMENABLEDNPC_EQUIP) == 0 && sd->state.using_megaphone == 0) return; } else if (sd->state.storage_flag != STORAGE_FLAG_CLOSED || sd->sc.opt1) ; //You can equip/unequip stuff while storage is open/under status changes @@ -12095,7 +12250,7 @@ static void clif_parse_UnequipItem(int fd, struct map_session_data *sd) } if( sd->npc_id ) { - if ((sd->npc_item_flag & ITEMENABLEDNPC_EQUIP) == 0) + if ((sd->npc_item_flag & ITEMENABLEDNPC_EQUIP) == 0 && sd->state.using_megaphone == 0) return; } else if (sd->state.storage_flag != STORAGE_FLAG_CLOSED || sd->sc.opt1) ; //You can equip/unequip stuff while storage is open/under status changes @@ -12295,7 +12450,7 @@ static void clif_parse_CreateChatRoom(int fd, struct map_session_data *sd) __att /// 1 = public static void clif_parse_CreateChatRoom(int fd, struct map_session_data *sd) { - if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + if (sd->state.trading || (pc_isdead(sd) && (battle_config.allowed_actions_when_dead & PCALLOWACTION_CHAT) == 0) || pc_isvending(sd)) return; int len = (int)RFIFOW(fd, 2) - 15; @@ -12343,7 +12498,7 @@ static void clif_parse_ChatAddMember(int fd, struct map_session_data *sd) __attr /// 00d9 <chat ID>.L <passwd>.8B static void clif_parse_ChatAddMember(int fd, struct map_session_data *sd) { - if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + if (sd->state.trading || (pc_isdead(sd) && (battle_config.allowed_actions_when_dead & PCALLOWACTION_CHAT) == 0) || pc_isvending(sd)) return; int chatid = RFIFOL(fd,2); @@ -12360,7 +12515,7 @@ static void clif_parse_ChatRoomStatusChange(int fd, struct map_session_data *sd) /// 1 = public static void clif_parse_ChatRoomStatusChange(int fd, struct map_session_data *sd) { - if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + if (sd->state.trading || (pc_isdead(sd) && (battle_config.allowed_actions_when_dead & PCALLOWACTION_CHAT) == 0) || pc_isvending(sd)) return; int len = (int)RFIFOW(fd, 2) - 15; @@ -12395,7 +12550,7 @@ static void clif_parse_ChangeChatOwner(int fd, struct map_session_data *sd) __at /// 1 = normal static void clif_parse_ChangeChatOwner(int fd, struct map_session_data *sd) { - if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + if (sd->state.trading || (pc_isdead(sd) && (battle_config.allowed_actions_when_dead & PCALLOWACTION_CHAT) == 0) || pc_isvending(sd)) return; chat->change_owner(sd, RFIFOP(fd,6)); // non null terminated @@ -12406,7 +12561,7 @@ static void clif_parse_KickFromChat(int fd, struct map_session_data *sd) __attri /// 00e2 <name>.24B static void clif_parse_KickFromChat(int fd, struct map_session_data *sd) { - if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + if (sd->state.trading || (pc_isdead(sd) && (battle_config.allowed_actions_when_dead & PCALLOWACTION_CHAT) == 0) || pc_isvending(sd)) return; chat->kick(sd, RFIFOP(fd,2)); // non null terminated @@ -12417,7 +12572,7 @@ static void clif_parse_ChatLeave(int fd, struct map_session_data *sd) __attribut /// 00e3 static void clif_parse_ChatLeave(int fd, struct map_session_data *sd) { - if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + if (sd->state.trading || (pc_isdead(sd) && (battle_config.allowed_actions_when_dead & PCALLOWACTION_CHAT) == 0) || pc_isvending(sd)) return; chat->leave(sd, false); @@ -12444,12 +12599,12 @@ static void clif_parse_TradeRequest(int fd, struct map_session_data *sd) __attri /// 00e4 <account id>.L static void clif_parse_TradeRequest(int fd, struct map_session_data *sd) { - if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + if (sd->state.trading || (pc_isdead(sd) && (battle_config.allowed_actions_when_dead & PCALLOWACTION_TRADE) == 0) || pc_isvending(sd)) return; struct map_session_data *t_sd = map->id2sd(RFIFOL(fd, 2)); - if (sd->chat_id == 0 && pc_cant_act(sd)) + if (pc_cant_act_except_npc_chat(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0)) return; //You can trade while in a chatroom. // @noask [LuzZza] @@ -12474,7 +12629,7 @@ static void clif_parse_TradeAck(int fd, struct map_session_data *sd) __attribute /// 4 = rejected static void clif_parse_TradeAck(int fd, struct map_session_data *sd) { - if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + if (sd->state.trading || (pc_isdead(sd) && (battle_config.allowed_actions_when_dead & PCALLOWACTION_TRADE) == 0) || pc_isvending(sd)) return; trade->ack(sd,RFIFOB(fd,2)); @@ -12485,7 +12640,7 @@ static void clif_parse_TradeAddItem(int fd, struct map_session_data *sd) __attri /// 00e8 <index>.W <amount>.L static void clif_parse_TradeAddItem(int fd, struct map_session_data *sd) { - if (!sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + if (sd->state.trading == 0 || (pc_isdead(sd) && (battle_config.allowed_actions_when_dead & PCALLOWACTION_TRADE) == 0) || pc_isvending(sd)) return; short index = RFIFOW(fd,2); @@ -12502,8 +12657,9 @@ static void clif_parse_TradeOk(int fd, struct map_session_data *sd) __attribute_ /// 00eb static void clif_parse_TradeOk(int fd, struct map_session_data *sd) { - if (pc_isdead(sd) || pc_isvending(sd)) + if ((pc_isdead(sd) && (battle_config.allowed_actions_when_dead & PCALLOWACTION_TRADE) == 0) || pc_isvending(sd)) return; + trade->ok(sd); } @@ -12512,7 +12668,7 @@ static void clif_parse_TradeCancel(int fd, struct map_session_data *sd) __attrib /// 00ed static void clif_parse_TradeCancel(int fd, struct map_session_data *sd) { - if (pc_isdead(sd) || pc_isvending(sd)) + if ((pc_isdead(sd) && (battle_config.allowed_actions_when_dead & PCALLOWACTION_TRADE) == 0) || pc_isvending(sd)) return; trade->cancel(sd); @@ -12523,7 +12679,7 @@ static void clif_parse_TradeCommit(int fd, struct map_session_data *sd) __attrib /// 00ef static void clif_parse_TradeCommit(int fd, struct map_session_data *sd) { - if (pc_isdead(sd) || pc_isvending(sd)) + if ((pc_isdead(sd) && (battle_config.allowed_actions_when_dead & PCALLOWACTION_TRADE) == 0) || pc_isvending(sd)) return; trade->commit(sd); @@ -12543,8 +12699,10 @@ static void clif_parse_PutItemToCart(int fd, struct map_session_data *sd) __attr static void clif_parse_PutItemToCart(int fd, struct map_session_data *sd) { int flag = 0; - if (pc_istrading(sd) || sd->state.prevend) + + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || sd->state.prevend != 0) return; + if (!pc_iscarton(sd)) return; if ( (flag = pc->putitemtocart(sd,RFIFOW(fd,2)-2,RFIFOL(fd,4))) ) { @@ -12558,8 +12716,9 @@ static void clif_parse_GetItemFromCart(int fd, struct map_session_data *sd) __at /// 0127 <index>.W <amount>.L static void clif_parse_GetItemFromCart(int fd, struct map_session_data *sd) { - if (pc_istrading(sd) || sd->state.prevend) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || sd->state.prevend != 0) return; + if (!pc_iscarton(sd)) return; pc->getitemfromcart(sd,RFIFOW(fd,2)-2,RFIFOL(fd,4)); @@ -12629,7 +12788,7 @@ static void clif_parse_ChangeCart(int fd, struct map_session_data *sd) if (pc->checkskill(sd, MC_CHANGECART) == 0) return; - if (sd->npc_id || sd->state.workinprogress & 1) { + if ((sd->npc_id != 0 && sd->state.using_megaphone == 0) || (sd->state.workinprogress & 1) != 0) { #if PACKETVER >= 20110308 clif->msgtable(sd, MSG_BUSY); #else @@ -12849,7 +13008,7 @@ static void clif_useSkillToIdReal(int fd, struct map_session_data *sd, int skill bool allow_self_skill = ((tmp & INF_SELF_SKILL) != 0 && (skill->get_nk(skill_id) & NK_NO_DAMAGE) != 0); allow_self_skill = (allow_self_skill && battle_config.skill_enabled_npc == SKILLENABLEDNPC_SELF); - if ((sd->npc_id != 0 && !allow_self_skill && battle_config.skill_enabled_npc != SKILLENABLEDNPC_ALL) + if ((sd->npc_id != 0 && sd->state.using_megaphone == 0 && !allow_self_skill && battle_config.skill_enabled_npc != SKILLENABLEDNPC_ALL) || (sd->state.workinprogress & 1) != 0) { #if PACKETVER >= 20110308 clif->msgtable(sd, MSG_BUSY); @@ -12996,7 +13155,7 @@ static void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, uin return; } - if ((sd->npc_id != 0 && battle_config.skill_enabled_npc != SKILLENABLEDNPC_ALL) + if ((sd->npc_id != 0 && sd->state.using_megaphone == 0 && battle_config.skill_enabled_npc != SKILLENABLEDNPC_ALL) || (sd->state.workinprogress & 1) != 0) { #if PACKETVER >= 20110308 clif->msgtable(sd, MSG_BUSY); @@ -13118,7 +13277,7 @@ static void clif_parse_UseSkillMap(int fd, struct map_session_data *sd) // It is possible to use teleport with the storage window open issue:8027 if ((pc_cant_act_except_npc(sd) && sd->state.storage_flag == STORAGE_FLAG_CLOSED && skill_id != AL_TELEPORT) - || (sd->npc_id != 0 && battle_config.skill_enabled_npc != SKILLENABLEDNPC_ALL)) { + || (sd->npc_id != 0 && sd->state.using_megaphone == 0 && battle_config.skill_enabled_npc != SKILLENABLEDNPC_ALL)) { clif_menuskill_clear(sd); return; } @@ -13161,7 +13320,8 @@ static void clif_parse_ProduceMix(int fd, struct map_session_data *sd) default: return; } - if (pc_istrading(sd) || pc_isdead(sd) || pc_isvending(sd)) { + if (pc_istrading_except_npc(sd) || pc_isdead(sd) || pc_isvending(sd) + || (sd->npc_id != 0 && sd->state.using_megaphone == 0)) { //Make it fail to avoid shop exploits where you sell something different than you see. clif->skill_fail(sd, sd->ud.skill_id, USESKILL_FAIL_LEVEL, 0, 0); clif_menuskill_clear(sd); @@ -13192,7 +13352,8 @@ static void clif_parse_Cooking(int fd, struct map_session_data *sd) if (type == 6 && sd->menuskill_id != GN_MIX_COOKING && sd->menuskill_id != GN_S_PHARMACY) return; - if (pc_istrading(sd) || pc_isdead(sd) || pc_isvending(sd)) { + if (pc_istrading_except_npc(sd) || pc_isdead(sd) || pc_isvending(sd) + || (sd->npc_id != 0 && sd->state.using_megaphone == 0)) { //Make it fail to avoid shop exploits where you sell something different than you see. clif->skill_fail(sd, sd->ud.skill_id, USESKILL_FAIL_LEVEL, 0, 0); clif_menuskill_clear(sd); @@ -13212,7 +13373,8 @@ static void clif_parse_RepairItem(int fd, struct map_session_data *sd) if (sd->menuskill_id != BS_REPAIRWEAPON) return; - if (pc_istrading(sd) || pc_isdead(sd) || pc_isvending(sd)) { + if (pc_istrading_except_npc(sd) || pc_isdead(sd) || pc_isvending(sd) + || (sd->npc_id != 0 && sd->state.using_megaphone == 0)) { //Make it fail to avoid shop exploits where you sell something different than you see. clif->skill_fail(sd, sd->ud.skill_id, USESKILL_FAIL_LEVEL, 0, 0); clif_menuskill_clear(sd); @@ -13231,7 +13393,8 @@ static void clif_parse_WeaponRefine(int fd, struct map_session_data *sd) if (sd->menuskill_id != WS_WEAPONREFINE) //Packet exploit? return; - if (pc_istrading(sd) || pc_isdead(sd) || pc_isvending(sd)) { + if (pc_istrading_except_npc(sd) || pc_isdead(sd) || pc_isvending(sd) + || (sd->npc_id != 0 && sd->state.using_megaphone == 0)) { //Make it fail to avoid shop exploits where you sell something different than you see. clif->skill_fail(sd, sd->ud.skill_id, USESKILL_FAIL_LEVEL, 0, 0); clif_menuskill_clear(sd); @@ -13318,7 +13481,7 @@ static void clif_parse_NpcStringInput(int fd, struct map_session_data *sd) __att /// 01d5 <packet len>.W <npc id>.L <string>.?B static void clif_parse_NpcStringInput(int fd, struct map_session_data *sd) { - if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + if ((sd->state.trading != 0 || pc_isvending(sd) || pc_isdead(sd)) && sd->state.using_megaphone == 0) return; int len = RFIFOW(fd, 2); @@ -13334,7 +13497,7 @@ static void clif_parse_NpcStringInput(int fd, struct map_session_data *sd) if (len < 9) return; - npcid = RFIFOL(fd, 4); + npcid = (sd->state.using_megaphone == 0) ? RFIFOL(fd, 4) : sd->npc_id; message = RFIFOP(fd, 8); safestrncpy(sd->npc_str, message, min(message_len,CHATBOX_SIZE)); @@ -13403,7 +13566,8 @@ static void clif_parse_SelectArrow(int fd, struct map_session_data *sd) __attrib static void clif_parse_SelectArrow(int fd, struct map_session_data *sd) { int itemId; - if (pc_istrading(sd) || pc_isdead(sd) || pc_isvending(sd)) { + if (pc_istrading_except_npc(sd) || pc_isdead(sd) || pc_isvending(sd) + || (sd->npc_id != 0 && sd->state.using_megaphone == 0)) { //Make it fail to avoid shop exploits where you sell something different than you see. clif->skill_fail(sd, sd->ud.skill_id, USESKILL_FAIL_LEVEL, 0, 0); clif_menuskill_clear(sd); @@ -13689,7 +13853,7 @@ static void clif_parse_CreateParty(int fd, struct map_session_data *sd) __attrib /// 01e8 <party name>.24B <item pickup rule>.B <item share rule>.B (CZ_MAKE_GROUP2) static void clif_parse_CreateParty(int fd, struct map_session_data *sd) { - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; char name[NAME_LENGTH]; @@ -13712,7 +13876,7 @@ static void clif_parse_CreateParty(int fd, struct map_session_data *sd) static void clif_parse_CreateParty2(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); static void clif_parse_CreateParty2(int fd, struct map_session_data *sd) { - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; char name[NAME_LENGTH]; @@ -13740,7 +13904,7 @@ static void clif_parse_PartyInvite(int fd, struct map_session_data *sd) __attrib /// 02c4 <char name>.24B (CZ_PARTY_JOIN_REQ) static void clif_parse_PartyInvite(int fd, struct map_session_data *sd) { - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; struct map_session_data *t_sd; @@ -13764,7 +13928,7 @@ static void clif_parse_PartyInvite(int fd, struct map_session_data *sd) static void clif_parse_PartyInvite2(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); static void clif_parse_PartyInvite2(int fd, struct map_session_data *sd) { - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; struct map_session_data *t_sd; @@ -13797,7 +13961,7 @@ static void clif_parse_ReplyPartyInvite(int fd, struct map_session_data *sd) __a /// 1 = accept static void clif_parse_ReplyPartyInvite(int fd, struct map_session_data *sd) { - if (pc_istrading(sd) || pc_isvending(sd)) { + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) { party->reply_invite(sd, RFIFOL(fd, 2), 0); return; } @@ -13808,7 +13972,7 @@ static void clif_parse_ReplyPartyInvite(int fd, struct map_session_data *sd) static void clif_parse_ReplyPartyInvite2(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); static void clif_parse_ReplyPartyInvite2(int fd, struct map_session_data *sd) { - if (pc_istrading(sd) || pc_isvending(sd)) { + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) { party->reply_invite(sd, RFIFOL(fd, 2), 0); return; } @@ -13821,7 +13985,7 @@ static void clif_parse_LeaveParty(int fd, struct map_session_data *sd) __attribu /// 0100 static void clif_parse_LeaveParty(int fd, struct map_session_data *sd) { - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; if (map->list[sd->bl.m].flag.partylock) { @@ -13837,7 +14001,7 @@ static void clif_parse_RemovePartyMember(int fd, struct map_session_data *sd) __ /// 0103 <account id>.L <char name>.24B static void clif_parse_RemovePartyMember(int fd, struct map_session_data *sd) { - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; if (map->list[sd->bl.m].flag.partylock) { @@ -13854,7 +14018,7 @@ static void clif_parse_PartyChangeOption(int fd, struct map_session_data *sd) __ /// 07d7 <exp share rule>.L <item pickup rule>.B <item share rule>.B (CZ_GROUPINFO_CHANGE_V2) static void clif_parse_PartyChangeOption(int fd, struct map_session_data *sd) { - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; struct party_data *p; @@ -13909,7 +14073,7 @@ static void clif_parse_PartyChangeLeader(int fd, struct map_session_data *sd) __ /// 07da <account id>.L static void clif_parse_PartyChangeLeader(int fd, struct map_session_data *sd) { - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; party->changeleader(sd, map->id2sd(RFIFOL(fd,2))); @@ -13924,7 +14088,7 @@ static void clif_parse_PartyBookingRegisterReq(int fd, struct map_session_data * static void clif_parse_PartyBookingRegisterReq(int fd, struct map_session_data *sd) { #ifndef PARTY_RECRUIT - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; short level = RFIFOW(fd,2); @@ -13969,7 +14133,7 @@ static void clif_parse_PartyBookingSearchReq(int fd, struct map_session_data *sd static void clif_parse_PartyBookingSearchReq(int fd, struct map_session_data *sd) { #ifndef PARTY_RECRUIT - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; short level = RFIFOW(fd,2); @@ -14023,7 +14187,7 @@ static void clif_parse_PartyBookingDeleteReq(int fd, struct map_session_data *sd static void clif_parse_PartyBookingDeleteReq(int fd, struct map_session_data *sd) { #ifndef PARTY_RECRUIT - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; if (party->booking_delete(sd)) @@ -14062,7 +14226,7 @@ static void clif_parse_PartyBookingUpdateReq(int fd, struct map_session_data *sd static void clif_parse_PartyBookingUpdateReq(int fd, struct map_session_data *sd) { #ifndef PARTY_RECRUIT - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; short job[PARTY_BOOKING_JOBS]; @@ -14081,7 +14245,7 @@ static void clif_parse_PartyBookingUpdateReq(int fd, struct map_session_data *sd static void clif_PartyBookingInsertNotify(struct map_session_data *sd, struct party_booking_ad_info *pb_ad) { #ifndef PARTY_RECRUIT - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; int i; @@ -14152,7 +14316,7 @@ static void clif_parse_PartyRecruitRegisterReq(int fd, struct map_session_data * static void clif_parse_PartyRecruitRegisterReq(int fd, struct map_session_data *sd) { #ifdef PARTY_RECRUIT - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; short level = RFIFOW(fd, 2); @@ -14226,7 +14390,7 @@ static void clif_parse_PartyRecruitSearchReq(int fd, struct map_session_data *sd static void clif_parse_PartyRecruitSearchReq(int fd, struct map_session_data *sd) { #ifdef PARTY_RECRUIT - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; short level = RFIFOW(fd, 2); @@ -14246,7 +14410,7 @@ static void clif_parse_PartyRecruitDeleteReq(int fd, struct map_session_data *sd static void clif_parse_PartyRecruitDeleteReq(int fd, struct map_session_data *sd) { #ifdef PARTY_RECRUIT - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; if (party->booking_delete(sd)) @@ -14285,7 +14449,7 @@ static void clif_parse_PartyRecruitUpdateReq(int fd, struct map_session_data *sd static void clif_parse_PartyRecruitUpdateReq(int fd, struct map_session_data *sd) { #ifdef PARTY_RECRUIT - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; const char *notice = RFIFOP(fd, 2); @@ -14361,7 +14525,7 @@ static void clif_parse_PartyBookingAddFilteringList(int fd, struct map_session_d static void clif_parse_PartyBookingAddFilteringList(int fd, struct map_session_data *sd) { #ifdef PARTY_RECRUIT - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; int index = RFIFOL(fd, 2); @@ -14378,7 +14542,7 @@ static void clif_parse_PartyBookingSubFilteringList(int fd, struct map_session_d static void clif_parse_PartyBookingSubFilteringList(int fd, struct map_session_data *sd) { #ifdef PARTY_RECRUIT - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; int gid = RFIFOL(fd, 2); @@ -14395,7 +14559,7 @@ static void clif_parse_PartyBookingReqVolunteer(int fd, struct map_session_data static void clif_parse_PartyBookingReqVolunteer(int fd, struct map_session_data *sd) { #ifdef PARTY_RECRUIT - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; int index = RFIFOL(fd, 2); @@ -14474,7 +14638,7 @@ static void clif_parse_PartyBookingRefuseVolunteer(int fd, struct map_session_da static void clif_parse_PartyBookingRefuseVolunteer(int fd, struct map_session_data *sd) { #ifdef PARTY_RECRUIT - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; unsigned int aid = RFIFOL(fd, 2); @@ -14506,7 +14670,7 @@ static void clif_parse_PartyBookingCancelVolunteer(int fd, struct map_session_da static void clif_parse_PartyBookingCancelVolunteer(int fd, struct map_session_data *sd) { #ifdef PARTY_RECRUIT - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; int index = RFIFOL(fd, 2); @@ -14584,7 +14748,7 @@ static void clif_parse_CloseVending(int fd, struct map_session_data *sd) __attri /// 012e static void clif_parse_CloseVending(int fd, struct map_session_data *sd) { - if (sd->npc_id || sd->state.buyingstore || sd->state.trading) + if ((sd->npc_id != 0 && sd->state.using_megaphone == 0) || sd->state.buyingstore != 0 || sd->state.trading != 0) return; vending->close(sd); @@ -14595,12 +14759,9 @@ static void clif_parse_VendingListReq(int fd, struct map_session_data *sd) __att /// 0130 <account id>.L static void clif_parse_VendingListReq(int fd, struct map_session_data *sd) { - if (pc_istrading(sd) || pc_isdead(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isdead(sd)) return; - if( sd->npc_id ) {// using an NPC - return; - } vending->list(sd,RFIFOL(fd,2)); } @@ -14662,8 +14823,10 @@ static void clif_parse_OpenVending(int fd, struct map_session_data *sd) __attrib /// 1 = open static void clif_parse_OpenVending(int fd, struct map_session_data *sd) { - if (pc_istrading(sd) || pc_isdead(sd) || sd->state.vending || sd->state.buyingstore) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) + || pc_isdead(sd) || sd->state.vending != 0 || sd->state.buyingstore != 0) { return; + } int len = (int)RFIFOW(fd, 2) - 85; @@ -16750,7 +16913,7 @@ static void clif_parse_AutoRevive(int fd, struct map_session_data *sd) __attribu /// 0292 static void clif_parse_AutoRevive(int fd, struct map_session_data *sd) { - if (pc_istrading(sd) || pc_isvending(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isvending(sd)) return; if (!pc_isdead(sd)) @@ -19178,7 +19341,7 @@ static void clif_parse_ReqOpenBuyingStore(int fd, struct map_session_data *sd) _ /// 1 = open static void clif_parse_ReqOpenBuyingStore(int fd, struct map_session_data *sd) { - if (pc_istrading(sd) || pc_isdead(sd)) + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0) || pc_isdead(sd)) return; const unsigned int blocksize = sizeof(struct PACKET_CZ_REQ_OPEN_BUYING_STORE_sub); @@ -20016,7 +20179,7 @@ static void clif_parse_SkillSelectMenu(int fd, struct map_session_data *sd) if (sd->menuskill_id != SC_AUTOSHADOWSPELL) return; - if (pc_istrading(sd) || sd->state.prevend) { + if (pc_istrading_except_npc(sd) || sd->state.prevend != 0 || (sd->npc_id != 0 && sd->state.using_megaphone == 0)) { clif->skill_fail(sd, sd->ud.skill_id, 0, 0, 0); clif_menuskill_clear(sd); return; @@ -20923,20 +21086,57 @@ static void clif_bank_withdraw(struct map_session_data *sd, enum e_BANKING_WITHD #endif } -/* TODO: official response packet (tried 0x8cb/0x97b but the display was quite screwed up.) */ -/* currently mimicing */ +/** + * Sends EXP, drop and death-penalty rates. + * 0x097b <packet len>.W <exp>.L <death>.L <drop>.L <DETAIL_EXP_INFO>13B (ZC_PERSONAL_INFOMATION2) + * <InfoType>.B <Exp>.L <Death>.L <Drop>.L (DETAIL_EXP_INFO) + * + * @param sd The character which should receive the messages. + * + **/ static void clif_show_modifiers(struct map_session_data *sd) { nullpo_retv(sd); - if( sd->status.mod_exp != 100 || sd->status.mod_drop != 100 || sd->status.mod_death != 100 ) { +#if PACKETVER_MAIN_NUM >= 20120503 || PACKETVER_RE_NUM >= 20120502 || defined(PACKETVER_ZERO) + int length = sizeof(struct PACKET_ZC_PERSONAL_INFOMATION) + 4 * sizeof(struct PACKET_ZC_PERSONAL_INFOMATION_SUB); + WFIFOHEAD(sd->fd, length); + struct PACKET_ZC_PERSONAL_INFOMATION *p = WFIFOP(sd->fd, 0); + + p->packetType = HEADER_ZC_PERSONAL_INFOMATION; + p->length = length; + // Single values. + p->details[0].type = PC_EXP_INFO; + p->details[0].exp = 0; + p->details[0].death = 0; + p->details[0].drop = 0; + p->details[1].type = TPLUS_EXP_INFO; + p->details[1].exp = 0; + p->details[1].death = 0; + p->details[1].drop = 0; + p->details[2].type = PREMIUM_EXP_INFO; + p->details[2].exp = (sd->status.mod_exp - 100) * 1000; + p->details[2].death = (sd->status.mod_death - 100) * 1000; + p->details[2].drop = (sd->status.mod_drop - 100) * 1000; + p->details[3].type = SERVER_EXP_INFO; + p->details[3].exp = battle_config.base_exp_rate * 1000; + p->details[3].death = battle_config.death_penalty_base * 10; + p->details[3].drop = battle_config.item_rate_common * 1000; + // Total values. + p->total_exp = (battle_config.base_exp_rate * sd->status.mod_exp / 100) * 1000; + p->total_death = (battle_config.base_exp_rate * sd->status.mod_death / 100) * 10; + p->total_drop = (battle_config.base_exp_rate * sd->status.mod_drop / 100) * 1000; + + WFIFOSET(sd->fd, length); +#else + if (sd->status.mod_exp != 100 || sd->status.mod_drop != 100 || sd->status.mod_death != 100) { char output[128]; - snprintf(output,128, msg_sd(sd, 896), // Base EXP : %d%% | Base Drop: %d%% | Base Death Penalty: %d%% - sd->status.mod_exp,sd->status.mod_drop,sd->status.mod_death); + // Base EXP : %d%% | Base Drop: %d%% | Base Death Penalty: %d%% + safesnprintf(output, sizeof(output), msg_sd(sd, 896), sd->status.mod_exp, sd->status.mod_drop, sd->status.mod_death); clif->broadcast2(&sd->bl, output, (int)strlen(output) + 1, 0xffbc90, 0x190, 12, 0, 0, SELF); } - +#endif // PACKETVER_MAIN_NUM >= 20120503 || PACKETVER_RE_NUM >= 20120502 || defined(PACKETVER_ZERO) } static void clif_notify_bounditem(struct map_session_data *sd, unsigned short index) @@ -22176,7 +22376,10 @@ static void clif_parse_rodex_open_write_mail(int fd, struct map_session_data *sd return; const struct PACKET_CZ_REQ_OPEN_WRITE_MAIL *rPacket = RFIFOP(fd, 0); - int8 result = (rodex->isenabled() == true && sd->npc_id == 0) ? 1 : 0; + int8 result = (rodex->isenabled() && (sd->npc_id == 0 || sd->state.using_megaphone != 0)) ? 1 : 0; + + if (result == 1) + sd->state.workinprogress |= 2; clif->rodex_open_write_mail(fd, rPacket->receiveName, result); } @@ -22959,61 +23162,65 @@ static void clif_parse_open_ui_request(int fd, struct map_session_data *sd) clif->open_ui(sd, p->UIType); } -static void clif_open_ui(struct map_session_data *sd, enum cz_ui_types uiType) +/** + * Does the actual packet sending for clif_open_ui(). + * + * @param sd The character who opens the UI. + * @param ui_type The UI which should be opened. + * + **/ +static void clif_open_ui_send(struct map_session_data *sd, enum zc_ui_types ui_type) { + nullpo_retv(sd); + #if PACKETVER >= 20150128 struct PACKET_ZC_OPEN_UI p; -#if PACKETVER_RE_NUM >= 20180307 || PACKETVER_MAIN_NUM >= 20180404 || PACKETVER_ZERO_NUM >= 20180411 - int claimed = 0; -#endif - - nullpo_retv(sd); p.PacketType = openUiType; - switch (uiType) { - case CZ_STYLIST_UI: - p.UIType = ZC_STYLIST_UI; -#if PACKETVER >= 20171122 - p.data = 0; -#endif - break; - case CZ_MACRO_REGISTER_UI: - p.UIType = ZC_CAPTCHA_UI; + p.UIType = ui_type; + + switch (ui_type) { + case ZC_BANK_UI: + case ZC_STYLIST_UI: + case ZC_CAPTCHA_UI: + case ZC_MACRO_UI: #if PACKETVER >= 20171122 p.data = 0; #endif break; - case CZ_MACRO_DETECTOR_UI: - p.UIType = ZC_MACRO_UI; #if PACKETVER >= 20171122 + case ZC_TIPBOX_UI: + case ZC_RENEWQUEST_UI: p.data = 0; -#endif break; - case CZ_ATTENDANCE_UI: - { + case ZC_ATTENDANCE_UI: + if (battle_config.feature_enable_attendance_system == 0) + return; + if (clif->attendance_getendtime() < time(NULL)) { #if PACKETVER >= 20180207 clif->msgtable_color(sd, MSG_ATTENDANCE_UNAVAILABLE, COLOR_RED); #endif return; } - if (battle_config.feature_enable_attendance_system != 1) - return; + #if PACKETVER_RE_NUM >= 20180307 || PACKETVER_MAIN_NUM >= 20180404 || PACKETVER_ZERO_NUM >= 20180411 - if (clif->attendance_timediff(sd) != true) + int claimed = 0; + + if (!clif->attendance_timediff(sd)) ++claimed; else if (sd->status.attendance_count >= VECTOR_LENGTH(clif->attendance_data)) sd->status.attendance_count = 0; - p.UIType = ZC_ATTENDANCE_UI; + p.data = sd->status.attendance_count * 10 + claimed; #else ShowWarning("Attendance System available only for PACKETVER_RE_NUM >= 20180307 || PACKETVER_MAIN_NUM >= 20180404 || PACKETVER_ZERO_NUM >= 20180411.\n"); return; #endif break; - } +#endif default: - ShowWarning("clif_open_ui: Requested UI (%u) is not implemented yet.\n", uiType); + ShowWarning("clif_open_ui_send: Requested UI (%u) is not implemented yet.\n", ui_type); return; } @@ -23021,6 +23228,37 @@ static void clif_open_ui(struct map_session_data *sd, enum cz_ui_types uiType) #endif } +static void clif_open_ui(struct map_session_data *sd, enum cz_ui_types uiType) +{ + nullpo_retv(sd); + + enum zc_ui_types send_ui_type; + + switch (uiType) { +#if PACKETVER >= 20150128 + case CZ_STYLIST_UI: + send_ui_type = ZC_STYLIST_UI; + break; + case CZ_MACRO_REGISTER_UI: + send_ui_type = ZC_CAPTCHA_UI; + break; + case CZ_MACRO_DETECTOR_UI: + send_ui_type = ZC_MACRO_UI; + break; +#endif +#if PACKETVER >= 20171122 + case CZ_ATTENDANCE_UI: + send_ui_type = ZC_ATTENDANCE_UI; + break; +#endif + default: + ShowWarning("clif_open_ui: Requested UI (%u) is not implemented yet.\n", uiType); + return; + } + + clif->open_ui_send(sd, send_ui_type); +} + static void clif_parse_attendance_reward_request(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); static void clif_parse_attendance_reward_request(int fd, struct map_session_data *sd) { @@ -25131,6 +25369,7 @@ void clif_defaults(void) clif->attendance_timediff = clif_attendance_timediff; clif->attendance_getendtime = clif_attendance_getendtime; clif->pOpenUIRequest = clif_parse_open_ui_request; + clif->open_ui_send = clif_open_ui_send; clif->open_ui = clif_open_ui; clif->pAttendanceRewardRequest = clif_parse_attendance_reward_request; clif->ui_action = clif_ui_action; diff --git a/src/map/clif.h b/src/map/clif.h index e43aad808..ee76e62e8 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -750,6 +750,14 @@ enum removeGear_flag { REMOVE_MOUNT_CART = 6, }; +/** Info types for PACKET_ZC_PERSONAL_INFOMATION (0x097b). **/ +enum detail_exp_info_type { + PC_EXP_INFO = 0x0, //!< PCBang internet cafe modifiers. (http://pcbang.gnjoy.com/) (Unused.) + PREMIUM_EXP_INFO = 0x1, //!< Premium user modifiers. Values aren't displayed in 20161207+ clients. + SERVER_EXP_INFO = 0x2, //!< Server rates. + TPLUS_EXP_INFO = 0x3, //!< Unknown. Values are displayed as "TPLUS" in kRO. (Unused.) +}; + /** * Clif.c Interface **/ @@ -1650,6 +1658,7 @@ struct clif_interface { bool (*attendance_timediff) (struct map_session_data *sd); time_t (*attendance_getendtime) (void); void (*pOpenUIRequest) (int fd, struct map_session_data *sd); + void (*open_ui_send) (struct map_session_data *sd, enum zc_ui_types ui_type); void (*open_ui) (struct map_session_data *sd, enum cz_ui_types uiType); void (*pAttendanceRewardRequest) (int fd, struct map_session_data *sd); void (*ui_action) (struct map_session_data *sd, int32 UIType, int32 data); diff --git a/src/map/guild.c b/src/map/guild.c index 90f870f1c..a78ea169c 100644 --- a/src/map/guild.c +++ b/src/map/guild.c @@ -887,6 +887,12 @@ static void guild_member_joined(struct map_session_data *sd) channel->join(g->channel, sd, "", true); } + for (int j = 0; j < g->instances; j++) { + if (g->instance[j] >= 0) { + clif->instance_join(sd->fd, g->instance[j]); + break; + } + } } } @@ -940,6 +946,13 @@ static int guild_member_added(int guild_id, int account_id, int char_id, int fla channel->join(g->channel, sd, "", true); } + for (int i = 0; i < g->instances; i++) { + if (g->instance[i] >= 0) { + clif->instance_join(sd->fd, g->instance[i]); + break; + } + } + return 0; } diff --git a/src/map/instance.c b/src/map/instance.c index 1104b7e88..d2c0a229c 100644 --- a/src/map/instance.c +++ b/src/map/instance.c @@ -68,7 +68,7 @@ static bool instance_is_valid(int instance_id) /*-------------------------------------- * name : instance name * Return value could be - * -4 = already exists | -3 = no free instances | -2 = owner not found | -1 = invalid type + * -4 = already exists | -2 = owner not found | -1 = invalid type * On success return instance_id *--------------------------------------*/ static int instance_create(int owner_id, const char *name, enum instance_owner_type type) @@ -734,7 +734,7 @@ static void instance_force_destroy(struct map_session_data *sd) switch (instance->list[i].owner_type) { case IOT_CHAR: { - if (instance->list[i].owner_id != sd->status.char_id) + if (instance->list[i].owner_id != sd->status.account_id) continue; break; } diff --git a/src/map/itemdb.h b/src/map/itemdb.h index 4b06a21d6..39f0e7945 100644 --- a/src/map/itemdb.h +++ b/src/map/itemdb.h @@ -152,6 +152,7 @@ enum item_itemid { ITEMID_BUBBLE_GUM = 12210, ITEMID_GIANT_FLY_WING = 12212, ITEMID_NEURALIZER = 12213, + ITEMID_MEGAPHONE = 12221, ITEMID_M_CENTER_POTION = 12241, ITEMID_M_AWAKENING_POTION = 12242, ITEMID_M_BERSERK_POTION = 12243, diff --git a/src/map/mail.c b/src/map/mail.c index a1176e8fc..b862900c7 100644 --- a/src/map/mail.c +++ b/src/map/mail.c @@ -81,9 +81,9 @@ static int mail_removezeny(struct map_session_data *sd, short flag) static unsigned char mail_setitem(struct map_session_data *sd, int idx, int amount) { - nullpo_retr(1, sd); - if( pc_istrading(sd) ) + + if (pc_istrading_except_npc(sd) || (sd->npc_id != 0 && sd->state.using_megaphone == 0)) return 1; if( idx == 0 ) { // Zeny Transfer diff --git a/src/map/map.c b/src/map/map.c index 084621e1d..221d60fa4 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -6692,6 +6692,8 @@ int do_init(int argc, char *argv[]) atcommand->msg_read(map->MSG_CONF_NAME, false); map->inter_config_read(map->INTER_CONF_NAME, false); logs->config_read(map->LOG_CONF_NAME, false); + } else { + battle->config_read(map->BATTLE_CONF_FILENAME, false); } script->config_read(map->SCRIPT_CONF_NAME, false); @@ -6742,8 +6744,8 @@ int do_init(int argc, char *argv[]) timer->add_func_list(map->removemobs_timer, "map_removemobs_timer"); timer->add_interval(timer->gettick()+1000, map->freeblock_timer, 0, 0, 60*1000); - HPM->event(HPET_INIT); } + HPM->event(HPET_INIT); atcommand->init(minimal); battle->init(minimal); @@ -6790,8 +6792,12 @@ int do_init(int argc, char *argv[]) exit(EXIT_SUCCESS); } - if( minimal ) { + if (minimal) { HPM->event(HPET_READY); + HPM->event(HPET_FINAL); + battle->final(); + HPM_map_do_final(); + HPM->event(HPET_POST_FINAL); exit(EXIT_SUCCESS); } diff --git a/src/map/packets_struct.h b/src/map/packets_struct.h index 3129a05d9..031a23b6d 100644 --- a/src/map/packets_struct.h +++ b/src/map/packets_struct.h @@ -3927,6 +3927,24 @@ struct PACKET_CZ_LAPINEUPGRADE_MAKE_ITEM { DEFINE_PACKET_HEADER(CZ_LAPINEUPGRADE_MAKE_ITEM, 0x0ab6); #endif // PACKETVER_MAIN_NUM >= 20170111 || PACKETVER_RE_NUM >= 20170111 || defined(PACKETVER_ZERO) +#if PACKETVER_MAIN_NUM >= 20120503 || PACKETVER_RE_NUM >= 20120502 || defined(PACKETVER_ZERO) +struct PACKET_ZC_PERSONAL_INFOMATION_SUB { + int8 type; + int32 exp; + int32 death; + int32 drop; +} __attribute__((packed)); +struct PACKET_ZC_PERSONAL_INFOMATION { + int16 packetType; + int16 length; + int32 total_exp; + int32 total_death; + int32 total_drop; + struct PACKET_ZC_PERSONAL_INFOMATION_SUB details[]; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_PERSONAL_INFOMATION, 0x097b); +#endif // PACKETVER_MAIN_NUM >= 20120503 || PACKETVER_RE_NUM >= 20120502 || defined(PACKETVER_ZERO) + #if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute #pragma pack(pop) #endif // not NetBSD < 6 / Solaris diff --git a/src/map/party.c b/src/map/party.c index 8eeae2215..5eecd3fe6 100644 --- a/src/map/party.c +++ b/src/map/party.c @@ -330,6 +330,8 @@ static int party_recv_info(const struct party *sp, int char_id) party->member_withdraw(sp->party_id, sd->status.account_id, sd->status.char_id); } + int option_auto_changed = p->state.option_auto_changed; // Preserve state. + memcpy(&p->party, sp, sizeof(struct party)); memset(&p->state, 0, sizeof(p->state)); memset(&p->data, 0, sizeof(p->data)); @@ -342,6 +344,7 @@ static int party_recv_info(const struct party *sp, int char_id) p->party.member[member_id].leader = 1; } party->check_state(p); + p->state.option_auto_changed = option_auto_changed; while( added_count > 0 ) { // new in party member_id = added[--added_count]; sd = p->data[member_id].sd; @@ -349,12 +352,9 @@ static int party_recv_info(const struct party *sp, int char_id) continue;// not online clif->charnameupdate(sd); //Update other people's display. [Skotlex] clif->party_member_info(p,sd); - clif->party_option(p,sd,0x100); clif->party_info(p,NULL); for( j = 0; j < p->instances; j++ ) { if( p->instance[j] >= 0 ) { - if( instance->list[p->instance[j]].idle_timer == INVALID_TIMER && instance->list[p->instance[j]].progress_timer == INVALID_TIMER ) - continue; clif->instance_join(sd->fd, p->instance[j]); break; } @@ -488,14 +488,14 @@ static void party_member_joined(struct map_session_data *sd) p->data[i].sd = sd; for( j = 0; j < p->instances; j++ ) { if( p->instance[j] >= 0 ) { - if( instance->list[p->instance[j]].idle_timer == INVALID_TIMER && instance->list[p->instance[j]].progress_timer == INVALID_TIMER ) - continue; clif->instance_join(sd->fd, p->instance[j]); break; } } } else sd->status.party_id = 0; //He does not belongs to the party really? + + party->send_movemap(sd); } /// Invoked (from char-server) when a new member is added to the party. @@ -536,6 +536,11 @@ static int party_member_added(int party_id, int account_id, int char_id, int fla clif->party_member_info(p,sd); clif->party_info(p,sd); + if (p->state.option_auto_changed != 0) + clif->party_option(p, sd, 0x04); + else + clif->party_option(p, sd, 0x08); + if( sd2 != NULL ) clif->party_inviteack(sd2,sd->status.name,2); @@ -551,8 +556,6 @@ static int party_member_added(int party_id, int account_id, int char_id, int fla for( j = 0; j < p->instances; j++ ) { if( p->instance[j] >= 0 ) { - if( instance->list[p->instance[j]].idle_timer == INVALID_TIMER && instance->list[p->instance[j]].progress_timer == INVALID_TIMER ) - continue; clif->instance_join(sd->fd, p->instance[j]); break; } @@ -622,6 +625,7 @@ static int party_member_withdraw(int party_id, int account_id, int char_id) prev_leader_accountId = p->party.member[i].account_id; } + clif->party_option(p, sd, 0x10); clif->party_withdraw(p,sd,account_id,p->party.member[i].name,0x0); memset(&p->party.member[i], 0, sizeof(p->party.member[0])); memset(&p->data[i], 0, sizeof(p->data[0])); @@ -729,8 +733,17 @@ static int party_optionchanged(int party_id, int account_id, int exp, int item, //Flag&0x1: Exp change denied. Flag&0x10: Item change denied. if(!(flag&0x01) && p->party.exp != exp) p->party.exp=exp; - if(!(flag&0x10) && p->party.item != item) { + if (p->party.item != item) p->party.item=item; + + if (account_id == 0) { + flag |= 0x04; + p->state.option_auto_changed = 1; + + if (p->state.member_level_changed == 0) + return 0; // clif_party_option() is handled in clif_parse_LoadEndAck(). + } else { + flag |= 0x02; } clif->party_option(p,sd,flag); @@ -811,7 +824,8 @@ static int party_recv_movemap(int party_id, int account_id, int char_id, unsigne ShowError("party_recv_movemap: char %d/%d not found in party %s (id:%d)",account_id,char_id,p->party.name,party_id); return 0; } - + + p->state.member_level_changed = 0; m = &p->party.member[i]; m->map = mapid; m->online = online; @@ -860,7 +874,12 @@ static void party_send_movemap(struct map_session_data *sd) static void party_send_levelup(struct map_session_data *sd) { - intif->party_changemap(sd,1); + struct party_data *p = party->search(sd->status.party_id); + + if (p != NULL) + p->state.member_level_changed = 1; + + intif->party_changemap(sd, 1); } static int party_send_logout(struct map_session_data *sd) diff --git a/src/map/party.h b/src/map/party.h index c2306b7a8..5f3458cd4 100644 --- a/src/map/party.h +++ b/src/map/party.h @@ -51,6 +51,8 @@ struct party_data { unsigned sg : 1; ///< There's at least one Star Gladiator in party? unsigned snovice :1; ///< There's a Super Novice unsigned tk : 1; ///< There's a taekwon + unsigned option_auto_changed : 1; ///< Party options were changed automatically. (inter_party_check_lv()) + unsigned member_level_changed : 1; ///< A party member's level has changed. } state; struct hplugin_data_store *hdata; ///< HPM Plugin Data Store }; diff --git a/src/map/pc.c b/src/map/pc.c index c1261c839..64e52848f 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -5163,7 +5163,7 @@ static int pc_useitem(struct map_session_data *sd, int n) nullpo_ret(sd); Assert_ret(n >= 0 && n < sd->status.inventorySize); - if ((sd->npc_id != 0 && (sd->npc_item_flag & ITEMENABLEDNPC_CONSUME) == 0) + if ((sd->npc_id != 0 && sd->state.using_megaphone == 0 && (sd->npc_item_flag & ITEMENABLEDNPC_CONSUME) == 0) || (sd->state.workinprogress & 1) != 0) { #if PACKETVER >= 20110308 clif->msgtable(sd, MSG_BUSY); @@ -5190,6 +5190,16 @@ static int pc_useitem(struct map_session_data *sd, int n) // Store information for later use before it is lost (via pc->delitem) [Paradox924X] nameid = sd->inventory_data[n]->nameid; + if (nameid == ITEMID_MEGAPHONE && ((sd->state.workinprogress & 2) != 0 || sd->state.using_megaphone != 0 + || sd->npc_id != 0)) { +#if PACKETVER >= 20110308 + clif->msgtable(sd, MSG_BUSY); +#else + clif->messagecolor_self(sd->fd, COLOR_WHITE, msg_sd(sd, 48)); +#endif + return 0; + } + if (nameid != ITEMID_NAUTHIZ && sd->sc.opt1 > 0 && sd->sc.opt1 != OPT1_STONEWAIT && sd->sc.opt1 != OPT1_BURNING) return 0; @@ -5320,6 +5330,9 @@ static int pc_useitem(struct map_session_data *sd, int n) // Update item use time. sd->canuseitem_tick = tick + battle_config.item_use_interval; + if (nameid == ITEMID_MEGAPHONE) + sd->state.using_megaphone = 1; + script->run_use_script(sd, sd->inventory_data[n], npc->fake_nd->bl.id); script->potion_flag = 0; @@ -8148,12 +8161,10 @@ static int pc_dead(struct map_session_data *sd, struct block_list *src) if (sd->status.pet_id > 0 && sd->pd != NULL) { struct pet_data *pd = sd->pd; - if (map->list[sd->bl.m].flag.noexppenalty == 0) { + if (map->list[sd->bl.m].flag.noexppenalty == 0) pet->set_intimate(pd, pd->pet.intimate - pd->petDB->die); - clif->send_petdata(sd, sd->pd, 1, pd->pet.intimate); - } - if (sd->pd->target_id != 0) // Unlock all targets. + if (sd->pd != NULL && sd->pd->target_id != 0) // Unlock all targets. pet->unlocktarget(sd->pd); } @@ -8173,7 +8184,7 @@ static int pc_dead(struct map_session_data *sd, struct block_list *src) duel->reject(sd->duel_invite, sd); } - if (sd->npc_id != 0 && sd->st != NULL && sd->st->state != RUN) + if (sd->npc_id != 0 && sd->state.using_megaphone == 0 && sd->st != NULL && sd->st->state != RUN) npc->event_dequeue(sd); pc_setglobalreg(sd, script->add_variable("PC_DIE_COUNTER"), sd->die_counter + 1); @@ -8196,7 +8207,7 @@ static int pc_dead(struct map_session_data *sd, struct block_list *src) npc->script_event(sd, NPCE_DIE); // Clear anything NPC-related if character died while interacting with one. - if ((sd->npc_id != 0 || sd->npc_shopid != 0) && sd->state.dialog != 0) { + if (((sd->npc_id != 0 && sd->state.using_megaphone == 0) || sd->npc_shopid != 0) && sd->state.dialog != 0) { if (sd->state.using_fake_npc != 0) { clif->clearunit_single(sd->npc_id, CLR_OUTSIGHT, sd->fd); sd->state.using_fake_npc = 0; @@ -12117,7 +12128,6 @@ static void pc_scdata_received(struct map_session_data *sd) { nullpo_retv(sd); pc->inventory_rentals(sd); - clif->show_modifiers(sd); if (sd->expiration_time != 0) { // don't display if it's unlimited or unknow value time_t exp_time = sd->expiration_time; diff --git a/src/map/pc.h b/src/map/pc.h index f2e911af3..e8e591b09 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -224,6 +224,7 @@ struct map_session_data { unsigned int size :2; // for tiny/large types unsigned int night :1; //Holds whether or not the player currently has the SI_NIGHT effect on. [Skotlex] unsigned int using_fake_npc :1; + unsigned int using_megaphone : 1; //!< Whether the character is currently using a Megephone (ID=12221). unsigned int rewarp :1; //Signals that a player should warp as soon as he is done loading a map. [Skotlex] unsigned int killer : 1; unsigned int killable : 1; @@ -681,9 +682,11 @@ END_ZEROED_BLOCK; #define pc_issit(sd) ( (sd)->vd.dead_sit == 2 ) #define pc_isidle(sd) ( (sd)->chat_id != 0 || (sd)->state.vending || (sd)->state.buyingstore || DIFF_TICK(sockt->last_tick, (sd)->idletime) >= battle->bc->idle_no_share ) #define pc_istrading(sd) ( (sd)->npc_id || (sd)->state.vending || (sd)->state.buyingstore || (sd)->state.trading ) +#define pc_istrading_except_npc(sd) ( (sd)->state.vending != 0 || (sd)->state.buyingstore != 0 || (sd)->state.trading != 0 ) #define pc_cant_act(sd) ( (sd)->npc_id || (sd)->state.vending || (sd)->state.buyingstore || (sd)->chat_id != 0 || ((sd)->sc.opt1 && (sd)->sc.opt1 != OPT1_BURNING) || (sd)->state.trading || (sd)->state.storage_flag || (sd)->state.prevend || (sd)->state.refine_ui == 1 || (sd)->state.lapine_ui == 1) #define pc_cant_act_except_lapine(sd) ((sd)->npc_id || (sd)->state.vending || (sd)->state.buyingstore || (sd)->chat_id != 0 || ((sd)->sc.opt1 && (sd)->sc.opt1 != OPT1_BURNING) || (sd)->state.trading || (sd)->state.storage_flag || (sd)->state.prevend || (sd)->state.refine_ui == 1) #define pc_cant_act_except_npc(sd) ( (sd)->state.vending != 0 || (sd)->state.buyingstore != 0 || (sd)->chat_id != 0 || ((sd)->sc.opt1 != 0 && (sd)->sc.opt1 != OPT1_BURNING) || (sd)->state.trading != 0 || (sd)->state.storage_flag != 0 || (sd)->state.prevend != 0 || (sd)->state.refine_ui == 1 || (sd)->state.lapine_ui == 1) +#define pc_cant_act_except_npc_chat(sd) ( (sd)->state.vending != 0 || (sd)->state.buyingstore != 0 || ((sd)->sc.opt1 != 0 && (sd)->sc.opt1 != OPT1_BURNING) || (sd)->state.trading != 0 || (sd)->state.storage_flag != 0 || (sd)->state.prevend != 0 || (sd)->state.refine_ui == 1 || (sd)->state.lapine_ui == 1) /* equals pc_cant_act except it doesn't check for chat rooms */ #define pc_cant_act2(sd) ( (sd)->npc_id || (sd)->state.buyingstore || ((sd)->sc.opt1 && (sd)->sc.opt1 != OPT1_BURNING) || (sd)->state.trading || (sd)->state.storage_flag || (sd)->state.prevend || (sd)->state.refine_ui == 1 || (sd)->state.lapine_ui == 1) @@ -727,6 +730,9 @@ END_ZEROED_BLOCK; /// Rune Knight Dragon #define pc_isridingdragon(sd) ( (sd)->sc.option&OPTION_DRAGON ) +// Check if character has a pet. +#define pc_has_pet(sd) ( (sd)->status.pet_id != 0 && (sd)->pd != NULL && (sd)->pd->pet.intimate > PET_INTIMACY_NONE ) + #define pc_stop_walking(sd, type) (unit->stop_walking(&(sd)->bl, (type))) #define pc_stop_attack(sd) (unit->stop_attack(&(sd)->bl)) @@ -873,6 +879,12 @@ struct class_exp_tables { struct class_exp_group *class_exp_table[CLASS_COUNT][2]; }; +enum player_actions_when_dead_flags { + PCALLOWACTION_NONE = 0x0, // Don't allow trading and open chat rooms. + PCALLOWACTION_TRADE = 0x1, // Allow trading when dead. + PCALLOWACTION_CHAT = 0x2, // Allow open chat room when dead. +}; + /*===================================== * Interface : pc.h * Generated by HerculesInterfaceMaker diff --git a/src/map/pet.c b/src/map/pet.c index 2a9831ed6..f10c55f57 100644 --- a/src/map/pet.c +++ b/src/map/pet.c @@ -94,8 +94,41 @@ static int pet_hungry_val(struct pet_data *pd) static void pet_set_hunger(struct pet_data *pd, int value) { nullpo_retv(pd); + nullpo_retv(pd->msd); pd->pet.hungry = cap_value(value, PET_HUNGER_STARVING, PET_HUNGER_STUFFED); + + clif->send_petdata(pd->msd, pd, 2, pd->pet.hungry); +} + +/** + * Calculates the value to store in a pet egg's 4th card slot + * based on the passed rename flag and intimacy value. + * + * @param rename_flag The pet's rename flag. + * @param intimacy The pet's intimacy value. + * @return The value to store in the pet egg's 4th card slot. (Defaults to 0 in case of error.) + * + **/ +static int pet_get_card4_value(int rename_flag, int intimacy) +{ + Assert_ret(rename_flag == 0 || rename_flag == 1); + Assert_ret(intimacy >= PET_INTIMACY_NONE && intimacy <= PET_INTIMACY_MAX); + + int card4 = rename_flag; + + if (intimacy <= PET_INTIMACY_SHY) + card4 |= (1 << 1); + else if (intimacy <= PET_INTIMACY_NEUTRAL) + card4 |= (2 << 1); + else if (intimacy <= PET_INTIMACY_CORDIAL) + card4 |= (3 << 1); + else if (intimacy <= PET_INTIMACY_LOYAL) + card4 |= (4 << 1); + else + card4 |= (5 << 1); + + return card4; } /** @@ -115,18 +148,24 @@ static void pet_set_intimate(struct pet_data *pd, int value) struct map_session_data *sd = pd->msd; - status_calc_pc(sd, SCO_NONE); - - if (pd->pet.intimate == PET_INTIMACY_NONE) { /// Pet is lost. Delete the egg. + if (pd->pet.intimate == PET_INTIMACY_NONE) { // Pet is lost, delete it. int i; ARR_FIND(0, sd->status.inventorySize, i, sd->status.inventory[i].card[0] == CARD0_PET - && pd->pet.pet_id == MakeDWord(sd->status.inventory[i].card[1], - sd->status.inventory[i].card[2])); + && pd->pet.pet_id == MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2])); if (i != sd->status.inventorySize) pc->delitem(sd, i, 1, 0, DELITEM_NORMAL, LOG_TYPE_EGG); + + if (battle_config.pet_remove_immediately != 0) { + pet_stop_attack(pd); + unit->remove_map(&pd->bl, CLR_OUTSIGHT, ALC_MARK); + } + } else { + clif->send_petdata(sd, pd, 1, pd->pet.intimate); } + + status_calc_pc(sd, SCO_NONE); } /** @@ -312,17 +351,15 @@ static int pet_hungry(int tid, int64 tick, int id, intptr_t data) pet_stop_attack(pd); pet->set_intimate(pd, pd->pet.intimate - pd->petDB->starving_decrement); - if (pd->pet.intimate == PET_INTIMACY_NONE) - pd->status.speed = pd->db->status.speed; + if (sd->pd == NULL) + return 0; status_calc_pet(pd, SCO_NONE); - clif->send_petdata(sd, pd, 1, pd->pet.intimate); if (pd->petDB->starving_delay > 0) interval = pd->petDB->starving_delay; } - clif->send_petdata(sd, pd, 2, pd->pet.hungry); interval = interval * battle_config.pet_hungry_delay_rate / 100; pd->pet_hungry_timer = timer->add(tick + max(interval, 1), pet->hungry, sd->bl.id, 0); @@ -404,7 +441,8 @@ static int pet_return_egg(struct map_session_data *sd, struct pet_data *pd) if (i != sd->status.inventorySize) { sd->status.inventory[i].attribute &= ~ATTR_BROKEN; sd->status.inventory[i].bound = IBT_NONE; - sd->status.inventory[i].card[3] = pd->pet.rename_flag; + sd->status.inventory[i].card[3] = pet->get_card4_value(pd->pet.rename_flag, pd->pet.intimate); + clif->inventoryList(sd); } else { // The pet egg wasn't found: it was probably hatched with the old system that deleted the egg. struct item tmp_item = {0}; @@ -415,14 +453,13 @@ static int pet_return_egg(struct map_session_data *sd, struct pet_data *pd) tmp_item.card[0] = CARD0_PET; tmp_item.card[1] = GetWord(pd->pet.pet_id, 0); tmp_item.card[2] = GetWord(pd->pet.pet_id, 1); - tmp_item.card[3] = pd->pet.rename_flag; + tmp_item.card[3] = pet->get_card4_value(pd->pet.rename_flag, pd->pet.intimate); if ((flag = pc->additem(sd, &tmp_item, 1, LOG_TYPE_EGG)) != 0) { clif->additem(sd, 0, 0, flag); map->addflooritem(&sd->bl, &tmp_item, 1, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0, false); } } #if PACKETVER >= 20180704 - clif->inventoryList(sd); clif->send_petdata(sd, pd, 6, 0); #endif pd->pet.incubate = 1; @@ -511,6 +548,35 @@ static int pet_data_init(struct map_session_data *sd, struct s_pet *petinfo) return 0; } +/** + * Spawns a pet. + * + * @param sd The pet's master. + * @param birth_process Whether the pet is spawned during birth process. + * @return 1 on failure, 0 on success. + * + **/ +static int pet_spawn(struct map_session_data *sd, bool birth_process) +{ + nullpo_retr(1, sd); + nullpo_retr(1, sd->pd); + + if (map->addblock(&sd->pd->bl) != 0 || !clif->spawn(&sd->pd->bl)) + return 1; + + clif->send_petdata(sd, sd->pd, 0, 0); + clif->send_petdata(sd, sd->pd, 5, battle_config.pet_hair_style); + +#if PACKETVER >= 20180704 + if (birth_process) + clif->send_petdata(sd, sd->pd, 6, 1); +#endif + + clif->send_petstatus(sd); + + return 0; +} + static int pet_birth_process(struct map_session_data *sd, struct s_pet *petinfo) { nullpo_retr(1, sd); @@ -535,17 +601,11 @@ static int pet_birth_process(struct map_session_data *sd, struct s_pet *petinfo) if (map->save_settings&8) chrif->save(sd,0); //is it REALLY Needed to save the char for hatching a pet? [Skotlex] - if(sd->bl.prev != NULL) { - map->addblock(&sd->pd->bl); - clif->spawn(&sd->pd->bl); - clif->send_petdata(sd,sd->pd, 0,0); - clif->send_petdata(sd,sd->pd, 5,battle_config.pet_hair_style); -#if PACKETVER >= 20180704 - clif->send_petdata(sd, sd->pd, 6, 1); -#endif - clif->send_petdata(NULL, sd->pd, 3, sd->pd->vd.head_bottom); - clif->send_petstatus(sd); + if (sd->pd != NULL && sd->bl.prev != NULL) { + if (pet->spawn(sd, true) != 0) + return 1; } + Assert_retr(1, sd->status.pet_id == 0 || sd->pd == 0 || sd->pd->msd == sd); return 0; @@ -584,13 +644,9 @@ static int pet_recv_petdata(int account_id, struct s_pet *p, int flag) } } else { pet->data_init(sd,p); - if(sd->pd && sd->bl.prev != NULL) { - map->addblock(&sd->pd->bl); - clif->spawn(&sd->pd->bl); - clif->send_petdata(sd,sd->pd,0,0); - clif->send_petdata(sd,sd->pd,5,battle_config.pet_hair_style); - clif->send_petdata(NULL, sd->pd, 3, sd->pd->vd.head_bottom); - clif->send_petstatus(sd); + if (sd->pd != NULL && sd->bl.prev != NULL) { + if (pet->spawn(sd, false) != 0) + return 1; } } @@ -644,8 +700,6 @@ static int pet_catch_process2(struct map_session_data *sd, int target_id) return 1; } - //FIXME: Delete taming item here, if this was an item-invoked capture and the item was flagged as delay-consume. [ultramage] - // catch_target_class == 0 is used for universal lures (except bosses for now). [Skotlex] if (sd->catch_target_class == 0 && (md->status.mode & MD_BOSS) == 0) sd->catch_target_class = md->class_; @@ -729,7 +783,7 @@ static bool pet_get_egg(int account_id, int pet_class, int pet_id) tmp_item.card[0] = CARD0_PET; tmp_item.card[1] = GetWord(pet_id,0); tmp_item.card[2] = GetWord(pet_id,1); - tmp_item.card[3] = 0; //New pets are not named. + tmp_item.card[3] = pet->get_card4_value(0, pet->db[i].intimate); if((ret = pc->additem(sd,&tmp_item,1,LOG_TYPE_PICKDROP_PLAYER))) { clif->additem(sd,0,0,ret); map->addflooritem(&sd->bl, &tmp_item, 1, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0, false); @@ -828,8 +882,6 @@ static int pet_change_name_ack(struct map_session_data *sd, const char *name, in aFree(newname); clif->blname_ack(0,&pd->bl); pd->pet.rename_flag = 1; - clif->send_petdata(NULL, sd->pd, 3, sd->pd->vd.head_bottom); - clif->send_petstatus(sd); return 1; } @@ -920,6 +972,7 @@ static int pet_food(struct map_session_data *sd, struct pet_data *pd) { nullpo_retr(1, sd); nullpo_retr(1, pd); + Assert_retr(1, sd->status.pet_id == pd->pet.pet_id); int i = pc->search_inventory(sd, pd->petDB->FoodID); @@ -948,15 +1001,11 @@ static int pet_food(struct map_session_data *sd, struct pet_data *pd) intimacy = intimacy * battle_config.pet_friendly_rate / 100; pet->set_intimate(pd, pd->pet.intimate + intimacy); - if (pd->pet.intimate == PET_INTIMACY_NONE) { - pet_stop_attack(pd); - pd->status.speed = pd->db->status.speed; - } + if (sd->pd == NULL) + return 0; status_calc_pet(pd, SCO_NONE); pet->set_hunger(pd, pd->pet.hungry + pd->petDB->fullness); - clif->send_petdata(sd, pd, 2, pd->pet.hungry); - clif->send_petdata(sd, pd, 1, pd->pet.intimate); clif->pet_food(sd, pd->petDB->FoodID, 1); return 0; @@ -1799,6 +1848,7 @@ void pet_defaults(void) pet->hungry_val = pet_hungry_val; pet->set_hunger = pet_set_hunger; + pet->get_card4_value = pet_get_card4_value; pet->set_intimate = pet_set_intimate; pet->create_egg = pet_create_egg; pet->unlocktarget = pet_unlocktarget; @@ -1811,6 +1861,7 @@ void pet_defaults(void) pet->performance = pet_performance; pet->return_egg = pet_return_egg; pet->data_init = pet_data_init; + pet->spawn = pet_spawn; pet->birth_process = pet_birth_process; pet->recv_petdata = pet_recv_petdata; pet->select_egg = pet_select_egg; diff --git a/src/map/pet.h b/src/map/pet.h index fa37e896a..c57df9de3 100644 --- a/src/map/pet.h +++ b/src/map/pet.h @@ -147,6 +147,7 @@ struct pet_interface { /* */ int (*hungry_val) (struct pet_data *pd); void (*set_hunger) (struct pet_data *pd, int value); + int (*get_card4_value) (int rename_flag, int intimacy); void (*set_intimate) (struct pet_data *pd, int value); int (*create_egg) (struct map_session_data *sd, int item_id); int (*unlocktarget) (struct pet_data *pd); @@ -159,6 +160,7 @@ struct pet_interface { int (*performance) (struct map_session_data *sd, struct pet_data *pd); int (*return_egg) (struct map_session_data *sd, struct pet_data *pd); int (*data_init) (struct map_session_data *sd, struct s_pet *petinfo); + int (*spawn) (struct map_session_data *sd, bool birth_process); int (*birth_process) (struct map_session_data *sd, struct s_pet *petinfo); int (*recv_petdata) (int account_id, struct s_pet *p, int flag); int (*select_egg) (struct map_session_data *sd, int egg_index); diff --git a/src/map/rodex.c b/src/map/rodex.c index 1ebed0623..f2bb8a0d4 100644 --- a/src/map/rodex.c +++ b/src/map/rodex.c @@ -231,7 +231,7 @@ static int rodex_send_mail(struct map_session_data *sd, const char *receiver_nam nullpo_retr(RODEX_SEND_MAIL_FATAL_ERROR, body); nullpo_retr(RODEX_SEND_MAIL_FATAL_ERROR, title); - if (!rodex->isenabled() || sd->npc_id > 0) { + if (!rodex->isenabled() || (sd->npc_id != 0 && sd->state.using_megaphone == 0)) { rodex->clean(sd, 1); return RODEX_SEND_MAIL_FATAL_ERROR; } @@ -575,6 +575,7 @@ static void rodex_clean(struct map_session_data *sd, int8 flag) if (flag == 0) VECTOR_CLEAR(sd->rodex.messages); + sd->state.workinprogress &= ~2; memset(&sd->rodex.tmp, 0x0, sizeof(sd->rodex.tmp)); } diff --git a/src/map/script.c b/src/map/script.c index 743a1779a..b49844320 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -12574,6 +12574,8 @@ static BUILDIN(loudhailer) clif->broadcast(&sd->bl, mes_formatted, (int)len_formatted, BC_MEGAPHONE, ALL_CLIENT); + sd->state.using_megaphone = 0; + return true; } @@ -20518,6 +20520,8 @@ static BUILDIN(setunitdata) break; case UDT_LEVEL: pd->pet.level = (short)val; + if (pd->msd != NULL) + clif->send_petstatus(pd->msd); // Send pet data. break; case UDT_HP: status->set_hp(bl, (unsigned int)val, STATUS_HEAL_DEFAULT); @@ -20629,7 +20633,6 @@ static BUILDIN(setunitdata) break; case UDT_INTIMACY: pet->set_intimate(pd, val); - clif->send_petdata(pd->msd, pd, 1, pd->pet.intimate); break; case UDT_HUNGER: pet->set_hunger(pd, val); @@ -20640,7 +20643,6 @@ static BUILDIN(setunitdata) return false; } - clif->send_petstatus(pd->msd); // Send pet data. break; } case BL_MER: { @@ -28358,6 +28360,11 @@ static void script_hardcoded_constants(void) script->set_constant("P_AIRSHIP_INVALID_END_MAP", P_AIRSHIP_INVALID_END_MAP, false, false); script->set_constant("P_AIRSHIP_ITEM_NOT_ENOUGH", P_AIRSHIP_ITEM_NOT_ENOUGH, false, false); script->set_constant("P_AIRSHIP_ITEM_INVALID", P_AIRSHIP_ITEM_INVALID, false, false); + + script->constdb_comment("player allowed actions when dead"); + script->set_constant("PCALLOWACTION_NONE", PCALLOWACTION_NONE, false, false); + script->set_constant("PCALLOWACTION_TRADE", PCALLOWACTION_TRADE, false, false); + script->set_constant("PCALLOWACTION_CHAT", PCALLOWACTION_CHAT, false, false); script->constdb_comment("questinfo types"); script->set_constant("QINFO_JOB", QINFO_JOB, false, false); diff --git a/src/map/script.h b/src/map/script.h index df5297ac0..4bf8c436b 100644 --- a/src/map/script.h +++ b/src/map/script.h @@ -703,7 +703,7 @@ struct script_state { int bk_npcid; unsigned freeloop : 1;// used by buildin_freeloop unsigned op2ref : 1;// used by op_2 - unsigned npc_item_flag : 1; + unsigned npc_item_flag : 2; unsigned int id; }; diff --git a/src/map/skill.c b/src/map/skill.c index 24fbe7892..ccc4264d9 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -370,6 +370,41 @@ static int skill_get_spiritball(int skill_id, int skill_lv) } /** + * Gets the index of the first required item for a skill at given level. + * + * @param skill_id The skill's ID. + * @param skill_lv The skill's level. + * @return The required item's index. Defaults to INDEX_NOT_FOUND (-1) in case of error or if no appropriate index was found. + * + **/ +static int skill_get_item_index(int skill_id, int skill_lv) +{ + if (skill_id == 0) + return INDEX_NOT_FOUND; + + Assert_retr(INDEX_NOT_FOUND, skill_lv > 0); + + int idx = skill->get_index(skill_id); + + Assert_retr(INDEX_NOT_FOUND, idx != 0); + + int item_index = INDEX_NOT_FOUND; + int level_index = skill_get_lvl_idx(skill_lv); + + for (int i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++) { + if (skill->dbs->db[idx].req_items.item[i].id == 0) + continue; + + if (skill->dbs->db[idx].req_items.item[i].amount[level_index] != -1) { + item_index = i; + break; + } + } + + return item_index; +} + +/** * Gets a skill's required item's ID by the skill's ID and the item's index. * * @param skill_id The skill's ID. @@ -7937,7 +7972,14 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * } if( sd ) { int bonus = 100, potion = min(500+skill_lv,505); - int item_idx = (skill_lv - 1) % MAX_SKILL_ITEM_REQUIRE; + int item_idx = skill->get_item_index(skill_id, skill_lv); + + if (item_idx == INDEX_NOT_FOUND) { + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); + map->freeblock_unlock(); + return 1; + } + int item_id = skill->get_itemid(skill_id, item_idx); int inventory_idx = pc->search_inventory(sd, item_id); if (inventory_idx == INDEX_NOT_FOUND || item_id <= 0) { @@ -11797,7 +11839,13 @@ static int skill_castend_pos2(struct block_list *src, int x, int y, uint16 skill // Slim Pitcher [Celest] case CR_SLIMPITCHER: if (sd) { - int item_idx = (skill_lv - 1) % MAX_SKILL_ITEM_REQUIRE; + int item_idx = skill->get_item_index(skill_id, skill_lv); + + if (item_idx == INDEX_NOT_FOUND) { + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); + return 1; + } + int item_id = skill->get_itemid(skill_id, item_idx); int inventory_idx = pc->search_inventory(sd, item_id); int bonus; @@ -11830,7 +11878,11 @@ static int skill_castend_pos2(struct block_list *src, int x, int y, uint16 skill skill->castend_nodamage_id); } } else { - int item_idx = (skill_lv - 1) % MAX_SKILL_ITEM_REQUIRE; + int item_idx = skill->get_item_index(skill_id, skill_lv); + + if (item_idx == INDEX_NOT_FOUND) + return 1; + int item_id = skill->get_itemid(skill_id, item_idx); struct item_data *item = itemdb->search(item_id); int bonus; @@ -17766,13 +17818,20 @@ static int skill_get_new_group_id(void) static struct skill_unit_group *skill_initunitgroup(struct block_list *src, int count, uint16 skill_id, uint16 skill_lv, int unit_id, int limit, int interval) { - struct unit_data* ud = unit->bl2ud( src ); struct skill_unit_group* group; int i; if(!(skill_id && skill_lv)) return 0; nullpo_retr(NULL, src); + + struct unit_data *ud; + + if (src->type == BL_NPC) + ud = unit->bl2ud2(src); + else + ud = unit->bl2ud(src); + nullpo_retr(NULL, ud); // find a free spot to store the new unit group @@ -23937,6 +23996,7 @@ void skill_defaults(void) skill->get_sp_rate = skill_get_sp_rate; skill->get_state = skill_get_state; skill->get_spiritball = skill_get_spiritball; + skill->get_item_index = skill_get_item_index; skill->get_itemid = skill_get_itemid; skill->get_itemqty = skill_get_itemqty; skill->get_item_any_flag = skill_get_item_any_flag; diff --git a/src/map/skill.h b/src/map/skill.h index b505412b6..fdeaefe01 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -2007,6 +2007,7 @@ struct skill_interface { int (*get_sp_rate) (int skill_id, int skill_lv); int (*get_state) (int skill_id, int skill_lv); int (*get_spiritball) (int skill_id, int skill_lv); + int (*get_item_index) (int skill_id, int skill_lv); int (*get_itemid) (int skill_id, int item_idx); int (*get_itemqty) (int skill_id, int item_idx, int skill_lv); bool (*get_item_any_flag) (int skill_id, int skill_lv); diff --git a/src/map/trade.c b/src/map/trade.c index e727c3c70..31970fbf9 100644 --- a/src/map/trade.c +++ b/src/map/trade.c @@ -63,7 +63,7 @@ static void trade_traderequest(struct map_session_data *sd, struct map_session_d return; } - if (target_sd->npc_id) { + if (target_sd->npc_id != 0 && target_sd->state.using_megaphone == 0) { //Trade fails if you are using an NPC. clif->tradestart(sd, 2); return; @@ -166,9 +166,10 @@ static void trade_tradeack(struct map_session_data *sd, int type) } //Check if you can start trade. - if (sd->npc_id || sd->state.vending || sd->state.prevend || sd->state.buyingstore || sd->state.storage_flag != STORAGE_FLAG_CLOSED - || tsd->npc_id || tsd->state.vending || tsd->state.prevend || tsd->state.buyingstore || tsd->state.storage_flag != STORAGE_FLAG_CLOSED - ) { + if ((sd->npc_id != 0 && sd->state.using_megaphone == 0) || sd->state.vending != 0 || sd->state.prevend != 0 + || sd->state.buyingstore != 0 || sd->state.storage_flag != STORAGE_FLAG_CLOSED + || (tsd->npc_id != 0 && tsd->state.using_megaphone == 0) || tsd->state.vending != 0 || tsd->state.prevend != 0 + || tsd->state.buyingstore != 0 || tsd->state.storage_flag != STORAGE_FLAG_CLOSED) { //Fail clif->tradestart(sd, 2); clif->tradestart(tsd, 2); diff --git a/src/map/vending.c b/src/map/vending.c index 4fd009025..9b3f48f38 100644 --- a/src/map/vending.c +++ b/src/map/vending.c @@ -251,8 +251,10 @@ static void vending_openvending(struct map_session_data *sd, const char *message int vending_skill_lvl; nullpo_retv(sd); - if ( pc_isdead(sd) || !sd->state.prevend || pc_istrading(sd)) - return; // can't open vendings lying dead || didn't use via the skill (wpe/hack) || can't have 2 shops at once + if (pc_isdead(sd) || sd->state.prevend == 0 || pc_istrading_except_npc(sd) + || (sd->npc_id != 0 && sd->state.using_megaphone == 0)) { + return; // Can't open vendings lying dead. || Didn't use via the skill. (wpe/hack) || Can't have 2 shops at once. + } vending_skill_lvl = pc->checkskill(sd, MC_VENDING); // skill level and cart check |