/*==========================================
* Standard call when a player connection is closed.
*------------------------------------------*/
int map_quit(struct map_session_data *sd) {
int i;
if(!sd->state.active) { //Removing a player that is not active.
struct auth_node *node = chrif->search(sd->status.account_id);
if (node && node->char_id == sd->status.char_id &&
node->state != ST_LOGOUT)
//Except when logging out, clear the auth-connect data immediately.
chrif->auth_delete(node->account_id, node->char_id, node->state);
//Non-active players should not have loaded any data yet (or it was cleared already) so no additional cleanups are needed.
return 0;
}
if( sd->expiration_tid != INVALID_TIMER )
timer->delete(sd->expiration_tid,pc->expiration_timer);
if (sd->npc_timer_id != INVALID_TIMER) //Cancel the event timer.
npc->timerevent_quit(sd);
if (sd->npc_id)
npc->event_dequeue(sd);
if( sd->bg_id && !sd->bg_queue.arena ) /* TODO: dump this chunk after bg_queue is fully enabled */
bg->team_leave(sd,1);
if( sd->state.autotrade && runflag != MAPSERVER_ST_SHUTDOWN && !hChSys.closing )
pc->autotrade_update(sd,PAUC_REMOVE);
skill->cooldown_save(sd);
pc->itemcd_do(sd,false);
for( i = 0; i < sd->queues_count; i++ ) {
struct hQueue *queue;
if( (queue = script->queue(sd->queues[i])) && queue->onLogOut[0] != '\0' ) {
npc->event(sd, queue->onLogOut, 0);
}
}
/* two times, the npc event above may assign a new one or delete others */
for( i = 0; i < sd->queues_count; i++ ) {
if( sd->queues[i] != -1 )
script->queue_remove(sd->queues[i],sd->status.account_id);
}
npc->script_event(sd, NPCE_LOGOUT);
//BEGIN Added by Matias [Harmony]
harmony_logout(sd);
//END Added
//Unit_free handles clearing the player related data,
//map->quit handles extra specific data which is related to quitting normally
//(changing map-servers invokes unit_free but bypasses map->quit)
if( sd->sc.count ) {
//Status that are not saved...
for(i=0; i < SC_MAX; i++){
if ( status->get_sc_type(i)&SC_NO_SAVE ) {
if ( !sd->sc.data[i] )
continue;
switch( i ){
case SC_ENDURE:
case SC_GDSKILL_REGENERATION:
if( !sd->sc.data[i]->val4 )
break;
default:
status_change_end(&sd->bl, (sc_type)i, INVALID_TIMER);
}
}
}
}
for( i = 0; i < EQI_MAX; i++ ) {
if( sd->equip_index[ i ] >= 0 )
if( !pc->isequip( sd , sd->equip_index[ i ] ) )
pc->unequipitem( sd , sd->equip_index[ i ] , 2 );
}
// Return loot to owner
if( sd->pd ) pet->lootitem_drop(sd->pd, sd);
if( sd->state.storage_flag == 1 ) sd->state.storage_flag = 0; // No need to Double Save Storage on Quit.
if( sd->ed ) {
elemental->clean_effect(sd->ed);
unit->remove_map(&sd->ed->bl,CLR_TELEPORT,ALC_MARK);
}
if( hChSys.local && map->list[sd->bl.m].channel && idb_exists(map->list[sd->bl.m].channel->users, sd->status.char_id) ) {
clif->chsys_left(map->list[sd->bl.m].channel,sd);
}
clif->chsys_quit(sd);
unit->remove_map_pc(sd,CLR_RESPAWN);
if( map->list[sd->bl.m].instance_id >= 0 ) { // Avoid map conflicts and warnings on next login
int16 m;
struct point *pt;
if( map->list[sd->bl.m].save.map )
pt = &map->list[sd->bl.m].save;
else
pt = &sd->status.save_point;
if( (m=map->mapindex2mapid(pt->map)) >= 0 ) {
sd->bl.m = m;
sd->bl.x = pt->x;
sd->bl.y = pt->y;
sd->mapindex = pt->map;
}
}
if( sd->state.vending ) {
idb_remove(vending->db, sd->status.char_id);
}
party->booking_delete(sd); // Party Booking [Spiria]
pc->makesavestatus(sd);
pc->clean_skilltree(sd);
chrif->save(sd,1);
unit->free_pc(sd);
return 0;
}