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:
parent
04ba3e3d96
commit
3804d7f603
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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?
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user