/*========================================== * 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; }