summaryrefslogtreecommitdiff
path: root/src/map
diff options
context:
space:
mode:
Diffstat (limited to 'src/map')
-rw-r--r--src/map/atcommand.c21
-rw-r--r--src/map/battle.c13
-rw-r--r--src/map/battle.h7
-rw-r--r--src/map/buyingstore.c13
-rw-r--r--src/map/clif.c601
-rw-r--r--src/map/clif.h9
-rw-r--r--src/map/guild.c13
-rw-r--r--src/map/instance.c4
-rw-r--r--src/map/itemdb.h1
-rw-r--r--src/map/mail.c4
-rw-r--r--src/map/map.c10
-rw-r--r--src/map/packets_struct.h18
-rw-r--r--src/map/party.c39
-rw-r--r--src/map/party.h2
-rw-r--r--src/map/pc.c26
-rw-r--r--src/map/pc.h12
-rw-r--r--src/map/pet.c131
-rw-r--r--src/map/pet.h2
-rw-r--r--src/map/rodex.c3
-rw-r--r--src/map/script.c11
-rw-r--r--src/map/script.h2
-rw-r--r--src/map/skill.c68
-rw-r--r--src/map/skill.h1
-rw-r--r--src/map/trade.c9
-rw-r--r--src/map/vending.c6
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