/**
* This file is part of Hercules.
* http://herc.ws - http://github.com/HerculesWS/Hercules
*
* Copyright (C) 2012-2020 Hercules Dev Team
* Copyright (C) Athena Dev Teams
*
* Hercules is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define HERCULES_CORE
#include "config/core.h" // CELL_NOSTACK
#include "instance.h"
#include "map/channel.h"
#include "map/clif.h"
#include "map/guild.h"
#include "map/map.h"
#include "map/npc.h"
#include "map/party.h"
#include "map/pc.h"
#include "map/quest.h"
#include "common/HPM.h"
#include "common/cbasetypes.h"
#include "common/db.h"
#include "common/memmgr.h"
#include "common/nullpo.h"
#include "common/showmsg.h"
#include "common/socket.h"
#include "common/strlib.h"
#include "common/timer.h"
#include "common/utils.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
static struct instance_interface instance_s;
struct instance_interface *instance;
/// Checks whether given instance id is valid or not.
static bool instance_is_valid(int instance_id)
{
if( instance_id < 0 || instance_id >= instance->instances ) {// out of range
return false;
}
if( instance->list[instance_id].state == INSTANCE_FREE ) {// uninitialized/freed instance slot
return false;
}
return true;
}
/*--------------------------------------
* name : instance name
* Return value could be
* -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)
{
struct map_session_data *sd = NULL;
unsigned short *icptr = NULL;
struct party_data *p = NULL;
struct guild *g = NULL;
short *iptr = NULL;
int i;
nullpo_retr(-1, name);
switch ( type ) {
case IOT_NONE:
break;
case IOT_CHAR:
if( ( sd = map->id2sd(owner_id) ) == NULL ) {
ShowError("instance_create: character %d not found for instance '%s'.\n", owner_id, name);
return -2;
}
iptr = sd->instance;
icptr = &sd->instances;
break;
case IOT_PARTY:
if( ( p = party->search(owner_id) ) == NULL ) {
ShowError("instance_create: party %d not found for instance '%s'.\n", owner_id, name);
return -2;
}
iptr = p->instance;
icptr = &p->instances;
break;
case IOT_GUILD:
if( ( g = guild->search(owner_id) ) == NULL ) {
ShowError("instance_create: guild %d not found for instance '%s'.\n", owner_id, name);
return -2;
}
iptr = g->instance;
icptr = &g->instances;
break;
default:
ShowError("instance_create: unknown type %u for owner_id %d and name %s.\n", type, owner_id, name);
return -1;
}
if( type != IOT_NONE && *icptr ) {
ARR_FIND(0, *icptr, i, iptr[i] != -1 && strcmp(instance->list[iptr[i]].name, name) == 0 );
if( i != *icptr )
return -4; /* already got this instance */
}
ARR_FIND(0, instance->instances, i, instance->list[i].state == INSTANCE_FREE);
if( i == instance->instances )
RECREATE(instance->list, struct instance_data, ++instance->instances);
instance->list[i].state = INSTANCE_IDLE;
instance->list[i].id = i;
instance->list[i].idle_timer = INVALID_TIMER;
instance->list[i].idle_timeout = instance->list[i].idle_timeoutval = 0;
instance->list[i].progress_timer = INVALID_TIMER;
instance->list[i].progress_timeout = 0;
instance->list[i].users = 0;
instance->list[i].map = NULL;
instance->list[i].num_map = 0;
instance->list[i].owner_id = owner_id;
instance->list[i].owner_type = type;
instance->list[i].regs.vars = i64db_alloc(DB_OPT_RELEASE_DATA);
instance->list[i].regs.arrays = NULL;
instance->list[i].respawn.map = 0;
instance->list[i].respawn.y = 0;
instance->list[i].respawn.x = 0;
safestrncpy( instance->list[i].name, name, sizeof(instance->list[i].name) );
if( type != IOT_NONE ) {
int j;
ARR_FIND(0, *icptr, j, iptr[j] == -1);
if (j == *icptr) {
switch( type ) {
case IOT_CHAR:
RECREATE(sd->instance, short, ++*icptr);
sd->instance[sd->instances-1] = i;
break;
case IOT_PARTY:
RECREATE(p->instance, short, ++*icptr);
p->instance[p->instances-1] = i;
break;
case IOT_GUILD:
RECREATE(g->instance, short, ++*icptr);
g->instance[g->instances-1] = i;
break;
}
} else {
iptr[j] = i;
}
}
clif->instance(i, 1, 0); // Start instancing window
return i;
}
/**
* Add a map to the instance using src map "name"
*
* @param name Source map name.
* @param instance_id The destination instance ID.
* @param usebasename Whether to generate a standard instance map name (only used if map_name is not NULL).
* @param map_name The name for the instanced map (may be NULL to generate a new one).
* @return The generated map's index.
* @retval -1 Map or instance not found.
* @retval -2 Duplicate map name.
* @retval -3 No more map indices available.
* @retval -4 Source map is already an instance.
**/
static int instance_add_map(const char *name, int instance_id, bool usebasename, const char *map_name)
{
int16 m = map->mapname2mapid(name);
int i, im = -1;
size_t num_cell, size, j;
nullpo_retr(-1, name);
if( m < 0 )
return -1; // source map not found
if( !instance->valid(instance_id) ) {
ShowError("instance_add_map: trying to attach '%s' map to non-existing instance %d.\n", name, instance_id);
return -1;
}
if( map_name != NULL && strdb_iget(mapindex->db, map_name) ) {
ShowError("instance_add_map: trying to create instanced map with existent name '%s'\n", map_name);
return -2;
}
if( map->list[m].instance_id >= 0 ) {
// Source map already belong to a Instance.
ShowError("instance_add_map: trying to instance already instanced map %s.\n", name);
return -4;
}
ARR_FIND( instance->start_id, map->count, i, map->list[i].name[0] == 0 ); // Searching for a Free Map
if( i < map->count )
im = i; // Unused map found (old instance)
else {
im = map->count; // Using next map index
RECREATE(map->list,struct map_data,++map->count);
}
if( map->list[m].cell == (struct mapcell *)0xdeadbeaf )
map->cellfromcache(&map->list[m]);
memcpy( &map->list[im], &map->list[m], sizeof(struct map_data) ); // Copy source map
if( map_name != NULL ) {
snprintf(map->list[im].name, MAP_NAME_LENGTH, "%s", map_name);
map->list[im].custom_name = true;
} else
snprintf(map->list[im].name, MAP_NAME_LENGTH, (usebasename ? "%.3d#%s" : "%.3d%s"), instance_id, name); // Generate Name for Instance Map
map->list[im].index = mapindex->addmap(-1, map->list[im].name); // Add map index
map->list[im].channel = NULL;
if( !map->list[im].index ) {
map->list[im].name[0] = '\0';
ShowError("instance_add_map: no more free map indexes.\n");
return -3; // No free map index
}
// Reallocate cells
num_cell = map->list[im].xs * map->list[im].ys;
CREATE( map->list[im].cell, struct mapcell, num_cell );
memcpy( map->list[im].cell, map->list[m].cell, num_cell * sizeof(struct mapcell) );
// Appropriately clear cell data
for(j = 0; j < num_cell; j++) {
#ifdef CELL_NOSTACK
map->list[im].cell[j].cell_bl = 0;
#endif // CELL_NOSTACK
map->list[im].cell[j].basilica = 0;
map->list[im].cell[j].icewall = 0;
map->list[im].cell[j].npc = 0;
map->list[im].cell[j].landprotector = 0;
}
size = map->list[im].bxs * map->list[im].bys * sizeof(struct block_list*);
map->list[im].block = (struct block_list**)aCalloc(size, 1);
map->list[im].block_mob = (struct block_list**)aCalloc(size, 1);
memset(map->list[im].npc, 0x00, sizeof(map->list[i].npc));
map->list[im].npc_num = 0;
memset(map->list[im].moblist, 0x00, sizeof(map->list[im].moblist));
map->list[im].mob_delete_timer = INVALID_TIMER;
//Mimic unit
if( map->list[m].unit_count ) {
map->list[im].unit_count = map->list[m].unit_count;
CREATE( map->list[im].units, struct mapflag_skill_adjust*, map->list[im].unit_count );
for(i = 0; i < map->list[im].unit_count; i++) {
CREATE( map->list[im].units[i], struct mapflag_skill_adjust, 1);
memcpy( map->list[im].units[i],map->list[m].units[i],sizeof(struct mapflag_skill_adjust));
}
}
//Mimic skills
if( map->list[m].skill_count ) {
map->list[im].skill_count = map->list[m].skill_count;
CREATE( map->list[im].skills, struct mapflag_skill_adjust*, map->list[im].skill_count );
for(i = 0; i < map->list[im].skill_count; i++) {
CREATE( map->list[im].skills[i], struct mapflag_skill_adjust, 1);
memcpy( map->list[im].skills[i],map->list[m].skills[i],sizeof(struct mapflag_skill_adjust));
}
}
//Mimic zone mf
if( map->list[m].zone_mf_count ) {
map->list[im].zone_mf_count = map->list[m].zone_mf_count;
CREATE( map->list[im].zone_mf, char *, map->list[im].zone_mf_count );
for(i = 0; i < map->list[im].zone_mf_count; i++) {
CREATE(map->list[im].zone_mf[i], char, MAP_ZONE_MAPFLAG_LENGTH);
safestrncpy(map->list[im].zone_mf[i],map->list[m].zone_mf[i],MAP_ZONE_MAPFLAG_LENGTH);
}
}
map->list[im].m = im;
map->list[im].instance_id = instance_id;
map->list[im].instance_src_map = m;
map->list[im].flag.src4instance = 0; //clear
map->list[m].flag.src4instance = 1; // Flag this map as a src map for instances
RECREATE(instance->list[instance_id].map, unsigned short, ++instance->list[instance_id].num_map);
instance->list[instance_id].map[instance->list[instance_id].num_map - 1] = im; // Attach to actual instance
map->addmap2db(&map->list[im]);
return im;
}
/*--------------------------------------
* m : source map of this instance
* party_id : source party of this instance
* type : result (0 = map id | 1 = instance id)
*--------------------------------------*/
static int instance_map2imap(int16 m, int instance_id)
{
int i;
if( !instance->valid(instance_id) ) {
return -1;
}
for( i = 0; i < instance->list[instance_id].num_map; i++ ) {
if( instance->list[instance_id].map[i] && map->list[instance->list[instance_id].map[i]].instance_src_map == m )
return instance->list[instance_id].map[i];
}
return -1;
}
static int instance_mapname2imap(const char *map_name, int instance_id)
{
int i;
nullpo_retr(-1, map_name);
if( !instance->valid(instance_id) ) {
return -1;
}
for( i = 0; i < instance->list[instance_id].num_map; i++ ) {
if( instance->list[instance_id].map[i] && !strcmpi(map->list[map->list[instance->list[instance_id].map[i]].instance_src_map].name,map_name) )
return instance->list[instance_id].map[i];
}
return -1;
}
/*--------------------------------------
* m : source map
* instance_id : where to search
* result : mapid of map "m" in this instance
*--------------------------------------*/
static int instance_mapid2imapid(int16 m, int instance_id)
{
Assert_retr(-1, m >= 0 && m < map->count);
if( map->list[m].flag.src4instance == 0 )
return m; // not instances found for this map
else if( map->list[m].instance_id >= 0 ) { // This map is a instance, not a src map instance
ShowError("map_instance_mapid2imapid: already instanced (%d / %d)\n", m, instance_id);
return -1;
}
if( !instance->valid(instance_id) )
return -1;
return instance->map2imap(m, instance_id);
}
/*--------------------------------------
* Used on Init instance. Duplicates each script on source map
*--------------------------------------*/
static int instance_map_npcsub(struct block_list *bl, va_list args)
{
struct npc_data *nd = NULL;
int16 m = va_arg(args, int); // Destination Map
nullpo_ret(bl);
Assert_ret(bl->type == BL_NPC);
nd = BL_UCAST(BL_NPC, bl);
if (npc->duplicate4instance(nd, m))
ShowDebug("instance_map_npcsub:npc_duplicate4instance failed (%s/%d)\n",nd->name,m);
return 1;
}
static int instance_init_npc(struct block_list *bl, va_list args)
{
struct npc_data *nd = NULL;
struct event_data *ev;
char evname[EVENT_NAME_LENGTH];
nullpo_ret(bl);
Assert_ret(bl->type == BL_NPC);
nd = BL_UCAST(BL_NPC, bl);
snprintf(evname, EVENT_NAME_LENGTH, "%s::OnInstanceInit", nd->exname);
if( ( ev = strdb_get(npc->ev_db, evname) ) )
script->run_npc(ev->nd->u.scr.script, ev->pos, 0, ev->nd->bl.id);
return 1;
}
/*--------------------------------------
* Init all map on the instance. Npcs are created here
*--------------------------------------*/
static void instance_init(int instance_id)
{
int i;
if( !instance->valid(instance_id) )
return; // nothing to do
for( i = 0; i < instance->list[instance_id].num_map; i++ )
map->foreachinmap(instance->map_npcsub, map->list[instance->list[instance_id].map[i]].instance_src_map, BL_NPC, instance->list[instance_id].map[i]);
/* cant be together with the previous because it will rely on all of them being up */
map->foreachininstance(instance->init_npc, instance_id, BL_NPC);
instance->list[instance_id].state = INSTANCE_BUSY;
}
/*--------------------------------------
* Used on instance deleting process.
* Warps all players on each instance map to its save points.
*--------------------------------------*/
static int instance_del_load(struct map_session_data *sd, va_list args)
{
int16 m = va_arg(args,int);
if( !sd || sd->bl.m != m )
return 0;
pc->setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, CLR_OUTSIGHT);
return 1;
}
/* for npcs behave differently when being unloaded within a instance */
static int instance_cleanup_sub(struct block_list *bl, va_list ap)
{
nullpo_ret(bl);
switch(bl->type) {
case BL_PC:
map->quit(BL_UCAST(BL_PC, bl));
break;
case BL_NPC:
npc->unload(BL_UCAST(BL_NPC, bl), true, true);
break;
case BL_MOB:
unit->free(bl,CLR_OUTSIGHT);
break;
case BL_PET:
//There is no need for this, the pet is removed together with the player. [Skotlex]
break;
case BL_ITEM:
map->clearflooritem(bl);
break;
case BL_SKILL:
skill->delunit(BL_UCAST(BL_SKILL, bl));
break;
}
return 1;
}
/*--------------------------------------
* Removes a simple instance map
*--------------------------------------*/
static void instance_del_map(int16 m)
{
int i;
if( m <= 0 || map->list[m].instance_id == -1 ) {
ShowError("instance_del_map: tried to remove non-existing instance map (%d)\n", m);
return;
}
map->foreachpc(instance_del_load, m);
map->foreachinmap(instance_cleanup_sub, m, BL_ALL);
if( map->list[m].mob_delete_timer != INVALID_TIMER )
timer->delete(map->list[m].mob_delete_timer, map->removemobs_timer);
mapindex->removemap(map_id2index(m));
// Free memory
aFree(map->list[m].cell);
aFree(map->list[m].block);
aFree(map->list[m].block_mob);
if (map->list[m].unit_count && map->list[m].units) {
for(i = 0; i < map->list[m].unit_count; i++) {
aFree(map->list[m].units[i]);
}
aFree(map->list[m].units);
}
if (map->list[m].skill_count && map->list[m].skills) {
for(i = 0; i < map->list[m].skill_count; i++) {
aFree(map->list[m].skills[i]);
}
aFree(map->list[m].skills);
}
if (map->list[m].zone_mf_count && map->list[m].zone_mf) {
for(i = 0; i < map->list[m].zone_mf_count; i++) {
aFree(map->list[m].zone_mf[i]);
}
aFree(map->list[m].zone_mf);
}
VECTOR_CLEAR(map->list[m].qi_list);
// Remove from instance
for( i = 0; i < instance->list[map->list[m].instance_id].num_map; i++ ) {
if( instance->list[map->list[m].instance_id].map[i] == m ) {
instance->list[map->list[m].instance_id].num_map--;
for( ; i < instance->list[map->list[m].instance_id].num_map; i++ )
instance->list[map->list[m].instance_id].map[i] = instance->list[map->list[m].instance_id].map[i+1];
i = -1;
break;
}
}
if( i == instance->list[map->list[m].instance_id].num_map )
ShowError("map_instance_del: failed to remove %s from instance list (%s): %d\n", map->list[m].name, instance->list[map->list[m].instance_id].name, m);
if( map->list[m].channel )
channel->delete(map->list[m].channel);
map->removemapdb(&map->list[m]);
memset(&map->list[m], 0x00, sizeof(map->list[0]));
map->list[m].name[0] = 0;
map->list[m].instance_id = -1;
map->list[m].mob_delete_timer = INVALID_TIMER;
}
/*--------------------------------------
* Timer to destroy instance by process or idle
*--------------------------------------*/
static int instance_destroy_timer(int tid, int64 tick, int id, intptr_t data)
{
instance->destroy(id);
return 0;
}
/*--------------------------------------
* Removes a instance, all its maps and npcs.
*--------------------------------------*/
static void instance_destroy(int instance_id)
{
struct map_session_data *sd = NULL;
unsigned short *icptr = NULL;
struct party_data *p = NULL;
struct guild *g = NULL;
short *iptr = NULL;
int type;
unsigned int now = (unsigned int)time(NULL);
if( !instance->valid(instance_id) )
return; // nothing to do
if( instance->list[instance_id].progress_timeout && instance->list[instance_id].progress_timeout <= now )
type = 1;
else if( instance->list[instance_id].idle_timeout && instance->list[instance_id].idle_timeout <= now )
type = 2;
else
type = 3;
clif->instance(instance_id, 5, type); // Report users this instance has been destroyed
switch ( instance->list[instance_id].owner_type ) {
case IOT_NONE:
break;
case IOT_CHAR:
if( ( sd = map->id2sd(instance->list[instance_id].owner_id) ) == NULL ) {
break;
}
iptr = sd->instance;
icptr = &sd->instances;
break;
case IOT_PARTY:
if( ( p = party->search(instance->list[instance_id].owner_id) ) == NULL ) {
break;
}
iptr = p->instance;
icptr = &p->instances;
break;
case IOT_GUILD:
if( ( g = guild->search(instance->list[instance_id].owner_id) ) == NULL ) {
break;
}
iptr = g->instance;
icptr = &g->instances;
break;
default:
ShowError("instance_destroy: unknown type %u for owner_id %d and name '%s'.\n", instance->list[instance_id].owner_type, instance->list[instance_id].owner_id, instance->list[instance_id].name);
break;
}
if( iptr != NULL ) {
int i;
ARR_FIND(0, *icptr, i, iptr[i] == instance_id);
if (i != *icptr)
iptr[i] = -1;
}
if (instance->list[instance_id].map) {
int last = 0;
while (instance->list[instance_id].num_map && last != instance->list[instance_id].map[0]) {
// Remove all maps from instance
last = instance->list[instance_id].map[0];
instance->del_map( instance->list[instance_id].map[0] );
}
}
if( instance->list[instance_id].regs.vars )
db_destroy(instance->list[instance_id].regs.vars);
if( instance->list[instance_id].regs.arrays )
instance->list[instance_id].regs.arrays->destroy(instance->list[instance_id].regs.arrays, script->array_free_db);
if( instance->list[instance_id].progress_timer != INVALID_TIMER )
timer->delete( instance->list[instance_id].progress_timer, instance->destroy_timer);
if( instance->list[instance_id].idle_timer != INVALID_TIMER )
timer->delete( instance->list[instance_id].idle_timer, instance->destroy_timer);
instance->list[instance_id].regs.vars = NULL;
if( instance->list[instance_id].map )
aFree(instance->list[instance_id].map);
instance->list[instance_id].map = NULL;
instance->list[instance_id].state = INSTANCE_FREE;
instance->list[instance_id].num_map = 0;
HPM->data_store_destroy(&instance->list[instance_id].hdata);
}
/*--------------------------------------
* Checks if there are users in the instance or not to start idle timer
*--------------------------------------*/
static void instance_check_idle(int instance_id)
{
bool idle = true;
unsigned int now = (unsigned int)time(NULL);
if( !instance->valid(instance_id) || instance->list[instance_id].idle_timeoutval == 0 )
return;
if( instance->list[instance_id].users )
idle = false;
if( instance->list[instance_id].idle_timer != INVALID_TIMER && !idle ) {
timer->delete(instance->list[instance_id].idle_timer, instance->destroy_timer);
instance->list[instance_id].idle_timer = INVALID_TIMER;
instance->list[instance_id].idle_timeout = 0;
clif->instance(instance_id, 3, 0); // Notify instance users normal instance expiration
} else if( instance->list[instance_id].idle_timer == INVALID_TIMER && idle ) {
instance->list[instance_id].idle_timeout = now + instance->list[instance_id].idle_timeoutval;
instance->list[instance_id].idle_timer = timer->add( timer->gettick() + instance->list[instance_id].idle_timeoutval * 1000, instance->destroy_timer, instance_id, 0);
clif->instance(instance_id, 4, 0); // Notify instance users it will be destroyed of no user join it again in "X" time
}
}
/*--------------------------------------
* Set instance Timers
*--------------------------------------*/
static void instance_set_timeout(int instance_id, unsigned int progress_timeout, unsigned int idle_timeout)
{
unsigned int now = (unsigned int)time(0);
if( !instance->valid(instance_id) )
return;
if( instance->list[instance_id].progress_timer != INVALID_TIMER )
timer->delete( instance->list[instance_id].progress_timer, instance->destroy_timer);
if( instance->list[instance_id].idle_timer != INVALID_TIMER )
timer->delete( instance->list[instance_id].idle_timer, instance->destroy_timer);
if( progress_timeout ) {
instance->list[instance_id].progress_timeout = now + progress_timeout;
instance->list[instance_id].progress_timer = timer->add( timer->gettick() + progress_timeout * 1000, instance->destroy_timer, instance_id, 0);
instance->list[instance_id].original_progress_timeout = progress_timeout;
} else {
instance->list[instance_id].progress_timeout = 0;
instance->list[instance_id].progress_timer = INVALID_TIMER;
instance->list[instance_id].original_progress_timeout = 0;
}
if( idle_timeout ) {
instance->list[instance_id].idle_timeoutval = idle_timeout;
instance->list[instance_id].idle_timer = INVALID_TIMER;
instance->check_idle(instance_id);
} else {
instance->list[instance_id].idle_timeoutval = 0;
instance->list[instance_id].idle_timeout = 0;
instance->list[instance_id].idle_timer = INVALID_TIMER;
}
if( instance->list[instance_id].idle_timer == INVALID_TIMER && instance->list[instance_id].progress_timer != INVALID_TIMER )
clif->instance(instance_id, 3, 0);
}
/*--------------------------------------
* Checks if sd in on a instance and should be kicked from it
*--------------------------------------*/
static void instance_check_kick(struct map_session_data *sd)
{
int16 m = sd->bl.m;
nullpo_retv(sd);
clif->instance_leave(sd->fd);
if( map->list[m].instance_id >= 0 ) { // User was on the instance map
if( map->list[m].save.map )
pc->setpos(sd, map->list[m].save.map, map->list[m].save.x, map->list[m].save.y, CLR_TELEPORT);
else
pc->setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, CLR_TELEPORT);
}
}
/**
* Look up existing memorial dungeon of the player and destroy it
*
* @param sd session data.
*
*/
static void instance_force_destroy(struct map_session_data *sd)
{
nullpo_retv(sd);
for (int i = 0; i < instance->instances; ++i) {
switch (instance->list[i].owner_type) {
case IOT_CHAR:
{
if (instance->list[i].owner_id != sd->status.account_id)
continue;
break;
}
case IOT_PARTY:
{
int party_id = sd->status.party_id;
if (instance->list[i].owner_id != party_id)
continue;
int j = 0;
struct party_data *pt = party->search(party_id);
nullpo_retv(pt);
ARR_FIND(0, MAX_PARTY, j, pt->party.member[j].leader);
if (j == MAX_PARTY) {
ShowWarning("clif_parse_memorial_dungeon_command: trying to destroy a party instance, while the party has no leader.");
return;
}
if (pt->party.member[j].char_id != sd->status.char_id) {
ShowWarning("clif_parse_memorial_dungeon_command: trying to destroy a party instance, from a non party-leader player.");
return;
}
break;
}
case IOT_GUILD:
{
int guild_id = sd->status.guild_id;
if (instance->list[i].owner_id != guild_id)
continue;
struct guild *g = guild->search(guild_id);
nullpo_retv(g);
if (g->member[0].char_id != sd->status.char_id) {
ShowWarning("clif_parse_memorial_dungeon_command: trying to destroy a guild instance, from a non guild-master player.");
return;
}
break;
}
default:
continue;
}
instance->destroy(instance->list[i].id);
return;
}
}
static void do_reload_instance(void)
{
struct s_mapiterator *iter;
struct map_session_data *sd;
int i, k;
for(i = 0; i < instance->instances; i++) {
for(k = 0; k < instance->list[i].num_map; k++) {
if( !map->list[map->list[instance->list[i].map[k]].instance_src_map].flag.src4instance )
break;
}
if( k != instance->list[i].num_map ) /* any (or all) of them were disabled, we destroy */
instance->destroy(i);
else {
/* populate the instance again */
instance->start(i);
/* restart timers */
instance->set_timeout(i,instance->list[i].original_progress_timeout,instance->list[i].idle_timeoutval);
}
}
iter = mapit_getallusers();
for (sd = BL_UCAST(BL_PC, mapit->first(iter)); mapit->exists(iter); sd = BL_UCAST(BL_PC, mapit->next(iter))) {
if(sd && map->list[sd->bl.m].instance_id >= 0) {
pc->setpos(sd,instance->list[map->list[sd->bl.m].instance_id].respawn.map,instance->list[map->list[sd->bl.m].instance_id].respawn.x,instance->list[map->list[sd->bl.m].instance_id].respawn.y,CLR_TELEPORT);
}
}
mapit->free(iter);
}
static void do_final_instance(void)
{
int i;
for(i = 0; i < instance->instances; i++) {
instance->destroy(i);
}
if( instance->list )
aFree(instance->list);
instance->list = NULL;
instance->instances = 0;
}
static void do_init_instance(bool minimal)
{
if (minimal)
return;
timer->add_func_list(instance->destroy_timer, "instance_destroy_timer");
}
void instance_defaults(void)
{
instance = &instance_s;
instance->init = do_init_instance;
instance->final = do_final_instance;
instance->reload = do_reload_instance;
/* start point */
instance->start_id = 0;
/* count */
instance->instances = 0;
/* */
instance->list = NULL;
/* */
instance->create = instance_create;
instance->add_map = instance_add_map;
instance->del_map = instance_del_map;
instance->map2imap = instance_map2imap;
instance->mapid2imapid = instance_mapid2imapid;
instance->mapname2imap = instance_mapname2imap;
instance->map_npcsub = instance_map_npcsub;
instance->init_npc = instance_init_npc;
instance->destroy = instance_destroy;
instance->start = instance_init;
instance->check_idle = instance_check_idle;
instance->check_kick = instance_check_kick;
instance->set_timeout = instance_set_timeout;
instance->valid = instance_is_valid;
instance->destroy_timer = instance_destroy_timer;
instance->force_destroy = instance_force_destroy;
}