Adds support for instance destruction button (#5073)

* Adds support for instance destruction button
* Adds an extra parameter in the instance database to toggle if an instance is destroy-able or not.
* Adds support for being notified about character and Clan instances on login.
* Fixes an issue with the instance window displaying wrong instance information on an instance map when multiples instances were running for the character.
Co-authored-by: atemo <capucrath@gmail.com>
This commit is contained in:
Aleos 2020-06-17 14:52:22 -04:00 committed by GitHub
parent 04ba3e3d96
commit 3804d7f603
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 185 additions and 34 deletions

View File

@ -26,6 +26,8 @@
# Name Instance Name.
# TimeLimit Total lifetime of instance in seconds. (Default: 3600)
# IdleTimeOut Time before an idle instance is destroyed in seconds. (Default: 300)
# Destroyable Toggles the ability to destroy the instance using instance 'Destroy' button. (Default: true)
# Note: the button is displayed based on parties. For any mode, it requires the party leader to be the instance owner to destroy it.
# Enter: Instance entrance coordinates.
# Map Map Name where players start.
# X X Coordinate where players start.

View File

@ -26,6 +26,8 @@
# Name Instance Name.
# TimeLimit Total lifetime of instance in seconds. (Default: 3600)
# IdleTimeOut Time before an idle instance is destroyed in seconds. (Default: 300)
# Destroyable Toggles the ability to destroy the instance using instance 'Destroy' button. (Default: true)
# Note: the button is displayed based on parties. For any mode, it requires the party leader to be the instance owner to destroy it.
# Enter: Instance entrance coordinates.
# Map Map Name where players start.
# X X Coordinate where players start.

View File

@ -26,6 +26,8 @@
# Name Instance Name.
# TimeLimit Total lifetime of instance in seconds. (Default: 3600)
# IdleTimeOut Time before an idle instance is destroyed in seconds. (Default: 300)
# Destroyable Toggles the ability to destroy the instance using instance 'Destroy' button. (Default: true)
# Note: the button is displayed based on parties. For any mode, it requires the party leader to be the instance owner to destroy it.
# Enter: Instance entrance coordinates.
# Map Map Name where players start.
# X X Coordinate where players start.

View File

@ -26,6 +26,8 @@
# Name Instance Name.
# TimeLimit Total lifetime of instance in seconds. (Default: 3600)
# IdleTimeOut Time before an idle instance is destroyed in seconds. (Default: 300)
# Destroyable Toggles the ability to destroy the instance using instance 'Destroy' button. (Default: true)
# Note: the button is displayed based on parties. For any mode, it requires the party leader to be the instance owner to destroy it.
# Enter: Instance entrance coordinates.
# Map Map Name where players start.
# X X Coordinate where players start.

View File

@ -9,6 +9,8 @@
# Name Instance Name.
# TimeLimit Total lifetime of instance in seconds. (Default: 3600)
# IdleTimeOut Time before an idle instance is destroyed in seconds. (Default: 300)
# Destroyable Toggles the ability to destroy the instance using instance 'Destroy' button. (Default: true)
# Note: the button is displayed based on parties. For any mode, it requires the party leader to be the instance owner to destroy it.
# Enter: Instance entrance coordinates.
# Map Map Name where players start.
# X X Coordinate where players start.

View File

@ -680,7 +680,7 @@ struct guild {
struct guild_expulsion expulsion[MAX_GUILDEXPULSION];
struct guild_skill skill[MAX_GUILDSKILL];
struct Channel *channel;
unsigned short instance_id;
int instance_id;
time_t last_leader_change;
/* Used by char-server to save events for guilds */

View File

@ -12,6 +12,7 @@
#include "../common/showmsg.hpp"
#include "clif.hpp"
#include "instance.hpp"
#include "intif.hpp"
#include "log.hpp"
#include "pc.hpp"
@ -128,6 +129,9 @@ void clan_member_joined( struct map_session_data* sd ){
intif_clan_member_joined(clan->id);
clif_clan_onlinecount(clan);
if (clan->instance_id > 0)
instance_reqinfo(sd, clan->instance_id);
}
}

View File

@ -17968,6 +17968,22 @@ void clif_instance_changestatus(int instance_id, e_instance_notify type, unsigne
return;
}
/// Destroy an instance from the status window
/// 02cf <command>.L (CZ_MEMORIALDUNGEON_COMMAND)
void clif_parse_MemorialDungeonCommand(int fd, map_session_data *sd)
{
if (pc_istrading(sd) || pc_isdead(sd) || map_getmapdata(sd->bl.m)->instance_id > 0)
return;
const PACKET_CZ_MEMORIALDUNGEON_COMMAND *p = (PACKET_CZ_MEMORIALDUNGEON_COMMAND *)RFIFOP(fd, 0);
switch (p->command) {
case COMMAND_MEMORIALDUNGEON_DESTROY_FORCE:
instance_destroy_command(sd);
break;
}
}
/// Notifies clients about item picked up by a party member.
/// 02b8 <account id>.L <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B (ZC_ITEM_PICKUP_PARTY)
void clif_party_show_picker( struct map_session_data* sd, struct item* item_data ){
@ -19047,6 +19063,15 @@ static void clif_loadConfirm( struct map_session_data *sd ){
p.packetType = HEADER_ZC_LOAD_CONFIRM;
clif_send( &p, sizeof(p), &sd->bl, SELF );
if (sd->instance_id > 0)
instance_reqinfo(sd, sd->instance_id);
if (sd->status.party_id > 0)
party_member_joined(sd);
if (sd->status.guild_id > 0)
guild_member_joined(sd);
if (sd->status.clan_id > 0)
clan_member_joined(sd);
#endif
}

View File

@ -574,6 +574,10 @@ enum e_config_type : uint32 {
CONFIG_HOMUNCULUS_AUTOFEED
};
enum e_memorial_dungeon_command : uint16 {
COMMAND_MEMORIALDUNGEON_DESTROY_FORCE = 0x3,
};
int clif_setip(const char* ip);
void clif_setbindip(const char* ip);
void clif_setport(uint16 port);
@ -843,6 +847,7 @@ void clif_instance_create(int instance_id, int num);
void clif_instance_changewait(int instance_id, int num);
void clif_instance_status(int instance_id, unsigned int limit1, unsigned int limit2);
void clif_instance_changestatus(int instance_id, e_instance_notify type, unsigned int limit);
void clif_parse_MemorialDungeonCommand(int fd, map_session_data *sd);
// Custom Fonts
void clif_font(struct map_session_data *sd);

View File

@ -1109,7 +1109,7 @@
packet(0x02cc,4);
packet(0x02cd,26);
packet(0x02ce,10);
packet(0x02cf,6);
parseable_packet(0x02cf,6,clif_parse_MemorialDungeonCommand,2);
packet(0x02d0,-1);
packet(0x02d1,-1);
packet(0x02d2,-1);

View File

@ -673,7 +673,7 @@ int guild_recv_info(struct guild *sg) {
clif_guild_notice(sd);
sd->guild_emblem_id = g->emblem_id;
}
if (g->instance_id != 0)
if (g->instance_id > 0)
instance_reqinfo(sd, g->instance_id);
}
@ -818,7 +818,7 @@ void guild_member_joined(struct map_session_data *sd) {
g->member[i].sd = sd;
sd->guild = g;
if (g->instance_id != 0)
if (g->instance_id > 0)
instance_reqinfo(sd, g->instance_id);
if( channel_config.ally_tmpl.name[0] && (channel_config.ally_tmpl.opt&CHAN_OPT_AUTOJOIN) ) {
channel_gjoin(sd,3);
@ -870,7 +870,7 @@ int guild_member_added(int guild_id,uint32 account_id,uint32 char_id,int flag) {
//Next line commented because it do nothing, look at guild_recv_info [LuzZza]
//clif_charnameupdate(sd); //Update display name [Skotlex]
if (g->instance_id != 0)
if (g->instance_id > 0)
instance_reqinfo(sd, g->instance_id);
return 0;

View File

@ -111,7 +111,6 @@ uint64 InstanceDatabase::parseBodyNode(const YAML::Node &node) {
instance->timeout = 300;
}
/*
if (this->nodeExists(node, "Destroyable")) {
bool destroy;
@ -123,7 +122,6 @@ uint64 InstanceDatabase::parseBodyNode(const YAML::Node &node) {
if (!exists)
instance->destroyable = true;
}
*/
if (this->nodeExists(node, "Enter")) {
const YAML::Node &enterNode = node["Enter"];
@ -539,7 +537,7 @@ int instance_create(int owner_id, const char *name, e_instance_mode mode) {
return -1;
}
struct map_session_data *sd;
struct map_session_data *sd = nullptr;
struct party_data *pd;
struct guild *gd;
struct clan* cd;
@ -603,15 +601,24 @@ int instance_create(int owner_id, const char *name, e_instance_mode mode) {
break;
case IM_PARTY:
pd->instance_id = instance_id;
int32 i;
ARR_FIND(0, MAX_PARTY, i, pd->party.member[i].leader);
if (i < MAX_PARTY)
sd = map_charid2sd(pd->party.member[i].char_id);
break;
case IM_GUILD:
gd->instance_id = instance_id;
sd = map_charid2sd(gd->member[0].char_id);
break;
case IM_CLAN:
cd->instance_id = instance_id;
break;
}
if (sd != nullptr)
sd->instance_mode = mode;
instance_wait.id.push_back(instance_id);
clif_instance_create(instance_id, instance_wait.id.size());
instance_subscription_timer(0,0,0,0);
@ -742,6 +749,89 @@ int16 instance_mapid(int16 m, int instance_id)
return m;
}
/**
* Removes an instance, all its maps, and NPCs invoked by the client button.
* @param sd: Player data
*/
void instance_destroy_command(map_session_data *sd) {
nullpo_retv(sd);
std::shared_ptr<s_instance_data> idata;
int instance_id = 0;
if (sd->instance_mode == IM_CHAR && sd->instance_id > 0) {
idata = util::umap_find(instances, sd->instance_id);
if (idata == nullptr)
return;
instance_id = sd->instance_id;
} else if (sd->instance_mode == IM_PARTY && sd->status.party_id > 0) {
party_data *pd = party_search(sd->status.party_id);
if (pd == nullptr)
return;
idata = util::umap_find(instances, pd->instance_id);
if (idata == nullptr)
return;
int32 i;
ARR_FIND(0, MAX_PARTY, i, pd->data[i].sd == sd && pd->party.member[i].leader);
if (i == MAX_PARTY) // Player is not party leader
return;
instance_id = pd->instance_id;
} else if (sd->instance_mode == IM_GUILD && sd->guild != nullptr && sd->guild->instance_id > 0) {
guild *gd = guild_search(sd->status.guild_id);
if (gd == nullptr)
return;
idata = util::umap_find(instances, gd->instance_id);
if (idata == nullptr)
return;
if (strcmp(sd->status.name, gd->master) != 0) // Player is not guild master
return;
instance_id = gd->instance_id;
}
if (instance_id == 0) // Checks above failed
return;
if (!instance_db.find(idata->id)->destroyable) // Instance is flagged as non-destroyable
return;
instance_destroy(instance_id);
// Check for any other active instances and display their info
if (sd->instance_id > 0)
instance_reqinfo(sd, sd->instance_id);
if (sd->status.party_id > 0) {
party_data *pd = party_search(sd->status.party_id);
if (pd == nullptr)
return;
if (pd->instance_id > 0)
instance_reqinfo(sd, pd->instance_id);
}
if (sd->guild != nullptr && sd->guild->instance_id > 0) {
guild *gd = guild_search(sd->status.guild_id);
if (gd == nullptr)
return;
instance_reqinfo(sd, gd->instance_id);
}
}
/**
* Removes an instance, all its maps, and NPCs.
* @param instance_id: Instance to remove
@ -972,11 +1062,17 @@ bool instance_reqinfo(struct map_session_data *sd, int instance_id)
for (int i = 0; i < instance_wait.id.size(); i++) {
if (instance_wait.id[i] == instance_id) {
clif_instance_create(instance_id, i + 1);
sd->instance_mode = idata->mode;
break;
}
}
} else if(idata->state == INSTANCE_BUSY) // Give info on the instance if busy
clif_instance_status(instance_id, idata->keep_limit, idata->idle_limit);
} else if (idata->state == INSTANCE_BUSY) { // Give info on the instance if busy
int map_instance_id = map_getmapdata(sd->bl.m)->instance_id;
if (map_instance_id == 0 || map_instance_id == instance_id) {
clif_instance_status(instance_id, idata->keep_limit, idata->idle_limit);
sd->instance_mode = idata->mode;
}
}
return true;
}

View File

@ -89,7 +89,7 @@ struct s_instance_db {
std::string name; ///< Instance name
uint32 limit, ///< Duration limit
timeout; ///< Timeout limit
//bool destroyable; ///< Destroyable flag
bool destroyable; ///< Destroyable flag
struct point enter; ///< Instance entry point
std::vector<int16> maplist; ///< Maps in instance
};
@ -113,6 +113,7 @@ void instance_getsd(int instance_id, struct map_session_data *&sd, enum send_tar
int instance_create(int owner_id, const char *name, e_instance_mode mode);
bool instance_destroy(int instance_id);
void instance_destroy_command(map_session_data *sd);
e_instance_enter instance_enter(struct map_session_data *sd, int instance_id, const char *name, short x, short y);
bool instance_reqinfo(struct map_session_data *sd, int instance_id);
bool instance_addusers(int instance_id);

View File

@ -57,11 +57,12 @@ struct npc_data {
struct view_data vd;
struct status_change sc; //They can't have status changes, but.. they want the visual opt values.
struct npc_data *master_nd;
short class_,speed,instance_id;
short class_,speed;
char name[NPC_NAME_LENGTH+1];// display name
char exname[NPC_NAME_LENGTH+1];// unique npc name
int chat_id,touching_id;
unsigned int next_walktime;
int instance_id;
unsigned size : 2;

View File

@ -359,8 +359,8 @@ int party_recv_info(struct party* sp, uint32 char_id)
}
clif_party_info(p,NULL);
if( p->instance_id != 0 )
instance_reqinfo(sd,p->instance_id);
if (p->instance_id > 0)
instance_reqinfo(sd, p->instance_id);
}
// If a player was renamed, make sure to resend the party information
@ -504,8 +504,8 @@ void party_member_joined(struct map_session_data *sd)
if (i < MAX_PARTY) {
p->data[i].sd = sd;
if( p->instance_id )
instance_reqinfo(sd,p->instance_id);
if (p->instance_id > 0)
instance_reqinfo(sd, p->instance_id);
} else
sd->status.party_id = 0; //He does not belongs to the party really?
}
@ -562,8 +562,8 @@ int party_member_added(int party_id,uint32 account_id,uint32 char_id, int flag)
clif_party_xy(sd);
clif_name_area(&sd->bl); //Update char name's display [Skotlex]
if( p->instance_id )
instance_reqinfo(sd,p->instance_id);
if (p->instance_id > 0)
instance_reqinfo(sd, p->instance_id);
return 0;
}

View File

@ -26,7 +26,7 @@ struct party_data {
struct party party;
struct party_member_data data[MAX_PARTY];
uint8 itemc; //For item distribution, position of last picker in party
unsigned short instance_id;
int instance_id;
struct {
unsigned monk : 1; //There's at least one monk in party?
unsigned sg : 1; //There's at least one Star Gladiator in party?

View File

@ -1843,12 +1843,24 @@ void pc_reg_received(struct map_session_data *sd)
intif_storage_request(sd,TABLE_CART, 0, STOR_MODE_ALL); // Request cart data
intif_storage_request(sd,TABLE_INVENTORY, 0, STOR_MODE_ALL); // Request inventory data
if (sd->status.party_id)
// Restore IM_CHAR instance to the player
for (const auto &instance : instances) {
if (instance.second->mode == IM_CHAR && instance.second->owner_id == sd->status.char_id) {
sd->instance_id = instance.first;
break;
}
}
#if PACKETVER_MAIN_NUM < 20190403 || PACKETVER_RE_NUM < 20190320 || PACKETVER_ZERO_NUM < 20190410
if (sd->instance_id > 0)
instance_reqinfo(sd, sd->instance_id);
if (sd->status.party_id > 0)
party_member_joined(sd);
if (sd->status.guild_id)
if (sd->status.guild_id > 0)
guild_member_joined(sd);
if( sd->status.clan_id )
if (sd->status.clan_id > 0)
clan_member_joined(sd);
#endif
// pet
if (sd->status.pet_id > 0)
@ -1908,14 +1920,6 @@ void pc_reg_received(struct map_session_data *sd)
}
channel_autojoin(sd);
// Restore IM_CHAR instance to the player
for (const auto &instance : instances) {
if (instance.second->mode == IM_CHAR && instance.second->owner_id == sd->status.char_id) {
sd->instance_id = instance.first;
break;
}
}
}
static int pc_calc_skillpoint(struct map_session_data* sd)
@ -5904,13 +5908,15 @@ enum e_setpos pc_setpos(struct map_session_data* sd, unsigned short mapindex, in
sd->state.workinprogress = WIP_DISABLE_NONE;
if( sd->state.changemap ) { // Misc map-changing settings
unsigned short curr_map_instance_id = map_getmapdata(sd->bl.m)->instance_id, new_map_instance_id = (mapdata ? mapdata->instance_id : 0);
int curr_map_instance_id = map_getmapdata(sd->bl.m)->instance_id, new_map_instance_id = (mapdata ? mapdata->instance_id : 0);
if (curr_map_instance_id != new_map_instance_id) {
if (curr_map_instance_id) // Update instance timer for the map on leave
if (curr_map_instance_id > 0) { // Update instance timer for the map on leave
instance_delusers(curr_map_instance_id);
sd->instance_mode = util::umap_find(instances, curr_map_instance_id)->mode; // Store mode for instance destruction button checks
}
if (new_map_instance_id) // Update instance timer for the map on enter
if (new_map_instance_id > 0) // Update instance timer for the map on enter
instance_addusers(new_map_instance_id);
}

View File

@ -24,6 +24,7 @@
#include "vending.hpp" // struct s_vending
enum AtCommandType : uint8;
enum e_instance_mode : uint8;
//enum e_log_chat_type : uint8;
enum e_log_pick_type : uint32;
enum sc_type : int16;
@ -762,7 +763,9 @@ struct map_session_data {
t_tick tick;
} roulette;
unsigned short instance_id;
int instance_id;
e_instance_mode instance_mode; ///< Mode of instance player last leaves from (used for instance destruction button)
short setlook_head_top, setlook_head_mid, setlook_head_bottom, setlook_robe; ///< Stores 'setlook' script command values.
#if PACKETVER >= 20150513