diff options
Diffstat (limited to 'src/map/pc.c')
-rw-r--r-- | src/map/pc.c | 4871 |
1 files changed, 3025 insertions, 1846 deletions
diff --git a/src/map/pc.c b/src/map/pc.c index 57b2fe19a..5faadf76a 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * 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 @@ -29,6 +29,7 @@ #include "map/channel.h" #include "map/chat.h" #include "map/chrif.h" +#include "map/clan.h" #include "map/clif.h" #include "map/date.h" // is_day_of_*() #include "map/duel.h" @@ -42,6 +43,7 @@ #include "map/mail.h" #include "map/map.h" #include "map/mercenary.h" +#include "map/messages.h" #include "map/mob.h" // struct mob_data #include "map/npc.h" // fake_nd #include "map/party.h" // party-"search() @@ -53,6 +55,7 @@ #include "map/skill.h" #include "map/status.h" // struct status_data #include "map/storage.h" +#include "map/achievement.h" #include "common/cbasetypes.h" #include "common/conf.h" #include "common/core.h" // get_svn_revision() @@ -74,16 +77,21 @@ #include <string.h> #include <time.h> -struct pc_interface pc_s; +static struct pc_interface pc_s; struct pc_interface *pc; +static struct class_exp_tables exptables; + //Converts a class to its array index for CLASS_COUNT defined arrays. //Note that it does not do a validity check for speed purposes, where parsing //player input make sure to use a pc->db_checkid first! -int pc_class2idx(int class_) { - if (class_ >= JOB_NOVICE_HIGH) - return class_- JOB_NOVICE_HIGH+JOB_MAX_BASIC; - return class_; +static int pc_class2idx(int class) +{ + if (class >= JOB_NOVICE_HIGH) { + class += - JOB_NOVICE_HIGH + JOB_MAX_BASIC; + } + Assert_ret(class >= 0 && class < CLASS_COUNT); + return class; } /** @@ -92,7 +100,7 @@ int pc_class2idx(int class_) { * required to provide a session. * Caller must release dummy on its own when it's no longer needed. */ -struct map_session_data* pc_get_dummy_sd(void) +static struct map_session_data *pc_get_dummy_sd(void) { struct map_session_data *dummy_sd; CREATE(dummy_sd, struct map_session_data, 1); @@ -106,9 +114,10 @@ struct map_session_data* pc_get_dummy_sd(void) * @param group_id Group ID * @return 1 on error, 0 on success */ -int pc_set_group(struct map_session_data *sd, int group_id) +static int pc_set_group(struct map_session_data *sd, int group_id) { GroupSettings *group = pcg->id2group(group_id); + nullpo_retr(1, sd); if (group == NULL) return 1; sd->group_id = group_id; @@ -119,12 +128,13 @@ int pc_set_group(struct map_session_data *sd, int group_id) /** * Checks if commands used by player should be logged. */ -bool pc_should_log_commands(struct map_session_data *sd) +static bool pc_should_log_commands(struct map_session_data *sd) { + nullpo_retr(true, sd); return pcg->should_log_commands(sd->group); } -int pc_invincible_timer(int tid, int64 tick, int id, intptr_t data) +static int pc_invincible_timer(int tid, int64 tick, int id, intptr_t data) { struct map_session_data *sd = map->id2sd(id); @@ -141,7 +151,8 @@ int pc_invincible_timer(int tid, int64 tick, int id, intptr_t data) return 0; } -void pc_setinvincibletimer(struct map_session_data* sd, int val) { +static void pc_setinvincibletimer(struct map_session_data *sd, int val) +{ nullpo_retv(sd); val += map->list[sd->bl.m].invincible_time_inc; @@ -151,7 +162,7 @@ void pc_setinvincibletimer(struct map_session_data* sd, int val) { sd->invincible_timer = timer->add(timer->gettick()+val,pc->invincible_timer,sd->bl.id,0); } -void pc_delinvincibletimer(struct map_session_data* sd) +static void pc_delinvincibletimer(struct map_session_data *sd) { nullpo_retv(sd); @@ -163,7 +174,8 @@ void pc_delinvincibletimer(struct map_session_data* sd) } } -int pc_spiritball_timer(int tid, int64 tick, int id, intptr_t data) { +static int pc_spiritball_timer(int tid, int64 tick, int id, intptr_t data) +{ struct map_session_data *sd = map->id2sd(id); int i; @@ -195,12 +207,13 @@ int pc_spiritball_timer(int tid, int64 tick, int id, intptr_t data) { } /** -* Get the possible number of spiritball that a player can call. -* @param sd the affected player structure -* @param min the minimum number of spiritball regardless the level of MO_CALLSPIRITS -* @retval total number of spiritball -**/ -int pc_getmaxspiritball(struct map_session_data *sd, int min) { + * Get the possible number of spiritball that a player can call. + * @param sd the affected player structure + * @param min the minimum number of spiritball regardless the level of MO_CALLSPIRITS + * @retval total number of spiritball + */ +static int pc_getmaxspiritball(struct map_session_data *sd, int min) +{ int result; nullpo_ret(sd); @@ -216,7 +229,7 @@ int pc_getmaxspiritball(struct map_session_data *sd, int min) { return result; } -int pc_addspiritball(struct map_session_data *sd,int interval,int max) +static int pc_addspiritball(struct map_session_data *sd, int interval, int max) { int tid, i; @@ -242,15 +255,22 @@ int pc_addspiritball(struct map_session_data *sd,int interval,int max) memmove(sd->spirit_timer+i+1, sd->spirit_timer+i, (sd->spiritball-i)*sizeof(int)); sd->spirit_timer[i] = tid; sd->spiritball++; - if( (sd->class_&MAPID_THIRDMASK) == MAPID_ROYAL_GUARD ) + pc->addspiritball_sub(sd); + + return 0; +} + +static int pc_addspiritball_sub(struct map_session_data *sd) +{ + nullpo_ret(sd); + if ((sd->job & MAPID_THIRDMASK) == MAPID_ROYAL_GUARD) clif->millenniumshield(&sd->bl,sd->spiritball); else clif->spiritball(&sd->bl); - return 0; } -int pc_delspiritball(struct map_session_data *sd,int count,int type) +static int pc_delspiritball(struct map_session_data *sd, int count, int type) { int i; @@ -280,15 +300,23 @@ int pc_delspiritball(struct map_session_data *sd,int count,int type) sd->spirit_timer[i] = INVALID_TIMER; } - if(!type) { - if( (sd->class_&MAPID_THIRDMASK) == MAPID_ROYAL_GUARD ) - clif->millenniumshield(&sd->bl,sd->spiritball); - else - clif->spiritball(&sd->bl); + if (!type) { + pc->delspiritball_sub(sd); } return 0; } -int pc_check_banding(struct block_list *bl, va_list ap) + +static int pc_delspiritball_sub(struct map_session_data *sd) +{ + nullpo_ret(sd); + if ((sd->job & MAPID_THIRDMASK) == MAPID_ROYAL_GUARD) + clif->millenniumshield(&sd->bl,sd->spiritball); + else + clif->spiritball(&sd->bl); + return 0; +} + +static int pc_check_banding(struct block_list *bl, va_list ap) { int *c, *b_sd; struct block_list *src; @@ -316,7 +344,8 @@ int pc_check_banding(struct block_list *bl, va_list ap) return 0; } -int pc_banding(struct map_session_data *sd, uint16 skill_lv) { +static int pc_banding(struct map_session_data *sd, uint16 skill_lv) +{ int c; int b_sd[MAX_PARTY]; // In case of a full Royal Guard party. int i, j, hp, extra_hp = 0, tmp_qty = 0; @@ -370,7 +399,7 @@ int pc_banding(struct map_session_data *sd, uint16 skill_lv) { for( j = 0; j < i; j++ ) { bsd = map->id2sd(b_sd[j]); if( bsd != NULL ) { - status->set_hp(&bsd->bl,hp,0); // Set hp + status->set_hp(&bsd->bl, hp, STATUS_HEAL_DEFAULT); // Set hp if( (sc = status->get_sc(&bsd->bl)) != NULL && sc->data[SC_BANDING] ) { sc->data[SC_BANDING]->val2 = c; // Set the counter. It doesn't count your self. status_calc_bl(&bsd->bl, status->sc2scb_flag(SC_BANDING)); // Set atk and def. @@ -381,53 +410,111 @@ int pc_banding(struct map_session_data *sd, uint16 skill_lv) { return c; } -// Increases a player's fame points and displays a notice to him -void pc_addfame(struct map_session_data *sd,int count) +/** + * Increases a player's fame points and displays a notice to them. + * + * If the character's job class doesn't allow the specified rank type, nothing + * happens and the request is ignored. + * + * @param sd The target character. + * @param type The fame list type (@see enum fame_list_type). + * @param count The amount of points to add. + */ +static void pc_addfame(struct map_session_data *sd, int ranktype, int count) { - int ranktype = -1; nullpo_retv(sd); + + switch (ranktype) { + case RANKTYPE_BLACKSMITH: + if ((sd->job & MAPID_UPPERMASK) != MAPID_BLACKSMITH) + return; + break; + case RANKTYPE_ALCHEMIST: + if ((sd->job & MAPID_UPPERMASK) != MAPID_ALCHEMIST) + return; + break; + case RANKTYPE_TAEKWON: + if ((sd->job & MAPID_UPPERMASK) != MAPID_TAEKWON) + return; + break; + case RANKTYPE_PK: + // Not supported + FALLTHROUGH + default: + Assert_retv(0); + } + sd->status.fame += count; - if(sd->status.fame > MAX_FAME) + if (sd->status.fame > MAX_FAME) sd->status.fame = MAX_FAME; - switch(sd->class_&MAPID_UPPERMASK){ - case MAPID_BLACKSMITH: ranktype = RANKTYPE_BLACKSMITH; break; - case MAPID_ALCHEMIST: ranktype = RANKTYPE_ALCHEMIST; break; - case MAPID_TAEKWON: ranktype = RANKTYPE_TAEKWON; break; - } + clif->update_rankingpoint(sd, ranktype, count); chrif->updatefamelist(sd); } -// Check whether a player ID is in the fame rankers' list of its job, returns his/her position if so, 0 else -unsigned char pc_famerank(int char_id, int job) +/** + * Returns a character's rank in the specified fame list. + * + * @param char_id The character ID. + * @param ranktype The rank list type (@see enum fame_list_type). + * @return The rank position (1-based index) + * @retval 0 if the character isn't in the specified list. + */ +static int pc_fame_rank(int char_id, int ranktype) { int i; - switch(job){ - case MAPID_BLACKSMITH: // Blacksmith - for(i = 0; i < MAX_FAME_LIST; i++){ - if(pc->smith_fame_list[i].id == char_id) - return i + 1; - } - break; - case MAPID_ALCHEMIST: // Alchemist - for(i = 0; i < MAX_FAME_LIST; i++){ - if(pc->chemist_fame_list[i].id == char_id) - return i + 1; - } - break; - case MAPID_TAEKWON: // Taekwon - for(i = 0; i < MAX_FAME_LIST; i++){ - if(pc->taekwon_fame_list[i].id == char_id) - return i + 1; - } - break; + switch (ranktype) { + case RANKTYPE_BLACKSMITH: + for (i = 0; i < MAX_FAME_LIST; i++) { + if (pc->smith_fame_list[i].id == char_id) + return i + 1; + } + break; + case RANKTYPE_ALCHEMIST: + for (i = 0; i < MAX_FAME_LIST; i++) { + if (pc->chemist_fame_list[i].id == char_id) + return i + 1; + } + break; + case RANKTYPE_TAEKWON: + for (i = 0; i < MAX_FAME_LIST; i++) { + if (pc->taekwon_fame_list[i].id == char_id) + return i + 1; + } + break; + case RANKTYPE_PK: // Not implemented + FALLTHROUGH + default: + Assert_ret(0); } return 0; } -int pc_setrestartvalue(struct map_session_data *sd,int type) { +/** + * Returns the appropriate fame list type for the given job. + * + * @param job_mapid The job (in MapID format) + * @return the appropriate fame list type (@see enum fame_list_type). + * @retval RANKTYPE_UNKNOWN if no appropriate type exists. + */ +static int pc_famelist_type(uint16 job_mapid) +{ + switch (job_mapid & MAPID_UPPERMASK) { + case MAPID_BLACKSMITH: + return RANKTYPE_BLACKSMITH; + case MAPID_ALCHEMIST: + return RANKTYPE_ALCHEMIST; + case MAPID_TAEKWON: + return RANKTYPE_TAEKWON; + default: + return RANKTYPE_UNKNOWN; + } +} + +static int pc_setrestartvalue(struct map_session_data *sd, int type) +{ struct status_data *st, *bst; nullpo_ret(sd); @@ -436,10 +523,9 @@ int pc_setrestartvalue(struct map_session_data *sd,int type) { if (type&1) { //Normal resurrection - st->hp = 1; //Otherwise status->heal may fail if dead. - status->heal(&sd->bl, bst->hp, 0, 1); + status->heal(&sd->bl, bst->hp, 0, STATUS_HEAL_FORCED | STATUS_HEAL_ALLOWREVIVE); if( st->sp < bst->sp ) - status->set_sp(&sd->bl, bst->sp, 1); + status->set_sp(&sd->bl, bst->sp, STATUS_HEAL_FORCED); } else { //Just for saving on the char-server (with values as if respawned) sd->status.hp = bst->hp; sd->status.sp = (st->sp < bst->sp) ? bst->sp : st->sp; @@ -450,7 +536,8 @@ int pc_setrestartvalue(struct map_session_data *sd,int type) { /*========================================== Rental System *------------------------------------------*/ -int pc_inventory_rental_end(int tid, int64 tick, int id, intptr_t data) { +static int pc_inventory_rental_end(int tid, int64 tick, int id, intptr_t data) +{ struct map_session_data *sd = map->id2sd(id); if( sd == NULL ) return 0; @@ -464,8 +551,9 @@ int pc_inventory_rental_end(int tid, int64 tick, int id, intptr_t data) { return 1; } -int pc_inventory_rental_clear(struct map_session_data *sd) +static int pc_inventory_rental_clear(struct map_session_data *sd) { + nullpo_ret(sd); if( sd->rental_timer != INVALID_TIMER ) { timer->delete(sd->rental_timer, pc->inventory_rental_end); @@ -475,79 +563,21 @@ int pc_inventory_rental_clear(struct map_session_data *sd) return 1; } /* assumes i is valid (from default areas where it is called, it is) */ -void pc_rental_expire(struct map_session_data *sd, int i) { - short nameid = sd->status.inventory[i].nameid; - - /* Soon to be dropped, we got plans to integrate it with item db */ - switch( nameid ) { - case ITEMID_REINS_OF_MOUNT: - status_change_end(&sd->bl,SC_ALL_RIDING,INVALID_TIMER); - break; - case ITEMID_LOVE_ANGEL: - if( sd->status.font == 1 ) { - sd->status.font = 0; - clif->font(sd); - } - break; - case ITEMID_SQUIRREL: - if( sd->status.font == 2 ) { - sd->status.font = 0; - clif->font(sd); - } - break; - case ITEMID_GOGO: - if( sd->status.font == 3 ) { - sd->status.font = 0; - clif->font(sd); - } - break; - case ITEMID_PICTURE_DIARY: - if( sd->status.font == 4 ) { - sd->status.font = 0; - clif->font(sd); - } - break; - case ITEMID_MINI_HEART: - if( sd->status.font == 5 ) { - sd->status.font = 0; - clif->font(sd); - } - break; - case ITEMID_NEWCOMER: - if( sd->status.font == 6 ) { - sd->status.font = 0; - clif->font(sd); - } - break; - case ITEMID_KID: - if( sd->status.font == 7 ) { - sd->status.font = 0; - clif->font(sd); - } - break; - case ITEMID_MAGIC_CASTLE: - if( sd->status.font == 8 ) { - sd->status.font = 0; - clif->font(sd); - } - break; - case ITEMID_BULGING_HEAD: - if( sd->status.font == 9 ) { - sd->status.font = 0; - clif->font(sd); - } - break; - } +static void pc_rental_expire(struct map_session_data *sd, int i) +{ + nullpo_retv(sd); + Assert_retv(i >= 0 && i < sd->status.inventorySize); clif->rental_expired(sd->fd, i, sd->status.inventory[i].nameid); pc->delitem(sd, i, sd->status.inventory[i].amount, 0, DELITEM_NORMAL, LOG_TYPE_RENTAL); } -void pc_inventory_rentals(struct map_session_data *sd) +static void pc_inventory_rentals(struct map_session_data *sd) { - int i, c = 0; + int c = 0; int64 expire_tick, next_tick = INT64_MAX; - for( i = 0; i < MAX_INVENTORY; i++ ) + nullpo_retv(sd); + for (int i = 0; i < sd->status.inventorySize; i++ ) { // Check for Rentals on Inventory if( sd->status.inventory[i].nameid == 0 ) continue; // Nothing here @@ -570,7 +600,7 @@ void pc_inventory_rentals(struct map_session_data *sd) sd->rental_timer = INVALID_TIMER; } -void pc_inventory_rental_add(struct map_session_data *sd, int seconds) +static void pc_inventory_rental_add(struct map_session_data *sd, int seconds) { int tick = seconds * 1000; @@ -594,7 +624,7 @@ void pc_inventory_rental_add(struct map_session_data *sd, int seconds) /*========================================== * prepares character for saving. *------------------------------------------*/ -int pc_makesavestatus(struct map_session_data *sd) +static int pc_makesavestatus(struct map_session_data *sd) { nullpo_ret(sd); @@ -660,7 +690,7 @@ int pc_makesavestatus(struct map_session_data *sd) /*========================================== * Off init ? Connection? *------------------------------------------*/ -int pc_setnewpc(struct map_session_data *sd, int account_id, int char_id, int login_id1, unsigned int client_tick, int sex, int fd) +static int pc_setnewpc(struct map_session_data *sd, int account_id, int char_id, int login_id1, unsigned int client_tick, int sex, int fd) { nullpo_ret(sd); @@ -673,7 +703,8 @@ int pc_setnewpc(struct map_session_data *sd, int account_id, int char_id, int lo sd->client_tick = client_tick; sd->state.active = 0; //to be set to 1 after player is fully authed and loaded. sd->bl.type = BL_PC; - sd->canlog_tick = timer->gettick(); + if (battle_config.prevent_logout_trigger & PLT_LOGIN) + sd->canlog_tick = timer->gettick(); //Required to prevent homunculus copuing a base speed of 0. sd->battle_status.speed = sd->base_status.speed = DEFAULT_WALK_SPEED; sd->state.warp_clean = 1; @@ -681,11 +712,13 @@ int pc_setnewpc(struct map_session_data *sd, int account_id, int char_id, int lo return 0; } -int pc_equippoint(struct map_session_data *sd,int n) +// [4144] probably pc_equippoint should be replaced to pc_item_equippoint +static int pc_equippoint(struct map_session_data *sd, int n) { int ep = 0; nullpo_ret(sd); + Assert_ret(n >= 0 && n < sd->status.inventorySize); if(!sd->inventory_data[n]) return 0; @@ -694,13 +727,13 @@ int pc_equippoint(struct map_session_data *sd,int n) return 0; //Not equippable by players. ep = sd->inventory_data[n]->equip; - if (sd->inventory_data[n]->look == W_DAGGER - || sd->inventory_data[n]->look == W_1HSWORD - || sd->inventory_data[n]->look == W_1HAXE + if (sd->inventory_data[n]->subtype == W_DAGGER + || sd->inventory_data[n]->subtype == W_1HSWORD + || sd->inventory_data[n]->subtype == W_1HAXE ) { if (pc->checkskill(sd,AS_LEFT) > 0 - || (sd->class_&MAPID_UPPERMASK) == MAPID_ASSASSIN - || (sd->class_&MAPID_UPPERMASK) == MAPID_KAGEROUOBORO + || (sd->job & MAPID_UPPERMASK) == MAPID_ASSASSIN + || (sd->job & MAPID_UPPERMASK) == MAPID_KAGEROUOBORO ) { //Kagerou and Oboro can dual wield daggers. [Rytech] if( ep == EQP_HAND_R ) @@ -712,64 +745,89 @@ int pc_equippoint(struct map_session_data *sd,int n) return ep; } -int pc_setinventorydata(struct map_session_data *sd) +static int pc_item_equippoint(struct map_session_data *sd, struct item_data *id) { - int i; + int ep = 0; + + nullpo_ret(sd); + nullpo_ret(id); + + if (!itemdb->isequip2(id)) + return 0; //Not equippable by players. + + ep = id->equip; + if (id->subtype == W_DAGGER || + id->subtype == W_1HSWORD || + id->subtype == W_1HAXE) { + if (pc->checkskill(sd, AS_LEFT) > 0 || + (sd->job & MAPID_UPPERMASK) == MAPID_ASSASSIN || + (sd->job & MAPID_UPPERMASK) == MAPID_KAGEROUOBORO) { + // Kagerou and Oboro can dual wield daggers. [Rytech] + if (ep == EQP_HAND_R) + return EQP_ARMS; + if (ep == EQP_SHADOW_WEAPON) + return EQP_SHADOW_ARMS; + } + } + return ep; +} +static int pc_setinventorydata(struct map_session_data *sd) +{ nullpo_ret(sd); - for (i = 0; i < MAX_INVENTORY; i++) { + for (int i = 0; i < sd->status.inventorySize; i++) { int id = sd->status.inventory[i].nameid; sd->inventory_data[i] = id?itemdb->search(id):NULL; } return 0; } -int pc_calcweapontype(struct map_session_data *sd) +static int pc_calcweapontype(struct map_session_data *sd) { nullpo_ret(sd); // single-hand if(sd->weapontype2 == W_FIST) { - sd->status.weapon = sd->weapontype1; + sd->weapontype = sd->weapontype1; return 1; } if(sd->weapontype1 == W_FIST) { - sd->status.weapon = sd->weapontype2; + sd->weapontype = sd->weapontype2; return 1; } // dual-wield - sd->status.weapon = 0; + sd->weapontype = W_FIST; switch (sd->weapontype1){ case W_DAGGER: switch (sd->weapontype2) { - case W_DAGGER: sd->status.weapon = W_DOUBLE_DD; break; - case W_1HSWORD: sd->status.weapon = W_DOUBLE_DS; break; - case W_1HAXE: sd->status.weapon = W_DOUBLE_DA; break; + case W_DAGGER: sd->weapontype = W_DOUBLE_DD; break; + case W_1HSWORD: sd->weapontype = W_DOUBLE_DS; break; + case W_1HAXE: sd->weapontype = W_DOUBLE_DA; break; } break; case W_1HSWORD: switch (sd->weapontype2) { - case W_DAGGER: sd->status.weapon = W_DOUBLE_DS; break; - case W_1HSWORD: sd->status.weapon = W_DOUBLE_SS; break; - case W_1HAXE: sd->status.weapon = W_DOUBLE_SA; break; + case W_DAGGER: sd->weapontype = W_DOUBLE_DS; break; + case W_1HSWORD: sd->weapontype = W_DOUBLE_SS; break; + case W_1HAXE: sd->weapontype = W_DOUBLE_SA; break; } break; case W_1HAXE: switch (sd->weapontype2) { - case W_DAGGER: sd->status.weapon = W_DOUBLE_DA; break; - case W_1HSWORD: sd->status.weapon = W_DOUBLE_SA; break; - case W_1HAXE: sd->status.weapon = W_DOUBLE_AA; break; + case W_DAGGER: sd->weapontype = W_DOUBLE_DA; break; + case W_1HSWORD: sd->weapontype = W_DOUBLE_SA; break; + case W_1HAXE: sd->weapontype = W_DOUBLE_AA; break; } } // unknown, default to right hand type - if (!sd->status.weapon) - sd->status.weapon = sd->weapontype1; + if (sd->weapontype == W_FIST) + sd->weapontype = sd->weapontype1; return 2; } -int pc_setequipindex(struct map_session_data *sd) +static int pc_setequipindex(struct map_session_data *sd) { int i,j; @@ -778,7 +836,7 @@ int pc_setequipindex(struct map_session_data *sd) for(i=0;i<EQI_MAX;i++) sd->equip_index[i] = -1; - for(i=0;i<MAX_INVENTORY;i++) { + for (i = 0; i < sd->status.inventorySize; i++) { if(sd->status.inventory[i].nameid <= 0) continue; if(sd->status.inventory[i].equip) { @@ -786,20 +844,30 @@ int pc_setequipindex(struct map_session_data *sd) if(sd->status.inventory[i].equip & pc->equip_pos[j]) sd->equip_index[j] = i; - if(sd->status.inventory[i].equip & EQP_HAND_R) - { - if(sd->inventory_data[i]) - sd->weapontype1 = sd->inventory_data[i]->look; - else - sd->weapontype1 = 0; + if (sd->status.inventory[i].equip & EQP_HAND_R) { + if (sd->inventory_data[i]) { + sd->weapontype1 = sd->inventory_data[i]->subtype; + sd->status.look.weapon = sd->inventory_data[i]->view_sprite; + } else { + sd->weapontype1 = W_FIST; + sd->status.look.weapon = 0; + } } - if( sd->status.inventory[i].equip & EQP_HAND_L ) - { - if( sd->inventory_data[i] && sd->inventory_data[i]->type == IT_WEAPON ) - sd->weapontype2 = sd->inventory_data[i]->look; - else - sd->weapontype2 = 0; + if (sd->status.inventory[i].equip & EQP_HAND_L) { + if (sd->inventory_data[i] != NULL) { + if (sd->inventory_data[i]->type == IT_WEAPON) + sd->weapontype2 = sd->inventory_data[i]->subtype; + else + sd->weapontype2 = W_FIST; + if (sd->inventory_data[i]->type == IT_ARMOR) + sd->has_shield = true; + else + sd->has_shield = false; + } else { + sd->weapontype2 = W_FIST; + sd->has_shield = false; + } } } } @@ -808,10 +876,11 @@ int pc_setequipindex(struct map_session_data *sd) return 0; } -bool pc_isequipped(struct map_session_data *sd, int nameid) +static bool pc_isequipped(struct map_session_data *sd, int nameid) { int i, j; + nullpo_retr(false, sd); for (i = 0; i < EQI_MAX; i++) { int index = sd->equip_index[i]; if( index < 0 ) continue; @@ -833,7 +902,7 @@ bool pc_isequipped(struct map_session_data *sd, int nameid) return false; } -bool pc_can_Adopt(struct map_session_data *p1_sd, struct map_session_data *p2_sd, struct map_session_data *b_sd ) +static bool pc_can_Adopt(struct map_session_data *p1_sd, struct map_session_data *p2_sd, struct map_session_data *b_sd) { if( !p1_sd || !p2_sd || !b_sd ) return false; @@ -874,7 +943,7 @@ bool pc_can_Adopt(struct map_session_data *p1_sd, struct map_session_data *p2_sd return false; } - if( !( ( b_sd->status.class_ >= JOB_NOVICE && b_sd->status.class_ <= JOB_THIEF ) || b_sd->status.class_ == JOB_SUPER_NOVICE ) ) + if (!(b_sd->status.class >= JOB_NOVICE && b_sd->status.class <= JOB_THIEF) && b_sd->status.class != JOB_SUPER_NOVICE) return false; return true; @@ -883,21 +952,22 @@ bool pc_can_Adopt(struct map_session_data *p1_sd, struct map_session_data *p2_sd /*========================================== * Adoption Process *------------------------------------------*/ -bool pc_adoption(struct map_session_data *p1_sd, struct map_session_data *p2_sd, struct map_session_data *b_sd) +static bool pc_adoption(struct map_session_data *p1_sd, struct map_session_data *p2_sd, struct map_session_data *b_sd) { - int job, joblevel; - unsigned int jobexp; + int class, joblevel; + uint64 jobexp; if( !pc->can_Adopt(p1_sd, p2_sd, b_sd) ) return false; + nullpo_retr(false, b_sd); // Preserve current job levels and progress joblevel = b_sd->status.job_level; jobexp = b_sd->status.job_exp; - job = pc->mapid2jobid(b_sd->class_|JOBL_BABY, b_sd->status.sex); - if( job != -1 && !pc->jobchange(b_sd, job, 0) ) - { // Success, proceed to configure parents and baby skills + class = pc->mapid2jobid(b_sd->job | JOBL_BABY, b_sd->status.sex); + if (class != -1 && !pc->jobchange(b_sd, class, 0)) { + // Success, proceed to configure parents and baby skills p1_sd->status.child = b_sd->status.char_id; p2_sd->status.child = b_sd->status.char_id; b_sd->status.father = p1_sd->status.char_id; @@ -917,25 +987,34 @@ bool pc_adoption(struct map_session_data *p1_sd, struct map_session_data *p2_sd, pc->skill(p1_sd, WE_CALLBABY, 1, SKILL_GRANT_PERMANENT); pc->skill(p2_sd, WE_CALLBABY, 1, SKILL_GRANT_PERMANENT); + // Achievements [Smokexyz/Hercules] + achievement->validate_adopt(p1_sd, true); // Parent 1 + achievement->validate_adopt(p2_sd, true); // Parent 2 + achievement->validate_adopt(b_sd, false); // Baby + return true; } return false; // Job Change Fail } -/*================================================= - * Checks if the player can equip the item at index n in inventory. - * Returns 0 (no) or 1 (yes). - *------------------------------------------------*/ -int pc_isequip(struct map_session_data *sd,int n) +/** + * Checks if a character can equip an item. + * + * @param sd The related character. + * @param n The item's inventory index. + * @retval 0 Character can't equip the item. + * @retval 1 Character can equip the item. + * + **/ +static int pc_isequip(struct map_session_data *sd, int n) { - struct item_data *item; - nullpo_ret(sd); + Assert_ret(n >= 0 && n < sd->status.inventorySize); - item = sd->inventory_data[n]; + struct item_data *item = sd->inventory_data[n]; - if(item == NULL) + if (item == NULL) return 0; #if PACKETVER <= 20100707 @@ -943,89 +1022,120 @@ int pc_isequip(struct map_session_data *sd,int n) return 0; #endif - if(pc_has_permission(sd, PC_PERM_USE_ALL_EQUIPMENT)) + if (pc_has_permission(sd, PC_PERM_USE_ALL_EQUIPMENT)) return 1; - if (item->elv && sd->status.base_level < (unsigned int)item->elv) { - clif->msgtable(sd, MSG_ITEM_CANT_EQUIP_LVL); + if (item->elv != 0 && sd->status.base_level < item->elv) { +#if PACKETVER >= 20100525 + clif->msgtable(sd, MSG_CANNOT_EQUIP_ITEM_LEVEL); +#endif return 0; } - if (item->elvmax && sd->status.base_level > (unsigned int)item->elvmax) { - clif->msgtable(sd, MSG_ITEM_CANT_EQUIP_LVL); + + if (item->elvmax != 0 && sd->status.base_level > item->elvmax) { +#if PACKETVER >= 20100525 + clif->msgtable(sd, MSG_CANNOT_EQUIP_ITEM_LEVEL); +#endif return 0; } - if(item->sex != 2 && sd->status.sex != item->sex) + + if (item->sex != SEX_SERVER && sd->status.sex != item->sex) return 0; - if ( item->equip & EQP_AMMO ) { - if ( (sd->state.active && !pc_iscarton(sd)) // check if sc data is already loaded. - && (sd->status.class_ == JOB_GENETIC_T || sd->status.class_ == JOB_GENETIC) ) { - clif->msgtable(sd, MSG_ITEM_NEED_CART); + if ((item->equip & EQP_AMMO) != 0) { + if (sd->state.active != 0 && !pc_iscarton(sd) && (sd->job & MAPID_THIRDMASK) == MAPID_GENETIC) { // Check if sc data is already loaded. +#if PACKETVER_RE_NUM >= 20090529 || PACKETVER_MAIN_NUM >= 20090603 || defined(PACKETVER_ZERO) + clif->msgtable(sd, MSG_USESKILL_FAIL_CART); +#endif return 0; } - if ( !pc_ismadogear(sd) && (sd->status.class_ == JOB_MECHANIC_T || sd->status.class_ == JOB_MECHANIC) ) { - clif->msgtable(sd, MSG_ITEM_NEED_MADO); + + if (!pc_ismadogear(sd) && (sd->job & MAPID_THIRDMASK) == MAPID_MECHANIC) { +#if PACKETVER_RE_NUM >= 20090226 || PACKETVER_MAIN_NUM >= 20090304 || defined(PACKETVER_ZERO) + clif->msgtable(sd, MSG_USESKILL_FAIL_MADOGEAR); +#endif return 0; } } - if (sd->sc.count) { - if(item->equip & EQP_ARMS && item->type == IT_WEAPON && sd->sc.data[SC_NOEQUIPWEAPON]) // Also works with left-hand weapons [DracoRPG] + if ((battle_config.unequip_restricted_equipment & 1) != 0) { + for (int i = 0; i < map->list[sd->bl.m].zone->disabled_items_count; i++) + if (map->list[sd->bl.m].zone->disabled_items[i] == item->nameid) + return 0; + } + + if ((battle_config.unequip_restricted_equipment & 2) != 0 && !itemdb_isspecial(sd->status.inventory[n].card[0])) { + for (int slot = 0; slot < item->slot; slot++) + for (int i = 0; i < map->list[sd->bl.m].zone->disabled_items_count; i++) + if (map->list[sd->bl.m].zone->disabled_items[i] == sd->status.inventory[n].card[slot]) + return 0; + } + + if (sd->sc.count != 0) { + if ((item->equip & EQP_ARMS) != 0 && item->type == IT_WEAPON && sd->sc.data[SC_NOEQUIPWEAPON] != NULL) // Also works with left-hand weapons. [DracoRPG] return 0; - if(item->equip & EQP_SHIELD && item->type == IT_ARMOR && sd->sc.data[SC_NOEQUIPSHIELD]) + + if ((item->equip & EQP_SHIELD) != 0 && item->type == IT_ARMOR && sd->sc.data[SC_NOEQUIPSHIELD] != NULL) return 0; - if(item->equip & EQP_ARMOR && sd->sc.data[SC_NOEQUIPARMOR]) + + if ((item->equip & EQP_ARMOR) != 0 && sd->sc.data[SC_NOEQUIPARMOR] != NULL) return 0; - if(item->equip & EQP_HEAD_TOP && sd->sc.data[SC_NOEQUIPHELM]) + + if ((item->equip & EQP_HEAD_TOP) != 0 && sd->sc.data[SC_NOEQUIPHELM] != NULL) return 0; - if(item->equip & EQP_ACC && sd->sc.data[SC__STRIPACCESSARY]) + + if ((item->equip & EQP_ACC) != 0 && sd->sc.data[SC__STRIPACCESSARY] != NULL) return 0; - if(item->equip && sd->sc.data[SC_KYOUGAKU]) + + if (item->equip != 0 && sd->sc.data[SC_KYOUGAKU] != NULL) return 0; - if (sd->sc.data[SC_SOULLINK] && sd->sc.data[SC_SOULLINK]->val2 == SL_SUPERNOVICE) { - //Spirit of Super Novice equip bonuses. [Skotlex] - if (sd->status.base_level > 90 && item->equip & EQP_HELM) - return 1; //Can equip all helms - - if (sd->status.base_level > 96 && item->equip & EQP_ARMS && item->type == IT_WEAPON) - switch(item->look) { //In weapons, the look determines type of weapon. - case W_DAGGER: //Level 4 Knives are equippable.. this means all knives, I'd guess? - case W_1HSWORD: //All 1H swords - case W_1HAXE: //All 1H Axes - case W_MACE: //All 1H Maces - case W_STAFF: //All 1H Staves - return 1; + if (sd->sc.data[SC_SOULLINK] != NULL && sd->sc.data[SC_SOULLINK]->val2 == SL_SUPERNOVICE) { // Spirit of Super Novice equip bonuses. [Skotlex] + if (sd->status.base_level > 90 && (item->equip & EQP_HELM) != 0) + return 1; // Can equip all helms. + + if (sd->status.base_level > 96 && (item->equip & EQP_ARMS) != 0 && item->type == IT_WEAPON) { + switch (item->subtype) { // In weapons, the look determines type of weapon. + case W_DAGGER: // Level 4 Knives are equippable.. this means all knives, I'd guess? + case W_1HSWORD: // All 1H swords. + case W_1HAXE: // All 1H axes. + case W_MACE: // All 1H maces. + case W_STAFF: // All 1H staffs. + return 1; } + } } } - //Not equipable by class. [Skotlex] - if (!(1ULL<<(sd->class_&MAPID_BASEMASK)&item->class_base[(sd->class_&JOBL_2_1)?1:((sd->class_&JOBL_2_2)?2:0)])) - return 0; - //Not usable by upper class. [Inkfish] - while( 1 ) { - if( item->class_upper&ITEMUPPER_NORMAL && !(sd->class_&(JOBL_UPPER|JOBL_THIRD|JOBL_BABY)) ) break; - if( item->class_upper&ITEMUPPER_UPPER && sd->class_&(JOBL_UPPER|JOBL_THIRD) ) break; - if( item->class_upper&ITEMUPPER_BABY && sd->class_&JOBL_BABY ) break; - if( item->class_upper&ITEMUPPER_THIRD && sd->class_&JOBL_THIRD ) break; + + uint64 mask_job = 1ULL << (sd->job & MAPID_BASEMASK); + uint64 mask_item = item->class_base[((sd->job & JOBL_2_1) != 0) ? 1 : (((sd->job & JOBL_2_2) != 0) ? 2 : 0)]; + + if ((mask_job & mask_item) == 0) // Not equipable by class. [Skotlex] return 0; - } - if ( battle_config.unequip_restricted_equipment & 1 ) { - int i; - for ( i = 0; i < map->list[sd->bl.m].zone->disabled_items_count; i++ ) - if ( map->list[sd->bl.m].zone->disabled_items[i] == sd->status.inventory[n].nameid ) - return 0; - } + // Not usable by upper class. [Inkfish] + while (1) { + if ((item->class_upper & ITEMUPPER_NORMAL) != 0) { + if ((sd->job & (JOBL_UPPER | JOBL_THIRD | JOBL_BABY)) == 0) + break; + } - if ( battle_config.unequip_restricted_equipment & 2 ) { - if ( !itemdb_isspecial( sd->status.inventory[n].card[0] ) ) { - int i, slot; - for ( slot = 0; slot < MAX_SLOTS; slot++ ) - for ( i = 0; i < map->list[sd->bl.m].zone->disabled_items_count; i++ ) - if ( map->list[sd->bl.m].zone->disabled_items[i] == sd->status.inventory[n].card[slot] ) - return 0; + if ((item->class_upper & ITEMUPPER_UPPER) != 0) { + if ((sd->job & (JOBL_UPPER | JOBL_THIRD)) != 0) + break; + } + + if ((item->class_upper & ITEMUPPER_BABY) != 0) { + if ((sd->job & JOBL_BABY) != 0) + break; } + + if ((item->class_upper & ITEMUPPER_THIRD) != 0) { + if ((sd->job & JOBL_THIRD) != 0) + break; + } + + return 0; } return 1; @@ -1035,11 +1145,14 @@ int pc_isequip(struct map_session_data *sd,int n) * No problem with the session id * set the status that has been sent from char server *------------------------------------------*/ -bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_time, int group_id, const struct mmo_charstatus *st, bool changing_mapservers) +static bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_time, int group_id, const struct mmo_charstatus *st, bool changing_mapservers) { int i; int64 tick = timer->gettick(); - uint32 ip = sockt->session[sd->fd]->client_addr; + uint32 ip; + + nullpo_retr(false, sd); + ip = sockt->session[sd->fd]->client_addr; sd->login_id2 = login_id2; @@ -1051,6 +1164,8 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim } memcpy(&sd->status, st, sizeof(*st)); + memset(&sd->rodex, 0x0, sizeof(sd->rodex)); + VECTOR_INIT(sd->rodex.messages); if (st->sex != sd->status.sex) { clif->authfail_fd(sd->fd, 0); @@ -1058,13 +1173,16 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim } //Set the map-server used job id. [Skotlex] - i = pc->jobid2mapid(sd->status.class_); - if (i == -1) { //Invalid class? - ShowError("pc_authok: Invalid class %d for player %s (%d:%d). Class was changed to novice.\n", sd->status.class_, sd->status.name, sd->status.account_id, sd->status.char_id); - sd->status.class_ = JOB_NOVICE; - sd->class_ = MAPID_NOVICE; - } else - sd->class_ = i; + { + int job = pc->jobid2mapid(sd->status.class); + if (job == -1) { + ShowError("pc_authok: Invalid class %d for player %s (%d:%d). Class was changed to novice.\n", sd->status.class, sd->status.name, sd->status.account_id, sd->status.char_id); + sd->status.class = JOB_NOVICE; + sd->job = MAPID_NOVICE; + } else { + sd->job = job; + } + } // Checks and fixes to character status data, that are required // in case of configuration change or stuff, which cannot be @@ -1145,7 +1263,7 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim sd->sc.option = sd->status.option; //This is the actual option used in battle. //Set here because we need the inventory data for weapon sprite parsing. - status->set_viewdata(&sd->bl, sd->status.class_); + status->set_viewdata(&sd->bl, sd->status.class); unit->dataset(&sd->bl); sd->guild_x = -1; @@ -1161,14 +1279,18 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim sd->bg_queue.client_has_bg_data = 0; sd->bg_queue.type = 0; + VECTOR_INIT(sd->channels); VECTOR_INIT(sd->script_queues); + VECTOR_INIT(sd->achievement); // Achievements [Smokexyz/Hercules] + VECTOR_INIT(sd->storage.item); // initialize storage item vector. + VECTOR_INIT(sd->hatEffectId); sd->state.dialog = 0; sd->delayed_damage = 0; - if( battle_config.item_check ) - sd->state.itemcheck = 1; + if (battle->bc->item_check != PCCHECKITEM_NONE) // Check and flag items for inspection. + sd->itemcheck = (enum pc_checkitem_types) battle->bc->item_check; // Event Timers for( i = 0; i < MAX_EVENTTIMER; i++ ) @@ -1207,6 +1329,8 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim pc->setpos(sd,sd->status.last_point.map,0,0,CLR_OUTSIGHT); } + clif->inventoryExpansionInfo(sd); + clif->overweight_percent(sd); clif->authok(sd); //Prevent S. Novices from getting the no-death bonus just yet. [Skotlex] @@ -1219,6 +1343,7 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim " Group '"CL_WHITE"%d"CL_RESET"').\n", sd->status.name, sd->status.account_id, sd->status.char_id, CONVIP(ip), sd->group_id); + // Send friends list clif->friendslist_send(sd); @@ -1240,16 +1365,6 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim clif->changemap(sd,sd->bl.m,sd->bl.x,sd->bl.y); } - /** - * Check if player have any cool downs on - **/ - skill->cooldown_load(sd); - - /** - * Check if player have any item cooldowns on - **/ - pc->itemcd_do(sd,true); - #ifdef GP_BOUND_ITEMS if( sd->status.party_id == 0 ) pc->bound_clear(sd,IBT_PARTY); @@ -1267,14 +1382,15 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim /*========================================== * Closes a connection because it failed to be authenticated from the char server. *------------------------------------------*/ -void pc_authfail(struct map_session_data *sd) +static void pc_authfail(struct map_session_data *sd) { + nullpo_retv(sd); clif->authfail_fd(sd->fd, 0); return; } //Attempts to set a mob. -int pc_set_hate_mob(struct map_session_data *sd, int pos, struct block_list *bl) +static int pc_set_hate_mob(struct map_session_data *sd, int pos, struct block_list *bl) { int class_; if (!sd || !bl || pos < 0 || pos >= MAX_PC_FEELHATE) @@ -1294,7 +1410,7 @@ int pc_set_hate_mob(struct map_session_data *sd, int pos, struct block_list *bl) return 0; //Wrong size } sd->hate_mob[pos] = class_; - pc_setglobalreg(sd,script->add_str(pc->sg_info[pos].hate_var),class_+1); + pc_setglobalreg(sd,script->add_variable(pc->sg_info[pos].hate_var),class_+1); clif->hate_info(sd, pos, class_, 1); return 1; } @@ -1302,33 +1418,34 @@ int pc_set_hate_mob(struct map_session_data *sd, int pos, struct block_list *bl) /*========================================== * Invoked once after the char/account/account2 registry variables are received. [Skotlex] *------------------------------------------*/ -int pc_reg_received(struct map_session_data *sd) +static int pc_reg_received(struct map_session_data *sd) { int i, idx = 0; + nullpo_ret(sd); sd->vars_ok = true; - sd->change_level_2nd = pc_readglobalreg(sd,script->add_str("jobchange_level")); - sd->change_level_3rd = pc_readglobalreg(sd,script->add_str("jobchange_level_3rd")); - sd->die_counter = pc_readglobalreg(sd,script->add_str("PC_DIE_COUNTER")); + sd->change_level_2nd = pc_readglobalreg(sd,script->add_variable("jobchange_level")); + sd->change_level_3rd = pc_readglobalreg(sd,script->add_variable("jobchange_level_3rd")); + sd->die_counter = pc_readglobalreg(sd,script->add_variable("PC_DIE_COUNTER")); // Cash shop - sd->cashPoints = pc_readaccountreg(sd,script->add_str("#CASHPOINTS")); - sd->kafraPoints = pc_readaccountreg(sd,script->add_str("#KAFRAPOINTS")); + sd->cashPoints = pc_readaccountreg(sd,script->add_variable("#CASHPOINTS")); + sd->kafraPoints = pc_readaccountreg(sd,script->add_variable("#KAFRAPOINTS")); // Cooking Exp - sd->cook_mastery = pc_readglobalreg(sd,script->add_str("COOK_MASTERY")); + sd->cook_mastery = pc_readglobalreg(sd,script->add_variable("COOK_MASTERY")); - if( (sd->class_&MAPID_BASEMASK) == MAPID_TAEKWON ) { + if ((sd->job & MAPID_BASEMASK) == MAPID_TAEKWON) { // Better check for class rather than skill to prevent "skill resets" from unsetting this - sd->mission_mobid = pc_readglobalreg(sd,script->add_str("TK_MISSION_ID")); - sd->mission_count = pc_readglobalreg(sd,script->add_str("TK_MISSION_COUNT")); + sd->mission_mobid = pc_readglobalreg(sd,script->add_variable("TK_MISSION_ID")); + sd->mission_count = pc_readglobalreg(sd,script->add_variable("TK_MISSION_COUNT")); } //SG map and mob read [Komurka] for (i = 0; i < MAX_PC_FEELHATE; i++) { //for now - someone need to make reading from txt/sql - int j = pc_readglobalreg(sd,script->add_str(pc->sg_info[i].feel_var)); + int j = pc_readglobalreg(sd,script->add_variable(pc->sg_info[i].feel_var)); if (j != 0) { sd->feel_map[i].index = j; sd->feel_map[i].m = map->mapindex2mapid(j); @@ -1336,24 +1453,24 @@ int pc_reg_received(struct map_session_data *sd) sd->feel_map[i].index = 0; sd->feel_map[i].m = -1; } - sd->hate_mob[i] = pc_readglobalreg(sd,script->add_str(pc->sg_info[i].hate_var))-1; + sd->hate_mob[i] = pc_readglobalreg(sd,script->add_variable(pc->sg_info[i].hate_var))-1; } if ((i = pc->checkskill(sd,RG_PLAGIARISM)) > 0) { - sd->cloneskill_id = pc_readglobalreg(sd,script->add_str("CLONE_SKILL")); + sd->cloneskill_id = pc_readglobalreg(sd,script->add_variable("CLONE_SKILL")); if (sd->cloneskill_id > 0 && (idx = skill->get_index(sd->cloneskill_id)) > 0) { sd->status.skill[idx].id = sd->cloneskill_id; - sd->status.skill[idx].lv = pc_readglobalreg(sd,script->add_str("CLONE_SKILL_LV")); + sd->status.skill[idx].lv = pc_readglobalreg(sd,script->add_variable("CLONE_SKILL_LV")); if (sd->status.skill[idx].lv > i) sd->status.skill[idx].lv = i; sd->status.skill[idx].flag = SKILL_FLAG_PLAGIARIZED; } } if ((i = pc->checkskill(sd,SC_REPRODUCE)) > 0) { - sd->reproduceskill_id = pc_readglobalreg(sd,script->add_str("REPRODUCE_SKILL")); + sd->reproduceskill_id = pc_readglobalreg(sd,script->add_variable("REPRODUCE_SKILL")); if( sd->reproduceskill_id > 0 && (idx = skill->get_index(sd->reproduceskill_id)) > 0) { sd->status.skill[idx].id = sd->reproduceskill_id; - sd->status.skill[idx].lv = pc_readglobalreg(sd,script->add_str("REPRODUCE_SKILL_LV")); + sd->status.skill[idx].lv = pc_readglobalreg(sd,script->add_variable("REPRODUCE_SKILL_LV")); if( i < sd->status.skill[idx].lv) sd->status.skill[idx].lv = i; sd->status.skill[idx].flag = SKILL_FLAG_PLAGIARIZED; @@ -1370,30 +1487,43 @@ int pc_reg_received(struct map_session_data *sd) if (sd->status.guild_id) guild->member_joined(sd); - // pet - if (sd->status.pet_id > 0) - intif->request_petdata(sd->status.account_id, sd->status.char_id, sd->status.pet_id); - - // Homunculus [albator] - if( sd->status.hom_id > 0 ) - intif->homunculus_requestload(sd->status.account_id, sd->status.hom_id); - if( sd->status.mer_id > 0 ) - intif->mercenary_request(sd->status.mer_id, sd->status.char_id); - if( sd->status.ele_id > 0 ) - intif->elemental_request(sd->status.ele_id, sd->status.char_id); + if (sd->state.standalone == 0 && sd->state.autotrade == 0) { // prevents loading pets, homunculi, mercenaries or elementals if the character doesn't have a client attached + if (sd->status.pet_id != 0) + intif->request_petdata(sd->status.account_id, sd->status.char_id, sd->status.pet_id); + if (sd->status.hom_id != 0) + intif->homunculus_requestload(sd->status.account_id, sd->status.hom_id); + if (sd->status.mer_id != 0) + intif->mercenary_request(sd->status.mer_id, sd->status.char_id); + if (sd->status.ele_id != 0) + intif->elemental_request(sd->status.ele_id, sd->status.char_id); + } map->addiddb(&sd->bl); map->delnickdb(sd->status.char_id, sd->status.name); if (!chrif->auth_finished(sd)) ShowError("pc_reg_received: Failed to properly remove player %d:%d from logging db!\n", sd->status.account_id, sd->status.char_id); + // Restore any cooldowns + skill->cooldown_load(sd); + pc->itemcd_do(sd, true); + pc->load_combo(sd); status_calc_pc(sd,SCO_FIRST|SCO_FORCE); chrif->scdata_request(sd->status.account_id, sd->status.char_id); + if (sd->status.clan_id) + clan->member_online(sd, true); + + //Auth is fully okay, update last_login + sd->status.last_login = time(NULL); + + // Storage Request + intif->request_account_storage(sd); + intif->Mail_requestinbox(sd->status.char_id, 0); // MAIL SYSTEM - Request Mail Inbox intif->request_questlog(sd); + intif->rodex_checkhasnew(sd); if (sd->state.connect_new == 0 && sd->fd) { //Character already loaded map! Gotta trigger LoadEndAck manually. sd->state.connect_new = 1; @@ -1401,7 +1531,7 @@ int pc_reg_received(struct map_session_data *sd) } if (pc_isinvisible(sd)) { - sd->vd.class_ = INVISIBLE_CLASS; + sd->vd.class = INVISIBLE_CLASS; clif->message(sd->fd, msg_sd(sd,11)); // Invisible: On // decrement the number of pvp players on the map map->list[sd->bl.m].users_pvp--; @@ -1416,15 +1546,19 @@ int pc_reg_received(struct map_session_data *sd) if( npc->motd ) /* [Ind/Hercules] */ script->run(npc->motd->u.scr.script, 0, sd->bl.id, npc->fake_nd->bl.id); + // Achievements [Smokexyz/Hercules] + intif->achievements_request(sd); + return 1; } -int pc_calc_skillpoint(struct map_session_data* sd) { +static int pc_calc_skillpoint(struct map_session_data *sd) +{ int i,inf2,skill_point=0; nullpo_ret(sd); - for (i = 1; i < MAX_SKILL; i++) { + for (i = 1; i < MAX_SKILL_DB; i++) { int skill_lv = pc->checkskill2(sd,i); if (skill_lv > 0) { inf2 = skill->dbs->db[i].inf2; @@ -1442,71 +1576,85 @@ int pc_calc_skillpoint(struct map_session_data* sd) { return skill_point; } -/*========================================== - * Calculation of skill level. - *------------------------------------------*/ -int pc_calc_skilltree(struct map_session_data *sd) +static void pc_calc_skilltree_clear(struct map_session_data *sd) { - int i,id=0,flag; - int c=0; + int i; - nullpo_ret(sd); - i = pc->calc_skilltree_normalize_job(sd); - c = pc->mapid2jobid(i, sd->status.sex); - if( c == -1 ) - { //Unable to normalize job?? - ShowError("pc_calc_skilltree: Unable to normalize job %d for character %s (%d:%d)\n", i, sd->status.name, sd->status.account_id, sd->status.char_id); - return 1; - } - c = pc->class2idx(c); + nullpo_retv(sd); - for( i = 0; i < MAX_SKILL; i++ ) { - if( sd->status.skill[i].flag != SKILL_FLAG_PLAGIARIZED && sd->status.skill[i].flag != SKILL_FLAG_PERM_GRANTED ) //Don't touch these + for (i = 0; i < MAX_SKILL_DB; i++) { + if (sd->status.skill[i].flag != SKILL_FLAG_PLAGIARIZED && sd->status.skill[i].flag != SKILL_FLAG_PERM_GRANTED) //Don't touch these sd->status.skill[i].id = 0; //First clear skills. /* permanent skills that must be re-checked */ - if( sd->status.skill[i].flag == SKILL_FLAG_PERMANENT ) { - switch( skill->dbs->db[i].nameid ) { + if (sd->status.skill[i].flag == SKILL_FLAG_PERMANENT) { + switch (skill->dbs->db[i].nameid) { case NV_TRICKDEAD: - if( (sd->class_&(MAPID_BASEMASK|JOBL_2)) != MAPID_NOVICE ) { - sd->status.skill[i].id = 0; - sd->status.skill[i].lv = 0; - sd->status.skill[i].flag = 0; + if ((sd->job & MAPID_UPPERMASK) != MAPID_NOVICE) { + sd->status.skill[i].id = 0; + sd->status.skill[i].lv = 0; + sd->status.skill[i].flag = 0; } break; } } } +} + +/*========================================== + * Calculation of skill level. + *------------------------------------------*/ +static int pc_calc_skilltree(struct map_session_data *sd) +{ + nullpo_ret(sd); + uint32 job = pc->calc_skilltree_normalize_job(sd); + int class = pc->mapid2jobid(job, sd->status.sex); + if (class == -1) { + //Unable to normalize job?? + ShowError("pc_calc_skilltree: Unable to normalize job %u for character %s (%d:%d)\n", job, sd->status.name, sd->status.account_id, sd->status.char_id); + return 1; + } + int classidx = pc->class2idx(class); - for( i = 0; i < MAX_SKILL; i++ ) { - if( sd->status.skill[i].flag != SKILL_FLAG_PERMANENT && sd->status.skill[i].flag != SKILL_FLAG_PERM_GRANTED && sd->status.skill[i].flag != SKILL_FLAG_PLAGIARIZED ) - { // Restore original level of skills after deleting earned skills. + pc->calc_skilltree_clear(sd); + + for (int i = 0; i < MAX_SKILL_DB; i++) { + if (sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[i].flag >= SKILL_FLAG_REPLACED_LV_0) { + // Restore original level of skills after deleting earned skills. sd->status.skill[i].lv = (sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY) ? 0 : sd->status.skill[i].flag - SKILL_FLAG_REPLACED_LV_0; sd->status.skill[i].flag = SKILL_FLAG_PERMANENT; } - if( sd->sc.count && sd->sc.data[SC_SOULLINK] && sd->sc.data[SC_SOULLINK]->val2 == SL_BARDDANCER && skill->dbs->db[i].nameid >= DC_HUMMING && skill->dbs->db[i].nameid <= DC_SERVICEFORYOU ) - { //Enable Bard/Dancer spirit linked skills. - if( sd->status.sex ) - { //Link dancer skills to bard. - // i can be < 8? - if( sd->status.skill[i-8].lv < 10 ) - continue; - sd->status.skill[i].id = skill->dbs->db[i].nameid; - sd->status.skill[i].lv = sd->status.skill[i-8].lv; // Set the level to the same as the linking skill - sd->status.skill[i].flag = SKILL_FLAG_TEMPORARY; // Tag it as a non-savable, non-uppable, bonus skill - } else { //Link bard skills to dancer. - if( sd->status.skill[i].lv < 10 ) - continue; - // i can be < 8? - sd->status.skill[i-8].id = skill->dbs->db[i-8].nameid; - sd->status.skill[i-8].lv = sd->status.skill[i].lv; // Set the level to the same as the linking skill - sd->status.skill[i-8].flag = SKILL_FLAG_TEMPORARY; // Tag it as a non-savable, non-uppable, bonus skill + if (sd->sc.count && sd->sc.data[SC_SOULLINK] && sd->sc.data[SC_SOULLINK]->val2 == SL_BARDDANCER + && ((skill->dbs->db[i].nameid >= BA_WHISTLE && skill->dbs->db[i].nameid <= BA_APPLEIDUN) + || (skill->dbs->db[i].nameid >= DC_HUMMING && skill->dbs->db[i].nameid <= DC_SERVICEFORYOU)) + ) { + //Enable Bard/Dancer spirit linked skills. + int linked_nameid = skill->get_linked_song_dance_id(skill->dbs->db[i].nameid); + if (linked_nameid == 0) { + Assert_report("Linked bard/dance skill not found"); + continue; } + int copy_from_index; + int copy_to_index; + if (sd->status.sex == SEX_MALE && skill->dbs->db[i].nameid >= BA_WHISTLE && skill->dbs->db[i].nameid <= BA_APPLEIDUN) { + copy_from_index = i; + copy_to_index = skill->get_index(linked_nameid); + } else { + copy_from_index = skill->get_index(linked_nameid); + copy_to_index = i; + } + if (copy_from_index < copy_to_index) + continue; // Copy only after the source skill has been filled into the tree + if (sd->status.skill[copy_from_index].lv < 10) + continue; // Copy only if the linked skill has been mastered + sd->status.skill[copy_to_index].id = skill->dbs->db[copy_to_index].nameid; + sd->status.skill[copy_to_index].lv = sd->status.skill[copy_from_index].lv; // Set the level to the same as the linking skill + sd->status.skill[copy_to_index].flag = SKILL_FLAG_TEMPORARY; // Tag it as a non-savable, non-uppable, bonus skill } } if( pc_has_permission(sd, PC_PERM_ALL_SKILL) ) { - for( i = 0; i < MAX_SKILL; i++ ) { + for (int i = 0; i < MAX_SKILL_DB; i++) { switch(skill->dbs->db[i].nameid) { /** * Dummy skills must be added here otherwise they'll be displayed in the, @@ -1538,18 +1686,20 @@ int pc_calc_skilltree(struct map_session_data *sd) return 0; } + bool changed = false; do { - flag = 0; - for (i = 0; i < MAX_SKILL_TREE && (id = pc->skill_tree[c][i].id) > 0; i++) { - int idx = pc->skill_tree[c][i].idx; + changed = false; + int id; + for (int i = 0; i < MAX_SKILL_TREE && (id = pc->skill_tree[classidx][i].id) > 0; i++) { + int idx = pc->skill_tree[classidx][i].idx; bool satisfied = true; if (sd->status.skill[idx].id > 0) continue; //Skill already known. if (!battle_config.skillfree) { int j; - for (j = 0; j < VECTOR_LENGTH(pc->skill_tree[c][i].need); j++) { - struct skill_tree_requirement *req = &VECTOR_INDEX(pc->skill_tree[c][i].need, j); + for (j = 0; j < VECTOR_LENGTH(pc->skill_tree[classidx][i].need); j++) { + struct skill_tree_requirement *req = &VECTOR_INDEX(pc->skill_tree[classidx][i].need, j); int level; if (sd->status.skill[req->idx].id == 0 || sd->status.skill[req->idx].flag == SKILL_FLAG_TEMPORARY @@ -1564,8 +1714,8 @@ int pc_calc_skilltree(struct map_session_data *sd) break; } } - if (sd->status.job_level < pc->skill_tree[c][i].joblv) { - int jobid = pc->mapid2jobid(sd->class_, sd->status.sex); // need to get its own skilltree + if (sd->status.job_level < (int)pc->skill_tree[classidx][i].joblv) { + int jobid = pc->mapid2jobid(sd->job, sd->status.sex); // need to get its own skilltree if (jobid > -1) { if (!pc->skill_tree[pc->class2idx(jobid)][i].inherited) satisfied = false; // job level requirement wasn't satisfied @@ -1590,22 +1740,36 @@ int pc_calc_skilltree(struct map_session_data *sd) sd->status.skill[idx].lv = 1; // need to manually specify a skill level sd->status.skill[idx].flag = SKILL_FLAG_TEMPORARY; //So it is not saved, and tagged as a "bonus" skill. } - flag = 1; // skill list has changed, perform another pass + changed = true; // skill list has changed, perform another pass } } - } while(flag); + } while (changed); + + pc->calc_skilltree_bonus(sd, classidx); + + return 0; +} + +static void pc_calc_skilltree_bonus(struct map_session_data *sd, int classidx) +{ + int i; + int id = 0; + + nullpo_retv(sd); + Assert_retv(classidx >= 0 && classidx < CLASS_COUNT); // - if( c > 0 && (sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && sd->status.skill_point == 0 && pc->famerank(sd->status.char_id, MAPID_TAEKWON) ) - { - /* Taekwon Ranger Bonus Skill Tree + if (classidx > 0 && (sd->job & MAPID_UPPERMASK) == MAPID_TAEKWON + && sd->status.base_level >= 90 && sd->status.skill_point == 0 + && pc->fame_rank(sd->status.char_id, RANKTYPE_TAEKWON) > 0) { + /* Taekwon Ranker Bonus Skill Tree ============================================ - Grant All Taekwon Tree, but only as Bonus Skills in case they drop from ranking. - (c > 0) to avoid grant Novice Skill Tree in case of Skill Reset (need more logic) - (sd->status.skill_point == 0) to wait until all skill points are asigned to avoid problems with Job Change quest. */ - for( i = 0; i < MAX_SKILL_TREE && (id = pc->skill_tree[c][i].id) > 0; i++ ) { - int idx = pc->skill_tree[c][i].idx; + for (i = 0; i < MAX_SKILL_TREE && (id = pc->skill_tree[classidx][i].id) > 0; i++) { + int idx = pc->skill_tree[classidx][i].idx; if( (skill->dbs->db[idx].inf2&(INF2_QUEST_SKILL|INF2_WEDDING_SKILL)) ) continue; //Do not include Quest/Wedding skills. @@ -1616,15 +1780,13 @@ int pc_calc_skilltree(struct map_session_data *sd) sd->status.skill[idx].flag = SKILL_FLAG_REPLACED_LV_0 + sd->status.skill[idx].lv; // Remember original level } - sd->status.skill[idx].lv = skill->tree_get_max(id, sd->status.class_); + sd->status.skill[idx].lv = skill->tree_get_max(id, sd->status.class); } } - - return 0; } //Checks if you can learn a new skill after having leveled up a skill. -void pc_check_skilltree(struct map_session_data *sd, int skill_id) +static void pc_check_skilltree(struct map_session_data *sd, int skill_id) { int i,id=0,flag; int c=0; @@ -1632,6 +1794,7 @@ void pc_check_skilltree(struct map_session_data *sd, int skill_id) if(battle_config.skillfree) return; //Function serves no purpose if this is set + nullpo_retv(sd); i = pc->calc_skilltree_normalize_job(sd); c = pc->mapid2jobid(i, sd->status.sex); if (c == -1) { //Unable to normalize job?? @@ -1667,8 +1830,8 @@ void pc_check_skilltree(struct map_session_data *sd, int skill_id) if (!satisfied) continue; - if (sd->status.job_level < pc->skill_tree[c][i].joblv) { - int jobid = pc->mapid2jobid(sd->class_, sd->status.sex); // need to get its own skilltree + if (sd->status.job_level < (int)pc->skill_tree[c][i].joblv) { + int jobid = pc->mapid2jobid(sd->job, sd->status.sex); // need to get its own skilltree if (jobid > -1) { if (!pc->skill_tree[pc->class2idx(jobid)][i].inherited) continue; @@ -1694,10 +1857,11 @@ void pc_check_skilltree(struct map_session_data *sd, int skill_id) // Make sure all the skills are in the correct condition // before persisting to the backend.. [MouseJstr] -int pc_clean_skilltree(struct map_session_data *sd) +static int pc_clean_skilltree(struct map_session_data *sd) { int i; - for (i = 0; i < MAX_SKILL; i++){ + nullpo_ret(sd); + for (i = 0; i < MAX_SKILL_DB; i++) { if (sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[i].flag == SKILL_FLAG_PLAGIARIZED) { sd->status.skill[i].id = 0; sd->status.skill[i].lv = 0; @@ -1711,76 +1875,73 @@ int pc_clean_skilltree(struct map_session_data *sd) return 0; } -int pc_calc_skilltree_normalize_job(struct map_session_data *sd) +static int pc_calc_skilltree_normalize_job(struct map_session_data *sd) { int skill_point, novice_skills; - int c = sd->class_; + uint16 job; + nullpo_ret(sd); + job = sd->job; if (!battle_config.skillup_limit || pc_has_permission(sd, PC_PERM_ALL_SKILL)) - return c; + return job; skill_point = pc->calc_skillpoint(sd); - novice_skills = pc->max_level[pc->class2idx(JOB_NOVICE)][1] - 1; + novice_skills = pc->dbs->class_exp_table[pc->class2idx(JOB_NOVICE)][CLASS_EXP_TABLE_JOB]->max_level - 1; sd->sktree.second = sd->sktree.third = 0; - // limit 1st class and above to novice job levels - if(skill_point < novice_skills) { - c = MAPID_NOVICE; - } - // limit 2nd class and above to first class job levels (super novices are exempt) - else if ((sd->class_&JOBL_2) && (sd->class_&MAPID_UPPERMASK) != MAPID_SUPER_NOVICE) - { + if (skill_point < novice_skills && (sd->job & MAPID_BASEMASK) != MAPID_SUMMONER) { + // limit 1st class and above to novice job levels + job = MAPID_NOVICE; + } else if ((sd->job & JOBL_2) != 0 && (sd->job & MAPID_UPPERMASK) != MAPID_SUPER_NOVICE) { + // limit 2nd class and above to first class job levels (super novices are exempt) // regenerate change_level_2nd - if (!sd->change_level_2nd) - { - if (sd->class_&JOBL_THIRD) - { + if (sd->change_level_2nd == 0) { + if ((sd->job & JOBL_THIRD) != 0) { // if neither 2nd nor 3rd jobchange levels are known, we have to assume a default for 2nd - if (!sd->change_level_3rd) - sd->change_level_2nd = pc->max_level[pc->class2idx(pc->mapid2jobid(sd->class_&MAPID_UPPERMASK, sd->status.sex))][1]; - else + if (sd->change_level_3rd == 0) { + sd->change_level_2nd = pc->dbs->class_exp_table[pc->class2idx(pc->mapid2jobid(sd->job & MAPID_UPPERMASK, sd->status.sex))][CLASS_EXP_TABLE_JOB]->max_level; + } else { sd->change_level_2nd = 1 + skill_point + sd->status.skill_point - (sd->status.job_level - 1) - (sd->change_level_3rd - 1) - novice_skills; - } - else - { + } + } else { sd->change_level_2nd = 1 + skill_point + sd->status.skill_point - (sd->status.job_level - 1) - novice_skills; } - pc_setglobalreg (sd, script->add_str("jobchange_level"), sd->change_level_2nd); + pc_setglobalreg(sd, script->add_variable("jobchange_level"), sd->change_level_2nd); } if (skill_point < novice_skills + (sd->change_level_2nd - 1)) { - c &= MAPID_BASEMASK; + job &= MAPID_BASEMASK; sd->sktree.second = ( novice_skills + (sd->change_level_2nd - 1) ) - skill_point; - } else if(sd->class_&JOBL_THIRD) { // limit 3rd class to 2nd class/trans job levels + } else if ((sd->job & JOBL_THIRD) != 0) { // limit 3rd class to 2nd class/trans job levels // regenerate change_level_3rd - if (!sd->change_level_3rd) { + if (sd->change_level_3rd == 0) { sd->change_level_3rd = 1 + skill_point + sd->status.skill_point - (sd->status.job_level - 1) - (sd->change_level_2nd - 1) - novice_skills; - pc_setglobalreg (sd, script->add_str("jobchange_level_3rd"), sd->change_level_3rd); + pc_setglobalreg(sd, script->add_variable("jobchange_level_3rd"), sd->change_level_3rd); } if (skill_point < novice_skills + (sd->change_level_2nd - 1) + (sd->change_level_3rd - 1)) { - c &= MAPID_UPPERMASK; + job &= MAPID_UPPERMASK; sd->sktree.third = (novice_skills + (sd->change_level_2nd - 1) + (sd->change_level_3rd - 1)) - skill_point; } } } // restore non-limiting flags - c |= sd->class_&(JOBL_UPPER|JOBL_BABY); + job |= sd->job & (JOBL_UPPER|JOBL_BABY); - return c; + return job; } /*========================================== @@ -1790,7 +1951,7 @@ int pc_calc_skilltree_normalize_job(struct map_session_data *sd) * 2: overweight 90% * It's assumed that SC_WEIGHTOVER50 and SC_WEIGHTOVER90 are only started/stopped here. */ -int pc_updateweightstatus(struct map_session_data *sd) +static int pc_updateweightstatus(struct map_session_data *sd) { int old_overweight; int new_overweight; @@ -1821,37 +1982,40 @@ int pc_updateweightstatus(struct map_session_data *sd) return 0; } -int pc_disguise(struct map_session_data *sd, int class_) { - if (class_ == -1 && sd->disguise == -1) +static int pc_disguise(struct map_session_data *sd, int class) +{ + nullpo_ret(sd); + if (class == -1 && sd->disguise == -1) return 0; - if (class_ >= 0 && sd->disguise == class_) + if (class >= 0 && sd->disguise == class) return 0; if (pc_isinvisible(sd)) { //Character is invisible. Stealth class-change. [Skotlex] - sd->disguise = class_; //viewdata is set on uncloaking. + sd->disguise = class; //viewdata is set on uncloaking. return 2; } if (sd->bl.prev != NULL) { - if( class_ == -1 && sd->disguise == sd->status.class_ ) { + if (class == -1 && sd->disguise == sd->status.class) { clif->clearunit_single(-sd->bl.id,CLR_OUTSIGHT,sd->fd); - } else if ( class_ != sd->status.class_ ) { + } else if (class != sd->status.class) { pc_stop_walking(sd, STOPWALKING_FLAG_NONE); clif->clearunit_area(&sd->bl, CLR_OUTSIGHT); } } - if (class_ == -1) { + if (class == -1) { sd->disguise = -1; - class_ = sd->status.class_; - } else - sd->disguise = class_; + class = sd->status.class; + } else { + sd->disguise = class; + } - status->set_viewdata(&sd->bl, class_); + status->set_viewdata(&sd->bl, class); clif->changeoption(&sd->bl); // We need to update the client so it knows that a costume is being used if( sd->sc.option&OPTION_COSTUME ) { - clif->changelook(&sd->bl,LOOK_BASE,sd->vd.class_); + clif->changelook(&sd->bl, LOOK_BASE, sd->vd.class); clif->changelook(&sd->bl,LOOK_WEAPON,0); clif->changelook(&sd->bl,LOOK_SHIELD,0); clif->changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->vd.cloth_color); @@ -1859,13 +2023,13 @@ int pc_disguise(struct map_session_data *sd, int class_) { if (sd->bl.prev != NULL) { clif->spawn(&sd->bl); - if (class_ == sd->status.class_ && pc_iscarton(sd)) { + if (class == sd->status.class && pc_iscarton(sd)) { //It seems the cart info is lost on undisguise. - clif->cartlist(sd); + clif->cartList(sd); clif->updatestatus(sd,SP_CARTINFO); } - if (sd->chatID) { - struct chat_data *cd = map->id2cd(sd->chatID); + if (sd->chat_id != 0) { + struct chat_data *cd = map->id2cd(sd->chat_id); if (cd != NULL) clif->dispchat(cd,0); @@ -1874,13 +2038,15 @@ int pc_disguise(struct map_session_data *sd, int class_) { return 1; } -int pc_bonus_autospell(struct s_autospell *spell, int max, short id, short lv, short rate, short flag, short card_id) +static int pc_bonus_autospell(struct s_autospell *spell, int max, short id, short lv, short rate, short flag, int card_id) { int i; if( !rate ) return 0; + nullpo_ret(spell); + Assert_ret(max <= 15); // autospell array size for( i = 0; i < max && spell[i].id; i++ ) { if( (spell[i].card_id == card_id || spell[i].rate < 0 || rate < 0) && spell[i].id == id && spell[i].lv == lv ) @@ -1910,13 +2076,15 @@ int pc_bonus_autospell(struct s_autospell *spell, int max, short id, short lv, s return 1; } -int pc_bonus_autospell_onskill(struct s_autospell *spell, int max, short src_skill, short id, short lv, short rate, short card_id) +static int pc_bonus_autospell_onskill(struct s_autospell *spell, int max, short src_skill, short id, short lv, short rate, int card_id) { int i; if( !rate ) return 0; + nullpo_ret(spell); + Assert_ret(max <= 15); // autospell array size for( i = 0; i < max && spell[i].id; i++ ) { ; // each autospell works independently @@ -1949,9 +2117,11 @@ int pc_bonus_autospell_onskill(struct s_autospell *spell, int max, short src_ski * @retval 1 on success. * @retval 0 on failure. */ -int pc_bonus_addeff(struct s_addeffect* effect, int max, enum sc_type id, int16 rate, int16 arrow_rate, uint8 flag, uint16 duration) +static int pc_bonus_addeff(struct s_addeffect *effect, int max, enum sc_type id, int16 rate, int16 arrow_rate, uint8 flag, uint16 duration) { int i; + + nullpo_ret(effect); if (!(flag&(ATF_SHORT|ATF_LONG))) flag|=ATF_SHORT|ATF_LONG; //Default range: both if (!(flag&(ATF_TARGET|ATF_SELF))) @@ -1979,8 +2149,11 @@ int pc_bonus_addeff(struct s_addeffect* effect, int max, enum sc_type id, int16 return 1; } -int pc_bonus_addeff_onskill(struct s_addeffectonskill* effect, int max, enum sc_type id, short rate, short skill_id, unsigned char target) { +static int pc_bonus_addeff_onskill(struct s_addeffectonskill *effect, int max, enum sc_type id, short rate, short skill_id, unsigned char target) +{ int i; + + nullpo_ret(effect); for( i = 0; i < max && effect[i].skill; i++ ) { if( effect[i].id == id && effect[i].skill == skill_id && effect[i].target == target ) { effect[i].rate += rate; @@ -1998,9 +2171,12 @@ int pc_bonus_addeff_onskill(struct s_addeffectonskill* effect, int max, enum sc_ return 1; } -int pc_bonus_item_drop(struct s_add_drop *drop, const short max, short id, short group, int race_mask, int rate) { +static int pc_bonus_item_drop(struct s_add_drop *drop, const short max, int id, bool is_group, int race_mask, int rate) +{ int i; + nullpo_ret(drop); + Assert_ret(is_group || id > 0); //Apply config rate adjustment settings. if (rate >= 0) { //Absolute drop. if (battle_config.item_rate_adddrop != 100) @@ -2015,17 +2191,14 @@ int pc_bonus_item_drop(struct s_add_drop *drop, const short max, short id, short if (rate > -1) rate = -1; } - for(i = 0; i < max && (drop[i].id || drop[i].group); i++) { - if (((id && drop[i].id == id) || (group && drop[i].group == group)) - && race_mask != RCMASK_NONE - ) { + for (i = 0; i < max && (drop[i].id != 0 || drop[i].is_group); i++) { + if (drop[i].id == id && race_mask != RCMASK_NONE) { drop[i].race |= race_mask; if (drop[i].rate > 0 && rate > 0) { //Both are absolute rates. if (drop[i].rate < rate) drop[i].rate = rate; - } else - if(drop[i].rate < 0 && rate < 0) { + } else if (drop[i].rate < 0 && rate < 0) { //Both are relative rates. if (drop[i].rate > rate) drop[i].rate = rate; @@ -2039,15 +2212,18 @@ int pc_bonus_item_drop(struct s_add_drop *drop, const short max, short id, short return 0; } drop[i].id = id; - drop[i].group = group; + drop[i].is_group = is_group; drop[i].race |= race_mask; drop[i].rate = rate; return 1; } -int pc_addautobonus(struct s_autobonus *bonus,char max,const char *bonus_script,short rate,unsigned int dur,short flag,const char *other_script,unsigned short pos,bool onskill) { +static int pc_addautobonus(struct s_autobonus *bonus, char max, const char *bonus_script, short rate, unsigned int dur, short flag, const char *other_script, unsigned int pos, bool onskill) +{ int i; + nullpo_ret(bonus); + nullpo_ret(bonus_script); ARR_FIND(0, max, i, bonus[i].rate == 0); if( i == max ) { @@ -2080,10 +2256,11 @@ int pc_addautobonus(struct s_autobonus *bonus,char max,const char *bonus_script, return 1; } -int pc_delautobonus(struct map_session_data* sd, struct s_autobonus *autobonus,char max,bool restore) +static int pc_delautobonus(struct map_session_data *sd, struct s_autobonus *autobonus, char max, bool restore) { int i; nullpo_ret(sd); + nullpo_ret(autobonus); for( i = 0; i < max; i++ ) { @@ -2117,7 +2294,7 @@ int pc_delautobonus(struct map_session_data* sd, struct s_autobonus *autobonus,c return 0; } -int pc_exeautobonus(struct map_session_data *sd,struct s_autobonus *autobonus) +static int pc_exeautobonus(struct map_session_data *sd, struct s_autobonus *autobonus) { nullpo_ret(sd); nullpo_ret(autobonus); @@ -2137,7 +2314,8 @@ int pc_exeautobonus(struct map_session_data *sd,struct s_autobonus *autobonus) return 0; } -int pc_endautobonus(int tid, int64 tick, int id, intptr_t data) { +static int pc_endautobonus(int tid, int64 tick, int id, intptr_t data) +{ struct map_session_data *sd = map->id2sd(id); struct s_autobonus *autobonus = (struct s_autobonus *)data; @@ -2150,11 +2328,12 @@ int pc_endautobonus(int tid, int64 tick, int id, intptr_t data) { return 0; } -int pc_bonus_addele(struct map_session_data* sd, unsigned char ele, short rate, short flag) +static int pc_bonus_addele(struct map_session_data *sd, unsigned char ele, short rate, short flag) { int i; struct weapon_data* wd; + nullpo_ret(sd); wd = (sd->state.lr_flag ? &sd->left_weapon : &sd->right_weapon); ARR_FIND(0, MAX_PC_BONUS, i, wd->addele2[i].rate == 0); @@ -2184,10 +2363,11 @@ int pc_bonus_addele(struct map_session_data* sd, unsigned char ele, short rate, return 0; } -int pc_bonus_subele(struct map_session_data* sd, unsigned char ele, short rate, short flag) +static int pc_bonus_subele(struct map_session_data *sd, unsigned char ele, short rate, short flag) { int i; + nullpo_ret(sd); ARR_FIND(0, MAX_PC_BONUS, i, sd->subele2[i].rate == 0); if (i == MAX_PC_BONUS) @@ -2229,7 +2409,8 @@ int pc_bonus_subele(struct map_session_data* sd, unsigned char ele, short rate, /*========================================== * Add a bonus(type) to player sd *------------------------------------------*/ -int pc_bonus(struct map_session_data *sd,int type,int val) { +static int pc_bonus(struct map_session_data *sd, int type, int val) +{ struct status_data *bst; int bonus; int i; @@ -2345,7 +2526,7 @@ int pc_bonus(struct map_session_data *sd,int type,int val) { } switch (sd->state.lr_flag) { case 2: - switch (sd->status.weapon) { + switch (sd->weapontype) { case W_BOW: case W_REVOLVER: case W_RIFLE: @@ -2412,7 +2593,7 @@ int pc_bonus(struct map_session_data *sd,int type,int val) { case SP_ATTACKRANGE: switch (sd->state.lr_flag) { case 2: - switch (sd->status.weapon) { + switch (sd->weapontype) { case W_BOW: case W_REVOLVER: case W_RIFLE: @@ -2673,7 +2854,7 @@ int pc_bonus(struct map_session_data *sd,int type,int val) { case SP_INTRAVISION: // Maya Purple Card effect allowing to see Hiding/Cloaking people [DracoRPG] if(sd->state.lr_flag != 2) { sd->special_state.intravision = 1; - clif->status_change(&sd->bl, SI_CLAIRVOYANCE, 1, 0, 0, 0, 0); + clif->status_change(&sd->bl, status->get_sc_icon(SC_CLAIRVOYANCE), status->get_sc_relevant_bl_types(SC_CLAIRVOYANCE), 1, 0, 0, 0, 0); } break; case SP_NO_KNOCKBACK: @@ -2878,7 +3059,7 @@ int pc_bonus(struct map_session_data *sd,int type,int val) { #endif case SP_ADD_MONSTER_DROP_CHAINITEM: if (sd->state.lr_flag != 2) - pc->bonus_item_drop(sd->add_drop, ARRAYLENGTH(sd->add_drop), 0, val, map->race_id2mask(RC_ALL), 10000); + pc->bonus_item_drop(sd->add_drop, ARRAYLENGTH(sd->add_drop), val, true, map->race_id2mask(RC_ALL), 10000); break; case SP_ADDMAXWEIGHT: if (sd->state.lr_flag != 2) @@ -2895,7 +3076,7 @@ int pc_bonus(struct map_session_data *sd,int type,int val) { /*========================================== * Player bonus (type) with args type2 and val, called trough bonus2 (npc) *------------------------------------------*/ -int pc_bonus2(struct map_session_data *sd,int type,int type2,int val) +static int pc_bonus2(struct map_session_data *sd, int type, int type2, int val) { int i; @@ -3453,7 +3634,7 @@ int pc_bonus2(struct map_session_data *sd,int type,int type2,int val) break; case SP_ADD_MONSTER_DROP_ITEM: if (sd->state.lr_flag != 2) - pc->bonus_item_drop(sd->add_drop, ARRAYLENGTH(sd->add_drop), type2, 0, map->race_id2mask(RC_ALL), val); + pc->bonus_item_drop(sd->add_drop, ARRAYLENGTH(sd->add_drop), type2, false, map->race_id2mask(RC_ALL), val); break; case SP_SP_LOSS_RATE: if(sd->state.lr_flag != 2) { @@ -3659,7 +3840,7 @@ int pc_bonus2(struct map_session_data *sd,int type,int type2,int val) break; } if (sd->state.lr_flag != 2) - pc->bonus_item_drop(sd->add_drop, ARRAYLENGTH(sd->add_drop), 0, type2, race_mask, 10000); + pc->bonus_item_drop(sd->add_drop, ARRAYLENGTH(sd->add_drop), type2, true, race_mask, 10000); } break; #ifdef RENEWAL @@ -3685,7 +3866,7 @@ int pc_bonus2(struct map_session_data *sd,int type,int type2,int val) return 0; } -int pc_bonus3(struct map_session_data *sd,int type,int type2,int type3,int val) +static int pc_bonus3(struct map_session_data *sd, int type, int type2, int type3, int val) { int i; nullpo_ret(sd); @@ -3699,12 +3880,12 @@ int pc_bonus3(struct map_session_data *sd,int type,int type2,int type3,int val) break; } if (sd->state.lr_flag != 2) - pc->bonus_item_drop(sd->add_drop, ARRAYLENGTH(sd->add_drop), type2, 0, race_mask, val); + pc->bonus_item_drop(sd->add_drop, ARRAYLENGTH(sd->add_drop), type2, false, race_mask, val); } break; case SP_ADD_CLASS_DROP_ITEM: if(sd->state.lr_flag != 2) - pc->bonus_item_drop(sd->add_drop, ARRAYLENGTH(sd->add_drop), type2, 0, -type3, val); + pc->bonus_item_drop(sd->add_drop, ARRAYLENGTH(sd->add_drop), type2, false, -type3, val); break; case SP_AUTOSPELL: if(sd->state.lr_flag != 2) @@ -3859,7 +4040,8 @@ int pc_bonus3(struct map_session_data *sd,int type,int type2,int type3,int val) return 0; } -int pc_bonus4(struct map_session_data *sd,int type,int type2,int type3,int type4,int val) { +static int pc_bonus4(struct map_session_data *sd, int type, int type2, int type3, int type4, int val) +{ int i; nullpo_ret(sd); @@ -3954,7 +4136,8 @@ int pc_bonus4(struct map_session_data *sd,int type,int type2,int type3,int type4 return 0; } -int pc_bonus5(struct map_session_data *sd,int type,int type2,int type3,int type4,int type5,int val) { +static int pc_bonus5(struct map_session_data *sd, int type, int type2, int type3, int type4, int type5, int val) +{ nullpo_ret(sd); switch(type){ @@ -3988,9 +4171,9 @@ int pc_bonus5(struct map_session_data *sd,int type,int type2,int type3,int type4 * Grants a player a given skill. * Flag values: @see enum pc_skill_flag *------------------------------------------*/ -int pc_skill(struct map_session_data *sd, int id, int level, int flag) +static int pc_skill(struct map_session_data *sd, int id, int level, int flag) { - uint16 index = 0; + int index = 0; nullpo_ret(sd); if (!(index = skill->get_index(id))) { @@ -4023,7 +4206,7 @@ int pc_skill(struct map_session_data *sd, int id, int level, int flag) if( sd->status.skill[index].id == id ) { if( sd->status.skill[index].lv >= level ) return 0; - if( sd->status.skill[index].flag == SKILL_FLAG_PERMANENT ) //Non-granted skill, store it's level. + if (sd->status.skill[index].flag == SKILL_FLAG_PERMANENT) // Non-granted skill, store its level. sd->status.skill[index].flag = SKILL_FLAG_REPLACED_LV_0 + sd->status.skill[index].lv; } else { sd->status.skill[index].id = id; @@ -4067,13 +4250,13 @@ int pc_skill(struct map_session_data *sd, int id, int level, int flag) * @param idx_equip The target equipment's inventory index. * @retval true if the card can be inserted. */ -bool pc_can_insert_card_into(struct map_session_data* sd, int idx_card, int idx_equip) +static bool pc_can_insert_card_into(struct map_session_data *sd, int idx_card, int idx_equip) { int i; nullpo_ret(sd); - if (idx_equip < 0 || idx_equip >= MAX_INVENTORY || sd->inventory_data[idx_equip] == NULL) + if (idx_equip < 0 || idx_equip >= sd->status.inventorySize || sd->inventory_data[idx_equip] == NULL) return false; //Invalid item index. if (sd->status.inventory[idx_equip].nameid <= 0 || sd->status.inventory[idx_equip].amount < 1) return false; // target item missing @@ -4093,7 +4276,7 @@ bool pc_can_insert_card_into(struct map_session_data* sd, int idx_card, int idx_ ARR_FIND( 0, sd->inventory_data[idx_equip]->slot, i, sd->status.inventory[idx_equip].card[i] == 0); if (i == sd->inventory_data[idx_equip]->slot) return false; // no free slots - return true; + return true; } /** @@ -4103,17 +4286,17 @@ bool pc_can_insert_card_into(struct map_session_data* sd, int idx_card, int idx_ * @param idx_card The card's inventory index. * @retval true if the card can be inserted. */ -bool pc_can_insert_card(struct map_session_data* sd, int idx_card) +static bool pc_can_insert_card(struct map_session_data *sd, int idx_card) { nullpo_ret(sd); - if (idx_card < 0 || idx_card >= MAX_INVENTORY || sd->inventory_data[idx_card] == NULL) + if (idx_card < 0 || idx_card >= sd->status.inventorySize || sd->inventory_data[idx_card] == NULL) return false; //Invalid card index. if (sd->status.inventory[idx_card].nameid <= 0 || sd->status.inventory[idx_card].amount < 1) return false; // target card missing if (sd->inventory_data[idx_card]->type != IT_CARD) return false; // must be a card - return true; + return true; } /*========================================== @@ -4122,7 +4305,7 @@ bool pc_can_insert_card(struct map_session_data* sd, int idx_card) * 0 = fail * 1 = success *------------------------------------------*/ -int pc_insert_card(struct map_session_data* sd, int idx_card, int idx_equip) +static int pc_insert_card(struct map_session_data *sd, int idx_card, int idx_equip) { int nameid; @@ -4164,11 +4347,9 @@ int pc_insert_card(struct map_session_data* sd, int idx_card, int idx_equip) /*========================================== * Update buying value by skills *------------------------------------------*/ -int pc_modifybuyvalue(struct map_session_data *sd, int orig_value) +static int pc_modifybuyvalue(struct map_session_data *sd, int orig_value) { int skill_lv, rate1 = 0, rate2 = 0; - if (orig_value <= 0) - return 0; if ((skill_lv=pc->checkskill(sd,MC_DISCOUNT)) > 0) // merchant discount rate1 = 5+skill_lv*2-((skill_lv==10)? 1:0); if ((skill_lv=pc->checkskill(sd,RG_COMPULSION)) > 0) // rogue discount @@ -4177,25 +4358,25 @@ int pc_modifybuyvalue(struct map_session_data *sd, int orig_value) rate1 = rate2; if (rate1 != 0) orig_value = apply_percentrate(orig_value, 100-rate1, 100); - if (orig_value < 1) - orig_value = 1; + + if (orig_value < battle_config.min_item_buy_price) + orig_value = battle_config.min_item_buy_price; return orig_value; } /*========================================== * Update selling value by skills *------------------------------------------*/ -int pc_modifysellvalue(struct map_session_data *sd, int orig_value) +static int pc_modifysellvalue(struct map_session_data *sd, int orig_value) { int skill_lv, rate = 0; - if (orig_value <= 0) - return 0; if ((skill_lv=pc->checkskill(sd,MC_OVERCHARGE)) > 0) //OverCharge rate = 5+skill_lv*2-((skill_lv==10)? 1:0); if (rate != 0) orig_value = apply_percentrate(orig_value, 100+rate, 100); - if (orig_value < 1) - orig_value = 1; + + if (orig_value < battle_config.min_item_sell_price) + orig_value = battle_config.min_item_sell_price; return orig_value; } @@ -4203,9 +4384,8 @@ int pc_modifysellvalue(struct map_session_data *sd, int orig_value) * Checking if we have enough place on inventory for new item * Make sure to take 30k as limit (for client I guess) *------------------------------------------*/ -int pc_checkadditem(struct map_session_data *sd,int nameid,int amount) +static int pc_checkadditem(struct map_session_data *sd, int nameid, int amount) { - int i; struct item_data* data; nullpo_ret(sd); @@ -4221,7 +4401,7 @@ int pc_checkadditem(struct map_session_data *sd,int nameid,int amount) if( data->stack.inventory && amount > data->stack.amount ) return ADDITEM_OVERAMOUNT; - for(i=0;i<MAX_INVENTORY;i++){ + for(int i = 0; i < sd->status.inventorySize; i++) { // FIXME: This does not consider the checked item's cards, thus could check a wrong slot for stackability. if(sd->status.inventory[i].nameid==nameid){ if( amount > MAX_AMOUNT - sd->status.inventory[i].amount || ( data->stack.inventory && amount > data->stack.amount - sd->status.inventory[i].amount ) ) @@ -4237,13 +4417,12 @@ int pc_checkadditem(struct map_session_data *sd,int nameid,int amount) * Return number of available place in inventory * Each non stackable item will reduce place by 1 *------------------------------------------*/ -int pc_inventoryblank(struct map_session_data *sd) +static int pc_inventoryblank(struct map_session_data *sd) { - int i,b; - nullpo_ret(sd); + int b = 0; - for(i=0,b=0;i<MAX_INVENTORY;i++){ + for (int i = 0; i < sd->status.inventorySize; i++) { if(sd->status.inventory[i].nameid==0) b++; } @@ -4254,7 +4433,7 @@ int pc_inventoryblank(struct map_session_data *sd) /*========================================== * attempts to remove zeny from player (sd) *------------------------------------------*/ -int pc_payzeny(struct map_session_data *sd,int zeny, enum e_log_pick_type type, struct map_session_data *tsd) +static int pc_payzeny(struct map_session_data *sd, int zeny, enum e_log_pick_type type, struct map_session_data *tsd) { nullpo_retr(-1,sd); @@ -4271,59 +4450,70 @@ int pc_payzeny(struct map_session_data *sd,int zeny, enum e_log_pick_type type, sd->status.zeny -= zeny; clif->updatestatus(sd,SP_ZENY); - if(!tsd) tsd = sd; - logs->zeny(sd, type, tsd, -zeny); - if( zeny > 0 && sd->state.showzeny ) { - char output[255]; - sprintf(output, "Removed %dz.", zeny); - clif_disp_onlyself(sd,output,strlen(output)); + if (zeny > 0) { + achievement->validate_zeny(sd, -zeny); // Achievements [Smokexyz/Hercules] + logs->zeny(sd, type, tsd ? tsd : sd, -zeny); + + if (sd->state.showzeny) { + char output[255]; + sprintf(output, msg_sd(sd, 885), zeny); // Removed %dz. + clif_disp_onlyself(sd, output); + } } return 0; } -/*========================================== - * Cash Shop - *------------------------------------------*/ -int pc_paycash(struct map_session_data *sd, int price, int points) +/** + * Calculates leftover cashpoints and kafrapoints when buying an item from cashshop + * + * @param price Price of the item. + * @param points Provided kafra points. + * + * @return points Leftover kafra points. + */ +//Changed Kafrapoints calculation. [Normynator] +static int pc_paycash(struct map_session_data *sd, int price, int points) { int cash; - nullpo_retr(-1,sd); + int mempoints; + nullpo_retr(-1, sd); - points = cap_value(points,-MAX_ZENY,MAX_ZENY); //prevent command UB - if( price < 0 || points < 0 ) - { + points = cap_value(points, -MAX_ZENY, MAX_ZENY); //prevent command UB + if (price < 0 || points < 0) { ShowError("pc_paycash: Paying negative points (price=%d, points=%d, account_id=%d, char_id=%d).\n", price, points, sd->status.account_id, sd->status.char_id); return -2; } - if( points > price ) - { + if (points > price) { ShowWarning("pc_paycash: More kafra points provided than needed (price=%d, points=%d, account_id=%d, char_id=%d).\n", price, points, sd->status.account_id, sd->status.char_id); - points = price; + points = points - price; + mempoints = price; + cash = 0; + } else { + cash = price - points; + mempoints = points; + points = 0; } - cash = price-points; - - if( sd->cashPoints < cash || sd->kafraPoints < points ) - { + if (sd->cashPoints < cash || sd->kafraPoints < mempoints) { ShowError("pc_paycash: Not enough points (cash=%d, kafra=%d) to cover the price (cash=%d, kafra=%d) (account_id=%d, char_id=%d).\n", sd->cashPoints, sd->kafraPoints, cash, points, sd->status.account_id, sd->status.char_id); return -1; } - pc_setaccountreg(sd, script->add_str("#CASHPOINTS"), sd->cashPoints-cash); - pc_setaccountreg(sd, script->add_str("#KAFRAPOINTS"), sd->kafraPoints-points); + pc_setaccountreg(sd, script->add_variable("#CASHPOINTS"), sd->cashPoints - cash); + pc_setaccountreg(sd, script->add_variable("#KAFRAPOINTS"), sd->kafraPoints - mempoints); - if( battle_config.cashshop_show_points ) - { + if (battle_config.cashshop_show_points) { char output[128]; sprintf(output, msg_sd(sd,504), points, cash, sd->kafraPoints, sd->cashPoints); - clif_disp_onlyself(sd, output, strlen(output)); + clif_disp_onlyself(sd, output); } - return cash+points; + + return points; } -int pc_getcash(struct map_session_data *sd, int cash, int points) +static int pc_getcash(struct map_session_data *sd, int cash, int points) { char output[128]; nullpo_retr(-1,sd); @@ -4338,12 +4528,12 @@ int pc_getcash(struct map_session_data *sd, int cash, int points) cash = MAX_ZENY-sd->cashPoints; } - pc_setaccountreg(sd, script->add_str("#CASHPOINTS"), sd->cashPoints+cash); + pc_setaccountreg(sd, script->add_variable("#CASHPOINTS"), sd->cashPoints+cash); if( battle_config.cashshop_show_points ) { sprintf(output, msg_sd(sd,505), cash, sd->cashPoints); - clif_disp_onlyself(sd, output, strlen(output)); + clif_disp_onlyself(sd, output); } return cash; } @@ -4361,12 +4551,12 @@ int pc_getcash(struct map_session_data *sd, int cash, int points) points = MAX_ZENY-sd->kafraPoints; } - pc_setaccountreg(sd, script->add_str("#KAFRAPOINTS"), sd->kafraPoints+points); + pc_setaccountreg(sd, script->add_variable("#KAFRAPOINTS"), sd->kafraPoints+points); if( battle_config.cashshop_show_points ) { sprintf(output, msg_sd(sd,506), points, sd->kafraPoints); - clif_disp_onlyself(sd, output, strlen(output)); + clif_disp_onlyself(sd, output); } return points; } @@ -4382,7 +4572,7 @@ int pc_getcash(struct map_session_data *sd, int cash, int points) * Attempts to give zeny to player (sd) * tsd (optional) from who for log (if null take sd) *------------------------------------------*/ -int pc_getzeny(struct map_session_data *sd,int zeny, enum e_log_pick_type type, struct map_session_data *tsd) +static int pc_getzeny(struct map_session_data *sd, int zeny, enum e_log_pick_type type, struct map_session_data *tsd) { nullpo_retr(-1,sd); @@ -4399,12 +4589,15 @@ int pc_getzeny(struct map_session_data *sd,int zeny, enum e_log_pick_type type, sd->status.zeny += zeny; clif->updatestatus(sd,SP_ZENY); - if(!tsd) tsd = sd; - logs->zeny(sd, type, tsd, zeny); - if( zeny > 0 && sd->state.showzeny ) { - char output[255]; - sprintf(output, "Gained %dz.", zeny); - clif_disp_onlyself(sd,output,strlen(output)); + if (zeny > 0) { + achievement->validate_zeny(sd, zeny); // Achievements [Smokexyz/Hercules] + logs->zeny(sd, type, tsd ? tsd : sd, zeny); + + if (sd->state.showzeny) { + char output[255]; + sprintf(output, msg_sd(sd, 886), zeny); // Gained %dz. + clif_disp_onlyself(sd, output); + } } return 0; @@ -4421,12 +4614,13 @@ int pc_getzeny(struct map_session_data *sd,int zeny, enum e_log_pick_type type, * @return the inventory index of the first instance of the requested item. * @retval INDEX_NOT_FOUND if the item wasn't found. */ -int pc_search_inventory(struct map_session_data *sd, int item_id) { +static int pc_search_inventory(struct map_session_data *sd, int item_id) +{ int i; nullpo_retr(INDEX_NOT_FOUND, sd); - ARR_FIND( 0, MAX_INVENTORY, i, sd->status.inventory[i].nameid == item_id && (sd->status.inventory[i].amount > 0 || item_id == 0) ); - return ( i < MAX_INVENTORY ) ? i : INDEX_NOT_FOUND; + ARR_FIND(0, sd->status.inventorySize, i, sd->status.inventory[i].nameid == item_id && (sd->status.inventory[i].amount > 0 || item_id == 0)); + return (i < sd->status.inventorySize) ? i : INDEX_NOT_FOUND; } /*========================================== @@ -4441,7 +4635,7 @@ int pc_search_inventory(struct map_session_data *sd, int item_id) { * 6 = ? * 7 = stack limitation *------------------------------------------*/ -int pc_additem(struct map_session_data *sd,struct item *item_data,int amount,e_log_pick_type log_type) +static int pc_additem(struct map_session_data *sd, const struct item *item_data, int amount, e_log_pick_type log_type) { struct item_data *data; int i; @@ -4488,11 +4682,11 @@ int pc_additem(struct map_session_data *sd,struct item *item_data,int amount,e_l } } - i = MAX_INVENTORY; + i = sd->status.inventorySize; // Stackable | Non Rental if( itemdb->isstackable2(data) && item_data->expire_time == 0 ) { - for( i = 0; i < MAX_INVENTORY; i++ ) { + for (i = 0; i < sd->status.inventorySize; i++) { if( sd->status.inventory[i].nameid == item_data->nameid && sd->status.inventory[i].bound == item_data->bound && sd->status.inventory[i].expire_time == 0 && @@ -4507,7 +4701,7 @@ int pc_additem(struct map_session_data *sd,struct item *item_data,int amount,e_l } } - if ( i >= MAX_INVENTORY ) { + if (i >= sd->status.inventorySize) { i = pc->search_inventory(sd,0); if (i == INDEX_NOT_FOUND) return 4; @@ -4522,6 +4716,7 @@ int pc_additem(struct map_session_data *sd,struct item *item_data,int amount,e_l sd->status.inventory[i].amount = amount; sd->inventory_data[i] = data; clif->additem(sd,i,amount,0); + } if( ( !itemdb->isstackable2(data) || data->flag.force_serial || data->type == IT_CASH) && !item_data->unique_id ) @@ -4529,22 +4724,34 @@ int pc_additem(struct map_session_data *sd,struct item *item_data,int amount,e_l logs->pick_pc(sd, log_type, amount, &sd->status.inventory[i],sd->inventory_data[i]); + achievement->validate_item_get(sd, sd->status.inventory[i].nameid, sd->status.inventory[i].amount); // Achievements [Smokexyz/Hercules] + sd->weight += w; clif->updatestatus(sd,SP_WEIGHT); + + // auto-favorite + if (data->flag.auto_favorite > 0) { + sd->status.inventory[i].favorite = 1; + clif->favorite_item(sd, i); + } + //Auto-equip if(data->flag.autoequip) pc->equipitem(sd, i, data->equip); /* rental item check */ - if( item_data->expire_time ) { - if( time(NULL) > item_data->expire_time ) { - pc->rental_expire(sd,i); + if (item_data->expire_time > 0) { + if (time(NULL) > item_data->expire_time) { + pc->rental_expire(sd, i); } else { - int seconds = (int)( item_data->expire_time - time(NULL) ); + int seconds = (int)(item_data->expire_time - time(NULL)); clif->rental_time(sd->fd, sd->status.inventory[i].nameid, seconds); pc->inventory_rental_add(sd, seconds); + if (data->rental_start_script != NULL) + script->run_item_rental_start_script(sd, data, 0); } } + quest->questinfo_refresh(sd); return 0; } @@ -4560,9 +4767,10 @@ int pc_additem(struct map_session_data *sd,struct item *item_data,int amount,e_l * 0 = success * 1 = invalid itemid or negative amount *------------------------------------------*/ -int pc_delitem(struct map_session_data *sd,int n,int amount,int type, short reason, e_log_pick_type log_type) +static int pc_delitem(struct map_session_data *sd, int n, int amount, int type, short reason, e_log_pick_type log_type) { nullpo_retr(1, sd); + Assert_retr(1, n >= 0 && n < sd->status.inventorySize); if(sd->status.inventory[n].nameid==0 || amount <= 0 || sd->status.inventory[n].amount<amount || sd->inventory_data[n] == NULL) return 1; @@ -4571,16 +4779,26 @@ int pc_delitem(struct map_session_data *sd,int n,int amount,int type, short reas sd->status.inventory[n].amount -= amount; sd->weight -= sd->inventory_data[n]->weight*amount ; + + // It's here because the data would most likely get zeroed in following if [Hemagx] + struct item_data *itd = sd->inventory_data[n]; + bool is_rental = (sd->status.inventory[n].expire_time > 0) ? true : false; + if( sd->status.inventory[n].amount <= 0 ){ if(sd->status.inventory[n].equip) pc->unequipitem(sd, n, PCUNEQUIPITEM_RECALC|PCUNEQUIPITEM_FORCE); memset(&sd->status.inventory[n],0,sizeof(sd->status.inventory[0])); sd->inventory_data[n] = NULL; } + + if (is_rental && itd->rental_end_script != NULL) + script->run_item_rental_end_script(sd, itd, 0); + if(!(type&1)) clif->delitem(sd,n,amount,reason); if(!(type&2)) clif->updatestatus(sd,SP_WEIGHT); + quest->questinfo_refresh(sd); return 0; } @@ -4591,11 +4809,11 @@ int pc_delitem(struct map_session_data *sd,int n,int amount,int type, short reas * 0 = fail * 1 = success *------------------------------------------*/ -int pc_dropitem(struct map_session_data *sd,int n,int amount) +static int pc_dropitem(struct map_session_data *sd, int n, int amount) { nullpo_retr(1, sd); - if(n < 0 || n >= MAX_INVENTORY) + if(n < 0 || n >= sd->status.inventorySize) return 0; if(amount <= 0) @@ -4604,23 +4822,23 @@ int pc_dropitem(struct map_session_data *sd,int n,int amount) if(sd->status.inventory[n].nameid <= 0 || sd->status.inventory[n].amount <= 0 || sd->status.inventory[n].amount < amount || - sd->state.trading || sd->state.vending || + sd->state.trading || sd->state.vending || sd->state.prevend || !sd->inventory_data[n] //pc->delitem would fail on this case. ) return 0; if( map->list[sd->bl.m].flag.nodrop ) { - clif->message (sd->fd, msg_sd(sd,271)); - return 0; //Can't drop items in nodrop mapflag maps. + clif->message (sd->fd, msg_sd(sd,271)); // You can't drop items in this map + return 0; } if( !pc->candrop(sd,&sd->status.inventory[n]) ) { - clif->message (sd->fd, msg_sd(sd,263)); + clif->message (sd->fd, msg_sd(sd,263)); // This item cannot be dropped. return 0; } - if (!map->addflooritem(&sd->bl, &sd->status.inventory[n], amount, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 2)) + if (!map->addflooritem(&sd->bl, &sd->status.inventory[n], amount, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 2, false)) return 0; pc->delitem(sd, n, amount, 1, DELITEM_NORMAL, LOG_TYPE_PICKDROP_PLAYER); @@ -4634,7 +4852,7 @@ int pc_dropitem(struct map_session_data *sd,int n,int amount) * 0 = fail * 1 = success *------------------------------------------*/ -int pc_takeitem(struct map_session_data *sd,struct flooritem_data *fitem) +static int pc_takeitem(struct map_session_data *sd, struct flooritem_data *fitem) { int flag=0; int64 tick = timer->gettick(); @@ -4700,12 +4918,13 @@ int pc_takeitem(struct map_session_data *sd,struct flooritem_data *fitem) * 0 = no * 1 = yes *------------------------------------------*/ -int pc_isUseitem(struct map_session_data *sd,int n) +static int pc_isUseitem(struct map_session_data *sd, int n) { struct item_data *item; int nameid; nullpo_ret(sd); + Assert_ret(n >= 0 && n < sd->status.inventorySize); item = sd->inventory_data[n]; nameid = sd->status.inventory[n].nameid; @@ -4719,33 +4938,73 @@ int pc_isUseitem(struct map_session_data *sd,int n) return 0; if ((item->item_usage.flag&INR_SITTING) && (pc_issit(sd) == 1) && (pc_get_group_level(sd) < item->item_usage.override)) { - clif->msgtable(sd, MSG_ITEM_NEED_STANDING); + clif->msgtable(sd, MSG_CANT_USE_WHEN_SITDOWN); //clif->messagecolor_self(sd->fd, COLOR_WHITE, msg_txt(1474)); return 0; // You cannot use this item while sitting. } - if (sd->state.storage_flag != STORAGE_FLAG_CLOSED && item->type != IT_CASH) { - clif->messagecolor_self(sd->fd, COLOR_RED, msg_sd(sd,1475)); - return 0; // You cannot use this item while storage is open. - } - switch( nameid ) { // TODO: Is there no better way to handle this, other than hardcoding item IDs? case ITEMID_ANODYNE: - if( map_flag_gvg2(sd->bl.m) ) - return 0; - /* Fall through */ - case ITEMID_ALOEBERA: - if( pc_issit(sd) ) + if (map_flag_gvg2(sd->bl.m)) { +#if PACKETVER >= 20080311 + clif->skill_mapinfomessage(sd, 3); +#else + clif->messagecolor_self(sd->fd, COLOR_CYAN, msg_sd(sd, 51)); +#endif return 0; + } break; + + case ITEMID_GIANT_FLY_WING: { + struct party_data *p; + + if (!sd->status.party_id) { +#if PACKETVER >= 20061030 + clif->msgtable(sd, MSG_CANNOT_PARTYCALL); +#endif + break; + } + + if ((p = party->search(sd->status.party_id)) != NULL) { + int i; + int16 m; + + ARR_FIND(0, MAX_PARTY, i, p->data[i].sd == sd); + + if (i == MAX_PARTY || !p->party.member[i].leader) { +#if PACKETVER >= 20061030 + clif->msgtable(sd, MSG_CANNOT_PARTYCALL); +#endif + break; + } + + m = sd->bl.m; + + ARR_FIND(0, MAX_PARTY, i, p->data[i].sd && p->data[i].sd != sd && p->data[i].sd->bl.m == m); + + if (i == MAX_PARTY || pc_isdead(p->data[i].sd)) { +#if PACKETVER >= 20061030 + clif->msgtable(sd, MSG_NO_PARTYMEM_ON_THISMAP); +#endif + break; + } + } + } + FALLTHROUGH case ITEMID_WING_OF_FLY: - case ITEMID_GIANT_FLY_WING: - if( map->list[sd->bl.m].flag.noteleport || map_flag_gvg2(sd->bl.m) ) { - clif->skill_mapinfomessage(sd,0); + case ITEMID_N_FLY_WING: + case ITEMID_C_WING_OF_FLY: + if (map->list[sd->bl.m].flag.noteleport || map_flag_gvg2(sd->bl.m)) { +#if PACKETVER >= 20080311 + clif->skill_mapinfomessage(sd, 0); +#else + clif->messagecolor_self(sd->fd, COLOR_RED, msg_sd(sd, 49)); +#endif return 0; } /* Fall through */ case ITEMID_WING_OF_BUTTERFLY: + case ITEMID_N_BUTTERFLY_WING: case ITEMID_DUN_TELE_SCROLL1: case ITEMID_DUN_TELE_SCROLL2: case ITEMID_WOB_RUNE: // Yellow Butterfly Wing @@ -4757,34 +5016,27 @@ int pc_isUseitem(struct map_session_data *sd,int n) clif->message(sd->fd, msg_sd(sd,863)); // "Duel: Can't use this item in duel." return 0; } - if( nameid != ITEMID_WING_OF_FLY && nameid != ITEMID_GIANT_FLY_WING && map->list[sd->bl.m].flag.noreturn ) + if (nameid != ITEMID_WING_OF_FLY && nameid != ITEMID_GIANT_FLY_WING && map->list[sd->bl.m].flag.noreturn) { +#if PACKETVER >= 20080311 + clif->skill_mapinfomessage(sd, 0); +#else + clif->messagecolor_self(sd->fd, COLOR_RED, msg_sd(sd, 49)); +#endif return 0; + } break; case ITEMID_BRANCH_OF_DEAD_TREE: case ITEMID_RED_POUCH_OF_SURPRISE: case ITEMID_BLOODY_DEAD_BRANCH: case ITEMID_PORING_BOX: - if( map->list[sd->bl.m].flag.nobranch || map_flag_gvg2(sd->bl.m) ) - return 0; - break; - case ITEMID_BUBBLE_GUM: - case ITEMID_COMP_BUBBLE_GUM: - if( sd->sc.data[SC_CASH_RECEIVEITEM] ) - return 0; - break; - case ITEMID_BATTLE_MANUAL: - case ITEMID_COMP_BATTLE_MANUAL: - case ITEMID_THICK_MANUAL50: - case ITEMID_NOBLE_NAMEPLATE: - case ITEMID_BATTLE_MANUAL25: - case ITEMID_BATTLE_MANUAL100: - case ITEMID_BATTLE_MANUAL_X3: - if( sd->sc.data[SC_CASH_PLUSEXP] ) - return 0; - break; - case ITEMID_JOB_MANUAL50: - if( sd->sc.data[SC_CASH_PLUSONLYJOBEXP] ) + if (map->list[sd->bl.m].flag.nobranch || map_flag_gvg2(sd->bl.m)) { +#if PACKETVER >= 20080311 + clif->skill_mapinfomessage(sd, 3); +#else + clif->messagecolor_self(sd->fd, COLOR_CYAN, msg_sd(sd, 51)); +#endif return 0; + } break; // Mercenary Items @@ -4804,28 +5056,23 @@ int pc_isUseitem(struct map_session_data *sd,int n) break; case ITEMID_NEURALIZER: - if( !map->list[sd->bl.m].flag.reset ) + if (!map->list[sd->bl.m].flag.reset) { +#if PACKETVER >= 20080311 + clif->skill_mapinfomessage(sd, 3); +#else + clif->messagecolor_self(sd->fd, COLOR_CYAN, msg_sd(sd, 51)); +#endif return 0; + } break; } if( nameid >= ITEMID_BOW_MERCENARY_SCROLL1 && nameid <= ITEMID_SPEARMERCENARY_SCROLL10 && sd->md != NULL ) // Mercenary Scrolls return 0; - /** - * Only Rune Knights may use runes - **/ - if( itemdb_is_rune(nameid) && (sd->class_&MAPID_THIRDMASK) != MAPID_RUNE_KNIGHT ) - return 0; - /** - * Only GCross may use poisons - **/ - else if( itemdb_is_poison(nameid) && (sd->class_&MAPID_THIRDMASK) != MAPID_GUILLOTINE_CROSS ) - return 0; - if( item->package || item->group ) { if (pc_is90overweight(sd)) { - clif->msgtable(sd, MSG_ITEM_CANT_OBTAIN_WEIGHT); + clif->msgtable(sd, MSG_CANT_GET_ITEM_BECAUSE_WEIGHT); return 0; } if (!pc->inventoryblank(sd)) { @@ -4838,42 +5085,62 @@ int pc_isUseitem(struct map_session_data *sd,int n) if(item->sex != 2 && sd->status.sex != item->sex) return 0; //Required level check - if (item->elv && sd->status.base_level < (unsigned int)item->elv) { - clif->msgtable(sd, MSG_ITEM_CANT_USE_LVL); + if (item->elv && sd->status.base_level < item->elv) { +#if PACKETVER >= 20100525 + clif->msgtable(sd, MSG_CANNOT_USE_ITEM_LEVEL); +#endif return 0; } - if (item->elvmax && sd->status.base_level > (unsigned int)item->elvmax) { - clif->msgtable(sd, MSG_ITEM_CANT_USE_LVL); + if (item->elvmax && sd->status.base_level > item->elvmax) { +#if PACKETVER >= 20100525 + clif->msgtable(sd, MSG_CANNOT_USE_ITEM_LEVEL); +#endif return 0; } //Not equipable by class. [Skotlex] - if (!( - (1ULL<<(sd->class_&MAPID_BASEMASK)) & - (item->class_base[(sd->class_&JOBL_2_1) ? 1 : ((sd->class_&JOBL_2_2) ? 2 : 0)]) - )) + if (((1ULL << (sd->job & MAPID_BASEMASK)) & (item->class_base[(sd->job & JOBL_2_1) ? 1 : ((sd->job & JOBL_2_2) ? 2 : 0)])) == 0) return 0; //Not usable by upper class. [Haru] while( 1 ) { // Normal classes (no upper, no baby, no third classes) - if( item->class_upper&ITEMUPPER_NORMAL && !(sd->class_&(JOBL_UPPER|JOBL_THIRD|JOBL_BABY)) ) break; + if ((item->class_upper & ITEMUPPER_NORMAL) != 0) { + if ((sd->job & (JOBL_UPPER|JOBL_THIRD|JOBL_BABY)) == 0) + break; + } + if ((item->class_upper & ITEMUPPER_UPPER) != 0) { #ifdef RENEWAL - // Upper classes (no third classes) - if( item->class_upper&ITEMUPPER_UPPER && sd->class_&JOBL_UPPER && !(sd->class_&JOBL_THIRD) ) break; + // Upper classes (no third classes) + if ((sd->job & JOBL_UPPER) != 0 && (sd->job&JOBL_THIRD) == 0) + break; #else - //pre-re has no use for the extra, so we maintain the previous for backwards compatibility - if( item->class_upper&ITEMUPPER_UPPER && sd->class_&(JOBL_UPPER|JOBL_THIRD) ) break; + //pre-re has no use for the extra, so we maintain the previous for backwards compatibility + if ((sd->job & (JOBL_UPPER|JOBL_THIRD)) != 0) + break; #endif + } // Baby classes (no third classes) - if( item->class_upper&ITEMUPPER_BABY && sd->class_&JOBL_BABY && !(sd->class_&JOBL_THIRD) ) break; + if ((item->class_upper & ITEMUPPER_BABY) != 0) { + if ((sd->job & JOBL_BABY) != 0 && (sd->job&JOBL_THIRD) == 0) + break; + } // Third classes (no upper, no baby classes) - if( item->class_upper&ITEMUPPER_THIRD && sd->class_&JOBL_THIRD && !(sd->class_&(JOBL_UPPER|JOBL_BABY)) ) break; + if ((item->class_upper & ITEMUPPER_THIRD) != 0) { + if ((sd->job & JOBL_THIRD) != 0 && (sd->job & (JOBL_UPPER|JOBL_BABY)) == 0) + break; + } // Upper third classes - if( item->class_upper&ITEMUPPER_THURDUPPER && sd->class_&JOBL_THIRD && sd->class_&JOBL_UPPER ) break; + if ((item->class_upper & ITEMUPPER_THIRDUPPER) != 0) { + if ((sd->job & JOBL_THIRD) != 0 && (sd->job & JOBL_UPPER) != 0) + break; + } // Baby third classes - if( item->class_upper&ITEMUPPER_THIRDBABY && sd->class_&JOBL_THIRD && sd->class_&JOBL_BABY ) break; + if ((item->class_upper & ITEMUPPER_THIRDBABY) != 0) { + if ((sd->job & JOBL_THIRD) != 0 && (sd->job & JOBL_BABY) != 0) + break; + } return 0; } @@ -4886,24 +5153,35 @@ int pc_isUseitem(struct map_session_data *sd,int n) * 0 = fail * 1 = success *------------------------------------------*/ -int pc_useitem(struct map_session_data *sd,int n) { +static int pc_useitem(struct map_session_data *sd, int n) +{ int64 tick = timer->gettick(); int amount, nameid, i; bool removeItem = false; nullpo_ret(sd); + Assert_ret(n >= 0 && n < sd->status.inventorySize); - if( sd->npc_id || sd->state.workinprogress&1 ){ - /* TODO: add to clif->messages enum */ -#ifdef RENEWAL - clif->msgtable(sd, MSG_NPC_WORK_IN_PROGRESS); // TODO look for the client date that has this message. + if (sd->npc_id || sd->state.workinprogress & 1) { +#if PACKETVER >= 20110308 + clif->msgtable(sd, MSG_BUSY); +#else + clif->messagecolor_self(sd->fd, COLOR_WHITE, msg_sd(sd, 48)); #endif return 0; } + if (battle_config.storage_use_item == 1 && sd->state.storage_flag != STORAGE_FLAG_CLOSED) { + clif->messagecolor_self(sd->fd, COLOR_RED, msg_sd(sd, 1475)); + return 0; // You cannot use this item while storage is open. + } + if( sd->status.inventory[n].nameid <= 0 || sd->status.inventory[n].amount <= 0 ) return 0; + if (sd->block_action.useitem) // *pcblock script command + return 0; + if( !pc->isUseitem(sd,n) ) return 0; @@ -4927,18 +5205,17 @@ int pc_useitem(struct map_session_data *sd,int n) { sd->sc.data[SC_DEEP_SLEEP] || sd->sc.data[SC_SATURDAY_NIGHT_FEVER] || sd->sc.data[SC_COLD] || + sd->sc.data[SC_SUHIDE] || pc_ismuted(&sd->sc, MANNER_NOITEM) )) return 0; - //Prevent mass item usage. [Skotlex] - if( DIFF_TICK(sd->canuseitem_tick, tick) > 0 || - (itemdb_iscashfood(nameid) && DIFF_TICK(sd->canusecashfood_tick, tick) > 0) - ) + // Prevent mass item usage. [Skotlex] + if (DIFF_TICK(sd->canuseitem_tick, tick) > 0) return 0; /* Items with delayed consume are not meant to work while in mounts except reins of mount(12622) */ - if( sd->inventory_data[n]->flag.delay_consume && nameid != ITEMID_REINS_OF_MOUNT ) { + if (sd->inventory_data[n]->flag.delay_consume && nameid != ITEMID_BOARDING_HALTER) { if( sd->sc.data[SC_ALL_RIDING] ) return 0; else if( pc_issit(sd) ) @@ -4951,21 +5228,27 @@ int pc_useitem(struct map_session_data *sd,int n) { if( sd->inventory_data[n]->flag.delay_consume && ( sd->ud.skilltimer != INVALID_TIMER /*|| !status->check_skilluse(&sd->bl, &sd->bl, ALL_RESURRECTION, 0)*/ ) ) return 0; - if( sd->inventory_data[n]->delay > 0 ) { - ARR_FIND(0, MAX_ITEMDELAYS, i, sd->item_delay[i].nameid == nameid ); - if( i == MAX_ITEMDELAYS ) /* item not found. try first empty now */ - ARR_FIND(0, MAX_ITEMDELAYS, i, !sd->item_delay[i].nameid ); - if( i < MAX_ITEMDELAYS ) { - if( sd->item_delay[i].nameid ) {// found - if( DIFF_TICK(sd->item_delay[i].tick, tick) > 0 ) { - int e_tick = (int)(DIFF_TICK(sd->item_delay[i].tick, tick)/1000); - clif->msgtable_num(sd, MSG_SECONDS_UNTIL_USE, e_tick + 1); // [%d] seconds left until you can use + if (sd->inventory_data[n]->delay > 0) { + ARR_FIND(0, MAX_ITEMDELAYS, i, sd->item_delay[i].nameid == nameid); + if (i == MAX_ITEMDELAYS) /* item not found. try first empty now */ + ARR_FIND(0, MAX_ITEMDELAYS, i, sd->item_delay[i].nameid == 0); + if (i < MAX_ITEMDELAYS) { + if (sd->item_delay[i].nameid != 0) {// found + if (DIFF_TICK(sd->item_delay[i].tick, tick) > 0) { + int delay_tick = (int)(DIFF_TICK(sd->item_delay[i].tick, tick) / 1000); +#if PACKETVER >= 20101123 + clif->msgtable_num(sd, MSG_ITEM_REUSE_LIMIT_SECOND, delay_tick + 1); // [%d] seconds left until you can use +#else + char delay_msg[100]; + sprintf(delay_msg, msg_sd(sd, 26), delay_tick + 1); + clif->messagecolor_self(sd->fd, COLOR_YELLOW, delay_msg); // [%d] seconds left until you can use +#endif return 0; // Delay has not expired yet } } else {// not yet used item (all slots are initially empty) sd->item_delay[i].nameid = nameid; } - if (!(nameid == ITEMID_REINS_OF_MOUNT && pc_hasmount(sd))) + if (!(nameid == ITEMID_BOARDING_HALTER && pc_hasmount(sd))) sd->item_delay[i].tick = tick + sd->inventory_data[n]->delay; } else {// should not happen ShowError("pc_useitem: Exceeded item delay array capacity! (nameid=%d, char_id=%d)\n", nameid, sd->status.char_id); @@ -4982,9 +5265,21 @@ int pc_useitem(struct map_session_data *sd,int n) { /* on restricted maps the item is consumed but the effect is not used */ for(i = 0; i < map->list[sd->bl.m].zone->disabled_items_count; i++) { if( map->list[sd->bl.m].zone->disabled_items[i] == nameid ) { - clif->msgtable(sd, MSG_ITEM_CANT_USE_AREA); // This item cannot be used within this area +#if PACKETVER >= 20080311 + clif->skill_mapinfomessage(sd, 3); +#else + clif->messagecolor_self(sd->fd, COLOR_CYAN, msg_sd(sd, 50)); +#endif if( battle_config.item_restricted_consumption_type && sd->status.inventory[n].expire_time == 0 ) { clif->useitemack(sd,n,sd->status.inventory[n].amount-1,true); + + // If Earth Spike Scroll is used while SC_EARTHSCROLL is active, there is a chance to don't consume the scroll. [Kenpachi] + if ((nameid == ITEMID_EARTH_SCROLL_1_3 || nameid == ITEMID_EARTH_SCROLL_1_5) + && sd->sc.count > 0 && sd->sc.data[SC_EARTHSCROLL] != NULL + && rnd() % 100 > sd->sc.data[SC_EARTHSCROLL]->val2) { + return 0; + } + pc->delitem(sd, n, 1, 1, DELITEM_NORMAL, LOG_TYPE_CONSUME); } return 0; @@ -5013,34 +5308,58 @@ int pc_useitem(struct map_session_data *sd,int n) { } } - if(sd->status.inventory[n].card[0]==CARD0_CREATE && - pc->famerank(MakeDWord(sd->status.inventory[n].card[2],sd->status.inventory[n].card[3]), MAPID_ALCHEMIST)) - { + if (sd->status.inventory[n].card[0] == CARD0_CREATE + && pc->fame_rank(MakeDWord(sd->status.inventory[n].card[2], sd->status.inventory[n].card[3]), RANKTYPE_ALCHEMIST) > 0) { script->potion_flag = 2; // Famous player's potions have 50% more efficiency if (sd->sc.data[SC_SOULLINK] && sd->sc.data[SC_SOULLINK]->val2 == SL_ROGUE) script->potion_flag = 3; //Even more effective potions. } - //Update item use time. + // Update item use time. sd->canuseitem_tick = tick + battle_config.item_use_interval; - if( itemdb_iscashfood(nameid) ) - sd->canusecashfood_tick = tick + battle_config.cashfood_use_interval; script->run_use_script(sd, sd->inventory_data[n], npc->fake_nd->bl.id); script->potion_flag = 0; + // If Earth Spike Scroll is used while SC_EARTHSCROLL is active, there is a chance to don't consume the scroll. [Kenpachi] + if ((nameid == ITEMID_EARTH_SCROLL_1_3 || nameid == ITEMID_EARTH_SCROLL_1_5) && sd->sc.count > 0 + && sd->sc.data[SC_EARTHSCROLL] != NULL && rnd() % 100 > sd->sc.data[SC_EARTHSCROLL]->val2) { + removeItem = false; + } + if (removeItem) pc->delitem(sd, n, 1, 1, DELITEM_NORMAL, LOG_TYPE_CONSUME); return 1; } +/** + * Unsets a character's auto-cast related data. + * + * @param sd The character's session data. + * @return 0 if parameter sd is NULL, otherwise 1. + */ +static int pc_autocast_clear(struct map_session_data *sd) +{ + nullpo_ret(sd); + + sd->autocast.type = AUTOCAST_NONE; + sd->autocast.skill_id = 0; + sd->autocast.skill_lv = 0; + sd->autocast.itemskill_conditions_checked = false; + sd->autocast.itemskill_check_conditions = false; + sd->autocast.itemskill_instant_cast = false; + sd->autocast.itemskill_cast_on_self = false; + + return 1; +} + /*========================================== * Add item on cart for given index. * Return: * 0 = success * 1 = fail *------------------------------------------*/ -int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amount,e_log_pick_type log_type) +static int pc_cart_additem(struct map_session_data *sd, struct item *item_data, int amount, e_log_pick_type log_type) { struct item_data *data; int i,w; @@ -5059,7 +5378,7 @@ int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amoun if (!itemdb_cancartstore(item_data, pc_get_group_level(sd)) || (item_data->bound > IBT_ACCOUNT && !pc_can_give_bound_items(sd))) { // Check item trade restrictions - clif->message (sd->fd, msg_sd(sd,264)); + clif->message (sd->fd, msg_sd(sd,264)); // This item cannot be stored. return 1;/* TODO: there is no official response to this? */ } @@ -5109,9 +5428,11 @@ int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amoun * 0 = success * 1 = fail *------------------------------------------*/ -int pc_cart_delitem(struct map_session_data *sd,int n,int amount,int type,e_log_pick_type log_type) { +static int pc_cart_delitem(struct map_session_data *sd, int n, int amount, int type, e_log_pick_type log_type) +{ struct item_data * data; nullpo_retr(1, sd); + Assert_retr(1, n >= 0 && n < MAX_CART); if( sd->status.cart[n].nameid == 0 || sd->status.cart[n].amount < amount || !(data = itemdb->exists(sd->status.cart[n].nameid)) ) return 1; @@ -5138,19 +5459,19 @@ int pc_cart_delitem(struct map_session_data *sd,int n,int amount,int type,e_log_ * 0 = fail * 1 = succes *------------------------------------------*/ -int pc_putitemtocart(struct map_session_data *sd,int idx,int amount) +static int pc_putitemtocart(struct map_session_data *sd, int idx, int amount) { struct item *item_data; int flag; nullpo_ret(sd); - if (idx < 0 || idx >= MAX_INVENTORY) //Invalid index check [Skotlex] + if (idx < 0 || idx >= sd->status.inventorySize) //Invalid index check [Skotlex] return 1; item_data = &sd->status.inventory[idx]; - if( item_data->nameid == 0 || amount < 1 || item_data->amount < amount || sd->state.vending ) + if (item_data->nameid == 0 || amount < 1 || item_data->amount < amount || sd->state.vending || sd->state.prevend) return 1; if( (flag = pc->cart_additem(sd,item_data,amount,LOG_TYPE_NONE)) == 0 ) @@ -5165,11 +5486,12 @@ int pc_putitemtocart(struct map_session_data *sd,int idx,int amount) * -1 = itemid not found or no amount found * x = remaining itemid on cart after get *------------------------------------------*/ -int pc_cartitem_amount(struct map_session_data* sd, int idx, int amount) +static int pc_cartitem_amount(struct map_session_data *sd, int idx, int amount) { struct item* item_data; nullpo_retr(-1, sd); + Assert_retr(-1, idx >= 0 && idx < MAX_CART); item_data = &sd->status.cart[idx]; if( item_data->nameid == 0 || item_data->amount == 0 ) @@ -5184,7 +5506,7 @@ int pc_cartitem_amount(struct map_session_data* sd, int idx, int amount) * 0 = player not found or (FIXME) succes (from pc->cart_delitem) * 1 = failure *------------------------------------------*/ -int pc_getitemfromcart(struct map_session_data *sd,int idx,int amount) +static int pc_getitemfromcart(struct map_session_data *sd, int idx, int amount) { struct item *item_data; int flag; @@ -5196,22 +5518,25 @@ int pc_getitemfromcart(struct map_session_data *sd,int idx,int amount) item_data=&sd->status.cart[idx]; - if(item_data->nameid==0 || amount < 1 || item_data->amount<amount || sd->state.vending ) + if (item_data->nameid == 0 || amount < 1 || item_data->amount < amount || sd->state.vending || sd->state.prevend) return 1; - if((flag = pc->additem(sd,item_data,amount,LOG_TYPE_NONE)) == 0) + if ((flag = pc->additem(sd,item_data,amount,LOG_TYPE_NONE)) == 0) return pc->cart_delitem(sd,idx,amount,0,LOG_TYPE_NONE); return flag; } -void pc_bound_clear(struct map_session_data *sd, enum e_item_bound_type type) { + +static void pc_bound_clear(struct map_session_data *sd, enum e_item_bound_type type) +{ int i; + nullpo_retv(sd); switch( type ) { /* both restricted to inventory */ case IBT_PARTY: case IBT_CHARACTER: - for( i = 0; i < MAX_INVENTORY; i++ ){ + for (i = 0; i < sd->status.inventorySize; i++ ) { if( sd->status.inventory[i].bound == type ) { pc->delitem(sd, i, sd->status.inventory[i].amount, 0, DELITEM_SKILLUSE, LOG_TYPE_OTHER); // FIXME: is this the correct reason flag? } @@ -5223,7 +5548,7 @@ void pc_bound_clear(struct map_session_data *sd, enum e_item_bound_type type) { case IBT_GUILD: { struct guild_storage *gstor = idb_get(gstorage->db,sd->status.guild_id); - for( i = 0; i < MAX_INVENTORY; i++ ){ + for (i = 0; i < sd->status.inventorySize; i++ ) { if(sd->status.inventory[i].bound == type) { if( gstor ) gstorage->additem(sd,gstor,&sd->status.inventory[i],sd->status.inventory[i].amount); @@ -5239,7 +5564,7 @@ void pc_bound_clear(struct map_session_data *sd, enum e_item_bound_type type) { /*========================================== * Display item stolen msg to player sd *------------------------------------------*/ -int pc_show_steal(struct block_list *bl,va_list ap) +static int pc_show_steal(struct block_list *bl, va_list ap) { struct map_session_data *sd = NULL, *tsd = NULL; int itemid; @@ -5256,9 +5581,9 @@ int pc_show_steal(struct block_list *bl,va_list ap) nullpo_ret(sd); if((item=itemdb->exists(itemid))==NULL) - sprintf(output,"%s stole an Unknown Item (id: %i).",sd->status.name, itemid); + sprintf(output, msg_sd(sd, 887), sd->status.name, itemid); // %s stole an Unknown Item (id: %i). else - sprintf(output,"%s stole %s.",sd->status.name,item->jname); + sprintf(output, msg_sd(sd, 888), sd->status.name, item->jname); // %s stole %s. clif->message(tsd->fd, output); return 0; @@ -5269,7 +5594,7 @@ int pc_show_steal(struct block_list *bl,va_list ap) * 0 = fail * 1 = succes *------------------------------------------*/ -int pc_steal_item(struct map_session_data *sd,struct block_list *bl, uint16 skill_lv) +static int pc_steal_item(struct map_session_data *sd, struct block_list *bl, uint16 skill_lv) { int i,itemid,flag; int rate; @@ -5304,15 +5629,17 @@ int pc_steal_item(struct map_session_data *sd,struct block_list *bl, uint16 skil // Try dropping one item, in the order from first to last possible slot. // Droprate is affected by the skill success rate. - for (i = 0; i < MAX_STEAL_DROP; i++) { + for (i = 0; i < MAX_MOB_DROP; i++) { if (md->db->dropitem[i].nameid == 0) continue; if ((data = itemdb->exists(md->db->dropitem[i].nameid)) == NULL) continue; + if (data->type == IT_CARD) + continue; if (rnd() % 10000 < apply_percentrate(md->db->dropitem[i].p, rate, 100)) break; } - if (i == MAX_STEAL_DROP) + if (i == MAX_MOB_DROP) return 0; itemid = md->db->dropitem[i].nameid; @@ -5336,26 +5663,21 @@ int pc_steal_item(struct map_session_data *sd,struct block_list *bl, uint16 skil //Logs items, Stolen from mobs [Lupus] logs->pick_mob(md, LOG_TYPE_STEAL, -1, &tmp_item, data); - //A Rare Steal Global Announce by Lupus - if(md->db->dropitem[i].p<=battle_config.rare_drop_announce) { - char message[128]; - sprintf (message, msg_txt(542), sd->status.name, md->db->jname, data->jname, (float)md->db->dropitem[i].p / 100); - //MSG: "'%s' stole %s's %s (chance: %0.02f%%)" - intif->broadcast(message, strlen(message)+1, BC_DEFAULT); - } return 1; } /** * Steals zeny from a monster through the RG_STEALCOIN skill. * - * @param sd Source character - * @param target Target monster + * @param sd Source character + * @param target Target monster + * @param skill_lv Skill Level * * @return Amount of stolen zeny (0 in case of failure) - **/ -int pc_steal_coin(struct map_session_data *sd, struct block_list *target) { - int rate, skill_lv; + */ +static int pc_steal_coin(struct map_session_data *sd, struct block_list *target, uint16 skill_lv) +{ + int rate; struct mob_data *md = BL_CAST(BL_MOB, target); if (sd == NULL || md == NULL) @@ -5367,7 +5689,6 @@ int pc_steal_coin(struct map_session_data *sd, struct block_list *target) { if (mob_is_treasure(md)) return 0; - skill_lv = pc->checkskill(sd, RG_STEALCOIN); rate = skill_lv * 10 + (sd->status.base_level - md->level) * 2 + sd->battle_status.dex / 2 + sd->battle_status.luk / 2; if(rnd()%1000 < rate) { int amount = md->level * skill_lv / 10 + md->level * 8 + rnd()%(md->level * 2 + 1); // mob_lv * skill_lv / 10 + random [mob_lv*8; mob_lv*10] @@ -5379,233 +5700,295 @@ int pc_steal_coin(struct map_session_data *sd, struct block_list *target) { return 0; } -/*========================================== - * Set's a player position. - * Return values: - * 0 - Success. - * 1 - Invalid map index. - * 2 - Map not in this map-server, and failed to locate alternate map-server. - *------------------------------------------*/ -int pc_setpos(struct map_session_data* sd, unsigned short map_index, int x, int y, clr_type clrtype) { - int16 m; +/** + * Sets a character's position. + * + * @param sd The related character. + * @param map_index The target map's index. + * @param x The target x-coordinate. + * @param y The target y-coordinate. + * @param clrtype The unit clear type, which should be used. + * @retval 0 Success. + * @retval 1 Invalid map index. + * @retval 2 Map not in this map-server, and failed to locate alternative map-server. + * @retval 3 No character data. (Parameter sd is a NULL pointer.) + * @retval 4 Character is jailed. + * + **/ +static int pc_setpos(struct map_session_data *sd, unsigned short map_index, int x, int y, enum clr_type clrtype) +{ + nullpo_retr(3, sd); - nullpo_ret(sd); + int map_id = map->mapindex2mapid(map_index); - if( !map_index || !mapindex_id2name(map_index) || ( m = map->mapindex2mapid(map_index) ) == -1 ) { - ShowDebug("pc_setpos: Passed mapindex(%d) is invalid!\n", map_index); + if (map_index == 0 || !mapindex_id2name(map_index) || map_id == INDEX_NOT_FOUND) { + ShowDebug("pc_setpos: Passed mapindex %d is invalid!\n", map_index); return 1; } - if( pc_isdead(sd) ) { //Revive dead people before warping them + if (pc_isdead(sd)) { // Revive dead character before warping. pc->setstand(sd); - pc->setrestartvalue(sd,1); + pc->setrestartvalue(sd, 1); } - if( map->list[m].flag.src4instance ) { - struct party_data *p; + if (map->list[map_id].flag.src4instance != 0) { bool stop = false; - int i = 0, j = 0; - if( sd->instances ) { - for( i = 0; i < sd->instances; i++ ) { - if( sd->instance[i] >= 0 ) { - ARR_FIND(0, instance->list[sd->instance[i]].num_map, j, map->list[instance->list[sd->instance[i]].map[j]].instance_src_map == m && !map->list[instance->list[sd->instance[i]].map[j]].custom_name); - if( j != instance->list[sd->instance[i]].num_map ) + if (sd->instances != 0) { + int i, j = 0; + + for (i = 0; i < sd->instances; i++) { + if (sd->instance[i] >= 0) { + ARR_FIND(0, instance->list[sd->instance[i]].num_map, j, + map->list[instance->list[sd->instance[i]].map[j]].instance_src_map == map_id + && !map->list[instance->list[sd->instance[i]].map[j]].custom_name); + + if (j != instance->list[sd->instance[i]].num_map) break; } } - if( i != sd->instances ) { - m = instance->list[sd->instance[i]].map[j]; - map_index = map_id2index(m); + + if (i != sd->instances) { + map_id = instance->list[sd->instance[i]].map[j]; + map_index = map_id2index(map_id); stop = true; } } - if ( !stop && sd->status.party_id && (p = party->search(sd->status.party_id)) != NULL && p->instances ) { - for( i = 0; i < p->instances; i++ ) { - if( p->instance[i] >= 0 ) { - ARR_FIND(0, instance->list[p->instance[i]].num_map, j, map->list[instance->list[p->instance[i]].map[j]].instance_src_map == m && !map->list[instance->list[p->instance[i]].map[j]].custom_name); - if( j != instance->list[p->instance[i]].num_map ) + + struct party_data *p = party->search(sd->status.party_id); + + if (!stop && sd->status.party_id != 0 && p != NULL && p->instances != 0) { + int i, j = 0; + + for (i = 0; i < p->instances; i++) { + if (p->instance[i] >= 0) { + ARR_FIND(0, instance->list[p->instance[i]].num_map, j, + map->list[instance->list[p->instance[i]].map[j]].instance_src_map == map_id + && !map->list[instance->list[p->instance[i]].map[j]].custom_name); + + if (j != instance->list[p->instance[i]].num_map) break; } } - if( i != p->instances ) { - m = instance->list[p->instance[i]].map[j]; - map_index = map_id2index(m); + + if (i != p->instances) { + map_id = instance->list[p->instance[i]].map[j]; + map_index = map_id2index(map_id); stop = true; } } - if ( !stop && sd->status.guild_id && sd->guild && sd->guild->instances ) { - for( i = 0; i < sd->guild->instances; i++ ) { - if( sd->guild->instance[i] >= 0 ) { - ARR_FIND(0, instance->list[sd->guild->instance[i]].num_map, j, map->list[instance->list[sd->guild->instance[i]].map[j]].instance_src_map == m && !map->list[instance->list[sd->guild->instance[i]].map[j]].custom_name); - if( j != instance->list[sd->guild->instance[i]].num_map ) + + if (!stop && sd->status.guild_id != 0 && sd->guild != NULL && sd->guild->instances != 0) { + int i, j = 0; + + for (i = 0; i < sd->guild->instances; i++) { + if (sd->guild->instance[i] >= 0) { + ARR_FIND(0, instance->list[sd->guild->instance[i]].num_map, j, + map->list[instance->list[sd->guild->instance[i]].map[j]].instance_src_map == map_id + && !map->list[instance->list[sd->guild->instance[i]].map[j]].custom_name); + + if (j != instance->list[sd->guild->instance[i]].num_map) break; } } - if( i != sd->guild->instances ) { - m = instance->list[sd->guild->instance[i]].map[j]; - map_index = map_id2index(m); - //stop = true; Uncomment if adding new checks + + if (i != sd->guild->instances) { + map_id = instance->list[sd->guild->instance[i]].map[j]; + map_index = map_id2index(map_id); + //stop = true; Uncomment when adding new checks. } } - /* we hit a instance, if empty we populate the spawn data */ - if( map->list[m].instance_id >= 0 && instance->list[map->list[m].instance_id].respawn.map == 0 && - instance->list[map->list[m].instance_id].respawn.x == 0 && - instance->list[map->list[m].instance_id].respawn.y == 0) { - instance->list[map->list[m].instance_id].respawn.map = map_index; - instance->list[map->list[m].instance_id].respawn.x = x; - instance->list[map->list[m].instance_id].respawn.y = y; + // We hit an instance. If empty we populate the spawn data. + if (map->list[map_id].instance_id >= 0 && instance->list[map->list[map_id].instance_id].respawn.map == 0 + && instance->list[map->list[map_id].instance_id].respawn.x == 0 + && instance->list[map->list[map_id].instance_id].respawn.y == 0) { + instance->list[map->list[map_id].instance_id].respawn.map = map_index; + instance->list[map->list[map_id].instance_id].respawn.x = x; + instance->list[map->list[map_id].instance_id].respawn.y = y; } } - sd->state.changemap = (sd->mapindex != map_index); + sd->state.changemap = (sd->mapindex != map_index) ? 1 : 0; sd->state.warping = 1; sd->state.workinprogress = 0; - if( sd->state.changemap ) { // Misc map-changing settings - int i; + + if (sd->state.changemap != 0) { // Miscellaneous map-changing settings. sd->state.pmap = sd->bl.m; - for (i = 0; i < VECTOR_LENGTH(sd->script_queues); i++) { + for (int i = 0; i < VECTOR_LENGTH(sd->script_queues); i++) { struct script_queue *queue = script->queue(VECTOR_INDEX(sd->script_queues, i)); - if (queue && queue->event_mapchange[0] != '\0') { - pc->setregstr(sd, script->add_str("@Queue_Destination_Map$"), map->list[m].name); + + if (queue != NULL && queue->event_mapchange[0] != '\0') { + pc->setregstr(sd, script->add_variable("@Queue_Destination_Map$"), map->list[map_id].name); npc->event(sd, queue->event_mapchange, 0); } } - if( map->list[m].cell == (struct mapcell *)0xdeadbeaf ) - map->cellfromcache(&map->list[m]); - if (sd->sc.count) { // Cancel some map related stuff. - if (sd->sc.data[SC_JAILED]) - return 1; //You may not get out! + if (map->list[map_id].cell == (struct mapcell *)0xdeadbeaf) + map->cellfromcache(&map->list[map_id]); + + if (sd->sc.count != 0) { // Cancel some map related stuff. + if (sd->sc.data[SC_JAILED] != NULL) + return 4; // You may not get out! + status_change_end(&sd->bl, SC_CASH_BOSS_ALARM, INVALID_TIMER); status_change_end(&sd->bl, SC_WARM, INVALID_TIMER); status_change_end(&sd->bl, SC_SUN_COMFORT, INVALID_TIMER); status_change_end(&sd->bl, SC_MOON_COMFORT, INVALID_TIMER); status_change_end(&sd->bl, SC_STAR_COMFORT, INVALID_TIMER); status_change_end(&sd->bl, SC_MIRACLE, INVALID_TIMER); - status_change_end(&sd->bl, SC_NEUTRALBARRIER_MASTER, INVALID_TIMER);//Will later check if this is needed. [Rytech] + status_change_end(&sd->bl, SC_NEUTRALBARRIER_MASTER, INVALID_TIMER); // Will later check if this is needed. [Rytech] status_change_end(&sd->bl, SC_NEUTRALBARRIER, INVALID_TIMER); status_change_end(&sd->bl, SC_STEALTHFIELD_MASTER, INVALID_TIMER); status_change_end(&sd->bl, SC_STEALTHFIELD, INVALID_TIMER); - if (sd->sc.data[SC_KNOWLEDGE]) { + + if (sd->sc.data[SC_KNOWLEDGE] != NULL) { struct status_change_entry *sce = sd->sc.data[SC_KNOWLEDGE]; + if (sce->timer != INVALID_TIMER) timer->delete(sce->timer, status->change_timer); - sce->timer = timer->add(timer->gettick() + skill->get_time(SG_KNOWLEDGE, sce->val1), status->change_timer, sd->bl.id, SC_KNOWLEDGE); + + sce->timer = timer->add(timer->gettick() + skill->get_time(SG_KNOWLEDGE, sce->val1), + status->change_timer, sd->bl.id, SC_KNOWLEDGE); } + status_change_end(&sd->bl, SC_PROPERTYWALK, INVALID_TIMER); status_change_end(&sd->bl, SC_CLOAKING, INVALID_TIMER); status_change_end(&sd->bl, SC_CLOAKINGEXCEED, 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], PCUNEQUIPITEM_FORCE); + + for (int i = 0; i < EQI_MAX; i++) { + if (sd->equip_index[i] >= 0 && pc->isequip(sd , sd->equip_index[i]) == 0) + pc->unequipitem(sd, sd->equip_index[i], PCUNEQUIPITEM_FORCE); } - if (battle_config.clear_unit_onwarp&BL_PC) + + if ((battle_config.clear_unit_onwarp & BL_PC) != 0) skill->clear_unitgroup(&sd->bl); - party->send_dot_remove(sd); //minimap dot fix [Kevin] + + party->send_dot_remove(sd); // Minimap dot fix. [Kevin] guild->send_dot_remove(sd); bg->send_dot_remove(sd); - if (sd->regen.state.gc) + + if (sd->regen.state.gc != 0) sd->regen.state.gc = 0; - // make sure vending is allowed here - if (sd->state.vending && map->list[m].flag.novending) { - clif->message (sd->fd, msg_sd(sd,276)); // "You can't open a shop on this map" + + // Make sure that vending is allowed here. + if (sd->state.vending != 0 && map->list[map_id].flag.novending != 0) { + clif->message(sd->fd, msg_sd(sd, 276)); // "You can't open a shop on this map" vending->close(sd); } - if (map->list[sd->bl.m].channel) { - channel->leave(map->list[sd->bl.m].channel,sd); - } + if (sd->mapindex != 0 && map->list[sd->bl.m].channel != NULL) // Only if the character is already on a map. + channel->leave(map->list[sd->bl.m].channel, sd); } - if( m < 0 ) { + if (map_id < 0) { uint32 ip; uint16 port; - //if can't find any map-servers, just abort setting position. - if(!sd->mapindex || map->mapname2ipport(map_index,&ip,&port)) + + // If can't find any map-servers, just abort setting position. + if (sd->mapindex == 0 || map->mapname2ipport(map_index, &ip, &port) != 0) return 2; - if (sd->npc_id) + if (sd->npc_id != 0) npc->event_dequeue(sd); + npc->script_event(sd, NPCE_LOGOUT); - //remove from map, THEN change x/y coordinates - unit->remove_map_pc(sd,clrtype); + + // Remove from map, THEN change x/y coordinates. + unit->remove_map_pc(sd, clrtype); + + if (battle_config.player_warp_keep_direction == 0) + sd->ud.dir = 0; /// Make character facing north. + sd->mapindex = map_index; - sd->bl.x=x; - sd->bl.y=y; + sd->bl.x= x; + sd->bl.y= y; pc->clean_skilltree(sd); - chrif->save(sd,2); - chrif->changemapserver(sd, ip, (short)port); + chrif->save(sd, 2); + chrif->changemapserver(sd, ip, port); - //Free session data from this map server [Kevin] + // Free session data from this map server. [Kevin] unit->free_pc(sd); return 0; } - if( x < 0 || x >= map->list[m].xs || y < 0 || y >= map->list[m].ys ) { - ShowError("pc_setpos: attempt to place player %s (%d:%d) on invalid coordinates (%s-%d,%d)\n", sd->status.name, sd->status.account_id, sd->status.char_id, mapindex_id2name(map_index),x,y); - x = y = 0; // make it random + if (x < 0 || x >= map->list[map_id].xs || y < 0 || y >= map->list[map_id].ys) { // Invalid coordinates. Randomize them. + ShowError("pc_setpos: Attempt to place player %s (%d:%d) on invalid coordinates (%s-%d,%d)!\n", + sd->status.name, sd->status.account_id, sd->status.char_id, + mapindex_id2name(map_index), x, y); + x = 0; + y = 0; } - if( x == 0 && y == 0 ) {// pick a random walkable cell + if (x == 0 && y == 0) { // Pick a random walkable cell. do { - x=rnd()%(map->list[m].xs-2)+1; - y=rnd()%(map->list[m].ys-2)+1; - } while(map->getcell(m, &sd->bl, x, y, CELL_CHKNOPASS)); + x = rnd() % (map->list[map_id].xs - 2) + 1; + y = rnd() % (map->list[map_id].ys - 2) + 1; + } while(map->getcell(map_id, &sd->bl, x, y, CELL_CHKNOPASS) != 0); } - if (sd->state.vending && map->getcell(m, &sd->bl, x, y, CELL_CHKNOVENDING)) { - clif->message (sd->fd, msg_sd(sd,204)); // "You can't open a shop on this cell." + if (sd->state.vending != 0 && map->getcell(map_id, &sd->bl, x, y, CELL_CHKNOVENDING) != 0) { + clif->message(sd->fd, msg_sd(sd, 204)); // "You can't open a shop on this cell." vending->close(sd); } - if(sd->bl.prev != NULL){ - unit->remove_map_pc(sd,clrtype); - clif->changemap(sd,m,x,y); // [MouseJstr] - } else if(sd->state.active) - //Tag player for rewarping after map-loading is done. [Skotlex] - sd->state.rewarp = 1; + if (battle_config.player_warp_keep_direction == 0) + sd->ud.dir = 0; // Make character facing north. + + if (sd->bl.prev != NULL) { + unit->remove_map_pc(sd, clrtype); + clif->changemap(sd, map_id, x, y); // [MouseJstr] + } else if (sd->state.active != 0) { + sd->state.rewarp = 1; // Tag character for re-warping after map-loading is done. [Skotlex] + } sd->mapindex = map_index; - sd->bl.m = m; - sd->bl.x = sd->ud.to_x = x; - sd->bl.y = sd->ud.to_y = y; + sd->bl.m = map_id; + sd->bl.x = x; + sd->bl.y = y; + sd->ud.to_x = x; + sd->ud.to_y = y; - if( sd->status.guild_id > 0 && map->list[m].flag.gvg_castle ) { // Increased guild castle regen [Valaris] + if (sd->status.guild_id > 0 && map->list[map_id].flag.gvg_castle != 0) { // Double regeneration in guild castle. [Valaris] struct guild_castle *gc = guild->mapindex2gc(sd->mapindex); - if(gc && gc->guild_id == sd->status.guild_id) + + if (gc != NULL && gc->guild_id == sd->status.guild_id) sd->regen.state.gc = 1; } - if( sd->status.pet_id > 0 && sd->pd && sd->pd->pet.intimate > 0 ) { - sd->pd->bl.m = m; - sd->pd->bl.x = sd->pd->ud.to_x = x; - sd->pd->bl.y = sd->pd->ud.to_y = y; + if (sd->status.pet_id > 0 && sd->pd != NULL && sd->pd->pet.intimate > PET_INTIMACY_NONE) { + sd->pd->bl.m = map_id; + sd->pd->bl.x = x; + sd->pd->bl.y = y; + sd->pd->ud.to_x = x; + sd->pd->ud.to_y = y; sd->pd->ud.dir = sd->ud.dir; } - if( homun_alive(sd->hd) ) { - sd->hd->bl.m = m; - sd->hd->bl.x = sd->hd->ud.to_x = x; - sd->hd->bl.y = sd->hd->ud.to_y = y; + if (homun_alive(sd->hd)) { + sd->hd->bl.m = map_id; + sd->hd->bl.x = x; + sd->hd->bl.y = y; + sd->hd->ud.to_x = x; + sd->hd->ud.to_y = y; sd->hd->ud.dir = sd->ud.dir; } - if( sd->md ) { - sd->md->bl.m = m; - sd->md->bl.x = sd->md->ud.to_x = x; - sd->md->bl.y = sd->md->ud.to_y = y; + if (sd->md != NULL) { + sd->md->bl.m = map_id; + sd->md->bl.x = x; + sd->md->bl.y = y; + sd->md->ud.to_x = x; + sd->md->ud.to_y = y; sd->md->ud.dir = sd->ud.dir; } - /* given autotrades have no clients you have to trigger this manually otherwise they get stuck in memory limbo bugreport:7495 */ - if( sd->state.autotrade ) - clif->pLoadEndAck(0,sd); + // Given autotrades have no clients. You have to trigger this manually, otherwise they get stuck in memory limbo. (bugreport:7495) + if (sd->state.autotrade != 0) + clif->pLoadEndAck(0, sd); return 0; } @@ -5617,7 +6000,8 @@ int pc_setpos(struct map_session_data* sd, unsigned short map_index, int x, int * 0 = fail or FIXME success (from pc->setpos) * x(1|2) = fail *------------------------------------------*/ -int pc_randomwarp(struct map_session_data *sd, clr_type type) { +static int pc_randomwarp(struct map_session_data *sd, enum clr_type type) +{ int x,y,i=0; int16 m; @@ -5643,7 +6027,8 @@ int pc_randomwarp(struct map_session_data *sd, clr_type type) { * Records a memo point at sd's current position * pos - entry to replace, (-1: shift oldest entry out) *------------------------------------------*/ -int pc_memo(struct map_session_data* sd, int pos) { +static int pc_memo(struct map_session_data *sd, int pos) +{ int skill_lv; nullpo_ret(sd); @@ -5693,8 +6078,9 @@ int pc_memo(struct map_session_data* sd, int pos) { /*========================================== * Return player sd skill_lv learned for given skill *------------------------------------------*/ -int pc_checkskill(struct map_session_data *sd,uint16 skill_id) { - uint16 index = 0; +static int pc_checkskill(struct map_session_data *sd, uint16 skill_id) +{ + int index = 0; if(sd == NULL) return 0; if( skill_id >= GD_SKILLBASE && skill_id < GD_MAX ) { struct guild *g; @@ -5712,9 +6098,11 @@ int pc_checkskill(struct map_session_data *sd,uint16 skill_id) { return 0; } -int pc_checkskill2(struct map_session_data *sd,uint16 index) { - if(sd == NULL) return 0; - if(index >= ARRAYLENGTH(sd->status.skill) ) { +static int pc_checkskill2(struct map_session_data *sd, uint16 index) +{ + if (sd == NULL) + return 0; + if (index >= MAX_SKILL_DB) { ShowError("pc_checkskill: Invalid skill index %d (char_id=%d).\n", index, sd->status.char_id); return 0; } @@ -5737,7 +6125,7 @@ int pc_checkskill2(struct map_session_data *sd,uint16 index) { * Return * 0 - No status found or all done *------------------------------------------*/ -int pc_checkallowskill(struct map_session_data *sd) +static int pc_checkallowskill(struct map_session_data *sd) { const enum sc_type scw_list[] = { SC_TWOHANDQUICKEN, @@ -5777,11 +6165,11 @@ int pc_checkallowskill(struct map_session_data *sd) status_change_end(&sd->bl, scw_list[i], INVALID_TIMER); } - if(sd->sc.data[SC_STRUP] && sd->status.weapon) + if(sd->sc.data[SC_STRUP] && sd->weapontype != W_FIST) // Spurt requires bare hands (feet, in fact xD) status_change_end(&sd->bl, SC_STRUP, INVALID_TIMER); - if(sd->status.shield <= 0) { // Skills requiring a shield + if (!sd->has_shield) { // Skills requiring a shield for (i = 0; i < ARRAYLENGTH(scs_list); i++) if(sd->sc.data[scs_list[i]]) status_change_end(&sd->bl, scs_list[i], INVALID_TIMER); @@ -5795,7 +6183,7 @@ int pc_checkallowskill(struct map_session_data *sd) * -1 : mean nothing equiped * idx : (this index could be used in inventory to found item_data) *------------------------------------------*/ -int pc_checkequip(struct map_session_data *sd,int pos) +static int pc_checkequip(struct map_session_data *sd, int pos) { int i; @@ -5813,10 +6201,9 @@ int pc_checkequip(struct map_session_data *sd,int pos) * Convert's from the client's lame Job ID system * to the map server's 'makes sense' system. [Skotlex] *------------------------------------------*/ -int pc_jobid2mapid(unsigned short b_class) +static int pc_jobid2mapid(int class) { - switch(b_class) - { + switch (class) { //Novice And 1-1 Jobs case JOB_NOVICE: return MAPID_NOVICE; case JOB_SWORDMAN: return MAPID_SWORDMAN; @@ -5832,6 +6219,7 @@ int pc_jobid2mapid(unsigned short b_class) case JOB_XMAS: return MAPID_XMAS; case JOB_SUMMER: return MAPID_SUMMER; case JOB_GANGSI: return MAPID_GANGSI; + case JOB_SUMMONER: return MAPID_SUMMONER; //2-1 Jobs case JOB_SUPER_NOVICE: return MAPID_SUPER_NOVICE; case JOB_KNIGHT: return MAPID_KNIGHT; @@ -5955,10 +6343,9 @@ int pc_jobid2mapid(unsigned short b_class) } //Reverts the map-style class id to the client-style one. -int pc_mapid2jobid(unsigned short class_, int sex) +static int pc_mapid2jobid(unsigned int class, int sex) { - switch(class_) - { + switch (class) { //Novice And 1-1 Jobs case MAPID_NOVICE: return JOB_NOVICE; case MAPID_SWORDMAN: return JOB_SWORDMAN; @@ -5974,6 +6361,7 @@ int pc_mapid2jobid(unsigned short class_, int sex) case MAPID_XMAS: return JOB_XMAS; case MAPID_SUMMER: return JOB_SUMMER; case MAPID_GANGSI: return JOB_GANGSI; + case MAPID_SUMMONER: return JOB_SUMMONER; //2-1 Jobs case MAPID_SUPER_NOVICE: return JOB_SUPER_NOVICE; case MAPID_KNIGHT: return JOB_KNIGHT; @@ -6092,9 +6480,9 @@ int pc_mapid2jobid(unsigned short class_, int sex) /*==================================================== * This function return the name of the job (by [Yor]) *----------------------------------------------------*/ -const char* job_name(int class_) +static const char *job_name(int class) { - switch (class_) { + switch (class) { case JOB_NOVICE: // 550 case JOB_SWORDMAN: // 551 case JOB_MAGE: // 552 @@ -6102,7 +6490,7 @@ const char* job_name(int class_) case JOB_ACOLYTE: // 554 case JOB_MERCHANT: // 555 case JOB_THIEF: // 556 - return msg_txt(550 - JOB_NOVICE+class_); + return msg_txt(550 - JOB_NOVICE + class); case JOB_KNIGHT: // 557 case JOB_PRIEST: // 558 @@ -6110,7 +6498,7 @@ const char* job_name(int class_) case JOB_BLACKSMITH: // 560 case JOB_HUNTER: // 561 case JOB_ASSASSIN: // 562 - return msg_txt(557 - JOB_KNIGHT+class_); + return msg_txt(557 - JOB_KNIGHT + class); case JOB_KNIGHT2: return msg_txt(557); @@ -6122,7 +6510,7 @@ const char* job_name(int class_) case JOB_ALCHEMIST: // 567 case JOB_BARD: // 568 case JOB_DANCER: // 569 - return msg_txt(563 - JOB_CRUSADER+class_); + return msg_txt(563 - JOB_CRUSADER + class); case JOB_CRUSADER2: return msg_txt(563); @@ -6132,7 +6520,7 @@ const char* job_name(int class_) case JOB_GUNSLINGER: // 572 case JOB_NINJA: // 573 case JOB_XMAS: // 574 - return msg_txt(570 - JOB_WEDDING+class_); + return msg_txt(570 - JOB_WEDDING + class); case JOB_SUMMER: return msg_txt(621); @@ -6144,7 +6532,7 @@ const char* job_name(int class_) case JOB_ACOLYTE_HIGH: // 579 case JOB_MERCHANT_HIGH: // 580 case JOB_THIEF_HIGH: // 581 - return msg_txt(575 - JOB_NOVICE_HIGH+class_); + return msg_txt(575 - JOB_NOVICE_HIGH + class); case JOB_LORD_KNIGHT: // 582 case JOB_HIGH_PRIEST: // 583 @@ -6152,7 +6540,7 @@ const char* job_name(int class_) case JOB_WHITESMITH: // 585 case JOB_SNIPER: // 586 case JOB_ASSASSIN_CROSS: // 587 - return msg_txt(582 - JOB_LORD_KNIGHT+class_); + return msg_txt(582 - JOB_LORD_KNIGHT + class); case JOB_LORD_KNIGHT2: return msg_txt(582); @@ -6164,7 +6552,7 @@ const char* job_name(int class_) case JOB_CREATOR: // 592 case JOB_CLOWN: // 593 case JOB_GYPSY: // 594 - return msg_txt(588 - JOB_PALADIN + class_); + return msg_txt(588 - JOB_PALADIN + class); case JOB_PALADIN2: return msg_txt(588); @@ -6176,7 +6564,7 @@ const char* job_name(int class_) case JOB_BABY_ACOLYTE: // 599 case JOB_BABY_MERCHANT: // 600 case JOB_BABY_THIEF: // 601 - return msg_txt(595 - JOB_BABY + class_); + return msg_txt(595 - JOB_BABY + class); case JOB_BABY_KNIGHT: // 602 case JOB_BABY_PRIEST: // 603 @@ -6184,7 +6572,7 @@ const char* job_name(int class_) case JOB_BABY_BLACKSMITH: // 605 case JOB_BABY_HUNTER: // 606 case JOB_BABY_ASSASSIN: // 607 - return msg_txt(602 - JOB_BABY_KNIGHT + class_); + return msg_txt(602 - JOB_BABY_KNIGHT + class); case JOB_BABY_KNIGHT2: return msg_txt(602); @@ -6196,7 +6584,7 @@ const char* job_name(int class_) case JOB_BABY_ALCHEMIST: // 612 case JOB_BABY_BARD: // 613 case JOB_BABY_DANCER: // 614 - return msg_txt(608 - JOB_BABY_CRUSADER + class_); + return msg_txt(608 - JOB_BABY_CRUSADER + class); case JOB_BABY_CRUSADER2: return msg_txt(608); @@ -6215,7 +6603,7 @@ const char* job_name(int class_) case JOB_GANGSI: // 622 case JOB_DEATH_KNIGHT: // 623 case JOB_DARK_COLLECTOR: // 624 - return msg_txt(622 - JOB_GANGSI+class_); + return msg_txt(622 - JOB_GANGSI + class); case JOB_RUNE_KNIGHT: // 625 case JOB_WARLOCK: // 626 @@ -6223,7 +6611,7 @@ const char* job_name(int class_) case JOB_ARCH_BISHOP: // 628 case JOB_MECHANIC: // 629 case JOB_GUILLOTINE_CROSS: // 630 - return msg_txt(625 - JOB_RUNE_KNIGHT+class_); + return msg_txt(625 - JOB_RUNE_KNIGHT + class); case JOB_RUNE_KNIGHT_T: // 656 case JOB_WARLOCK_T: // 657 @@ -6231,7 +6619,7 @@ const char* job_name(int class_) case JOB_ARCH_BISHOP_T: // 659 case JOB_MECHANIC_T: // 660 case JOB_GUILLOTINE_CROSS_T: // 661 - return msg_txt(656 - JOB_RUNE_KNIGHT_T+class_); + return msg_txt(656 - JOB_RUNE_KNIGHT_T + class); case JOB_ROYAL_GUARD: // 631 case JOB_SORCERER: // 632 @@ -6240,7 +6628,7 @@ const char* job_name(int class_) case JOB_SURA: // 635 case JOB_GENETIC: // 636 case JOB_SHADOW_CHASER: // 637 - return msg_txt(631 - JOB_ROYAL_GUARD+class_); + return msg_txt(631 - JOB_ROYAL_GUARD + class); case JOB_ROYAL_GUARD_T: // 662 case JOB_SORCERER_T: // 663 @@ -6249,7 +6637,7 @@ const char* job_name(int class_) case JOB_SURA_T: // 666 case JOB_GENETIC_T: // 667 case JOB_SHADOW_CHASER_T: // 668 - return msg_txt(662 - JOB_ROYAL_GUARD_T+class_); + return msg_txt(662 - JOB_ROYAL_GUARD_T + class); case JOB_RUNE_KNIGHT2: return msg_txt(625); @@ -6288,7 +6676,7 @@ const char* job_name(int class_) case JOB_BABY_SURA: // 648 case JOB_BABY_GENETIC: // 649 case JOB_BABY_CHASER: // 650 - return msg_txt(638 - JOB_BABY_RUNE+class_); + return msg_txt(638 - JOB_BABY_RUNE + class); case JOB_BABY_RUNE2: return msg_txt(638); @@ -6304,21 +6692,25 @@ const char* job_name(int class_) case JOB_SUPER_NOVICE_E: // 651 case JOB_SUPER_BABY_E: // 652 - return msg_txt(651 - JOB_SUPER_NOVICE_E+class_); + return msg_txt(651 - JOB_SUPER_NOVICE_E + class); case JOB_KAGEROU: // 653 case JOB_OBORO: // 654 - return msg_txt(653 - JOB_KAGEROU+class_); + return msg_txt(653 - JOB_KAGEROU + class); case JOB_REBELLION: return msg_txt(655); + case JOB_SUMMONER: + return msg_txt(669); + default: return msg_txt(620); // "Unknown Job" } } -int pc_check_job_name(const char *name) { +static int pc_check_job_name(const char *name) +{ int i, len; struct { const char *name; @@ -6438,8 +6830,10 @@ int pc_check_job_name(const char *name) { { "Kagerou", JOB_KAGEROU }, { "Oboro", JOB_OBORO }, { "Rebellion", JOB_REBELLION }, + { "Summoner", JOB_SUMMONER }, }; + nullpo_retr(-1, name); len = ARRAYLENGTH(names); ARR_FIND(0, len, i, strcmpi(names[i].name, name) == 0); @@ -6450,7 +6844,8 @@ int pc_check_job_name(const char *name) { return names[i].id; } -int pc_follow_timer(int tid, int64 tick, int id, intptr_t data) { +static int pc_follow_timer(int tid, int64 tick, int id, intptr_t data) +{ struct map_session_data *sd; struct block_list *tbl; @@ -6488,7 +6883,7 @@ int pc_follow_timer(int tid, int64 tick, int id, intptr_t data) { return 0; } -int pc_stop_following (struct map_session_data *sd) +static int pc_stop_following(struct map_session_data *sd) { nullpo_ret(sd); @@ -6504,8 +6899,10 @@ int pc_stop_following (struct map_session_data *sd) return 0; } -int pc_follow(struct map_session_data *sd,int target_id) { +static int pc_follow(struct map_session_data *sd, int target_id) +{ struct block_list *bl = map->id2bl(target_id); + nullpo_retr(1, sd); if (bl == NULL /*|| bl->type != BL_PC*/) return 1; if (sd->followtimer != INVALID_TIMER) @@ -6517,21 +6914,24 @@ int pc_follow(struct map_session_data *sd,int target_id) { return 0; } -int pc_checkbaselevelup(struct map_session_data *sd) { - unsigned int next = pc->nextbaseexp(sd); +static int pc_checkbaselevelup(struct map_session_data *sd) +{ + uint64 next = pc->nextbaseexp(sd); + nullpo_ret(sd); if (!next || sd->status.base_exp < next) return 0; do { + int status_points = 0; sd->status.base_exp -= next; //Kyoki pointed out that the max overcarry exp is the exp needed for the previous level -1. [Skotlex] if(!battle_config.multi_level_up && sd->status.base_exp > next-1) sd->status.base_exp = next-1; - next = pc->gets_status_point(sd->status.base_level); - sd->status.base_level ++; - sd->status.status_point += next; + status_points = pc->gets_status_point(sd->status.base_level); + sd->status.base_level++; + sd->status.status_point += status_points; } while ((next=pc->nextbaseexp(sd)) > 0 && sd->status.base_exp >= next); @@ -6545,41 +6945,55 @@ int pc_checkbaselevelup(struct map_session_data *sd) { status_calc_pc(sd,SCO_FORCE); status_percent_heal(&sd->bl,100,100); - if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE) { - sc_start(NULL,&sd->bl,status->skill2sc(PR_KYRIE),100,1,skill->get_time(PR_KYRIE,1)); - sc_start(NULL,&sd->bl,status->skill2sc(PR_IMPOSITIO),100,1,skill->get_time(PR_IMPOSITIO,1)); - sc_start(NULL,&sd->bl,status->skill2sc(PR_MAGNIFICAT),100,1,skill->get_time(PR_MAGNIFICAT,1)); - sc_start(NULL,&sd->bl,status->skill2sc(PR_GLORIA),100,1,skill->get_time(PR_GLORIA,1)); - sc_start(NULL,&sd->bl,status->skill2sc(PR_SUFFRAGIUM),100,1,skill->get_time(PR_SUFFRAGIUM,1)); - if (sd->state.snovice_dead_flag) - sd->state.snovice_dead_flag = 0; //Reenable steelbody resurrection on dead. - } else if( (sd->class_&MAPID_BASEMASK) == MAPID_TAEKWON ) { - sc_start(NULL,&sd->bl,status->skill2sc(AL_INCAGI),100,10,600000); - sc_start(NULL,&sd->bl,status->skill2sc(AL_BLESSING),100,10,600000); - } + pc->checkbaselevelup_sc(sd); clif->misceffect(&sd->bl,0); npc->script_event(sd, NPCE_BASELVUP); //LORDALFA - LVLUPEVENT - if(sd->status.party_id) + if (sd->status.party_id) party->send_levelup(sd); pc->baselevelchanged(sd); + + quest->questinfo_refresh(sd); + + achievement->validate_stats(sd, SP_BASELEVEL, sd->status.base_level); + return 1; } -void pc_baselevelchanged(struct map_session_data *sd) { +static void pc_checkbaselevelup_sc(struct map_session_data *sd) +{ + nullpo_retv(sd); + + if ((sd->job & MAPID_UPPERMASK) == MAPID_SUPER_NOVICE) { + sc_start(NULL, &sd->bl, status->skill2sc(PR_KYRIE), 100, 1, skill->get_time(PR_KYRIE, 1)); + sc_start(NULL, &sd->bl, status->skill2sc(PR_IMPOSITIO), 100, 1, skill->get_time(PR_IMPOSITIO, 1)); + sc_start(NULL, &sd->bl, status->skill2sc(PR_MAGNIFICAT), 100, 1, skill->get_time(PR_MAGNIFICAT, 1)); + sc_start(NULL, &sd->bl, status->skill2sc(PR_GLORIA), 100, 1, skill->get_time(PR_GLORIA, 1)); + sc_start(NULL, &sd->bl, status->skill2sc(PR_SUFFRAGIUM), 100, 1, skill->get_time(PR_SUFFRAGIUM, 1)); + if (sd->state.snovice_dead_flag) + sd->state.snovice_dead_flag = 0; //Reenable steelbody resurrection on dead. + } else if ((sd->job & MAPID_BASEMASK) == MAPID_TAEKWON) { + sc_start(NULL, &sd->bl, status->skill2sc(AL_INCAGI), 100, 10, 600000); + sc_start(NULL, &sd->bl, status->skill2sc(AL_BLESSING), 100, 10, 600000); + } +} + +static void pc_baselevelchanged(struct map_session_data *sd) +{ int i; + nullpo_retv(sd); for( i = 0; i < EQI_MAX; i++ ) { if( sd->equip_index[i] >= 0 ) { - if( sd->inventory_data[ sd->equip_index[i] ]->elvmax && sd->status.base_level > (unsigned int)sd->inventory_data[ sd->equip_index[i] ]->elvmax ) + if (sd->inventory_data[sd->equip_index[i]]->elvmax != 0 && sd->status.base_level > sd->inventory_data[ sd->equip_index[i] ]->elvmax) pc->unequipitem(sd, sd->equip_index[i], PCUNEQUIPITEM_RECALC|PCUNEQUIPITEM_FORCE); } } } -int pc_checkjoblevelup(struct map_session_data *sd) +static int pc_checkjoblevelup(struct map_session_data *sd) { - unsigned int next = pc->nextjobexp(sd); + uint64 next = pc->nextjobexp(sd); nullpo_ret(sd); if(!next || sd->status.job_exp < next) @@ -6591,8 +7005,8 @@ int pc_checkjoblevelup(struct map_session_data *sd) if(!battle_config.multi_level_up && sd->status.job_exp > next-1) sd->status.job_exp = next-1; - sd->status.job_level ++; - sd->status.skill_point ++; + sd->status.job_level++; + sd->status.skill_point++; } while ((next=pc->nextjobexp(sd)) > 0 && sd->status.job_exp >= next); @@ -6603,44 +7017,88 @@ int pc_checkjoblevelup(struct map_session_data *sd) status_calc_pc(sd,SCO_FORCE); clif->misceffect(&sd->bl,1); if (pc->checkskill(sd, SG_DEVIL) && !pc->nextjobexp(sd)) - clif->status_change(&sd->bl,SI_DEVIL1, 1, 0, 0, 0, 1); //Permanent blind effect from SG_DEVIL. + clif->status_change(&sd->bl, status->get_sc_icon(SC_DEVIL1), status->get_sc_relevant_bl_types(SC_DEVIL1), 1, 0, 0, 0, 1); //Permanent blind effect from SG_DEVIL. npc->script_event(sd, NPCE_JOBLVUP); + + quest->questinfo_refresh(sd); + + achievement->validate_stats(sd, SP_BASELEVEL, sd->status.job_level); + return 1; } /** * Alters EXP based on self bonuses that do not get shared with the party **/ -void pc_calcexp(struct map_session_data *sd, unsigned int *base_exp, unsigned int *job_exp, struct block_list *src) { - int bonus = 0; - struct status_data *st = status->get_status_data(src); +static void pc_calcexp(struct map_session_data *sd, uint64 *base_exp, uint64 *job_exp, struct block_list *src) +{ + int buff_ratio = 0, buff_job_ratio = 0, race_ratio = 0, pk_ratio = 0; + int64 jexp, bexp; + + nullpo_retv(sd); + nullpo_retv(base_exp); + nullpo_retv(job_exp); + + jexp = *job_exp; + bexp = *base_exp; + + if (src != NULL) { + const struct status_data *st = status->get_status_data(src); + +#ifdef RENEWAL_EXP //should happen first before we caluclate any modifiers + if (src->type == BL_MOB) { + const struct mob_data *md = BL_UCAST(BL_MOB, src); + int re_mod; + re_mod = pc->level_penalty_mod(md->level - sd->status.base_level, md->status.race, md->status.mode, 1); + jexp = apply_percentrate64(jexp, re_mod, 100); + bexp = apply_percentrate64(bexp, re_mod, 100); + } +#endif - if (sd->expaddrace[st->race]) - bonus += sd->expaddrace[st->race]; - bonus += sd->expaddrace[(st->mode&MD_BOSS) ? RC_BOSS : RC_NONBOSS]; + //Race modifier + if (sd->expaddrace[st->race]) + race_ratio += sd->expaddrace[st->race]; + race_ratio += sd->expaddrace[(st->mode&MD_BOSS) ? RC_BOSS : RC_NONBOSS]; + } - if (battle_config.pk_mode - && (int)(status->get_lv(src) - sd->status.base_level) >= 20) - bonus += 15; // pk_mode additional exp if monster >20 levels [Valaris] - if (sd->sc.data[SC_CASH_PLUSEXP]) - bonus += sd->sc.data[SC_CASH_PLUSEXP]->val1; - if (sd->sc.data[SC_OVERLAPEXPUP]) - bonus += sd->sc.data[SC_OVERLAPEXPUP]->val1; + //PK modifier + /* this doesn't exist in Aegis, instead there's a CrazyKiller check which double all EXP from this point */ + if (battle_config.pk_mode && status->get_lv(src) - sd->status.base_level >= 20) + pk_ratio += 15; // pk_mode additional exp if monster >20 levels [Valaris] - *base_exp = (unsigned int) cap_value(*base_exp + apply_percentrate64(*base_exp, bonus, 100), 1, UINT_MAX); + //Buffs modifier + if (sd->sc.data[SC_CASH_PLUSEXP]) { + buff_job_ratio += sd->sc.data[SC_CASH_PLUSEXP]->val1; + buff_ratio += sd->sc.data[SC_CASH_PLUSEXP]->val1; + } + if (sd->sc.data[SC_OVERLAPEXPUP]) { + buff_job_ratio += sd->sc.data[SC_OVERLAPEXPUP]->val1; + buff_ratio += sd->sc.data[SC_OVERLAPEXPUP]->val1; + } if (sd->sc.data[SC_CASH_PLUSONLYJOBEXP]) - bonus += sd->sc.data[SC_CASH_PLUSONLYJOBEXP]->val1; + buff_job_ratio += sd->sc.data[SC_CASH_PLUSONLYJOBEXP]->val1; - *job_exp = (unsigned int) cap_value(*job_exp + apply_percentrate64(*job_exp, bonus, 100), 1, UINT_MAX); + //Applying Race and PK modifier First then Premium (Perment modifier) and finally buff modifier + jexp += apply_percentrate64(jexp, race_ratio, 100); + jexp += apply_percentrate64(jexp, pk_ratio, 100); + + bexp += apply_percentrate64(bexp, race_ratio, 100); + bexp += apply_percentrate64(bexp, pk_ratio, 100); - if (sd->status.mod_exp != 100) { - *base_exp = (unsigned int) cap_value(apply_percentrate64(*base_exp, sd->status.mod_exp, 100), 1, UINT_MAX); - *job_exp = (unsigned int) cap_value(apply_percentrate64(*job_exp, sd->status.mod_exp, 100), 1, UINT_MAX); + if (sd->status.mod_exp != 100) { + jexp = apply_percentrate64(jexp, sd->status.mod_exp, 100); + bexp = apply_percentrate64(bexp, sd->status.mod_exp, 100); } + + bexp += apply_percentrate64(bexp, buff_ratio, 100); + jexp += apply_percentrate64(jexp, buff_ratio + buff_job_ratio, 100); + + *job_exp = cap_value(jexp, 1, UINT64_MAX); + *base_exp = cap_value(bexp, 1, UINT64_MAX); } /** @@ -6649,44 +7107,46 @@ void pc_calcexp(struct map_session_data *sd, unsigned int *base_exp, unsigned in * @param is_quest Used to let client know that the EXP was from a quest (clif->displayexp) PACKETVER >= 20091027 * @retval true success **/ -bool pc_gainexp(struct map_session_data *sd, struct block_list *src, unsigned int base_exp,unsigned int job_exp,bool is_quest) { - float nextbp=0, nextjp=0; - unsigned int nextb=0, nextj=0; +static bool pc_gainexp(struct map_session_data *sd, struct block_list *src, uint64 base_exp, uint64 job_exp, bool is_quest) +{ + float nextbp = 0, nextjp = 0; + uint64 nextb = 0, nextj = 0; nullpo_ret(sd); - if(sd->bl.prev == NULL || pc_isdead(sd)) + if (sd->bl.prev == NULL || pc_isdead(sd)) return false; - if(!battle_config.pvp_exp && map->list[sd->bl.m].flag.pvp) // [MouseJstr] + if (!battle_config.pvp_exp && map->list[sd->bl.m].flag.pvp) // [MouseJstr] return false; // no exp on pvp maps - if( pc_has_permission(sd,PC_PERM_DISABLE_EXP) ) + if (pc_has_permission(sd,PC_PERM_DISABLE_EXP)) return false; - if(sd->status.guild_id>0) - base_exp-=guild->payexp(sd,base_exp); + if (src) + pc->calcexp(sd, &base_exp, &job_exp, src); - if(src) pc->calcexp(sd, &base_exp, &job_exp, src); + if (sd->status.guild_id > 0) + base_exp -= guild->payexp(sd, base_exp); nextb = pc->nextbaseexp(sd); nextj = pc->nextjobexp(sd); - if(sd->state.showexp || battle_config.max_exp_gain_rate){ + if (sd->state.showexp || battle_config.max_exp_gain_rate) { if (nextb > 0) nextbp = (float) base_exp / (float) nextb; if (nextj > 0) nextjp = (float) job_exp / (float) nextj; - if(battle_config.max_exp_gain_rate) { + if (battle_config.max_exp_gain_rate) { if (nextbp > battle_config.max_exp_gain_rate/1000.) { //Note that this value should never be greater than the original //base_exp, therefore no overflow checks are needed. [Skotlex] - base_exp = (unsigned int)(battle_config.max_exp_gain_rate/1000.*nextb); + base_exp = (uint64)(battle_config.max_exp_gain_rate / 1000. * nextb); if (sd->state.showexp) nextbp = (float) base_exp / (float) nextb; } if (nextjp > battle_config.max_exp_gain_rate/1000.) { - job_exp = (unsigned int)(battle_config.max_exp_gain_rate/1000.*nextj); + job_exp = (uint64)(battle_config.max_exp_gain_rate / 1000. * nextj); if (sd->state.showexp) nextjp = (float) job_exp / (float) nextj; } @@ -6696,23 +7156,23 @@ bool pc_gainexp(struct map_session_data *sd, struct block_list *src, unsigned in // Cap exp to the level up requirement of the previous level when you are at max level, // otherwise cap at UINT_MAX (this is required for some S. Novice bonuses). [Skotlex] if (base_exp) { - nextb = nextb?UINT_MAX:pc->thisbaseexp(sd); - if(sd->status.base_exp > nextb - base_exp) + nextb = nextb ? UINT64_MAX : pc->thisbaseexp(sd); + if (sd->status.base_exp > nextb - base_exp) sd->status.base_exp = nextb; else sd->status.base_exp += base_exp; pc->checkbaselevelup(sd); - clif->updatestatus(sd,SP_BASEEXP); + clif->updatestatus(sd, SP_BASEEXP); } if (job_exp) { - nextj = nextj?UINT_MAX:pc->thisjobexp(sd); - if(sd->status.job_exp > nextj - job_exp) + nextj = nextj ? UINT64_MAX : pc->thisjobexp(sd); + if (sd->status.job_exp > nextj - job_exp) sd->status.job_exp = nextj; else sd->status.job_exp += job_exp; pc->checkjoblevelup(sd); - clif->updatestatus(sd,SP_JOBEXP); + clif->updatestatus(sd, SP_JOBEXP); } #if PACKETVER >= 20091027 @@ -6725,8 +7185,14 @@ bool pc_gainexp(struct map_session_data *sd, struct block_list *src, unsigned in if(sd->state.showexp) { char output[256]; sprintf(output, - "Experience Gained Base:%u (%.2f%%) Job:%u (%.2f%%)",base_exp,nextbp*(float)100,job_exp,nextjp*(float)100); - clif_disp_onlyself(sd,output,strlen(output)); + msg_sd(sd, 889), // Experience Gained Base:%"PRIu64" (%.2f%%) Job:%"PRIu64" (%.2f%%) + base_exp, nextbp * (float)100, job_exp, nextjp * (float)100); + clif_disp_onlyself(sd, output); + } + + // Share master EXP to homunculus + if (sd->hd != NULL && battle_config.hom_bonus_exp_from_master > 0) { + homun->gainexp(sd->hd, apply_percentrate((int)base_exp, battle_config.hom_bonus_exp_from_master, 100)); } return true; @@ -6735,14 +7201,18 @@ bool pc_gainexp(struct map_session_data *sd, struct block_list *src, unsigned in /*========================================== * Returns max level for this character. *------------------------------------------*/ -unsigned int pc_maxbaselv(struct map_session_data *sd) +static int pc_maxbaselv(const struct map_session_data *sd) { - return pc->max_level[pc->class2idx(sd->status.class_)][0]; + nullpo_ret(sd); + + return pc->dbs->class_exp_table[pc->class2idx(sd->status.class)][CLASS_EXP_TABLE_BASE]->max_level; } -unsigned int pc_maxjoblv(struct map_session_data *sd) +static int pc_maxjoblv(const struct map_session_data *sd) { - return pc->max_level[pc->class2idx(sd->status.class_)][1]; + nullpo_ret(sd); + + return pc->dbs->class_exp_table[pc->class2idx(sd->status.class)][CLASS_EXP_TABLE_JOB]->max_level; } /*========================================== @@ -6750,23 +7220,37 @@ unsigned int pc_maxjoblv(struct map_session_data *sd) *------------------------------------------*/ //Base exp needed for next level. -unsigned int pc_nextbaseexp(struct map_session_data *sd) +static uint64 pc_nextbaseexp(const struct map_session_data *sd) { + const struct class_exp_group *exp_group = NULL; + nullpo_ret(sd); - if(sd->status.base_level>=pc->maxbaselv(sd) || sd->status.base_level<=0) + if (sd->status.base_level >= pc->maxbaselv(sd) || sd->status.base_level <= 0) return 0; - return pc->exp_table[pc->class2idx(sd->status.class_)][0][sd->status.base_level-1]; + exp_group = pc->dbs->class_exp_table[pc->class2idx(sd->status.class)][CLASS_EXP_TABLE_BASE]; + + nullpo_ret(exp_group); + + return VECTOR_INDEX(exp_group->exp, sd->status.base_level >= exp_group->max_level ? 0 : sd->status.base_level - 1); } //Base exp needed for this level. -unsigned int pc_thisbaseexp(struct map_session_data *sd) +static uint64 pc_thisbaseexp(const struct map_session_data *sd) { - if(sd->status.base_level>pc->maxbaselv(sd) || sd->status.base_level<=1) + const struct class_exp_group *exp_group = NULL; + + nullpo_ret(sd); + + if (sd->status.base_level > pc->maxbaselv(sd) || sd->status.base_level <= 1) return 0; - return pc->exp_table[pc->class2idx(sd->status.class_)][0][sd->status.base_level-2]; + exp_group = pc->dbs->class_exp_table[pc->class2idx(sd->status.class)][CLASS_EXP_TABLE_BASE]; + + nullpo_ret(exp_group); + + return VECTOR_INDEX(exp_group->exp, sd->status.base_level - 2); } /*========================================== @@ -6777,25 +7261,39 @@ unsigned int pc_thisbaseexp(struct map_session_data *sd) *------------------------------------------*/ //Job exp needed for next level. -unsigned int pc_nextjobexp(struct map_session_data *sd) +static uint64 pc_nextjobexp(const struct map_session_data *sd) { + const struct class_exp_group *exp_group = NULL; + nullpo_ret(sd); - if(sd->status.job_level>=pc->maxjoblv(sd) || sd->status.job_level<=0) + if (sd->status.job_level >= pc->maxjoblv(sd) || sd->status.job_level <= 0) return 0; - return pc->exp_table[pc->class2idx(sd->status.class_)][1][sd->status.job_level-1]; + + exp_group = pc->dbs->class_exp_table[pc->class2idx(sd->status.class)][CLASS_EXP_TABLE_JOB]; + + nullpo_ret(exp_group); + + return VECTOR_INDEX(exp_group->exp, sd->status.job_level >= exp_group->max_level ? 0 : sd->status.job_level - 1); } //Job exp needed for this level. -unsigned int pc_thisjobexp(struct map_session_data *sd) +static uint64 pc_thisjobexp(const struct map_session_data *sd) { - if(sd->status.job_level>pc->maxjoblv(sd) || sd->status.job_level<=1) + const struct class_exp_group *exp_group = NULL; + + nullpo_ret(sd); + + if (sd->status.job_level > pc->maxjoblv(sd) || sd->status.job_level <= 1) return 0; - return pc->exp_table[pc->class2idx(sd->status.class_)][1][sd->status.job_level-2]; + + exp_group = pc->dbs->class_exp_table[pc->class2idx(sd->status.class)][CLASS_EXP_TABLE_JOB]; + + return VECTOR_INDEX(exp_group->exp, sd->status.job_level - 2); } /// Returns the value of the specified stat. -int pc_getstat(struct map_session_data* sd, int type) +static int pc_getstat(struct map_session_data *sd, int type) { nullpo_retr(-1, sd); @@ -6813,7 +7311,7 @@ int pc_getstat(struct map_session_data* sd, int type) /// Sets the specified stat to the specified value. /// Returns the new value. -int pc_setstat(struct map_session_data* sd, int type, int val) +static int pc_setstat(struct map_session_data *sd, int type, int val) { nullpo_retr(-1, sd); @@ -6828,11 +7326,13 @@ int pc_setstat(struct map_session_data* sd, int type, int val) return -1; } + achievement->validate_stats(sd, type, val); // Achievements [Smokexyz/Hercules] + return val; } // Calculates the number of status points PC gets when leveling up (from level to level+1) -int pc_gets_status_point(int level) +static int pc_gets_status_point(int level) { if (battle_config.use_statpoint_table) //Use values from "db/statpoint.txt" return (pc->statp[level+1] - pc->statp[level]); @@ -6843,7 +7343,7 @@ int pc_gets_status_point(int level) /// Returns the number of stat points needed to change the specified stat by val. /// If val is negative, returns the number of stat points that would be needed to /// raise the specified stat from (current value - val) to current value. -int pc_need_status_point(struct map_session_data* sd, int type, int val) +static int pc_need_status_point(struct map_session_data *sd, int type, int val) { int low, high, sp = 0; @@ -6878,7 +7378,8 @@ int pc_need_status_point(struct map_session_data* sd, int type, int val) * @param type Stat to verify. * @return Maximum value the stat could grow by. */ -int pc_maxparameterincrease(struct map_session_data* sd, int type) { +static int pc_maxparameterincrease(struct map_session_data *sd, int type) +{ int base, final, status_points = sd->status.status_point; base = final = pc->getstat(sd, type); @@ -6908,35 +7409,35 @@ int pc_maxparameterincrease(struct map_session_data* sd, int type) { * @retval true if the stat was increased by any amount. * @retval false if there were no changes. */ -bool pc_statusup(struct map_session_data* sd, int type, int increase) { - int max_increase = 0, current = 0, needed_points = 0, final_value = 0; - +static bool pc_statusup(struct map_session_data *sd, int type, int increase) +{ nullpo_ret(sd); + int realIncrease = increase; // check conditions - if (type < SP_STR || type > SP_LUK || increase <= 0) { - clif->statusupack(sd, type, 0, 0); + if (type < SP_STR || type > SP_LUK || realIncrease <= 0) { + clif->statusupack(sd, type, 0, increase); return false; } // check limits - current = pc->getstat(sd, type); - max_increase = pc->maxparameterincrease(sd, type); - increase = cap_value(increase, 0, max_increase); // cap to the maximum status points available - if (increase <= 0 || current + increase > pc_maxparameter(sd)) { - clif->statusupack(sd, type, 0, 0); + int current = pc->getstat(sd, type); + int max_increase = pc->maxparameterincrease(sd, type); + realIncrease = cap_value(realIncrease, 0, max_increase); // cap to the maximum status points available + if (realIncrease <= 0 || current + realIncrease > pc_maxparameter(sd)) { + clif->statusupack(sd, type, 0, increase); return false; } // check status points - needed_points = pc->need_status_point(sd, type, increase); + int needed_points = pc->need_status_point(sd, type, realIncrease); if (needed_points < 0 || needed_points > sd->status.status_point) { // Sanity check - clif->statusupack(sd, type, 0, 0); + clif->statusupack(sd, type, 0, increase); return false; } // set new values - final_value = pc->setstat(sd, type, current + increase); + int final_value = pc->setstat(sd, type, current + realIncrease); sd->status.status_point -= needed_points; status_calc_pc(sd, SCO_NONE); @@ -6967,7 +7468,7 @@ bool pc_statusup(struct map_session_data* sd, int type, int increase) { * @return the stat increase amount. * @retval 0 if no changes were made. */ -int pc_statusup2(struct map_session_data* sd, int type, int val) +static int pc_statusup2(struct map_session_data *sd, int type, int val) { int max, need; nullpo_ret(sd); @@ -7002,8 +7503,9 @@ int pc_statusup2(struct map_session_data* sd, int type, int val) * Update skill_lv for player sd * Skill point allocation *------------------------------------------*/ -int pc_skillup(struct map_session_data *sd,uint16 skill_id) { - uint16 index = 0; +static int pc_skillup(struct map_session_data *sd, uint16 skill_id) +{ + int index = 0; nullpo_ret(sd); if( skill_id >= GD_SKILLBASE && skill_id < GD_SKILLBASE+MAX_GUILDSKILL ) { @@ -7022,13 +7524,14 @@ int pc_skillup(struct map_session_data *sd,uint16 skill_id) { if( sd->status.skill_point > 0 && sd->status.skill[index].id && sd->status.skill[index].flag == SKILL_FLAG_PERMANENT && //Don't allow raising while you have granted skills. [Skotlex] - sd->status.skill[index].lv < skill->tree_get_max(skill_id, sd->status.class_) ) + sd->status.skill[index].lv < skill->tree_get_max(skill_id, sd->status.class) ) { sd->status.skill[index].lv++; sd->status.skill_point--; if( !skill->dbs->db[index].inf ) status_calc_pc(sd,SCO_NONE); // Only recalculate for passive skills. - else if( sd->status.skill_point == 0 && (sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && pc->famerank(sd->status.char_id, MAPID_TAEKWON) ) + else if (sd->status.skill_point == 0 && (sd->job & MAPID_UPPERMASK) == MAPID_TAEKWON + && sd->status.base_level >= 90 && pc->fame_rank(sd->status.char_id, RANKTYPE_TAEKWON) > 0) pc->calc_skilltree(sd); // Required to grant all TK Ranger skills. else pc->check_skilltree(sd, skill_id); // Check if a new skill can Lvlup @@ -7039,13 +7542,18 @@ int pc_skillup(struct map_session_data *sd,uint16 skill_id) { clif->updatestatus(sd,SP_CARTINFO); if (!pc_has_permission(sd, PC_PERM_ALL_SKILL)) // may skill everything at any time anyways, and this would cause a huge slowdown clif->skillinfoblock(sd); - } else if( battle_config.skillup_limit ){ - if (sd->sktree.second) - clif->msgtable_num(sd, MSG_SKILL_POINTS_LEFT_JOB1, sd->sktree.second); - else if (sd->sktree.third) - clif->msgtable_num(sd, MSG_SKILL_POINTS_LEFT_JOB2, sd->sktree.third); - else if (pc->calc_skillpoint(sd) < 9) /* TODO: official response? */ + } else if (battle_config.skillup_limit) { + if (sd->sktree.second != 0) { +#if PACKETVER >= 20090805 + clif->msgtable_num(sd, MSG_UPGRADESKILLERROR_MORE_FIRSTJOBSKILL, sd->sktree.second); +#endif + } else if (sd->sktree.third != 0) { +#if PACKETVER >= 20091013 + clif->msgtable_num(sd, MSG_UPGRADESKILLERROR_MORE_SECONDJOBSKILL, sd->sktree.third); +#endif + } else if (pc->calc_skillpoint(sd) < 9) { /* TODO: official response? */ clif->messagecolor_self(sd->fd, COLOR_RED, "You need the basic skills"); + } } return 0; } @@ -7053,14 +7561,14 @@ int pc_skillup(struct map_session_data *sd,uint16 skill_id) { /*========================================== * /allskill *------------------------------------------*/ -int pc_allskillup(struct map_session_data *sd) +static int pc_allskillup(struct map_session_data *sd) { int i; nullpo_ret(sd); - for(i=0;i<MAX_SKILL;i++){ - if (sd->status.skill[i].flag != SKILL_FLAG_PERMANENT && sd->status.skill[i].flag != SKILL_FLAG_PERM_GRANTED && sd->status.skill[i].flag != SKILL_FLAG_PLAGIARIZED) { + for (i = 0; i < MAX_SKILL_DB; i++) { + if (sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[i].flag >= SKILL_FLAG_REPLACED_LV_0) { sd->status.skill[i].lv = (sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY) ? 0 : sd->status.skill[i].flag - SKILL_FLAG_REPLACED_LV_0; sd->status.skill[i].flag = SKILL_FLAG_PERMANENT; if (sd->status.skill[i].lv == 0) @@ -7070,7 +7578,7 @@ int pc_allskillup(struct map_session_data *sd) if (pc_has_permission(sd, PC_PERM_ALL_SKILL)) { //Get ALL skills except npc/guild ones. [Skotlex] //and except SG_DEVIL [Komurka] and MO_TRIPLEATTACK and RG_SNATCHER [ultramage] - for(i=0;i<MAX_SKILL;i++){ + for (i = 0; i < MAX_SKILL_DB; i++) { switch( skill->dbs->db[i].nameid ) { case SG_DEVIL: case MO_TRIPLEATTACK: @@ -7084,8 +7592,8 @@ int pc_allskillup(struct map_session_data *sd) } } else { int id; - for (i = 0; i < MAX_SKILL_TREE && (id=pc->skill_tree[pc->class2idx(sd->status.class_)][i].id) > 0; i++) { - int idx = pc->skill_tree[pc->class2idx(sd->status.class_)][i].idx; + for (i = 0; i < MAX_SKILL_TREE && (id=pc->skill_tree[pc->class2idx(sd->status.class)][i].id) > 0; i++) { + int idx = pc->skill_tree[pc->class2idx(sd->status.class)][i].idx; int inf2 = skill->dbs->db[idx].inf2; if ( (inf2&INF2_QUEST_SKILL && !battle_config.quest_skill_learn) || @@ -7095,7 +7603,7 @@ int pc_allskillup(struct map_session_data *sd) continue; //Cannot be learned normally. sd->status.skill[idx].id = id; - sd->status.skill[idx].lv = skill->tree_get_max(id, sd->status.class_); // celest + sd->status.skill[idx].lv = skill->tree_get_max(id, sd->status.class); // celest } } status_calc_pc(sd,SCO_NONE); @@ -7108,7 +7616,7 @@ int pc_allskillup(struct map_session_data *sd) /*========================================== * /resetlvl *------------------------------------------*/ -int pc_resetlvl(struct map_session_data* sd,int type) +static int pc_resetlvl(struct map_session_data *sd, int type) { int i; @@ -7132,7 +7640,7 @@ int pc_resetlvl(struct map_session_data* sd,int type) sd->status.int_=1; sd->status.dex=1; sd->status.luk=1; - if(sd->status.class_ == JOB_NOVICE_HIGH) { + if (sd->status.class == JOB_NOVICE_HIGH) { sd->status.status_point=100; // not 88 [celest] // give platinum skills upon changing pc->skill(sd, NV_FIRSTAID, 1, SKILL_GRANT_PERMANENT); @@ -7196,7 +7704,7 @@ int pc_resetlvl(struct map_session_data* sd,int type) /*========================================== * /resetstate *------------------------------------------*/ -int pc_resetstate(struct map_session_data* sd) +static int pc_resetstate(struct map_session_data *sd) { nullpo_ret(sd); @@ -7204,12 +7712,12 @@ int pc_resetstate(struct map_session_data* sd) // New statpoint table used here - Dexity if (sd->status.base_level > MAX_LEVEL) { //pc->statp[] goes out of bounds, can't reset! - ShowError("pc_resetstate: Can't reset stats of %d:%d, the base level (%u) is greater than the max level supported (%d)\n", + ShowError("pc_resetstate: Can't reset stats of %d:%d, the base level (%d) is greater than the max level supported (%d)\n", sd->status.account_id, sd->status.char_id, sd->status.base_level, MAX_LEVEL); return 0; } - sd->status.status_point = pc->statp[sd->status.base_level] + ((sd->class_&JOBL_UPPER) ? 52 : 0); // extra 52+48=100 stat points + sd->status.status_point = pc->statp[sd->status.base_level] + ((sd->job & JOBL_UPPER) != 0 ? 52 : 0); // extra 52+48=100 stat points } else { @@ -7250,7 +7758,7 @@ int pc_resetstate(struct map_session_data* sd) if( sd->mission_mobid ) { //bugreport:2200 sd->mission_mobid = 0; sd->mission_count = 0; - pc_setglobalreg(sd,script->add_str("TK_MISSION_ID"), 0); + pc_setglobalreg(sd,script->add_variable("TK_MISSION_ID"), 0); } status_calc_pc(sd,SCO_NONE); @@ -7262,12 +7770,12 @@ int pc_resetstate(struct map_session_data* sd) * /resetskill * @param flag: @see enum pc_resetskill_flag *------------------------------------------*/ -int pc_resetskill(struct map_session_data* sd, int flag) +static int pc_resetskill(struct map_session_data *sd, int flag) { int i, inf2, skill_point=0; nullpo_ret(sd); - if( flag&PCRESETSKILL_CHSEX && (sd->class_&MAPID_UPPERMASK) != MAPID_BARDDANCER ) + if (flag&PCRESETSKILL_CHSEX && (sd->job & MAPID_UPPERMASK) != MAPID_BARDDANCER) return 0; if( !(flag&PCRESETSKILL_RECOUNT) ) { //Remove stuff lost when resetting skills. @@ -7275,11 +7783,11 @@ int pc_resetskill(struct map_session_data* sd, int flag) /** * It has been confirmed on official server that when you reset skills with a ranked tweakwon your skills are not reset (because you have all of them anyway) **/ - if( (sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && pc->famerank(sd->status.char_id, MAPID_TAEKWON) ) + if ((sd->job & MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && pc->fame_rank(sd->status.char_id, RANKTYPE_TAEKWON)) return 0; if( pc->checkskill(sd, SG_DEVIL) && !pc->nextjobexp(sd) ) //Remove perma blindness due to skill-reset. [Skotlex] - clif->sc_end(&sd->bl, sd->bl.id, SELF, SI_DEVIL1); + clif->sc_end(&sd->bl, sd->bl.id, SELF, status->get_sc_icon(SC_DEVIL1)); i = sd->sc.option; if( i&OPTION_RIDING && pc->checkskill(sd, KN_RIDING) ) i &= ~OPTION_RIDING; @@ -7291,7 +7799,7 @@ int pc_resetskill(struct map_session_data* sd, int flag) i &= ~OPTION_WUG; if( i&OPTION_WUGRIDER && pc->checkskill(sd, RA_WUGRIDER) ) i &= ~OPTION_WUGRIDER; - if( i&OPTION_MADOGEAR && ( sd->class_&MAPID_THIRDMASK ) == MAPID_MECHANIC ) + if (i&OPTION_MADOGEAR && (sd->job & MAPID_THIRDMASK) == MAPID_MECHANIC) i &= ~OPTION_MADOGEAR; #ifndef NEW_CARTS if( i&OPTION_CART && pc->checkskill(sd, MC_PUSHCART) ) @@ -7304,13 +7812,13 @@ int pc_resetskill(struct map_session_data* sd, int flag) pc->setoption(sd, i); if( homun_alive(sd->hd) && pc->checkskill(sd, AM_CALLHOMUN) ) - homun->vaporize(sd, HOM_ST_REST); + homun->vaporize(sd, HOM_ST_REST, true); + + if ((sd->sc.data[SC_SPRITEMABLE] && pc->checkskill(sd, SU_SPRITEMABLE))) + status_change_end(&sd->bl, SC_SPRITEMABLE, INVALID_TIMER); } - for( i = 1; i < MAX_SKILL; i++ ) { - // FIXME: We're looping on i = [1..MAX_SKILL-1] (which makes sense as index for sd->status.skill[]) but then we're using the - // same i to access skill->dbs->db[], and especially to check skill_ischangesex(). This is wrong. - uint16 skill_id = 0; + for (i = 1; i < MAX_SKILL_DB; i++) { int lv = sd->status.skill[i].lv; if (lv < 1) continue; @@ -7319,17 +7827,7 @@ int pc_resetskill(struct map_session_data* sd, int flag) if( inf2&(INF2_WEDDING_SKILL|INF2_SPIRIT_SKILL) ) //Avoid reseting wedding/linker skills. continue; - skill_id = skill->dbs->db[i].nameid; - - // Don't reset trick dead if not a novice/baby - if( skill_id == NV_TRICKDEAD && (sd->class_&(MAPID_BASEMASK|JOBL_2)) != MAPID_NOVICE ) { - sd->status.skill[i].lv = 0; - sd->status.skill[i].flag = 0; - continue; - } - - // do not reset basic skill - if( skill_id == NV_BASIC && (sd->class_&(MAPID_BASEMASK|JOBL_2)) != MAPID_NOVICE ) + if (pc->resetskill_job(sd, i)) continue; if( sd->status.skill[i].flag == SKILL_FLAG_PERM_GRANTED ) @@ -7384,10 +7882,34 @@ int pc_resetskill(struct map_session_data* sd, int flag) return skill_point; } +static bool pc_resetskill_job(struct map_session_data *sd, int index) +{ + uint16 skill_id; + + nullpo_retr(false, sd); + Assert_retr(false, index >= 0 && index < MAX_SKILL_DB); + + skill_id = skill->dbs->db[index].nameid; + + // Don't reset trick dead if not a novice/baby + if (skill_id == NV_TRICKDEAD && (sd->job & MAPID_UPPERMASK) != MAPID_NOVICE) { + sd->status.skill[index].lv = 0; + sd->status.skill[index].flag = 0; + return true; + } + + // do not reset basic skill + if (skill_id == NV_BASIC && (sd->job & MAPID_UPPERMASK) != MAPID_NOVICE) + return true; + if (skill_id == SU_BASIC_SKILL && (sd->job & MAPID_BASEMASK) != MAPID_SUMMONER) + return true; + return false; +} + /*========================================== * /resetfeel [Komurka] *------------------------------------------*/ -int pc_resetfeel(struct map_session_data* sd) +static int pc_resetfeel(struct map_session_data *sd) { int i; nullpo_ret(sd); @@ -7396,25 +7918,25 @@ int pc_resetfeel(struct map_session_data* sd) { sd->feel_map[i].m = -1; sd->feel_map[i].index = 0; - pc_setglobalreg(sd,script->add_str(pc->sg_info[i].feel_var),0); + pc_setglobalreg(sd,script->add_variable(pc->sg_info[i].feel_var),0); } return 0; } -int pc_resethate(struct map_session_data* sd) +static int pc_resethate(struct map_session_data *sd) { int i; nullpo_ret(sd); for (i = 0; i < MAX_PC_FEELHATE; i++) { sd->hate_mob[i] = -1; - pc_setglobalreg(sd,script->add_str(pc->sg_info[i].hate_var),0); + pc_setglobalreg(sd,script->add_variable(pc->sg_info[i].hate_var),0); } return 0; } -int pc_skillatk_bonus(struct map_session_data *sd, uint16 skill_id) +static int pc_skillatk_bonus(struct map_session_data *sd, uint16 skill_id) { int i, bonus = 0; nullpo_ret(sd); @@ -7428,28 +7950,49 @@ int pc_skillatk_bonus(struct map_session_data *sd, uint16 skill_id) return bonus; } -int pc_skillheal_bonus(struct map_session_data *sd, uint16 skill_id) { +static int pc_skillheal_bonus(struct map_session_data *sd, uint16 skill_id) +{ int i, bonus = sd->bonus.add_heal_rate; - if( bonus ) { - switch( skill_id ) { - case AL_HEAL: if( !(battle_config.skill_add_heal_rate&1) ) bonus = 0; break; - case PR_SANCTUARY: if( !(battle_config.skill_add_heal_rate&2) ) bonus = 0; break; - case AM_POTIONPITCHER: if( !(battle_config.skill_add_heal_rate&4) ) bonus = 0; break; - case CR_SLIMPITCHER: if( !(battle_config.skill_add_heal_rate&8) ) bonus = 0; break; - case BA_APPLEIDUN: if( !(battle_config.skill_add_heal_rate&16)) bonus = 0; break; + if (bonus) { + switch (skill_id) { + case AL_HEAL: + if ((battle_config.skill_add_heal_rate & 1) == 0) + bonus = 0; + break; + case PR_SANCTUARY: + if ((battle_config.skill_add_heal_rate & 2) == 0) + bonus = 0; + break; + case AM_POTIONPITCHER: + if ((battle_config.skill_add_heal_rate & 4) == 0) + bonus = 0; + break; + case CR_SLIMPITCHER: + if ((battle_config.skill_add_heal_rate & 8) == 0) + bonus = 0; + break; + case BA_APPLEIDUN: + if ((battle_config.skill_add_heal_rate & 16) == 0) + bonus = 0; + break; + case AB_HIGHNESSHEAL: + if ((battle_config.skill_add_heal_rate & 32) == 0) + bonus = 0; + break; } } ARR_FIND(0, ARRAYLENGTH(sd->skillheal), i, sd->skillheal[i].id == skill_id); - if( i < ARRAYLENGTH(sd->skillheal) ) + if (i < ARRAYLENGTH(sd->skillheal)) bonus += sd->skillheal[i].val; return bonus; } -int pc_skillheal2_bonus(struct map_session_data *sd, uint16 skill_id) { +static int pc_skillheal2_bonus(struct map_session_data *sd, uint16 skill_id) +{ int i, bonus = sd->bonus.add_heal2_rate; ARR_FIND(0, ARRAYLENGTH(sd->skillheal2), i, sd->skillheal2[i].id == skill_id); @@ -7460,7 +8003,7 @@ int pc_skillheal2_bonus(struct map_session_data *sd, uint16 skill_id) { return bonus; } -void pc_respawn(struct map_session_data* sd, clr_type clrtype) +static void pc_respawn(struct map_session_data *sd, enum clr_type clrtype) { if( !pc_isdead(sd) ) return; // not applicable @@ -7473,7 +8016,8 @@ void pc_respawn(struct map_session_data* sd, clr_type clrtype) clif->resurrection(&sd->bl, 1); //If warping fails, send a normal stand up packet. } -int pc_respawn_timer(int tid, int64 tick, int id, intptr_t data) { +static int pc_respawn_timer(int tid, int64 tick, int id, intptr_t data) +{ struct map_session_data *sd = map->id2sd(id); if( sd != NULL ) { @@ -7487,7 +8031,7 @@ int pc_respawn_timer(int tid, int64 tick, int id, intptr_t data) { /*========================================== * Invoked when a player has received damage *------------------------------------------*/ -void pc_damage(struct map_session_data *sd,struct block_list *src,unsigned int hp, unsigned int sp) +static void pc_damage(struct map_session_data *sd, struct block_list *src, unsigned int hp, unsigned int sp) { if (sp) clif->updatestatus(sd,SP_SP); if (hp) clif->updatestatus(sd,SP_HP); @@ -7509,163 +8053,192 @@ void pc_damage(struct map_session_data *sd,struct block_list *src,unsigned int h if( sd->status.pet_id > 0 && sd->pd && battle_config.pet_damage_support ) pet->target_check(sd,src,1); - if( sd->status.ele_id > 0 ) + if (sd->status.ele_id != 0 && sd->ed != NULL) elemental->set_target(sd,src); - sd->canlog_tick = timer->gettick(); + if (battle_config.prevent_logout_trigger & PLT_DAMAGE) + sd->canlog_tick = timer->gettick(); + + // Achievements [Smokexyz/Hercules] + if (src != NULL) { + if (src->type == BL_PC) + achievement->validate_pc_damage(BL_UCAST(BL_PC, src), sd, hp); + else if (src->type == BL_MOB) + achievement->validate_mob_damage(sd, hp, true); + } } -/*========================================== - * Invoked when a player has negative current hp - *------------------------------------------*/ -int pc_dead(struct map_session_data *sd,struct block_list *src) { - int i=0,j=0; - int64 tick = timer->gettick(); +/** + * Invoked when a character died. + * + * @param sd The died character. + * @param src The unit which caused the death. + * @retval 0 Death canceled. + * @retval 1 Standard death. + * @retval 9 Died in PVP/GVG/BG. + * + **/ +static int pc_dead(struct map_session_data *sd, struct block_list *src) +{ + nullpo_ret(sd); - nullpo_retr(0, sd); + for (int i = 0; i < MAX_PC_DEVOTION; i++) { + if (sd->devotion[i] != 0) { + struct map_session_data *devsd = map->id2sd(sd->devotion[i]); - for (j = 0; j < MAX_PC_DEVOTION; j++) { - if (sd->devotion[j]) { - struct map_session_data *devsd = map->id2sd(sd->devotion[j]); - if (devsd) + if (devsd != NULL) status_change_end(&devsd->bl, SC_DEVOTION, INVALID_TIMER); - sd->devotion[j] = 0; + + sd->devotion[i] = 0; } } - if(sd->status.pet_id > 0 && sd->pd) { + if (sd->status.pet_id > 0 && sd->pd != NULL) { struct pet_data *pd = sd->pd; - if( !map->list[sd->bl.m].flag.noexppenalty ) { + + if (map->list[sd->bl.m].flag.noexppenalty == 0) { pet->set_intimate(pd, pd->pet.intimate - pd->petDB->die); - if( pd->pet.intimate < 0 ) - pd->pet.intimate = 0; - clif->send_petdata(sd,sd->pd,1,pd->pet.intimate); + clif->send_petdata(sd, sd->pd, 1, pd->pet.intimate); } - if( sd->pd->target_id ) // Unlock all targets... + + if (sd->pd->target_id != 0) // Unlock all targets. pet->unlocktarget(sd->pd); } - if (sd->status.hom_id > 0){ - if(battle_config.homunculus_auto_vapor && sd->hd) - homun->vaporize(sd, HOM_ST_REST); - } + if (sd->status.hom_id > 0 && sd->hd != NULL && battle_config.homunculus_auto_vapor != 0) + homun->vaporize(sd, HOM_ST_REST, true); - if( sd->md ) - mercenary->delete(sd->md, 3); // Your mercenary soldier has ran away. + if (sd->md != NULL) + mercenary->delete(sd->md, 3); // Your mercenary soldier ran away. - if( sd->ed ) + if (sd->ed != NULL) elemental->delete(sd->ed, 0); - // Leave duel if you die [LuzZza] - if(battle_config.duel_autoleave_when_die) { - if(sd->duel_group > 0) + if (battle_config.duel_autoleave_when_die != 0) { // Leave duel if character died. [LuzZza] + if (sd->duel_group > 0) duel->leave(sd->duel_group, sd); - if(sd->duel_invite > 0) + if (sd->duel_invite > 0) duel->reject(sd->duel_invite, sd); } - if (sd->npc_id && sd->st && sd->st->state != RUN) + if (sd->npc_id != 0 && sd->st != NULL && sd->st->state != RUN) npc->event_dequeue(sd); - pc_setglobalreg(sd,script->add_str("PC_DIE_COUNTER"),sd->die_counter+1); - pc->setparam(sd, SP_KILLERRID, src?src->id:0); + pc_setglobalreg(sd, script->add_variable("PC_DIE_COUNTER"), sd->die_counter + 1); + pc->setparam(sd, SP_KILLERRID, (src != NULL) ? src->id : 0); - if( sd->bg_id ) {/* TODO: purge when bgqueue is deemed ok */ - struct battleground_data *bgd; - if( (bgd = bg->team_search(sd->bg_id)) != NULL && bgd->die_event[0] ) + if (sd->bg_id != 0) { //TODO: Purge when bgqueue is deemed ok. + struct battleground_data *bgd = bg->team_search(sd->bg_id); + + if (bgd != NULL && bgd->die_event[0] != '\0') npc->event(sd, bgd->die_event, 0); } - for (i = 0; i < VECTOR_LENGTH(sd->script_queues); i++ ) { + for (int i = 0; i < VECTOR_LENGTH(sd->script_queues); i++) { struct script_queue *queue = script->queue(VECTOR_INDEX(sd->script_queues, i)); - if (queue && queue->event_death[0] != '\0') + + if (queue != NULL && queue->event_death[0] != '\0') npc->event(sd, queue->event_death, 0); } - npc->script_event(sd,NPCE_DIE); + npc->script_event(sd, NPCE_DIE); - // Clear anything NPC-related when you die and was interacting with one. - if ( (sd->npc_id || sd->npc_shopid) && sd->state.dialog) { - if (sd->state.using_fake_npc) { + // Clear anything NPC-related if character died while interacting with one. + if ((sd->npc_id != 0 || sd->npc_shopid != 0) && sd->state.dialog != 0) { + if (sd->state.using_fake_npc != 0) { clif->clearunit_single(sd->npc_id, CLR_OUTSIGHT, sd->fd); sd->state.using_fake_npc = 0; } - if (sd->state.menu_or_input) + + if (sd->state.menu_or_input != 0) sd->state.menu_or_input = 0; - if (sd->npc_menu) + + if (sd->npc_menu != 0) sd->npc_menu = 0; sd->npc_id = 0; sd->npc_shopid = 0; - if (sd->st && sd->st->state != END) + + if (sd->st != NULL && sd->st->state != END) sd->st->state = END; } - /* e.g. not killed through pc->damage */ - if( pc_issit(sd) ) { - clif->sc_end(&sd->bl,sd->bl.id,SELF,SI_SIT); - } + // E.g. not killed through pc->damage(). + if (pc_issit(sd)) + clif->sc_end(&sd->bl, sd->bl.id, SELF, status->get_sc_icon(SC_SIT)); pc_setdead(sd); - //Reset menu skills/item skills - if (sd->skillitem) - sd->skillitem = sd->skillitemlv = 0; - if (sd->menuskill_id) - sd->menuskill_id = sd->menuskill_val = 0; - //Reset ticks. - sd->hp_loss.tick = sd->sp_loss.tick = sd->hp_regen.tick = sd->sp_regen.tick = 0; - - if ( sd->spiritball ) + clif->party_dead_notification(sd); + + pc->autocast_clear(sd); // Unset auto-cast data. + + if (sd->menuskill_id != 0) { // Reset menu skills. + sd->menuskill_id = 0; + sd->menuskill_val = 0; + } + + // Reset ticks. + sd->hp_loss.tick = 0; + sd->sp_loss.tick = 0; + sd->hp_regen.tick = 0; + sd->sp_regen.tick = 0; + + if (sd->spiritball != 0) pc->delspiritball(sd, sd->spiritball, 0); + if (sd->charm_type != CHARM_TYPE_NONE && sd->charm_count > 0) pc->del_charm(sd, sd->charm_count, sd->charm_type); + int64 tick = timer->gettick(); + if (src != NULL) { switch (src->type) { - case BL_MOB: - { - struct mob_data *md = BL_UCAST(BL_MOB, src); - if (md->target_id==sd->bl.id) - mob->unlocktarget(md,tick); - if (battle_config.mobs_level_up && md->status.hp - && (unsigned int)md->level < pc->maxbaselv(sd) - && !md->guardian_data && md->special_state.ai == AI_NONE// Guardians/summons should not level. [Skotlex] - ) { - // monster level up [Valaris] - clif->misceffect(&md->bl,0); - md->level++; - status_calc_mob(md, SCO_NONE); - status_percent_heal(src,10,0); - - if( battle_config.show_mob_info&4 ) - {// update name with new level - clif->charnameack(0, &md->bl); - } - } - src = battle->get_master(src); // Maybe Player Summon + case BL_MOB: { + struct mob_data *md = BL_UCAST(BL_MOB, src); + + if (md->target_id == sd->bl.id) + mob->unlocktarget(md, tick); + + if (battle_config.mobs_level_up != 0 && md->status.hp != 0 && md->level < pc->maxbaselv(sd) + && md->guardian_data == NULL && md->special_state.ai == AI_NONE) { // Guardians/summons should not level up. [Skotlex] + /// Monster level up. [Valaris] + clif->misceffect(&md->bl, 0); + md->level++; + status_calc_mob(md, SCO_NONE); + status_percent_heal(src, 10, 0); + + if ((battle_config.show_mob_info & 4) != 0) + clif->blname_ack(0, &md->bl); // Update name with new level. } + + src = battle->get_master(src); // Maybe character summon. break; - case BL_PET: //Pass on to master... - src = &BL_UCAST(BL_PET, src)->msd->bl; + } + case BL_PET: + src = &BL_UCAST(BL_PET, src)->msd->bl; // Pass on to master. break; - case BL_HOM: - src = &BL_UCAST(BL_HOM, src)->master->bl; + case BL_HOM: + src = &BL_UCAST(BL_HOM, src)->master->bl; // Pass on to master. break; - case BL_MER: - src = &BL_UCAST(BL_MER, src)->master->bl; + case BL_MER: + src = &BL_UCAST(BL_MER, src)->master->bl; // Pass on to master. break; } } if (src != NULL && src->type == BL_PC) { struct map_session_data *ssd = BL_UCAST(BL_PC, src); + pc->setparam(ssd, SP_KILLEDRID, sd->bl.id); npc->script_event(ssd, NPCE_KILLPC); + achievement->validate_pc_kill(ssd, sd); - if (battle_config.pk_mode&2) { + if ((battle_config.pk_mode & 2) != 0) { ssd->status.manner -= 5; - if(ssd->status.manner < 0) - sc_start(NULL,src,SC_NOCHAT,100,0,0); + + if (ssd->status.manner < 0) + sc_start(NULL, src, SC_NOCHAT, 100, 0, 0); + #if 0 // PK/Karma system code (not enabled yet) [celest] // originally from Kade Online, so i don't know if any of these is correct ^^; @@ -7677,14 +8250,15 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) { // If player killed was more evil sd->status.karma--; ssd->status.karma--; - } - else if (sd->status.karma < ssd->status.karma) // If player killed was more good + } else if (sd->status.karma < ssd->status.karma) { // If player killed was more good ssd->status.karma++; + } // or the PK System way... if (sd->status.karma > 0) // player killed is dishonourable? ssd->status.karma--; // honour points earned + sd->status.karma++; // honour points lost // To-do: Receive exp on certain occasions @@ -7692,136 +8266,156 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) { } } - if( battle_config.bone_drop==2 - || (battle_config.bone_drop==1 && map->list[sd->bl.m].flag.pvp) - ) { + if (battle_config.bone_drop == 2 || (battle_config.bone_drop == 1 && map->list[sd->bl.m].flag.pvp != 0)) { struct item item_tmp; - memset(&item_tmp,0,sizeof(item_tmp)); - item_tmp.nameid=ITEMID_SKULL_; - item_tmp.identify=1; - item_tmp.card[0]=CARD0_CREATE; - item_tmp.card[1]=0; - item_tmp.card[2]=GetWord(sd->status.char_id,0); // CharId - item_tmp.card[3]=GetWord(sd->status.char_id,1); - map->addflooritem(&sd->bl, &item_tmp, 1, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0); - } - - // activate Steel body if a super novice dies at 99+% exp [celest] - if ((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && !sd->state.snovice_dead_flag) { - unsigned int next = pc->nextbaseexp(sd); - if( next == 0 ) next = pc->thisbaseexp(sd); - if( get_percentage(sd->status.base_exp,next) >= 99 ) { + + memset(&item_tmp, 0, sizeof(item_tmp)); + item_tmp.nameid = ITEMID_SKULL_; + item_tmp.identify = 1; + item_tmp.card[0] = CARD0_CREATE; + item_tmp.card[1] = 0; + item_tmp.card[2] = GetWord(sd->status.char_id, 0); + item_tmp.card[3] = GetWord(sd->status.char_id, 1); + map->addflooritem(&sd->bl, &item_tmp, 1, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0, false); + } + + // Activate Steel Body if a Super Novice dies at 99+% EXP. [celest] + if ((sd->job & MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->state.snovice_dead_flag == 0) { + uint64 next = pc->nextbaseexp(sd); + + if (next == 0) + next = pc->thisbaseexp(sd); + + if (get_percentage64(sd->status.base_exp, next) >= 99) { sd->state.snovice_dead_flag = 1; pc->setstand(sd); status_percent_heal(&sd->bl, 100, 100); clif->resurrection(&sd->bl, 1); - if(battle_config.pc_invincible_time) + + if (battle_config.pc_invincible_time != 0) pc->setinvincibletimer(sd, battle_config.pc_invincible_time); - sc_start(NULL,&sd->bl,status->skill2sc(MO_STEELBODY),100,1,skill->get_time(MO_STEELBODY,1)); - if(map_flag_gvg2(sd->bl.m)) + + sc_start(NULL, &sd->bl, status->skill2sc(MO_STEELBODY), 100, 1, skill->get_time(MO_STEELBODY, 1)); + + if (map_flag_gvg2(sd->bl.m)) pc->respawn_timer(INVALID_TIMER, timer->gettick(), sd->bl.id, 0); + return 0; } } - // changed penalty options, added death by player if pk_mode [Valaris] - if( battle_config.death_penalty_type - && (sd->class_&MAPID_UPPERMASK) != MAPID_NOVICE // only novices will receive no penalty - && !map->list[sd->bl.m].flag.noexppenalty && !map_flag_gvg2(sd->bl.m) - && !sd->sc.data[SC_BABY] && !sd->sc.data[SC_CASH_DEATHPENALTY] - ) { + if (battle_config.death_penalty_type != 0 && pc->isDeathPenaltyJob(sd->job) && !map_flag_gvg2(sd->bl.m) + && map->list[sd->bl.m].flag.noexppenalty == 0 && sd->sc.data[SC_BABY] == NULL + && sd->sc.data[SC_CASH_DEATHPENALTY] == NULL && !pc->auto_exp_insurance(sd)) { if (battle_config.death_penalty_base > 0) { unsigned int base_penalty = 0; + int rate = battle_config.death_penalty_base; + switch (battle_config.death_penalty_type) { - case 1: - base_penalty = (unsigned int) apply_percentrate64(pc->nextbaseexp(sd), battle_config.death_penalty_base, 10000); - break; - case 2: - base_penalty = (unsigned int) apply_percentrate64(sd->status.base_exp, battle_config.death_penalty_base, 10000); - break; + case 1: + base_penalty = (unsigned int)apply_percentrate64(pc->nextbaseexp(sd), rate, 10000); + break; + case 2: + base_penalty = (unsigned int)apply_percentrate64(sd->status.base_exp, rate, 10000); + break; } if (base_penalty != 0) { - if (battle_config.pk_mode && src && src->type==BL_PC) - base_penalty*=2; - if( sd->status.mod_death != 100 ) + if (battle_config.pk_mode != 0 && src != NULL && src->type == BL_PC) + base_penalty *= 2; + + if (sd->status.mod_death != 100) base_penalty = base_penalty * sd->status.mod_death / 100; + sd->status.base_exp -= min(sd->status.base_exp, base_penalty); - clif->updatestatus(sd,SP_BASEEXP); + clif->updatestatus(sd, SP_BASEEXP); } } - if(battle_config.death_penalty_job > 0) { + if (battle_config.death_penalty_job > 0) { unsigned int job_penalty = 0; + int rate = battle_config.death_penalty_job; switch (battle_config.death_penalty_type) { - case 1: - job_penalty = (unsigned int) apply_percentrate64(pc->nextjobexp(sd), battle_config.death_penalty_job, 10000); - break; - case 2: - job_penalty = (unsigned int) apply_percentrate64(sd->status.job_exp, battle_config.death_penalty_job, 10000); - break; + case 1: + job_penalty = (unsigned int)apply_percentrate64(pc->nextjobexp(sd), rate, 10000); + break; + case 2: + job_penalty = (unsigned int)apply_percentrate64(sd->status.job_exp, rate, 10000); + break; } if (job_penalty != 0) { - if (battle_config.pk_mode && src && src->type==BL_PC) - job_penalty*=2; - if( sd->status.mod_death != 100 ) + if (battle_config.pk_mode != 0 && src != NULL && src->type == BL_PC) + job_penalty *= 2; + + if (sd->status.mod_death != 100) job_penalty = job_penalty * sd->status.mod_death / 100; + sd->status.job_exp -= min(sd->status.job_exp, job_penalty); - clif->updatestatus(sd,SP_JOBEXP); + clif->updatestatus(sd, SP_JOBEXP); } } - if (battle_config.zeny_penalty > 0 && !map->list[sd->bl.m].flag.nozenypenalty) { + if (battle_config.zeny_penalty > 0 && map->list[sd->bl.m].flag.nozenypenalty == 0) { int zeny_penalty = apply_percentrate(sd->status.zeny, battle_config.zeny_penalty, 10000); + if (zeny_penalty != 0) pc->payzeny(sd, zeny_penalty, LOG_TYPE_PICKDROP_PLAYER, NULL); } } - if(map->list[sd->bl.m].flag.pvp_nightmaredrop) { - // Moved this outside so it works when PVP isn't enabled and during pk mode [Ancyker] - for(j=0;j<map->list[sd->bl.m].drop_list_count;j++){ - int id = map->list[sd->bl.m].drop_list[j].drop_id; - int type = map->list[sd->bl.m].drop_list[j].drop_type; - int per = map->list[sd->bl.m].drop_list[j].drop_per; - if(id == 0) + if (map->list[sd->bl.m].flag.pvp_nightmaredrop != 0) { + // Moved this outside so it works when PVP isn't enabled and during pk mode. [Ancyker] + for (int i = 0; i < map->list[sd->bl.m].drop_list_count; i++) { + int id = map->list[sd->bl.m].drop_list[i].drop_id; + int type = map->list[sd->bl.m].drop_list[i].drop_type; + int per = map->list[sd->bl.m].drop_list[i].drop_per; + + if (id == 0) continue; - if(id == -1){ - int eq_num=0,eq_n[MAX_INVENTORY],k; - memset(eq_n,0,sizeof(eq_n)); - for(i=0;i<MAX_INVENTORY;i++){ - if( (type == 1 && !sd->status.inventory[i].equip) - || (type == 2 && sd->status.inventory[i].equip) - || type == 3) - { - ARR_FIND( 0, MAX_INVENTORY, k, eq_n[k] <= 0 ); - if( k < MAX_INVENTORY ) - eq_n[k] = i; + + if (id == -1) { + int eq_num = 0; + int eq_n[MAX_INVENTORY]; + + memset(eq_n, 0, sizeof(eq_n)); + + for (int j = 0; j < sd->status.inventorySize; j++) { + bool is_equipped = (sd->status.inventory[j].equip != 0); + + if ((type == 1 && !is_equipped) || (type == 2 && is_equipped) || type == 3) { + int k; + + ARR_FIND(0, sd->status.inventorySize, k, eq_n[k] <= 0); + + if (k < sd->status.inventorySize) + eq_n[k] = j; eq_num++; } } - if(eq_num > 0){ - int n = eq_n[rnd()%eq_num]; - if(rnd()%10000 < per){ - if(sd->status.inventory[n].equip) + + if (eq_num > 0) { + int n = eq_n[rnd() % eq_num]; + + if (rnd() % 10000 < per) { + if (sd->status.inventory[n].equip != 0) pc->unequipitem(sd, n, PCUNEQUIPITEM_RECALC|PCUNEQUIPITEM_FORCE); - pc->dropitem(sd,n,1); + + pc->dropitem(sd, n, 1); } } - } - else if(id > 0){ - for(i=0;i<MAX_INVENTORY;i++){ - if(sd->status.inventory[i].nameid == id - && rnd()%10000 < per - && ((type == 1 && !sd->status.inventory[i].equip) - || (type == 2 && sd->status.inventory[i].equip) - || type == 3) ){ - if(sd->status.inventory[i].equip) - pc->unequipitem(sd, i, PCUNEQUIPITEM_RECALC|PCUNEQUIPITEM_FORCE); - pc->dropitem(sd,i,1); + } else if (id > 0) { + for (int j = 0; j < sd->status.inventorySize; j++) { + bool is_equipped = (sd->status.inventory[j].equip != 0); + + if (((type == 1 && !is_equipped) || (type == 2 && is_equipped) || type == 3) + && sd->status.inventory[j].nameid == id && rnd() % 10000 < per) { + if (is_equipped) + pc->unequipitem(sd, j, PCUNEQUIPITEM_RECALC|PCUNEQUIPITEM_FORCE); + + pc->dropitem(sd, j, 1); break; } } @@ -7829,52 +8423,64 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) { } } - // Remove autotrade to prevent autotrading from save point - if( (sd->state.standalone || sd->state.autotrade) - && (map->list[sd->bl.m].flag.pvp || map->list[sd->bl.m].flag.gvg) - ) { + // Remove autotrade to prevent autotrading from save point. + if ((map->list[sd->bl.m].flag.pvp != 0 || map->list[sd->bl.m].flag.gvg != 0) + && (sd->state.standalone != 0 || sd->state.autotrade != 0)) { sd->state.autotrade = 0; sd->state.standalone = 0; - pc->autotrade_update(sd,PAUC_REMOVE); + pc->autotrade_update(sd, PAUC_REMOVE); map->quit(sd); } - // pvp - // disable certain pvp functions on pk_mode [Valaris] - if( map->list[sd->bl.m].flag.pvp && !battle_config.pk_mode && !map->list[sd->bl.m].flag.pvp_nocalcrank ) { + // Disable certain PVP functions on pk_mode. [Valaris] + if (map->list[sd->bl.m].flag.pvp != 0 && battle_config.pk_mode == 0 + && map->list[sd->bl.m].flag.pvp_nocalcrank == 0) { sd->pvp_point -= 5; sd->pvp_lost++; + if (src != NULL && src->type == BL_PC) { struct map_session_data *ssd = BL_UCAST(BL_PC, src); + ssd->pvp_point++; ssd->pvp_won++; } - if( sd->pvp_point < 0 ) - { - timer->add(tick+1, pc->respawn_timer,sd->bl.id,0); + + if (sd->pvp_point < 0) { + timer->add(tick + 1, pc->respawn_timer, sd->bl.id, 0); return 1|8; } } - //GvG - if( map_flag_gvg2(sd->bl.m) ) { - timer->add(tick+1, pc->respawn_timer, sd->bl.id, 0); + + // GVG + if (map_flag_gvg2(sd->bl.m)) { + timer->add(tick + 1, pc->respawn_timer, sd->bl.id, 0); return 1|8; - } else if( sd->bg_id ) { + } + + if (sd->bg_id != 0) { struct battleground_data *bgd = bg->team_search(sd->bg_id); - if( bgd && bgd->mapindex > 0 ) { // Respawn by BG - timer->add(tick+1000, pc->respawn_timer, sd->bl.id, 0); + + if (bgd != NULL && bgd->mapindex > 0) { // Respawn by BG. + timer->add(tick + 1000, pc->respawn_timer, sd->bl.id, 0); return 1|8; } } - //Reset "can log out" tick. - if( battle_config.prevent_logout ) + // Reset "can log out" tick. + if (battle_config.prevent_logout != 0) sd->canlog_tick = timer->gettick() - battle_config.prevent_logout; return 1; } -void pc_revive(struct map_session_data *sd,unsigned int hp, unsigned int sp) { +static bool pc_isDeathPenaltyJob(uint16 job) +{ + return (job & MAPID_UPPERMASK) != MAPID_NOVICE; // only novices will receive no penalty +} + +static void pc_revive(struct map_session_data *sd, unsigned int hp, unsigned int sp) +{ + nullpo_retv(sd); if(hp) clif->updatestatus(sd,SP_HP); if(sp) clif->updatestatus(sd,SP_SP); @@ -7894,9 +8500,9 @@ void pc_revive(struct map_session_data *sd,unsigned int hp, unsigned int sp) { /*========================================== * script reading pc status registry *------------------------------------------*/ -int pc_readparam(struct map_session_data* sd,int type) +static int64 pc_readparam(const struct map_session_data *sd, int type) { - int val = 0; + int64 val = 0; nullpo_ret(sd); @@ -7904,12 +8510,13 @@ int pc_readparam(struct map_session_data* sd,int type) case SP_SKILLPOINT: val = sd->status.skill_point; break; case SP_STATUSPOINT: val = sd->status.status_point; break; case SP_ZENY: val = sd->status.zeny; break; + case SP_BANKVAULT: val = sd->status.bank_vault; break; case SP_BASELEVEL: val = sd->status.base_level; break; case SP_JOBLEVEL: val = sd->status.job_level; break; - case SP_CLASS: val = sd->status.class_; break; - case SP_BASEJOB: val = pc->mapid2jobid(sd->class_&MAPID_UPPERMASK, sd->status.sex); break; //Base job, extracting upper type. - case SP_UPPER: val = (sd->class_&JOBL_UPPER) ? 1 : ((sd->class_&JOBL_BABY) ? 2 : 0); break; - case SP_BASECLASS: val = pc->mapid2jobid(sd->class_&MAPID_BASEMASK, sd->status.sex); break; //Extract base class tree. [Skotlex] + case SP_CLASS: val = sd->status.class; break; + case SP_BASEJOB: val = pc->mapid2jobid(sd->job & MAPID_UPPERMASK, sd->status.sex); break; //Base job, extracting upper type. + case SP_UPPER: val = (sd->job & JOBL_UPPER) != 0 ? 1 : ((sd->job & JOBL_BABY) != 0 ? 2 : 0); break; + case SP_BASECLASS: val = pc->mapid2jobid(sd->job & MAPID_BASEMASK, sd->status.sex); break; //Extract base class tree. [Skotlex] case SP_SEX: val = sd->status.sex; break; case SP_WEIGHT: val = sd->weight; break; case SP_MAXWEIGHT: val = sd->max_weight; break; @@ -7948,11 +8555,15 @@ int pc_readparam(struct map_session_data* sd,int type) case SP_FLEE1: val = sd->battle_status.flee; break; case SP_FLEE2: val = sd->battle_status.flee2; break; case SP_DEFELE: val = sd->battle_status.def_ele; break; -#ifndef RENEWAL_CAST case SP_VARCASTRATE: +#ifdef RENEWAL_CAST + val = sd->bonus.varcastrate; + break; +#else + FALLTHROUGH #endif case SP_CASTRATE: - val = sd->castrate+=val; + val = sd->castrate; break; case SP_MAXHPRATE: val = sd->hprate; break; case SP_MAXSPRATE: val = sd->sprate; break; @@ -8035,7 +8646,6 @@ int pc_readparam(struct map_session_data* sd,int type) case SP_FIXCASTRATE: val = sd->bonus.fixcastrate; break; case SP_ADD_FIXEDCAST: val = sd->bonus.add_fixcast; break; #ifdef RENEWAL_CAST - case SP_VARCASTRATE: val = sd->bonus.varcastrate; break; case SP_ADD_VARIABLECAST:val = sd->bonus.add_varcast; break; #endif } @@ -8046,21 +8656,22 @@ int pc_readparam(struct map_session_data* sd,int type) /*========================================== * script set pc status registry *------------------------------------------*/ -int pc_setparam(struct map_session_data *sd,int type,int val) +static int pc_setparam(struct map_session_data *sd, int type, int64 val) { + int delta; nullpo_ret(sd); switch(type){ case SP_BASELEVEL: - if ((unsigned int)val > pc->maxbaselv(sd)) //Capping to max + if (val > pc->maxbaselv(sd)) //Capping to max val = pc->maxbaselv(sd); - if ((unsigned int)val > sd->status.base_level) { + if (val > sd->status.base_level) { int stat = 0, i; - for (i = 0; i < (int)((unsigned int)val - sd->status.base_level); i++) + for (i = 0; i < val - sd->status.base_level; i++) stat += pc->gets_status_point(sd->status.base_level + i); sd->status.status_point += stat; } - sd->status.base_level = (unsigned int)val; + sd->status.base_level = (int32)val; sd->status.base_exp = 0; // clif->updatestatus(sd, SP_BASELEVEL); // Gets updated at the bottom clif->updatestatus(sd, SP_NEXTBASEEXP); @@ -8073,12 +8684,13 @@ int pc_setparam(struct map_session_data *sd,int type,int val) } break; case SP_JOBLEVEL: - if ((unsigned int)val >= sd->status.job_level) { - if ((unsigned int)val > pc->maxjoblv(sd)) val = pc->maxjoblv(sd); - sd->status.skill_point += val - sd->status.job_level; + if (val >= sd->status.job_level) { + if (val > pc->maxjoblv(sd)) + val = pc->maxjoblv(sd); + sd->status.skill_point += (int)val - sd->status.job_level; clif->updatestatus(sd, SP_SKILLPOINT); } - sd->status.job_level = (unsigned int)val; + sd->status.job_level = (int32)val; sd->status.job_exp = 0; // clif->updatestatus(sd, SP_JOBLEVEL); // Gets updated at the bottom clif->updatestatus(sd, SP_NEXTJOBEXP); @@ -8086,17 +8698,30 @@ int pc_setparam(struct map_session_data *sd,int type,int val) status_calc_pc(sd, SCO_FORCE); break; case SP_SKILLPOINT: - sd->status.skill_point = val; + sd->status.skill_point = (int32)val; break; case SP_STATUSPOINT: - sd->status.status_point = val; + sd->status.status_point = (int32)val; break; case SP_ZENY: if( val < 0 ) return 0;// can't set negative zeny - logs->zeny(sd, LOG_TYPE_SCRIPT, sd, -(sd->status.zeny - cap_value(val, 0, MAX_ZENY))); - sd->status.zeny = cap_value(val, 0, MAX_ZENY); + logs->zeny(sd, LOG_TYPE_SCRIPT, sd, -(sd->status.zeny - cap_value((int32)val, 0, MAX_ZENY))); + sd->status.zeny = cap_value((int32)val, 0, MAX_ZENY); break; + case SP_BANKVAULT: + val = cap_value(val, 0, MAX_BANK_ZENY); + delta = ((int32)val - sd->status.bank_vault); + sd->status.bank_vault = (int32)val; + if (map->save_settings & 256) { + chrif->save(sd, 0); // send to char server + } + if (delta > 0) { + clif->bank_deposit(sd, BDA_SUCCESS); + } else if (delta < 0) { + clif->bank_withdraw(sd, BWA_SUCCESS); + } + return 1; // the vault uses a different packet case SP_BASEEXP: if(pc->nextbaseexp(sd) > 0) { sd->status.base_exp = val; @@ -8113,16 +8738,16 @@ int pc_setparam(struct map_session_data *sd,int type,int val) sd->status.sex = val ? SEX_MALE : SEX_FEMALE; break; case SP_WEIGHT: - sd->weight = val; + sd->weight = (int32)val; break; case SP_MAXWEIGHT: - sd->max_weight = val; + sd->max_weight = (int32)val; break; case SP_HP: - sd->battle_status.hp = cap_value(val, 1, (int)sd->battle_status.max_hp); + sd->battle_status.hp = cap_value((int32)val, 1, (int)sd->battle_status.max_hp); break; case SP_MAXHP: - sd->battle_status.max_hp = cap_value(val, 1, battle_config.max_hp); + sd->battle_status.max_hp = cap_value((int32)val, 1, battle_config.max_hp); if( sd->battle_status.max_hp < sd->battle_status.hp ) { @@ -8131,10 +8756,10 @@ int pc_setparam(struct map_session_data *sd,int type,int val) } break; case SP_SP: - sd->battle_status.sp = cap_value(val, 0, (int)sd->battle_status.max_sp); + sd->battle_status.sp = cap_value((int32)val, 0, (int)sd->battle_status.max_sp); break; case SP_MAXSP: - sd->battle_status.max_sp = cap_value(val, 1, battle_config.max_sp); + sd->battle_status.max_sp = cap_value((int32)val, 1, battle_config.max_sp); if( sd->battle_status.max_sp < sd->battle_status.sp ) { @@ -8143,28 +8768,28 @@ int pc_setparam(struct map_session_data *sd,int type,int val) } break; case SP_STR: - sd->status.str = cap_value(val, 1, pc_maxparameter(sd)); + sd->status.str = cap_value((int)val, 1, pc_maxparameter(sd)); break; case SP_AGI: - sd->status.agi = cap_value(val, 1, pc_maxparameter(sd)); + sd->status.agi = cap_value((int)val, 1, pc_maxparameter(sd)); break; case SP_VIT: - sd->status.vit = cap_value(val, 1, pc_maxparameter(sd)); + sd->status.vit = cap_value((int)val, 1, pc_maxparameter(sd)); break; case SP_INT: - sd->status.int_ = cap_value(val, 1, pc_maxparameter(sd)); + sd->status.int_ = cap_value((int)val, 1, pc_maxparameter(sd)); break; case SP_DEX: - sd->status.dex = cap_value(val, 1, pc_maxparameter(sd)); + sd->status.dex = cap_value((int)val, 1, pc_maxparameter(sd)); break; case SP_LUK: - sd->status.luk = cap_value(val, 1, pc_maxparameter(sd)); + sd->status.luk = cap_value((int)val, 1, pc_maxparameter(sd)); break; case SP_KARMA: - sd->status.karma = val; + sd->status.karma = (int)val; break; case SP_MANNER: - sd->status.manner = val; + sd->status.manner = (int)val; if( val < 0 ) sc_start(NULL, &sd->bl, SC_NOCHAT, 100, 0, 0); else { @@ -8173,28 +8798,28 @@ int pc_setparam(struct map_session_data *sd,int type,int val) } return 1; // status_change_start/status_change_end already sends packets warning the client case SP_FAME: - sd->status.fame = val; + sd->status.fame = (int32)val; break; case SP_KILLERRID: - sd->killerrid = val; + sd->killerrid = (int32)val; return 1; case SP_KILLEDRID: - sd->killedrid = val; + sd->killedrid = (int32)val; return 1; case SP_SLOTCHANGE: - sd->status.slotchange = val; + sd->status.slotchange = (int32)val; return 1; case SP_CHARRENAME: - sd->status.rename = val; + sd->status.rename = (int32)val; return 1; case SP_MOD_EXP: - sd->status.mod_exp = val; + sd->status.mod_exp = (int32)val; return 1; case SP_MOD_DROP: - sd->status.mod_drop = val; + sd->status.mod_drop = (int32)val; return 1; case SP_MOD_DEATH: - sd->status.mod_death = val; + sd->status.mod_death = (int32)val; return 1; default: ShowError("pc_setparam: Attempted to set unknown parameter '%d'.\n", type); @@ -8208,8 +8833,9 @@ int pc_setparam(struct map_session_data *sd,int type,int val) /*========================================== * HP/SP Healing. If flag is passed, the heal type is through clif->heal, otherwise update status. *------------------------------------------*/ -void pc_heal(struct map_session_data *sd,unsigned int hp,unsigned int sp, int type) +static void pc_heal(struct map_session_data *sd, unsigned int hp, unsigned int sp, int type) { + nullpo_retv(sd); if (type) { if (hp) clif->heal(sd->fd,SP_HP,hp); @@ -8229,10 +8855,11 @@ void pc_heal(struct map_session_data *sd,unsigned int hp,unsigned int sp, int ty * Heal player hp and/or sp linearly. * Calculate bonus by status. *------------------------------------------*/ -int pc_itemheal(struct map_session_data *sd,int itemid, int hp,int sp) +static int pc_itemheal(struct map_session_data *sd, int itemid, int hp, int sp) { int bonus, tmp; + nullpo_ret(sd); if(hp) { int i; bonus = 100 + (sd->battle_status.vit<<1) @@ -8263,6 +8890,10 @@ int pc_itemheal(struct map_session_data *sd,int itemid, int hp,int sp) // 2014 Halloween Event : Pumpkin Bonus if ( sd->sc.data[SC_MTF_PUMPKIN] && itemid == ITEMID_PUMPKIN ) hp += (int)(hp * sd->sc.data[SC_MTF_PUMPKIN]->val1/100); + + // Activation Potion + if (sd->sc.data[SC_VITALIZE_POTION] != NULL) + hp += hp * sd->sc.data[SC_VITALIZE_POTION]->val3 / 100; } if(sp) { bonus = 100 + (sd->battle_status.int_<<1) @@ -8299,16 +8930,19 @@ int pc_itemheal(struct map_session_data *sd,int itemid, int hp,int sp) if( sd->sc.data[SC_EXTREMITYFIST2] ) sp = 0; #endif + if (sd->sc.data[SC_BITESCAR]) { + hp = 0; + } } - return status->heal(&sd->bl, hp, sp, 1); + return status->heal(&sd->bl, hp, sp, STATUS_HEAL_FORCED); } /*========================================== * HP/SP Recovery * Heal player hp nad/or sp by rate *------------------------------------------*/ -int pc_percentheal(struct map_session_data *sd,int hp,int sp) +static int pc_percentheal(struct map_session_data *sd, int hp, int sp) { nullpo_ret(sd); @@ -8343,7 +8977,7 @@ int pc_percentheal(struct map_session_data *sd,int hp,int sp) return 0; } -int jobchange_killclone(struct block_list *bl, va_list ap) +static int jobchange_killclone(struct block_list *bl, va_list ap) { struct mob_data *md = NULL; int flag = va_arg(ap, int); @@ -8361,46 +8995,45 @@ int jobchange_killclone(struct block_list *bl, va_list ap) * Called when player changes job * Rewrote to make it tidider [Celest] *------------------------------------------*/ -int pc_jobchange(struct map_session_data *sd,int job, int upper) +static int pc_jobchange(struct map_session_data *sd, int class, int upper) { int i, fame_flag=0; - int b_class, idx = 0; + int job, idx = 0; nullpo_ret(sd); - if (job < 0) + if (class < 0) return 1; //Normalize job. - b_class = pc->jobid2mapid(job); - if (b_class == -1) + job = pc->jobid2mapid(class); + if (job == -1) return 1; switch (upper) { case 1: - b_class|= JOBL_UPPER; + job |= JOBL_UPPER; break; case 2: - b_class|= JOBL_BABY; + job |= JOBL_BABY; break; } //This will automatically adjust bard/dancer classes to the correct gender //That is, if you try to jobchange into dancer, it will turn you to bard. - job = pc->mapid2jobid(b_class, sd->status.sex); - if (job == -1) + class = pc->mapid2jobid(job, sd->status.sex); + if (class == -1) return 1; - if ((unsigned short)b_class == sd->class_) + if ((uint16)job == sd->job) return 1; //Nothing to change. - // changing from 1st to 2nd job - if ((b_class&JOBL_2) && !(sd->class_&JOBL_2) && (b_class&MAPID_UPPERMASK) != MAPID_SUPER_NOVICE) { + if ((job & JOBL_2) != 0 && (sd->job & JOBL_2) == 0 && (job & MAPID_UPPERMASK) != MAPID_SUPER_NOVICE) { + // changing from 1st to 2nd job sd->change_level_2nd = sd->status.job_level; - pc_setglobalreg (sd, script->add_str("jobchange_level"), sd->change_level_2nd); - } - // changing from 2nd to 3rd job - else if((b_class&JOBL_THIRD) && !(sd->class_&JOBL_THIRD)) { + pc_setglobalreg(sd, script->add_variable("jobchange_level"), sd->change_level_2nd); + } else if((job & JOBL_THIRD) != 0 && (sd->job & JOBL_THIRD) == 0) { + // changing from 2nd to 3rd job sd->change_level_3rd = sd->status.job_level; - pc_setglobalreg (sd, script->add_str("jobchange_level_3rd"), sd->change_level_3rd); + pc_setglobalreg(sd, script->add_variable("jobchange_level_3rd"), sd->change_level_3rd); } if(sd->cloneskill_id) { @@ -8412,8 +9045,8 @@ int pc_jobchange(struct map_session_data *sd,int job, int upper) clif->deleteskill(sd,sd->cloneskill_id); } sd->cloneskill_id = 0; - pc_setglobalreg(sd, script->add_str("CLONE_SKILL"), 0); - pc_setglobalreg(sd, script->add_str("CLONE_SKILL_LV"), 0); + pc_setglobalreg(sd, script->add_variable("CLONE_SKILL"), 0); + pc_setglobalreg(sd, script->add_variable("CLONE_SKILL_LV"), 0); } if(sd->reproduceskill_id) { @@ -8425,14 +9058,14 @@ int pc_jobchange(struct map_session_data *sd,int job, int upper) clif->deleteskill(sd,sd->reproduceskill_id); } sd->reproduceskill_id = 0; - pc_setglobalreg(sd, script->add_str("REPRODUCE_SKILL"),0); - pc_setglobalreg(sd, script->add_str("REPRODUCE_SKILL_LV"),0); + pc_setglobalreg(sd, script->add_variable("REPRODUCE_SKILL"),0); + pc_setglobalreg(sd, script->add_variable("REPRODUCE_SKILL_LV"),0); } - if ( (b_class&MAPID_UPPERMASK) != (sd->class_&MAPID_UPPERMASK) ) { //Things to remove when changing class tree. - const int class_ = pc->class2idx(sd->status.class_); + if ((job & MAPID_UPPERMASK) != (sd->job & MAPID_UPPERMASK)) { //Things to remove when changing class tree. + const int class_idx = pc->class2idx(sd->status.class); short id; - for(i = 0; i < MAX_SKILL_TREE && (id = pc->skill_tree[class_][i].id) > 0; i++) { + for (i = 0; i < MAX_SKILL_TREE && (id = pc->skill_tree[class_idx][i].id) > 0; i++) { //Remove status specific to your current tree skills. enum sc_type sc = status->skill2sc(id); if (sc > SC_COMMON_MAX && sd->sc.data[sc]) @@ -8440,14 +9073,18 @@ int pc_jobchange(struct map_session_data *sd,int job, int upper) } } - if( (sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR && (b_class&MAPID_UPPERMASK) != MAPID_STAR_GLADIATOR) { + if ((sd->job & MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR && (job & MAPID_UPPERMASK) != MAPID_STAR_GLADIATOR) { /* going off star glad lineage, reset feel to not store no-longer-used vars in the database */ pc->resetfeel(sd); } - sd->status.class_ = job; - fame_flag = pc->famerank(sd->status.char_id,sd->class_&MAPID_UPPERMASK); - sd->class_ = (unsigned short)b_class; + sd->status.class = class; + { + int fame_list_type = pc->famelist_type(sd->job); + if (fame_list_type != RANKTYPE_UNKNOWN) + fame_flag = pc->fame_rank(sd->status.char_id, fame_list_type); + } + sd->job = (uint16)job; sd->status.job_level=1; sd->status.job_exp=0; @@ -8476,8 +9113,15 @@ int pc_jobchange(struct map_session_data *sd,int job, int upper) if (sd->disguise != -1) pc->disguise(sd, -1); - status->set_viewdata(&sd->bl, job); - clif->changelook(&sd->bl,LOOK_BASE,sd->vd.class_); // move sprite update to prevent client crashes with incompatible equipment [Valaris] + // Fix atcommand @jobchange when the player changing from 3rd job having alternate body style into non-3rd job, crashing the client + if (pc->has_second_costume(sd) == false) { + sd->status.body = 0; + sd->vd.body_style = 0; + clif->changelook(&sd->bl, LOOK_BODY2, sd->vd.body_style); + } + + status->set_viewdata(&sd->bl, class); + clif->changelook(&sd->bl, LOOK_BASE, sd->vd.class); // move sprite update to prevent client crashes with incompatible equipment [Valaris] if(sd->vd.cloth_color) clif->changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->vd.cloth_color); if (sd->vd.body_style) @@ -8496,7 +9140,7 @@ int pc_jobchange(struct map_session_data *sd,int job, int upper) //Remove peco/cart/falcon i = sd->sc.option; - if( i&OPTION_RIDING && (!pc->checkskill(sd, KN_RIDING) || (sd->class_&MAPID_THIRDMASK) == MAPID_RUNE_KNIGHT) ) + if (i&OPTION_RIDING && (!pc->checkskill(sd, KN_RIDING) || (sd->job & MAPID_THIRDMASK) == MAPID_RUNE_KNIGHT)) i&=~OPTION_RIDING; if( i&OPTION_FALCON && !pc->checkskill(sd, HT_FALCON) ) i&=~OPTION_FALCON; @@ -8519,7 +9163,10 @@ int pc_jobchange(struct map_session_data *sd,int job, int upper) pc->setoption(sd, i); if(homun_alive(sd->hd) && !pc->checkskill(sd, AM_CALLHOMUN)) - homun->vaporize(sd, HOM_ST_REST); + homun->vaporize(sd, HOM_ST_REST, true); + + if ((sd->sc.data[SC_SPRITEMABLE] && pc->checkskill(sd, SU_SPRITEMABLE))) + status_change_end(&sd->bl, SC_SPRITEMABLE, INVALID_TIMER); if(sd->status.manner < 0) clif->changestatus(sd,SP_MANNER,sd->status.manner); @@ -8527,14 +9174,15 @@ int pc_jobchange(struct map_session_data *sd,int job, int upper) status_calc_pc(sd,SCO_FORCE); pc->checkallowskill(sd); pc->equiplookall(sd); + pc->update_job_and_level(sd); //if you were previously famous, not anymore. - if (fame_flag) { + if (fame_flag != 0) { chrif->save(sd,0); chrif->buildfamelist(); } else if (sd->status.fame > 0) { //It may be that now they are famous? - switch (sd->class_&MAPID_UPPERMASK) { + switch (sd->job & MAPID_UPPERMASK) { case MAPID_BLACKSMITH: case MAPID_ALCHEMIST: case MAPID_TAEKWON: @@ -8543,6 +9191,9 @@ int pc_jobchange(struct map_session_data *sd,int job, int upper) break; } } + quest->questinfo_refresh(sd); + + achievement->validate_jobchange(sd); // Achievements [Smokexyz/Hercules] return 0; } @@ -8550,16 +9201,16 @@ int pc_jobchange(struct map_session_data *sd,int job, int upper) /*========================================== * Tell client player sd has change equipement *------------------------------------------*/ -int pc_equiplookall(struct map_session_data *sd) +static int pc_equiplookall(struct map_session_data *sd) { nullpo_ret(sd); clif->changelook(&sd->bl,LOOK_WEAPON,0); clif->changelook(&sd->bl,LOOK_SHOES,0); - clif->changelook(&sd->bl,LOOK_HEAD_BOTTOM,sd->status.head_bottom); - clif->changelook(&sd->bl,LOOK_HEAD_TOP,sd->status.head_top); - clif->changelook(&sd->bl,LOOK_HEAD_MID,sd->status.head_mid); - clif->changelook(&sd->bl,LOOK_ROBE, sd->status.robe); + clif->changelook(&sd->bl, LOOK_HEAD_BOTTOM, sd->status.look.head_bottom); + clif->changelook(&sd->bl, LOOK_HEAD_TOP, sd->status.look.head_top); + clif->changelook(&sd->bl, LOOK_HEAD_MID, sd->status.look.head_mid); + clif->changelook(&sd->bl, LOOK_ROBE, sd->status.look.robe); return 0; } @@ -8567,15 +9218,15 @@ int pc_equiplookall(struct map_session_data *sd) /*========================================== * Tell client player sd has change look (hair,equip...) *------------------------------------------*/ -int pc_changelook(struct map_session_data *sd,int type,int val) +static int pc_changelook(struct map_session_data *sd, int type, int val) { nullpo_ret(sd); switch(type){ case LOOK_BASE: status->set_viewdata(&sd->bl, val); - clif->changelook(&sd->bl,LOOK_BASE,sd->vd.class_); - clif->changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon); + clif->changelook(&sd->bl, LOOK_BASE, sd->vd.class); + clif->changelook(&sd->bl, LOOK_WEAPON, sd->status.look.weapon); if (sd->vd.cloth_color) clif->changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->vd.cloth_color); if (sd->vd.body_style) @@ -8594,16 +9245,16 @@ int pc_changelook(struct map_session_data *sd,int type,int val) } break; case LOOK_WEAPON: - sd->status.weapon=val; + sd->status.look.weapon = val; break; case LOOK_HEAD_BOTTOM: - sd->status.head_bottom=val; + sd->status.look.head_bottom = val; break; case LOOK_HEAD_TOP: - sd->status.head_top=val; + sd->status.look.head_top = val; break; case LOOK_HEAD_MID: - sd->status.head_mid=val; + sd->status.look.head_mid = val; break; case LOOK_HAIR_COLOR: //Use the battle_config limits! [Skotlex] val = cap_value(val, MIN_HAIR_COLOR, MAX_HAIR_COLOR); @@ -8621,12 +9272,12 @@ int pc_changelook(struct map_session_data *sd,int type,int val) sd->status.clothes_color=val; break; case LOOK_SHIELD: - sd->status.shield=val; + sd->status.look.shield = val; break; case LOOK_SHOES: break; case LOOK_ROBE: - sd->status.robe = val; + sd->status.look.robe = val; break; case LOOK_BODY2: val = cap_value(val, MIN_BODY_STYLE, MAX_BODY_STYLE); @@ -8637,10 +9288,76 @@ int pc_changelook(struct map_session_data *sd,int type,int val) return 0; } +/** + * Hides a character. + * + * @param sd The character to hide. + * @param show_msg Whether to show message to the character or not. + * + **/ +static void pc_hide(struct map_session_data *sd, bool show_msg) +{ + nullpo_retv(sd); + + clif->clearunit_area(&sd->bl, CLR_OUTSIGHT); + sd->sc.option |= OPTION_INVISIBLE; + sd->vd.class = INVISIBLE_CLASS; + + if (show_msg) + clif->message(sd->fd, atcommand->msgsd(sd, 11)); // Invisible: On + + // Decrement the number of pvp players on the map. + map->list[sd->bl.m].users_pvp--; + + if (map->list[sd->bl.m].flag.pvp != 0 && map->list[sd->bl.m].flag.pvp_nocalcrank == 0 + && sd->pvp_timer != INVALID_TIMER) { // Unregister the player for ranking. + timer->delete(sd->pvp_timer, pc->calc_pvprank_timer); + sd->pvp_timer = INVALID_TIMER; + } + + clif->changeoption(&sd->bl); +} + +/** + * Unhides a character. + * + * @param sd The character to unhide. + * @param show_msg Whether to show message to the character or not. + * + **/ +static void pc_unhide(struct map_session_data *sd, bool show_msg) +{ + nullpo_retv(sd); + + sd->sc.option &= ~OPTION_INVISIBLE; + + if (sd->disguise != -1) + status->set_viewdata(&sd->bl, sd->disguise); + else + status->set_viewdata(&sd->bl, sd->status.class); + + if (show_msg) + clif->message(sd->fd, atcommand->msgsd(sd, 10)); // Invisible: Off + + // Increment the number of pvp players on the map. + map->list[sd->bl.m].users_pvp++; + + if (map->list[sd->bl.m].flag.pvp != 0 && map->list[sd->bl.m].flag.pvp_nocalcrank == 0) // Register the player for ranking. + sd->pvp_timer = timer->add(timer->gettick() + 200, pc->calc_pvprank_timer, sd->bl.id, 0); + + // bugreport:2266 + map->foreachinmovearea(clif->insight, &sd->bl, AREA_SIZE, sd->bl.x, sd->bl.y, BL_ALL, &sd->bl); + + if (sd->disguise != -1) + clif->spawn_unit(&sd->bl, AREA_WOS); + + clif->changeoption(&sd->bl); +} + /*========================================== * Give an option (type) to player (sd) and display it to client *------------------------------------------*/ -int pc_setoption(struct map_session_data *sd,int type) +static int pc_setoption(struct map_session_data *sd, int type) { int p_type, new_look=0; nullpo_ret(sd); @@ -8648,21 +9365,27 @@ int pc_setoption(struct map_session_data *sd,int type) //Option has to be changed client-side before the class sprite or it won't always work (eg: Wedding sprite) [Skotlex] sd->sc.option=type; - clif->changeoption(&sd->bl); + + if ((p_type & OPTION_INVISIBLE) != 0 && (type & OPTION_INVISIBLE) == 0) // Unhide character. + pc->unhide(sd, false); + else if ((p_type & OPTION_INVISIBLE) == 0 && (type & OPTION_INVISIBLE) != 0) // Hide character. + pc->hide(sd, false); + else + clif->changeoption(&sd->bl); if( (type&OPTION_RIDING && !(p_type&OPTION_RIDING)) || (type&OPTION_DRAGON && !(p_type&OPTION_DRAGON) && pc->checkskill(sd,RK_DRAGONTRAINING) > 0) ) { // Mounting - clif->sc_load(&sd->bl,sd->bl.id,AREA,SI_RIDING, 0, 0, 0); + clif->sc_load(&sd->bl, sd->bl.id, AREA, status->get_sc_icon(SC_RIDING), 0, 0, 0); status_calc_pc(sd,SCO_NONE); } else if( (!(type&OPTION_RIDING) && p_type&OPTION_RIDING) || (!(type&OPTION_DRAGON) && p_type&OPTION_DRAGON) ) { // Dismount - clif->sc_end(&sd->bl,sd->bl.id,AREA,SI_RIDING); + clif->sc_end(&sd->bl, sd->bl.id, AREA, status->get_sc_icon(SC_RIDING)); status_calc_pc(sd,SCO_NONE); } #ifndef NEW_CARTS if( type&OPTION_CART && !( p_type&OPTION_CART ) ) { //Cart On - clif->cartlist(sd); + clif->cartList(sd); clif->updatestatus(sd, SP_CARTINFO); if(pc->checkskill(sd, MC_PUSHCART) < 10) status_calc_pc(sd,SCO_NONE); //Apply speed penalty. @@ -8676,15 +9399,15 @@ int pc_setoption(struct map_session_data *sd,int type) #endif if (type&OPTION_FALCON && !(p_type&OPTION_FALCON)) //Falcon ON - clif->sc_load(&sd->bl,sd->bl.id,AREA,SI_FALCON, 0, 0, 0); + clif->sc_load(&sd->bl, sd->bl.id, AREA, status->get_sc_icon(SC_FALCON), 0, 0, 0); else if (!(type&OPTION_FALCON) && p_type&OPTION_FALCON) //Falcon OFF - clif->sc_end(&sd->bl,sd->bl.id,AREA,SI_FALCON); + clif->sc_end(&sd->bl, sd->bl.id, AREA, status->get_sc_icon(SC_FALCON)); if( type&OPTION_WUGRIDER && !(p_type&OPTION_WUGRIDER) ) { // Mounting - clif->sc_load(&sd->bl,sd->bl.id,AREA,SI_WUGRIDER, 0, 0, 0); + clif->sc_load(&sd->bl, sd->bl.id, AREA, status->get_sc_icon(SC_WUGRIDER), 0, 0, 0); status_calc_pc(sd,SCO_NONE); } else if( !(type&OPTION_WUGRIDER) && p_type&OPTION_WUGRIDER ) { // Dismount - clif->sc_end(&sd->bl,sd->bl.id,AREA,SI_WUGRIDER); + clif->sc_end(&sd->bl, sd->bl.id, AREA, status->get_sc_icon(SC_WUGRIDER)); status_calc_pc(sd,SCO_NONE); } @@ -8720,8 +9443,8 @@ int pc_setoption(struct map_session_data *sd,int type) return 0; //Disguises break sprite changes if (new_look < 0) { //Restore normal look. - status->set_viewdata(&sd->bl, sd->status.class_); - new_look = sd->vd.class_; + status->set_viewdata(&sd->bl, sd->status.class); + new_look = sd->vd.class; } pc_stop_attack(sd); //Stop attacking on new view change (to prevent wedding/santa attacks. @@ -8738,7 +9461,8 @@ int pc_setoption(struct map_session_data *sd,int type) /*========================================== * Give player a cart *------------------------------------------*/ -int pc_setcart(struct map_session_data *sd,int type) { +static int pc_setcart(struct map_session_data *sd, int type) +{ #ifndef NEW_CARTS int cart[6] = {OPTION_NOTHING,OPTION_CART1,OPTION_CART2,OPTION_CART3,OPTION_CART4,OPTION_CART5}; int option; @@ -8768,10 +9492,10 @@ int pc_setcart(struct map_session_data *sd,int type) { break; default:/* everything else is an allowed ID so we can move on */ if( !sd->sc.data[SC_PUSH_CART] ) /* first time, so fill cart data */ - clif->cartlist(sd); + clif->cartList(sd); clif->updatestatus(sd, SP_CARTINFO); sc_start(NULL,&sd->bl, SC_PUSH_CART, 100, type, 0); - clif->sc_load(&sd->bl, sd->bl.id, AREA, SI_ON_PUSH_CART, type, 0, 0); + clif->sc_load(&sd->bl, sd->bl.id, AREA, status->get_sc_icon(SC_ON_PUSH_CART), type, 0, 0); if( sd->sc.data[SC_PUSH_CART] )/* forcefully update */ sd->sc.data[SC_PUSH_CART]->val1 = type; break; @@ -8801,8 +9525,9 @@ int pc_setcart(struct map_session_data *sd,int type) { * @param sd Target player. * @param flag New state. **/ -void pc_setfalcon(struct map_session_data *sd, bool flag) +static void pc_setfalcon(struct map_session_data *sd, bool flag) { + nullpo_retv(sd); if (flag) { if (pc->checkskill(sd,HT_FALCON) > 0) // add falcon if he have the skill pc->setoption(sd,sd->sc.option|OPTION_FALCON); @@ -8819,8 +9544,9 @@ void pc_setfalcon(struct map_session_data *sd, bool flag) * @param sd Target player. * @param flag New state. **/ -void pc_setridingpeco(struct map_session_data *sd, bool flag) +static void pc_setridingpeco(struct map_session_data *sd, bool flag) { + nullpo_retv(sd); if (flag) { if (pc->checkskill(sd, KN_RIDING)) pc->setoption(sd, sd->sc.option|OPTION_RIDING); @@ -8836,14 +9562,23 @@ void pc_setridingpeco(struct map_session_data *sd, bool flag) * * @param sd Target player. * @param flag New state. + * @param mtype Type of the mado gear. **/ -void pc_setmadogear(struct map_session_data *sd, bool flag) +static void pc_setmadogear(struct map_session_data *sd, bool flag, enum mado_type mtype) { + nullpo_retv(sd); + Assert_retv(mtype >= MADO_ROBOT && mtype < MADO_MAX); + if (flag) { - if ((sd->class_&MAPID_THIRDMASK) == MAPID_MECHANIC) + if ((sd->job & MAPID_THIRDMASK) == MAPID_MECHANIC) { pc->setoption(sd, sd->sc.option|OPTION_MADOGEAR); +#if PACKETVER_MAIN_NUM >= 20191120 || PACKETVER_RE_NUM >= 20191106 + sc_start(&sd->bl, &sd->bl, SC_MADOGEAR, 100, (int)mtype, INFINITE_DURATION); +#endif + } } else if (pc_ismadogear(sd)) { pc->setoption(sd, sd->sc.option&~OPTION_MADOGEAR); + // pc->setoption resets status effects when changing mado, no need to re do it here. } } @@ -8855,8 +9590,9 @@ void pc_setmadogear(struct map_session_data *sd, bool flag) * @param sd Target player. * @param type New state. This must be a valid OPTION_DRAGON* or 0. **/ -void pc_setridingdragon(struct map_session_data *sd, unsigned int type) +static void pc_setridingdragon(struct map_session_data *sd, unsigned int type) { + nullpo_retv(sd); if (type&OPTION_DRAGON) { // Ensure only one dragon is set at a time. if (type&OPTION_DRAGON1) @@ -8887,8 +9623,9 @@ void pc_setridingdragon(struct map_session_data *sd, unsigned int type) * @param sd Target player. * @param flag New state. **/ -void pc_setridingwug(struct map_session_data *sd, bool flag) +static void pc_setridingwug(struct map_session_data *sd, bool flag) { + nullpo_retv(sd); if (flag) { if (pc->checkskill(sd, RA_WUGRIDER) > 0) pc->setoption(sd,sd->sc.option|OPTION_WUGRIDER); @@ -8905,7 +9642,8 @@ void pc_setridingwug(struct map_session_data *sd, bool flag) * Called from unit_attack and unit_attack_timer_sub * @retval true Can attack **/ -bool pc_can_attack( struct map_session_data *sd, int target_id ) { +static bool pc_can_attack(struct map_session_data *sd, int target_id) +{ nullpo_retr(false, sd); if( sd->sc.data[SC_BASILICA] || @@ -8919,7 +9657,8 @@ bool pc_can_attack( struct map_session_data *sd, int target_id ) { (sd->sc.data[SC_SIREN] && sd->sc.data[SC_SIREN]->val2 == target_id) || sd->sc.data[SC_BLADESTOP] || sd->sc.data[SC_DEEP_SLEEP] || - sd->sc.data[SC_FALLENEMPIRE] ) + sd->sc.data[SC_FALLENEMPIRE] || + sd->block_action.attack) return false; return true; @@ -8930,12 +9669,14 @@ bool pc_can_attack( struct map_session_data *sd, int target_id ) { * Called from clif_parse_GlobalMessage and clif_parse_WisMessage * @retval true Can talk **/ -bool pc_can_talk( struct map_session_data *sd ) { +static bool pc_can_talk(struct map_session_data *sd) +{ nullpo_retr(false, sd); if( sd->sc.data[SC_BERSERK] || (sd->sc.data[SC_DEEP_SLEEP] && sd->sc.data[SC_DEEP_SLEEP]->val2) || - pc_ismuted(&sd->sc, MANNER_NOCHAT) ) + pc_ismuted(&sd->sc, MANNER_NOCHAT) || + sd->block_action.chat) return false; return true; @@ -8944,7 +9685,7 @@ bool pc_can_talk( struct map_session_data *sd ) { /*========================================== * Check if player can drop an item *------------------------------------------*/ -int pc_candrop(struct map_session_data *sd, struct item *item) +static int pc_candrop(struct map_session_data *sd, struct item *item) { if( item && (item->expire_time || (item->bound && !pc_can_give_bound_items(sd))) ) return 0; @@ -8955,15 +9696,19 @@ int pc_candrop(struct map_session_data *sd, struct item *item) /** * For '@type' variables (temporary numeric char reg) **/ -int pc_readreg(struct map_session_data* sd, int64 reg) { +static int pc_readreg(struct map_session_data *sd, int64 reg) +{ + nullpo_ret(sd); return i64db_iget(sd->regs.vars, reg); } /** * For '@type' variables (temporary numeric char reg) **/ -void pc_setreg(struct map_session_data* sd, int64 reg, int val) { +static void pc_setreg(struct map_session_data *sd, int64 reg, int val) +{ unsigned int index = script_getvaridx(reg); + nullpo_retv(sd); if( val ) { i64db_iput(sd->regs.vars, reg, val); if( index ) @@ -8978,9 +9723,11 @@ void pc_setreg(struct map_session_data* sd, int64 reg, int val) { /** * For '@type$' variables (temporary string char reg) **/ -char* pc_readregstr(struct map_session_data* sd, int64 reg) { +static char *pc_readregstr(struct map_session_data *sd, int64 reg) +{ struct script_reg_str *p = NULL; + nullpo_retr(NULL, sd); p = i64db_get(sd->regs.vars, reg); return p ? p->value : NULL; @@ -8988,11 +9735,14 @@ char* pc_readregstr(struct map_session_data* sd, int64 reg) { /** * For '@type$' variables (temporary string char reg) **/ -void pc_setregstr(struct map_session_data* sd, int64 reg, const char* str) { +static void pc_setregstr(struct map_session_data *sd, int64 reg, const char *str) +{ struct script_reg_str *p = NULL; unsigned int index = script_getvaridx(reg); struct DBData prev; + nullpo_retv(sd); + nullpo_retv(str); if( str[0] ) { p = ers_alloc(pc->str_reg_ers, struct script_reg_str); @@ -9025,9 +9775,11 @@ void pc_setregstr(struct map_session_data* sd, int64 reg, const char* str) { * - '#type' (permanent numeric account reg) * - '##type' (permanent numeric account reg2) **/ -int pc_readregistry(struct map_session_data *sd, int64 reg) { +static int pc_readregistry(struct map_session_data *sd, int64 reg) +{ struct script_reg_num *p = NULL; + nullpo_ret(sd); if (!sd->vars_ok) { ShowError("pc_readregistry: Trying to read reg %s before it's been loaded!\n", script->get_str(script_getvarid(reg))); //This really shouldn't happen, so it's possible the data was lost somewhere, we should request it again. @@ -9046,9 +9798,11 @@ int pc_readregistry(struct map_session_data *sd, int64 reg) { * - '#type$' (permanent str account reg) * - '##type$' (permanent str account reg2) **/ -char* pc_readregistry_str(struct map_session_data *sd, int64 reg) { +static char *pc_readregistry_str(struct map_session_data *sd, int64 reg) +{ struct script_reg_str *p = NULL; + nullpo_retr(NULL, sd); if (!sd->vars_ok) { ShowError("pc_readregistry_str: Trying to read reg %s before it's been loaded!\n", script->get_str(script_getvarid(reg))); //This really shouldn't happen, so it's possible the data was lost somewhere, we should request it again. @@ -9067,16 +9821,18 @@ char* pc_readregistry_str(struct map_session_data *sd, int64 reg) { * - '#type' (permanent numeric account reg) * - '##type' (permanent numeric account reg2) **/ -int pc_setregistry(struct map_session_data *sd, int64 reg, int val) { +static int pc_setregistry(struct map_session_data *sd, int64 reg, int val) +{ struct script_reg_num *p = NULL; const char *regname = script->get_str( script_getvarid(reg) ); unsigned int index = script_getvaridx(reg); + nullpo_ret(sd); /* SAAD! those things should be stored elsewhere e.g. char ones in char table, the cash ones in account_data table! */ switch( regname[0] ) { default: //Char reg if( !strcmp(regname,"PC_DIE_COUNTER") && sd->die_counter != val ) { - int i = (!sd->die_counter && (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE); + int i = (!sd->die_counter && (sd->job & MAPID_UPPERMASK) == MAPID_SUPER_NOVICE); sd->die_counter = val; if( i ) status_calc_pc(sd,SCO_NONE); // Lost the bonus. @@ -9142,11 +9898,14 @@ int pc_setregistry(struct map_session_data *sd, int64 reg, int val) { * - '#type$' (permanent str account reg) * - '##type$' (permanent str account reg2) **/ -int pc_setregistry_str(struct map_session_data *sd, int64 reg, const char *val) { +static int pc_setregistry_str(struct map_session_data *sd, int64 reg, const char *val) +{ struct script_reg_str *p = NULL; const char *regname = script->get_str( script_getvarid(reg) ); unsigned int index = script_getvaridx(reg); + nullpo_ret(sd); + nullpo_ret(val); if ( !pc->reg_load && !sd->vars_ok ) { ShowError("pc_setregistry_str : refusing to set %s until vars are received.\n", regname); return 0; @@ -9196,7 +9955,8 @@ int pc_setregistry_str(struct map_session_data *sd, int64 reg, const char *val) /*========================================== * Exec eventtimer for player sd (retrieved from map_session (id)) *------------------------------------------*/ -int pc_eventtimer(int tid, int64 tick, int id, intptr_t data) { +static int pc_eventtimer(int tid, int64 tick, int id, intptr_t data) +{ struct map_session_data *sd=map->id2sd(id); char *p = (char *)data; int i; @@ -9220,10 +9980,11 @@ int pc_eventtimer(int tid, int64 tick, int id, intptr_t data) { /*========================================== * Add eventtimer for player sd ? *------------------------------------------*/ -int pc_addeventtimer(struct map_session_data *sd,int tick,const char *name) +static int pc_addeventtimer(struct map_session_data *sd, int tick, const char *name) { int i; nullpo_ret(sd); + nullpo_ret(name); ARR_FIND( 0, MAX_EVENTTIMER, i, sd->eventtimer[i] == INVALID_TIMER ); if( i == MAX_EVENTTIMER ) @@ -9238,12 +9999,13 @@ int pc_addeventtimer(struct map_session_data *sd,int tick,const char *name) /*========================================== * Del eventtimer for player sd ? *------------------------------------------*/ -int pc_deleventtimer(struct map_session_data *sd,const char *name) +static int pc_deleventtimer(struct map_session_data *sd, const char *name) { char* p = NULL; int i; nullpo_ret(sd); + nullpo_ret(name); if (sd->eventcount <= 0) return 0; @@ -9268,7 +10030,7 @@ int pc_deleventtimer(struct map_session_data *sd,const char *name) /*========================================== * Update eventtimer count for player sd *------------------------------------------*/ -int pc_addeventtimercount(struct map_session_data *sd,const char *name,int tick) +static int pc_addeventtimercount(struct map_session_data *sd, const char *name, int tick) { int i; @@ -9287,7 +10049,7 @@ int pc_addeventtimercount(struct map_session_data *sd,const char *name,int tick) /*========================================== * Remove all eventtimer for player sd *------------------------------------------*/ -int pc_cleareventtimer(struct map_session_data *sd) +static int pc_cleareventtimer(struct map_session_data *sd) { int i; @@ -9307,11 +10069,14 @@ int pc_cleareventtimer(struct map_session_data *sd) return 0; } /* called when a item with combo is worn */ -int pc_checkcombo(struct map_session_data *sd, struct item_data *data ) { +static int pc_checkcombo(struct map_session_data *sd, struct item_data *data) +{ int i, j, k, z; int index, success = 0; struct pc_combos *combo; + nullpo_ret(sd); + nullpo_ret(data); for( i = 0; i < data->combos_count; i++ ) { /* ensure this isn't a duplicate combo */ @@ -9384,9 +10149,12 @@ int pc_checkcombo(struct map_session_data *sd, struct item_data *data ) { } /* called when a item with combo is removed */ -int pc_removecombo(struct map_session_data *sd, struct item_data *data ) { +static int pc_removecombo(struct map_session_data *sd, struct item_data *data) +{ int i, retval = 0; + nullpo_ret(sd); + nullpo_ret(data); if( !sd->combos ) return 0;/* nothing to do here, player has no combos */ @@ -9429,8 +10197,10 @@ int pc_removecombo(struct map_session_data *sd, struct item_data *data ) { return retval; } -int pc_load_combo(struct map_session_data *sd) { +static int pc_load_combo(struct map_session_data *sd) +{ int i, ret = 0; + nullpo_ret(sd); for( i = 0; i < EQI_MAX; i++ ) { struct item_data *id = NULL; int idx = sd->equip_index[i]; @@ -9455,429 +10225,477 @@ int pc_load_combo(struct map_session_data *sd) { } /** -* Equip item at given position. -* @param sd the affected player structure. Must be checked before. -* @param id item structure for equip. Must be checked before. -* @param n inventory item position. Must be checked before. -* @param pos slot position. Must be checked before. -**/ -void pc_equipitem_pos(struct map_session_data *sd, struct item_data *id, int n, int pos) + * Equip item at given position. + * @param sd the affected player structure. Must be checked before. + * @param id item structure for equip. Must be checked before. + * @param n inventory item position. Must be checked before. + * @param pos slot position. Must be checked before. + */ +static void pc_equipitem_pos(struct map_session_data *sd, struct item_data *id, int n, int pos) { + nullpo_retv(sd); if ((!map_no_view(sd->bl.m,EQP_SHADOW_WEAPON) && pos & EQP_SHADOW_WEAPON) || (pos & EQP_HAND_R)) { - if(id) - sd->weapontype1 = id->look; - else - sd->weapontype1 = 0; + if (id != NULL) { + sd->weapontype1 = id->subtype; + sd->status.look.weapon = id->view_sprite; + } else { + sd->weapontype1 = W_FIST; + sd->status.look.weapon = 0; + } pc->calcweapontype(sd); - clif->changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon); + clif->changelook(&sd->bl, LOOK_WEAPON, sd->status.look.weapon); } if ((!map_no_view(sd->bl.m,EQP_SHADOW_SHIELD) && pos & EQP_SHADOW_SHIELD) || (pos & EQP_HAND_L)) { - if (id) { - if(id->type == IT_WEAPON) { - sd->status.shield = 0; - sd->weapontype2 = id->look; - } else if(id->type == IT_ARMOR) { - sd->status.shield = id->look; - sd->weapontype2 = 0; + if (id != NULL) { + if (id->type == IT_WEAPON) { + sd->has_shield = false; + sd->status.look.shield = 0; + sd->weapontype2 = id->subtype; + } else if (id->type == IT_ARMOR) { + sd->has_shield = true; + sd->status.look.shield = id->view_sprite; + sd->weapontype2 = W_FIST; } - } else - sd->status.shield = sd->weapontype2 = 0; + } else { + sd->has_shield = false; + sd->status.look.shield = 0; + sd->weapontype2 = W_FIST; + } pc->calcweapontype(sd); - clif->changelook(&sd->bl,LOOK_SHIELD,sd->status.shield); + clif->changelook(&sd->bl, LOOK_SHIELD, sd->status.look.shield); } //Added check to prevent sending the same look on multiple slots -> //causes client to redraw item on top of itself. (suggested by Lupus) if (!map_no_view(sd->bl.m,EQP_HEAD_LOW) && pos & EQP_HEAD_LOW && pc->checkequip(sd,EQP_COSTUME_HEAD_LOW) == -1) { if (id && !(pos&(EQP_HEAD_TOP|EQP_HEAD_MID))) - sd->status.head_bottom = id->look; + sd->status.look.head_bottom = id->view_sprite; else - sd->status.head_bottom = 0; - clif->changelook(&sd->bl,LOOK_HEAD_BOTTOM,sd->status.head_bottom); + sd->status.look.head_bottom = 0; + clif->changelook(&sd->bl, LOOK_HEAD_BOTTOM, sd->status.look.head_bottom); } if (!map_no_view(sd->bl.m,EQP_HEAD_TOP) && pos & EQP_HEAD_TOP && pc->checkequip(sd,EQP_COSTUME_HEAD_TOP) == -1) { if (id) - sd->status.head_top = id->look; + sd->status.look.head_top = id->view_sprite; else - sd->status.head_top = 0; - clif->changelook(&sd->bl,LOOK_HEAD_TOP,sd->status.head_top); + sd->status.look.head_top = 0; + clif->changelook(&sd->bl, LOOK_HEAD_TOP, sd->status.look.head_top); } if (!map_no_view(sd->bl.m,EQP_HEAD_MID) && pos & EQP_HEAD_MID && pc->checkequip(sd,EQP_COSTUME_HEAD_MID) == -1) { if (id && !(pos&EQP_HEAD_TOP)) - sd->status.head_mid = id->look; + sd->status.look.head_mid = id->view_sprite; else - sd->status.head_mid = 0; - clif->changelook(&sd->bl,LOOK_HEAD_MID,sd->status.head_mid); + sd->status.look.head_mid = 0; + clif->changelook(&sd->bl, LOOK_HEAD_MID, sd->status.look.head_mid); } if (!map_no_view(sd->bl.m,EQP_COSTUME_HEAD_TOP) && pos & EQP_COSTUME_HEAD_TOP) { if (id){ - sd->status.head_top = id->look; + sd->status.look.head_top = id->view_sprite; } else - sd->status.head_top = 0; - clif->changelook(&sd->bl,LOOK_HEAD_TOP,sd->status.head_top); + sd->status.look.head_top = 0; + clif->changelook(&sd->bl, LOOK_HEAD_TOP, sd->status.look.head_top); } if (!map_no_view(sd->bl.m,EQP_COSTUME_HEAD_MID) && pos & EQP_COSTUME_HEAD_MID) { if(id && !(pos&EQP_HEAD_TOP)){ - sd->status.head_mid = id->look; + sd->status.look.head_mid = id->view_sprite; } else - sd->status.head_mid = 0; - clif->changelook(&sd->bl,LOOK_HEAD_MID,sd->status.head_mid); + sd->status.look.head_mid = 0; + clif->changelook(&sd->bl, LOOK_HEAD_MID, sd->status.look.head_mid); } if (!map_no_view(sd->bl.m,EQP_COSTUME_HEAD_LOW) && pos & EQP_COSTUME_HEAD_LOW) { if (id && !(pos&(EQP_HEAD_TOP|EQP_HEAD_MID))){ - sd->status.head_bottom = id->look; + sd->status.look.head_bottom = id->view_sprite; } else - sd->status.head_bottom = 0; - clif->changelook(&sd->bl,LOOK_HEAD_BOTTOM,sd->status.head_bottom); + sd->status.look.head_bottom = 0; + clif->changelook(&sd->bl, LOOK_HEAD_BOTTOM, sd->status.look.head_bottom); } if (!map_no_view(sd->bl.m,EQP_SHOES) && pos & EQP_SHOES) clif->changelook(&sd->bl,LOOK_SHOES,0); if (!map_no_view(sd->bl.m,EQP_GARMENT) && pos&EQP_GARMENT && pc->checkequip(sd,EQP_COSTUME_GARMENT) == -1) { - sd->status.robe = id ? id->look : 0; - clif->changelook(&sd->bl, LOOK_ROBE, sd->status.robe); + sd->status.look.robe = id ? id->view_sprite : 0; + clif->changelook(&sd->bl, LOOK_ROBE, sd->status.look.robe); } if (!map_no_view(sd->bl.m,EQP_COSTUME_GARMENT) && pos & EQP_COSTUME_GARMENT) { - sd->status.robe = id ? id->look : 0; - clif->changelook(&sd->bl,LOOK_ROBE,sd->status.robe); + sd->status.look.robe = id ? id->view_sprite : 0; + clif->changelook(&sd->bl, LOOK_ROBE, sd->status.look.robe); } } -/*========================================== - * Equip item on player sd at req_pos from inventory index n - * Return: - * 0 = fail - * 1 = success - *------------------------------------------*/ -int pc_equipitem(struct map_session_data *sd,int n,int req_pos) +/** + * Attempts to equip an item. + * + * @param sd The related character. + * @param n The item's inventory index. + * @param req_pos The equipment slot, where the item should be equipped. (See enum equip_pos.) + * @return 0 on failure, 1 on success. + * + **/ +static int pc_equipitem(struct map_session_data *sd, int n, int req_pos) { - int i,pos,flag=0,iflag; - struct item_data *id; - nullpo_ret(sd); - if( n < 0 || n >= MAX_INVENTORY ) { - clif->equipitemack(sd,0,0,EIA_FAIL); + if (n < 0 || n >= sd->status.inventorySize) { + clif->equipitemack(sd, 0, 0, EIA_FAIL); return 0; } - if( DIFF_TICK(sd->canequip_tick,timer->gettick()) > 0 ) - { - clif->equipitemack(sd,n,0,EIA_FAIL); + // If the character is in berserk mode, the item can't be equipped. + if (sd->sc.count != 0 && (sd->sc.data[SC_BERSERK] != NULL || sd->sc.data[SC_NO_SWITCH_EQUIP] != NULL)) { + clif->equipitemack(sd, n, 0, EIA_FAIL); return 0; } - id = sd->inventory_data[n]; - pos = pc->equippoint(sd,n); //With a few exceptions, item should go in all specified slots. + if (battle_config.battle_log != 0) + ShowInfo("equip %d(%d) %x:%x\n", sd->status.inventory[n].nameid, n, sd->status.inventory[n].equip, + (unsigned int)req_pos); - if(battle_config.battle_log) - ShowInfo("equip %d(%d) %x:%x\n", sd->status.inventory[n].nameid, n, (unsigned int)(id ? id->equip : 0), (unsigned int)req_pos); - if(!pc->isequip(sd,n) || !(pos&req_pos) || sd->status.inventory[n].equip != 0 || sd->status.inventory[n].attribute==1 ) { // [Valaris] - // FIXME: pc->isequip: equip level failure uses 2 instead of 0 - clif->equipitemack(sd,n,0,EIA_FAIL); // fail + if (DIFF_TICK(sd->canequip_tick, timer->gettick()) > 0) { + clif->equipitemack(sd, n, 0, EIA_FAIL); return 0; } - if (sd->sc.data[SC_BERSERK] || sd->sc.data[SC_NO_SWITCH_EQUIP]) - { - clif->equipitemack(sd,n,0,EIA_FAIL); // fail + int pos = pc->equippoint(sd, n); // With a few exceptions, item should go in all specified slots. + + if (pc->isequip(sd,n) == 0 || (pos & req_pos) == 0 || sd->status.inventory[n].equip != 0 + || (sd->status.inventory[n].attribute & ATTR_BROKEN) != 0) { + clif->equipitemack(sd, n, 0, EIA_FAIL); return 0; + } + + if (sd->inventory_data[n]->flag.bindonequip != 0 && sd->status.inventory[n].bound == 0) { + sd->status.inventory[n].bound = IBT_CHARACTER; + clif->notify_bounditem(sd, n); } - /* won't fail from this point onwards */ - if( id->flag.bindonequip && !sd->status.inventory[n].bound ) { - sd->status.inventory[n].bound = (unsigned char)IBT_CHARACTER; - clif->notify_bounditem(sd,n); - } - - if(pos == EQP_ACC) { //Accesories should only go in one of the two, - pos = req_pos&EQP_ACC; - if (pos == EQP_ACC) //User specified both slots.. - pos = sd->equip_index[EQI_ACC_R] >= 0 ? EQP_ACC_L : EQP_ACC_R; - } else if(pos == EQP_ARMS && id->equip == EQP_HAND_R) { //Dual wield capable weapon. - pos = (req_pos&EQP_ARMS); - if (pos == EQP_ARMS) //User specified both slots, pick one for them. - pos = sd->equip_index[EQI_HAND_R] >= 0 ? EQP_HAND_L : EQP_HAND_R; - } else if(pos == EQP_SHADOW_ACC) { //Accesories should only go in one of the two, - pos = req_pos&EQP_SHADOW_ACC; - if (pos == EQP_SHADOW_ACC) //User specified both slots.. - pos = sd->equip_index[EQI_SHADOW_ACC_R] >= 0 ? EQP_SHADOW_ACC_L : EQP_SHADOW_ACC_R; - } else if( pos == EQP_SHADOW_ARMS && id->equip == EQP_SHADOW_WEAPON) { //Dual wield capable weapon. - pos = (req_pos&EQP_SHADOW_ARMS); - if (pos == EQP_SHADOW_ARMS) //User specified both slots, pick one for them. - pos = sd->equip_index[EQI_SHADOW_WEAPON] >= 0 ? EQP_SHADOW_SHIELD : EQP_SHADOW_WEAPON; - } - - if (pos&EQP_HAND_R && battle_config.use_weapon_skill_range&BL_PC) { - //Update skill-block range database when weapon range changes. [Skotlex] - i = sd->equip_index[EQI_HAND_R]; - if (i < 0 || !sd->inventory_data[i]) //No data, or no weapon equipped + if (pos == EQP_ACC) { // Accesories should only go in one of the two. + pos = req_pos & EQP_ACC; + + if (pos == EQP_ACC) // User specified both slots. + pos = (sd->equip_index[EQI_ACC_R] >= 0) ? EQP_ACC_L : EQP_ACC_R; + } else if (pos == EQP_ARMS && sd->inventory_data[n]->equip == EQP_HAND_R) { // Dual wield capable weapon. + pos = req_pos & EQP_ARMS; + + if (pos == EQP_ARMS) // User specified both slots, pick one for them. + pos = (sd->equip_index[EQI_HAND_R] >= 0) ? EQP_HAND_L : EQP_HAND_R; + } else if (pos == EQP_SHADOW_ACC) { // Accesories should only go in one of the two, + pos = req_pos & EQP_SHADOW_ACC; + + if (pos == EQP_SHADOW_ACC) // User specified both slots. + pos = (sd->equip_index[EQI_SHADOW_ACC_R] >= 0) ? EQP_SHADOW_ACC_L : EQP_SHADOW_ACC_R; + } else if (pos == EQP_SHADOW_ARMS && sd->inventory_data[n]->equip == EQP_SHADOW_WEAPON) { // Dual wield capable weapon. + pos = req_pos & EQP_SHADOW_ARMS; + + if (pos == EQP_SHADOW_ARMS) // User specified both slots, pick one for them. + pos = (sd->equip_index[EQI_SHADOW_WEAPON] >= 0) ? EQP_SHADOW_SHIELD : EQP_SHADOW_WEAPON; + } + + int flag = 0; + + // Update skill-block range database when weapon range changes. [Skotlex] + if ((pos & EQP_HAND_R) != 0 && (battle_config.use_weapon_skill_range & BL_PC) != 0) { + int idx = sd->equip_index[EQI_HAND_R]; + + if (idx < 0 || sd->inventory_data[idx] == NULL) // No data, or no weapon equipped. flag = 1; else - flag = id->range != sd->inventory_data[i]->range; + flag = (sd->inventory_data[n]->range != sd->inventory_data[idx]->range) ? 1 : 0; } - for(i=0;i<EQI_MAX;i++) { - if(pos & pc->equip_pos[i]) { - if(sd->equip_index[i] >= 0) //Slot taken, remove item from there. + for (int i = 0; i < EQI_MAX; i++) { + if ((pos & pc->equip_pos[i]) != 0) { + if (sd->equip_index[i] >= 0) // Slot taken, remove item from there. pc->unequipitem(sd, sd->equip_index[i], PCUNEQUIPITEM_FORCE); sd->equip_index[i] = n; } } - if(pos==EQP_AMMO){ - clif->arrowequip(sd,n); - clif->arrow_fail(sd,3); + if (pos == EQP_AMMO) { + clif->arrowequip(sd, n); + clif->arrow_fail(sd, 3); + } else { + clif->equipitemack(sd, n, pos, EIA_SUCCESS); } - else - clif->equipitemack(sd,n,pos,EIA_SUCCESS); - sd->status.inventory[n].equip=pos; + sd->status.inventory[n].equip = pos; + pc->equipitem_pos(sd, sd->inventory_data[n], n, pos); + pc->checkallowskill(sd); // Check if status changes should be halted. - pc->equipitem_pos(sd, id, n, pos); + int iflag = sd->npc_item_flag; - pc->checkallowskill(sd); //Check if status changes should be halted. - iflag = sd->npc_item_flag; + // Check for combos. (MUST be done before status->calc_pc()!) + if (sd->inventory_data[n]->combos_count != 0) + pc->checkcombo(sd, sd->inventory_data[n]); - /* check for combos (MUST be before status_calc_pc) */ - if( id->combos_count ) - pc->checkcombo(sd,id); - if(itemdb_isspecial(sd->status.inventory[n].card[0])) - ; //No cards - else { - for( i = 0; i < id->slot; i++ ) { - struct item_data *data; - if (!sd->status.inventory[n].card[i]) + if (!itemdb_isspecial(sd->status.inventory[n].card[0])) { + for (int i = 0; i < sd->inventory_data[n]->slot; i++) { + if (sd->status.inventory[n].card[i] == 0) continue; - if ( ( data = itemdb->exists(sd->status.inventory[n].card[i]) ) != NULL ) { - if( data->combos_count ) - pc->checkcombo(sd,data); - } + + struct item_data *data = itemdb->exists(sd->status.inventory[n].card[i]); + + if (data != NULL && data->combos_count != 0) + pc->checkcombo(sd, data); } } - status_calc_pc(sd,SCO_NONE); - if (flag) //Update skill data + status_calc_pc(sd, SCO_NONE); + + if (flag != 0) // Update skill data. clif->skillinfoblock(sd); + + // Execute equip script. [Skotlex] + struct item_data *equip_data = sd->inventory_data[n]; + struct map_zone_data *zone = map->list[sd->bl.m].zone; + int dis_items_cnt = zone->disabled_items_count; - //OnEquip script [Skotlex] - if (id->equip_script) - script->run_item_equip_script(sd, id, npc->fake_nd->bl.id); + if (equip_data->equip_script != NULL) { + int idx; - if(itemdb_isspecial(sd->status.inventory[n].card[0])) - ; //No cards - else { - for( i = 0; i < id->slot; i++ ) { - struct item_data *data; - if (!sd->status.inventory[n].card[i]) + ARR_FIND(0, dis_items_cnt, idx, zone->disabled_items[idx] == equip_data->nameid); + + if (idx == dis_items_cnt) + script->run_item_equip_script(sd, equip_data, npc->fake_nd->bl.id); + } + + struct item *equip = &sd->status.inventory[n]; + + if (!itemdb_isspecial(equip->card[0])) { + for (int slot = 0; slot < equip_data->slot; slot++) { + if (equip->card[slot] == 0) continue; - if ( ( data = itemdb->exists(sd->status.inventory[n].card[i]) ) != NULL ) { - if (data->equip_script) - script->run_item_equip_script(sd, data, npc->fake_nd->bl.id); + + struct item_data *card_data = itemdb->exists(equip->card[slot]); + + if (card_data != NULL && card_data->equip_script != NULL) { + int idx; + + ARR_FIND(0, dis_items_cnt, idx, zone->disabled_items[idx] == card_data->nameid); + + if (idx == dis_items_cnt) + script->run_item_equip_script(sd, card_data, npc->fake_nd->bl.id); } } } + sd->npc_item_flag = iflag; return 1; } /** -* Unrquip item ad given position. -* @param sd the affected player structure. Must be checked before. -* @param n inventory item position. Must be checked before. -* @param pos slot position. Must be checked before. -**/ -void pc_unequipitem_pos(struct map_session_data *sd, int n, int pos) + * Unequip an item at the given position. + * @param sd the affected player structure. Must be checked before. + * @param n inventory item position. Must be checked before. + * @param pos slot position. Must be checked before. + */ +static void pc_unequipitem_pos(struct map_session_data *sd, int n, int pos) { + nullpo_retv(sd); if (pos & EQP_HAND_R) { - sd->weapontype1 = 0; - sd->status.weapon = sd->weapontype2; + sd->weapontype1 = W_FIST; pc->calcweapontype(sd); - clif->changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon); + sd->status.look.weapon = 0; + clif->changelook(&sd->bl, LOOK_WEAPON, sd->status.look.weapon); if (!battle_config.dancing_weaponswitch_fix) status_change_end(&sd->bl, SC_DANCING, INVALID_TIMER); // Unequipping => stop dancing. } if (pos & EQP_HAND_L) { - sd->status.shield = sd->weapontype2 = 0; + sd->has_shield = false; + sd->status.look.shield = 0; + sd->weapontype2 = W_FIST; pc->calcweapontype(sd); - clif->changelook(&sd->bl,LOOK_SHIELD,sd->status.shield); + clif->changelook(&sd->bl, LOOK_SHIELD, sd->status.look.shield); } if (pos & EQP_HEAD_LOW && pc->checkequip(sd,EQP_COSTUME_HEAD_LOW) == -1) { - sd->status.head_bottom = 0; - clif->changelook(&sd->bl,LOOK_HEAD_BOTTOM,sd->status.head_bottom); + sd->status.look.head_bottom = 0; + clif->changelook(&sd->bl, LOOK_HEAD_BOTTOM, sd->status.look.head_bottom); } if (pos & EQP_HEAD_TOP && pc->checkequip(sd,EQP_COSTUME_HEAD_TOP) == -1) { - sd->status.head_top = 0; - clif->changelook(&sd->bl,LOOK_HEAD_TOP,sd->status.head_top); + sd->status.look.head_top = 0; + clif->changelook(&sd->bl, LOOK_HEAD_TOP, sd->status.look.head_top); } if (pos & EQP_HEAD_MID && pc->checkequip(sd,EQP_COSTUME_HEAD_MID) == -1) { - sd->status.head_mid = 0; - clif->changelook(&sd->bl,LOOK_HEAD_MID,sd->status.head_mid); + sd->status.look.head_mid = 0; + clif->changelook(&sd->bl, LOOK_HEAD_MID, sd->status.look.head_mid); } if (pos & EQP_COSTUME_HEAD_TOP) { - sd->status.head_top = ( pc->checkequip(sd,EQP_HEAD_TOP) >= 0 ) ? sd->inventory_data[pc->checkequip(sd,EQP_HEAD_TOP)]->look : 0; - clif->changelook(&sd->bl,LOOK_HEAD_TOP,sd->status.head_top); + sd->status.look.head_top = ( pc->checkequip(sd,EQP_HEAD_TOP) >= 0 ) ? sd->inventory_data[pc->checkequip(sd,EQP_HEAD_TOP)]->view_sprite : 0; + clif->changelook(&sd->bl, LOOK_HEAD_TOP, sd->status.look.head_top); } if (pos & EQP_COSTUME_HEAD_MID) { - sd->status.head_mid = ( pc->checkequip(sd,EQP_HEAD_MID) >= 0 ) ? sd->inventory_data[pc->checkequip(sd,EQP_HEAD_MID)]->look : 0; - clif->changelook(&sd->bl,LOOK_HEAD_MID,sd->status.head_mid); + sd->status.look.head_mid = ( pc->checkequip(sd,EQP_HEAD_MID) >= 0 ) ? sd->inventory_data[pc->checkequip(sd,EQP_HEAD_MID)]->view_sprite : 0; + clif->changelook(&sd->bl, LOOK_HEAD_MID, sd->status.look.head_mid); } if (pos & EQP_COSTUME_HEAD_LOW) { - sd->status.head_bottom = ( pc->checkequip(sd,EQP_HEAD_LOW) >= 0 ) ? sd->inventory_data[pc->checkequip(sd,EQP_HEAD_LOW)]->look : 0; - clif->changelook(&sd->bl,LOOK_HEAD_BOTTOM,sd->status.head_bottom); + sd->status.look.head_bottom = ( pc->checkequip(sd,EQP_HEAD_LOW) >= 0 ) ? sd->inventory_data[pc->checkequip(sd,EQP_HEAD_LOW)]->view_sprite : 0; + clif->changelook(&sd->bl, LOOK_HEAD_BOTTOM, sd->status.look.head_bottom); } if (pos & EQP_SHOES) clif->changelook(&sd->bl,LOOK_SHOES,0); if (pos & EQP_GARMENT && pc->checkequip(sd,EQP_COSTUME_GARMENT) == -1) { - sd->status.robe = 0; + sd->status.look.robe = 0; clif->changelook(&sd->bl, LOOK_ROBE, 0); } if (pos & EQP_COSTUME_GARMENT) { - sd->status.robe = ( pc->checkequip(sd,EQP_GARMENT) >= 0 ) ? sd->inventory_data[pc->checkequip(sd,EQP_GARMENT)]->look : 0; - clif->changelook(&sd->bl,LOOK_ROBE,sd->status.robe); + sd->status.look.robe = ( pc->checkequip(sd,EQP_GARMENT) >= 0 ) ? sd->inventory_data[pc->checkequip(sd,EQP_GARMENT)]->view_sprite : 0; + clif->changelook(&sd->bl, LOOK_ROBE, sd->status.look.robe); } } -/*========================================== - * Called when attemting to unequip an item from player - * type: @see enum pc_unequipitem_flag - * Return: - * 0 = fail - * 1 = success - *------------------------------------------*/ -int pc_unequipitem(struct map_session_data *sd,int n,int flag) +/** + * Attempts to unequip an item. + * + * @param sd The related character. + * @param n The item's inventory index. + * @param flag Modifier for additional actions. (See enum pc_unequipitem_flag.) + * @return 0 on failure, 1 on success. + * + **/ +static int pc_unequipitem(struct map_session_data *sd, int n, int flag) { - int i,iflag; - bool status_cacl = false; - int pos; nullpo_ret(sd); - if( n < 0 || n >= MAX_INVENTORY ) { - clif->unequipitemack(sd,0,0,UIA_FAIL); + if (n < 0 || n >= sd->status.inventorySize) { + clif->unequipitemack(sd, 0, 0, UIA_FAIL); return 0; } - // if player is berserk then cannot unequip - if (!(flag&PCUNEQUIPITEM_FORCE) && sd->sc.count && (sd->sc.data[SC_BERSERK] || sd->sc.data[SC_NO_SWITCH_EQUIP]) ) - { - clif->unequipitemack(sd,n,0,UIA_FAIL); + // If the character is in berserk mode, the item can't be unequipped. + if (sd->sc.count != 0 && (sd->sc.data[SC_BERSERK] != NULL || sd->sc.data[SC_NO_SWITCH_EQUIP] != NULL) + && (flag & PCUNEQUIPITEM_FORCE) == 0) { + clif->unequipitemack(sd, n, 0, UIA_FAIL); return 0; } - if( !(flag&PCUNEQUIPITEM_FORCE) && sd->sc.count && sd->sc.data[SC_KYOUGAKU] ) - { - clif->unequipitemack(sd,n,0,UIA_FAIL); + if ((flag & PCUNEQUIPITEM_FORCE) == 0 && sd->sc.count != 0 && sd->sc.data[SC_KYOUGAKU] != NULL) { + clif->unequipitemack(sd, n, 0, UIA_FAIL); return 0; } - if(battle_config.battle_log) + if (battle_config.battle_log != 0) ShowInfo("unequip %d %x:%x\n", n, (unsigned int)(pc->equippoint(sd, n)), sd->status.inventory[n].equip); - if(!sd->status.inventory[n].equip){ //Nothing to unequip - clif->unequipitemack(sd,n,0,UIA_FAIL); + if (sd->status.inventory[n].equip == 0) { // Nothing to unequip. + clif->unequipitemack(sd, n, 0, UIA_FAIL); return 0; } - for(i=0;i<EQI_MAX;i++) { - if(sd->status.inventory[n].equip & pc->equip_pos[i]) + + for (int i = 0; i < EQI_MAX; i++) { + if ((sd->status.inventory[n].equip & pc->equip_pos[i]) != 0) sd->equip_index[i] = -1; } - pos = sd->status.inventory[n].equip; - pc->unequipitem_pos(sd, n, pos); + int pos = sd->status.inventory[n].equip; - clif->unequipitemack(sd,n,pos,UIA_SUCCESS); + pc->unequipitem_pos(sd, n, pos); + clif->unequipitemack(sd, n, pos, UIA_SUCCESS); - if((pos & EQP_ARMS) && - sd->weapontype1 == 0 && sd->weapontype2 == 0 && (!sd->sc.data[SC_TK_SEVENWIND] || sd->sc.data[SC_ASPERSIO])) //Check for seven wind (but not level seven!) - skill->enchant_elemental_end(&sd->bl,-1); + if ((pos & EQP_ARMS) != 0 && sd->weapontype1 == W_FIST && sd->weapontype2 == W_FIST + && (sd->sc.data[SC_TK_SEVENWIND] == NULL || sd->sc.data[SC_ASPERSIO] != NULL)) { // Check for Seven Wind. (But not level seven!) + skill->enchant_elemental_end(&sd->bl, -1); + } - if(pos & EQP_ARMOR) { - // On Armor Change... + if ((pos & EQP_ARMOR) != 0) { status_change_end(&sd->bl, SC_BENEDICTIO, INVALID_TIMER); status_change_end(&sd->bl, SC_ARMOR_RESIST, INVALID_TIMER); } - if( sd->state.autobonus&pos ) - sd->state.autobonus &= ~sd->status.inventory[n].equip; //Check for activated autobonus [Inkfish] +#ifdef RENEWAL + if (battle->bc->bow_unequip_arrow != 0 && (pos & EQP_ARMS) != 0 && sd->equip_index[EQI_AMMO] > 0) + pc->unequipitem(sd, sd->equip_index[EQI_AMMO], PCUNEQUIPITEM_FORCE); +#endif - sd->status.inventory[n].equip=0; - iflag = sd->npc_item_flag; + if ((sd->state.autobonus & pos) != 0) // Check for activated autobonus. [Inkfish] + sd->state.autobonus &= ~sd->status.inventory[n].equip; - /* check for combos (MUST be before status_calc_pc) */ - if ( sd->inventory_data[n] ) { - if( sd->inventory_data[n]->combos_count ) { - if( pc->removecombo(sd,sd->inventory_data[n]) ) - status_cacl = true; - } if(itemdb_isspecial(sd->status.inventory[n].card[0])) - ; //No cards - else { - for( i = 0; i < sd->inventory_data[n]->slot; i++ ) { - struct item_data *data; - if (!sd->status.inventory[n].card[i]) + sd->status.inventory[n].equip = 0; + + bool status_calc = false; + int iflag = sd->npc_item_flag; + + // Check for combos. (MUST be done before status->calc_pc()!) + if (sd->inventory_data[n] != NULL) { + if (sd->inventory_data[n]->combos_count != 0 && pc->removecombo(sd, sd->inventory_data[n]) != 0) + status_calc = true; + + if (!itemdb_isspecial(sd->status.inventory[n].card[0])) { + for (int i = 0; i < sd->inventory_data[n]->slot; i++) { + if (sd->status.inventory[n].card[i] == 0) continue; - if ( ( data = itemdb->exists(sd->status.inventory[n].card[i]) ) != NULL ) { - if( data->combos_count ) { - if( pc->removecombo(sd,data) ) - status_cacl = true; - } - } + + struct item_data *data = itemdb->exists(sd->status.inventory[n].card[i]); + + if (data != NULL && data->combos_count != 0 && pc->removecombo(sd, data) != 0) + status_calc = true; } } + + // Check item options. + for (int i = 0; i < MAX_ITEM_OPTIONS; i++) { + if (sd->status.inventory[n].option[i].index <= 0) + continue; + + if (itemdb->option_exists(sd->status.inventory[n].option[i].index) == NULL) + continue; + + status_calc = true; + } } - if(flag&PCUNEQUIPITEM_RECALC || status_cacl) { + if ((flag & PCUNEQUIPITEM_RECALC) != 0 || status_calc) { pc->checkallowskill(sd); - status_calc_pc(sd,SCO_NONE); + status_calc_pc(sd, SCO_NONE); } - if(sd->sc.data[SC_CRUCIS] && !battle->check_undead(sd->battle_status.race,sd->battle_status.def_ele)) + if (sd->sc.data[SC_CRUCIS] != NULL && !battle->check_undead(sd->battle_status.race, sd->battle_status.def_ele)) status_change_end(&sd->bl, SC_CRUCIS, INVALID_TIMER); - //OnUnEquip script [Skotlex] - if (sd->inventory_data[n]) { - if (sd->inventory_data[n]->unequip_script) { - if ( battle_config.unequip_restricted_equipment & 1 ) { - ARR_FIND(0, map->list[sd->bl.m].zone->disabled_items_count, i, map->list[sd->bl.m].zone->disabled_items[i] == sd->status.inventory[n].nameid); - if ( i == map->list[sd->bl.m].zone->disabled_items_count ) - script->run_item_unequip_script(sd, sd->inventory_data[n], npc->fake_nd->bl.id); - } - else - script->run_item_unequip_script(sd, sd->inventory_data[n], npc->fake_nd->bl.id); + // Execute unequip script. [Skotlex] + if (sd->inventory_data[n] != NULL) { + struct item_data *equip_data = sd->inventory_data[n]; + struct map_zone_data *zone = map->list[sd->bl.m].zone; + int dis_items_cnt = zone->disabled_items_count; + + if (equip_data->unequip_script != NULL) { + int idx; + + ARR_FIND(0, dis_items_cnt, idx, zone->disabled_items[idx] == equip_data->nameid); + + if (idx == dis_items_cnt) + script->run_item_unequip_script(sd, equip_data, npc->fake_nd->bl.id); } - if(itemdb_isspecial(sd->status.inventory[n].card[0])) - ; //No cards - else { - for( i = 0; i < sd->inventory_data[n]->slot; i++ ) { - struct item_data *data; - if (!sd->status.inventory[n].card[i]) + + struct item *equip = &sd->status.inventory[n]; + + if (!itemdb_isspecial(equip->card[0])) { + for (int slot = 0; slot < equip_data->slot; slot++) { + if (equip->card[slot] == 0) continue; - if ( ( data = itemdb->exists(sd->status.inventory[n].card[i]) ) != NULL ) { - if ( data->unequip_script ) { - if ( battle_config.unequip_restricted_equipment & 2 ) { - int j; - ARR_FIND(0, map->list[sd->bl.m].zone->disabled_items_count, j, map->list[sd->bl.m].zone->disabled_items[j] == sd->status.inventory[n].card[i]); - if ( j == map->list[sd->bl.m].zone->disabled_items_count ) - script->run_item_unequip_script(sd, data, npc->fake_nd->bl.id); - } - else - script->run_item_unequip_script(sd, data, npc->fake_nd->bl.id); - } - } + struct item_data *card_data = itemdb->exists(equip->card[slot]); + + if (card_data != NULL && card_data->unequip_script != NULL) { + int idx; + ARR_FIND(0, dis_items_cnt, idx, zone->disabled_items[idx] == card_data->nameid); + + if (idx == dis_items_cnt) + script->run_item_unequip_script(sd, card_data, npc->fake_nd->bl.id); + } } } } + sd->npc_item_flag = iflag; return 1; @@ -9887,105 +10705,114 @@ int pc_unequipitem(struct map_session_data *sd,int n,int flag) * Checking if player (sd) have unauthorize, invalide item * on inventory, cart, equiped for the map (item_noequip) *------------------------------------------*/ -int pc_checkitem(struct map_session_data *sd) +static int pc_checkitem(struct map_session_data *sd) { int i, calc_flag = 0; nullpo_ret(sd); - if (sd->state.vending) //Avoid reorganizing items when we are vending, as that leads to exploits (pointed out by End of Exam) + if (sd->state.vending == 1) // Avoid reorganizing items when we are vending, as that leads to exploits (pointed out by End of Exam) return 0; - if (sd->state.itemcheck) { // check for invalid(ated) items - int id; - for (i = 0; i < MAX_INVENTORY; i++) { - id = sd->status.inventory[i].nameid; + if (sd->itemcheck != PCCHECKITEM_NONE) { // check for invalid(ated) items + int id = 0; - if (!id) - continue; + if (sd->itemcheck & PCCHECKITEM_INVENTORY) { + for (i = 0; i < sd->status.inventorySize; i++) { + if ((id = sd->status.inventory[i].nameid) == 0) + continue; - if (!itemdb_available(id)) { - ShowWarning("Removed invalid/disabled item id %d from inventory (amount=%d, char_id=%d).\n", id, sd->status.inventory[i].amount, sd->status.char_id); - pc->delitem(sd, i, sd->status.inventory[i].amount, 0, DELITEM_NORMAL, LOG_TYPE_INV_INVALID); - continue; + if (!itemdb_available(id)) { + ShowWarning("pc_checkitem: Removed invalid/disabled item id %d from inventory (amount=%d, char_id=%d).\n", id, sd->status.inventory[i].amount, sd->status.char_id); + pc->delitem(sd, i, sd->status.inventory[i].amount, 0, DELITEM_NORMAL, LOG_TYPE_INV_INVALID); + continue; + } + + if (sd->status.inventory[i].unique_id == 0 && !itemdb->isstackable(id)) + sd->status.inventory[i].unique_id = itemdb->unique_id(sd); } - if (!sd->status.inventory[i].unique_id && !itemdb->isstackable(id)) - sd->status.inventory[i].unique_id = itemdb->unique_id(sd); + sd->itemcheck &= ~PCCHECKITEM_INVENTORY; } - for( i = 0; i < MAX_CART; i++ ) { - id = sd->status.cart[i].nameid; + if (sd->itemcheck & PCCHECKITEM_CART) { + for (i = 0; i < MAX_CART; i++) { + if ((id = sd->status.cart[i].nameid) == 0) + continue; - if (!id) - continue; + if( !itemdb_available(id) ) { + ShowWarning("pc_checkitem: Removed invalid/disabled item id %d from cart (amount=%d, char_id=%d).\n", id, sd->status.cart[i].amount, sd->status.char_id); + pc->cart_delitem(sd, i, sd->status.cart[i].amount, 0, LOG_TYPE_CART_INVALID); + continue; + } - if( !itemdb_available(id) ) { - ShowWarning("Removed invalid/disabled item id %d from cart (amount=%d, char_id=%d).\n", id, sd->status.cart[i].amount, sd->status.char_id); - pc->cart_delitem(sd, i, sd->status.cart[i].amount, 0, LOG_TYPE_CART_INVALID); - continue; + if (sd->status.cart[i].unique_id == 0 && !itemdb->isstackable(id)) + sd->status.cart[i].unique_id = itemdb->unique_id(sd); } - if ( !sd->status.cart[i].unique_id && !itemdb->isstackable(id) ) - sd->status.cart[i].unique_id = itemdb->unique_id(sd); + sd->itemcheck &= ~PCCHECKITEM_CART; } - for( i = 0; i < MAX_STORAGE; i++ ) { - id = sd->status.storage.items[i].nameid; + if (sd->itemcheck & PCCHECKITEM_STORAGE && sd->storage.received == true) { + for (i = 0; i < VECTOR_LENGTH(sd->storage.item); i++) { + struct item *it = &VECTOR_INDEX(sd->storage.item, i); - if (!id) - continue; + if ((id = it->nameid) == 0) + continue; - if( id && !itemdb_available(id) ) { - ShowWarning("Removed invalid/disabled item id %d from storage (amount=%d, char_id=%d).\n", id, sd->status.storage.items[i].amount, sd->status.char_id); - storage->delitem(sd, i, sd->status.storage.items[i].amount); - storage->close(sd); - continue; + if (!itemdb_available(id)) { + ShowWarning("pc_checkitem: Removed invalid/disabled item id %d from storage (amount=%d, char_id=%d).\n", id, it->amount, sd->status.char_id); + storage->delitem(sd, i, it->amount); + continue; + } + + if (it->unique_id == 0 && itemdb->isstackable(id) == 0) + it->unique_id = itemdb->unique_id(sd); } - if ( !sd->status.storage.items[i].unique_id && !itemdb->isstackable(id) ) - sd->status.storage.items[i].unique_id = itemdb->unique_id(sd); + storage->close(sd); + + sd->itemcheck &= ~PCCHECKITEM_STORAGE; } - if (sd->guild) { + if (sd->guild && sd->itemcheck & PCCHECKITEM_GSTORAGE) { struct guild_storage *guild_storage = idb_get(gstorage->db,sd->guild->guild_id); if (guild_storage) { - for( i = 0; i < MAX_GUILD_STORAGE; i++ ) { - id = guild_storage->items[i].nameid; - - if (!id) + for (i = 0; i < MAX_GUILD_STORAGE; i++) { + if ((id = guild_storage->items[i].nameid) == 0) continue; - if( !itemdb_available(id) ) { - ShowWarning("Removed invalid/disabled item id %d from guild storage (amount=%d, char_id=%d, guild_id=%d).\n", id, guild_storage->items[i].amount, sd->status.char_id, sd->guild->guild_id); + if (!itemdb_available(id)) { + ShowWarning("pc_checkitem: Removed invalid/disabled item id %d from guild storage (amount=%d, char_id=%d, guild_id=%d).\n", id, guild_storage->items[i].amount, sd->status.char_id, sd->guild->guild_id); gstorage->delitem(sd, guild_storage, i, guild_storage->items[i].amount); gstorage->close(sd); // force closing continue; } - if (!guild_storage->items[i].unique_id && !itemdb->isstackable(id)) + if (guild_storage->items[i].unique_id == 0 && !itemdb->isstackable(id)) guild_storage->items[i].unique_id = itemdb->unique_id(sd); } } + + sd->itemcheck &= ~PCCHECKITEM_GSTORAGE; } - sd->state.itemcheck = 0; } - for( i = 0; i < MAX_INVENTORY; i++) { + for (i = 0; i < sd->status.inventorySize; i++) { - if( sd->status.inventory[i].nameid == 0 ) + if (sd->status.inventory[i].nameid == 0) continue; - if( !sd->status.inventory[i].equip ) + if (sd->status.inventory[i].equip == 0) continue; - if( sd->status.inventory[i].equip&~pc->equippoint(sd,i) ) { + if (sd->status.inventory[i].equip & ~pc->equippoint(sd,i)) { pc->unequipitem(sd, i, PCUNEQUIPITEM_FORCE); calc_flag = 1; continue; } - if (battle_config.unequip_restricted_equipment&1) { + if (battle_config.unequip_restricted_equipment & 1) { int j; for (j = 0; j < map->list[sd->bl.m].zone->disabled_items_count; j++) { if (map->list[sd->bl.m].zone->disabled_items[j] == sd->status.inventory[i].nameid) { @@ -9995,7 +10822,7 @@ int pc_checkitem(struct map_session_data *sd) } } - if (battle_config.unequip_restricted_equipment&2) { + if (battle_config.unequip_restricted_equipment & 2) { if (!itemdb_isspecial(sd->status.inventory[i].card[0])) { int j, slot; for (slot = 0; slot < MAX_SLOTS; slot++) { @@ -10011,9 +10838,9 @@ int pc_checkitem(struct map_session_data *sd) } - if( calc_flag && sd->state.active ) { + if (calc_flag != 0 && sd->state.active == 1) { pc->checkallowskill(sd); - status_calc_pc(sd,SCO_NONE); + status_calc_pc(sd, SCO_NONE); } return 0; @@ -10022,7 +10849,7 @@ int pc_checkitem(struct map_session_data *sd) /*========================================== * Update PVP rank for sd1 in cmp to sd2 *------------------------------------------*/ -int pc_calc_pvprank_sub(struct block_list *bl, va_list ap) +static int pc_calc_pvprank_sub(struct block_list *bl, va_list ap) { struct map_session_data *sd1 = NULL; struct map_session_data *sd2 = va_arg(ap,struct map_session_data *); @@ -10045,9 +10872,11 @@ int pc_calc_pvprank_sub(struct block_list *bl, va_list ap) * Calculate new rank beetween all present players (map->foreachinarea) * and display result *------------------------------------------*/ -int pc_calc_pvprank(struct map_session_data *sd) { +static int pc_calc_pvprank(struct map_session_data *sd) +{ int old; struct map_data *m; + nullpo_ret(sd); m=&map->list[sd->bl.m]; old=sd->pvp_rank; sd->pvp_rank=1; @@ -10059,7 +10888,8 @@ int pc_calc_pvprank(struct map_session_data *sd) { /*========================================== * Calculate next sd ranking calculation from config *------------------------------------------*/ -int pc_calc_pvprank_timer(int tid, int64 tick, int id, intptr_t data) { +static int pc_calc_pvprank_timer(int tid, int64 tick, int id, intptr_t data) +{ struct map_session_data *sd; sd=map->id2sd(id); @@ -10083,7 +10913,7 @@ int pc_calc_pvprank_timer(int tid, int64 tick, int id, intptr_t data) { * partner_id = yes * 0 = no *------------------------------------------*/ -int pc_ismarried(struct map_session_data *sd) +static int pc_ismarried(struct map_session_data *sd) { if(sd == NULL) return -1; @@ -10098,14 +10928,19 @@ int pc_ismarried(struct map_session_data *sd) * -1 = fail * 0 = success *------------------------------------------*/ -int pc_marriage(struct map_session_data *sd,struct map_session_data *dstsd) +static int pc_marriage(struct map_session_data *sd, struct map_session_data *dstsd) { if(sd == NULL || dstsd == NULL || sd->status.partner_id > 0 || dstsd->status.partner_id > 0 || - (sd->class_&JOBL_BABY) || (dstsd->class_&JOBL_BABY)) + (sd->job & JOBL_BABY) != 0 || (dstsd->job & JOBL_BABY) != 0) return -1; sd->status.partner_id = dstsd->status.char_id; dstsd->status.partner_id = sd->status.char_id; + + // Achievements [Smokexyz/Hercules] + achievement->validate_marry(sd); + achievement->validate_marry(dstsd); + return 0; } @@ -10115,7 +10950,7 @@ int pc_marriage(struct map_session_data *sd,struct map_session_data *dstsd) * -1 = fail * 0 = success *------------------------------------------*/ -int pc_divorce(struct map_session_data *sd) +static int pc_divorce(struct map_session_data *sd) { struct map_session_data *p_sd; int i; @@ -10137,11 +10972,14 @@ int pc_divorce(struct map_session_data *sd) // Both players online, lets do the divorce manually sd->status.partner_id = 0; p_sd->status.partner_id = 0; - for( i = 0; i < MAX_INVENTORY; i++ ) + for (i = 0; i < sd->status.inventorySize; i++) { - if( sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F ) + if (sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F) pc->delitem(sd, i, 1, 0, DELITEM_NORMAL, LOG_TYPE_DIVORCE); - if( p_sd->status.inventory[i].nameid == WEDDING_RING_M || p_sd->status.inventory[i].nameid == WEDDING_RING_F ) + } + for (i = 0; i < p_sd->status.inventorySize; i++) + { + if (p_sd->status.inventory[i].nameid == WEDDING_RING_M || p_sd->status.inventory[i].nameid == WEDDING_RING_F) pc->delitem(p_sd, i, 1, 0, DELITEM_NORMAL, LOG_TYPE_DIVORCE); } @@ -10154,7 +10992,8 @@ int pc_divorce(struct map_session_data *sd) /*========================================== * Get sd partner charid. (Married partner) *------------------------------------------*/ -struct map_session_data *pc_get_partner(struct map_session_data *sd) { +static struct map_session_data *pc_get_partner(struct map_session_data *sd) +{ if (sd && pc->ismarried(sd)) // charid2sd returns NULL if not found return map->charid2sd(sd->status.partner_id); @@ -10165,8 +11004,9 @@ struct map_session_data *pc_get_partner(struct map_session_data *sd) { /*========================================== * Get sd father charid. (Need to be baby) *------------------------------------------*/ -struct map_session_data *pc_get_father(struct map_session_data *sd) { - if (sd && sd->class_&JOBL_BABY && sd->status.father > 0) +static struct map_session_data *pc_get_father(struct map_session_data *sd) +{ + if (sd && (sd->job & JOBL_BABY) != 0 && sd->status.father > 0) // charid2sd returns NULL if not found return map->charid2sd(sd->status.father); @@ -10176,8 +11016,9 @@ struct map_session_data *pc_get_father(struct map_session_data *sd) { /*========================================== * Get sd mother charid. (Need to be baby) *------------------------------------------*/ -struct map_session_data *pc_get_mother(struct map_session_data *sd) { - if (sd && sd->class_&JOBL_BABY && sd->status.mother > 0) +static struct map_session_data *pc_get_mother(struct map_session_data *sd) +{ + if (sd && (sd->job & JOBL_BABY) != 0 && sd->status.mother > 0) // charid2sd returns NULL if not found return map->charid2sd(sd->status.mother); @@ -10187,7 +11028,8 @@ struct map_session_data *pc_get_mother(struct map_session_data *sd) { /*========================================== * Get sd children charid. (Need to be married) *------------------------------------------*/ -struct map_session_data *pc_get_child(struct map_session_data *sd) { +static struct map_session_data *pc_get_child(struct map_session_data *sd) +{ if (sd && pc->ismarried(sd) && sd->status.child > 0) // charid2sd returns NULL if not found return map->charid2sd(sd->status.child); @@ -10198,10 +11040,11 @@ struct map_session_data *pc_get_child(struct map_session_data *sd) { /*========================================== * Set player sd to bleed. (losing hp and/or sp each diff_tick) *------------------------------------------*/ -void pc_bleeding (struct map_session_data *sd, unsigned int diff_tick) +static void pc_bleeding(struct map_session_data *sd, unsigned int diff_tick) { int hp = 0, sp = 0; + nullpo_retv(sd); if( pc_isdead(sd) ) return; @@ -10232,9 +11075,11 @@ void pc_bleeding (struct map_session_data *sd, unsigned int diff_tick) //Character regen. Flag is used to know which types of regen can take place. //&1: HP regen //&2: SP regen -void pc_regen (struct map_session_data *sd, unsigned int diff_tick) { +static void pc_regen(struct map_session_data *sd, unsigned int diff_tick) +{ int hp = 0, sp = 0; + nullpo_retv(sd); if (sd->hp_regen.value) { sd->hp_regen.tick += diff_tick; while (sd->hp_regen.tick >= sd->hp_regen.rate) { @@ -10252,7 +11097,7 @@ void pc_regen (struct map_session_data *sd, unsigned int diff_tick) { } if (hp > 0 || sp > 0) - status->heal(&sd->bl, hp, sp, 0); + status->heal(&sd->bl, hp, sp, STATUS_HEAL_DEFAULT); return; } @@ -10260,7 +11105,8 @@ void pc_regen (struct map_session_data *sd, unsigned int diff_tick) { /*========================================== * Memo player sd savepoint. (map,x,y) *------------------------------------------*/ -int pc_setsavepoint(struct map_session_data *sd, short map_index, int x, int y) { +static int pc_setsavepoint(struct map_session_data *sd, short map_index, int x, int y) +{ nullpo_ret(sd); sd->status.save_point.map = map_index; @@ -10273,7 +11119,8 @@ int pc_setsavepoint(struct map_session_data *sd, short map_index, int x, int y) /*========================================== * Save 1 player data at autosave intervall *------------------------------------------*/ -int pc_autosave(int tid, int64 tick, int id, intptr_t data) { +static int pc_autosave(int tid, int64 tick, int id, intptr_t data) +{ int interval; struct s_mapiterator* iter; struct map_session_data* sd; @@ -10311,9 +11158,11 @@ int pc_autosave(int tid, int64 tick, int id, intptr_t data) { return 0; } -int pc_daynight_timer_sub(struct map_session_data *sd,va_list ap) { +static int pc_daynight_timer_sub(struct map_session_data *sd, va_list ap) +{ + nullpo_ret(sd); if (sd->state.night != map->night_flag && map->list[sd->bl.m].flag.nightenabled) { //Night/day state does not match. - clif->status_change(&sd->bl, SI_SKE, map->night_flag, 0, 0, 0, 0); //New night effect by dynamix [Skotlex] + clif->status_change(&sd->bl, status->get_sc_icon(SC_SKE), status->get_sc_relevant_bl_types(SC_SKE), map->night_flag, 0, 0, 0, 0); //New night effect by dynamix [Skotlex] sd->state.night = map->night_flag; return 1; } @@ -10323,7 +11172,8 @@ int pc_daynight_timer_sub(struct map_session_data *sd,va_list ap) { * timer to do the day [Yor] * data: 0 = called by timer, 1 = gmcommand/script *------------------------------------------------*/ -int map_day_timer(int tid, int64 tick, int id, intptr_t data) { +static int map_day_timer(int tid, int64 tick, int id, intptr_t data) +{ char tmp_soutput[1024]; if (data == 0 && battle_config.day_duration <= 0) // if we want a day @@ -10335,7 +11185,7 @@ int map_day_timer(int tid, int64 tick, int id, intptr_t data) { map->night_flag = 0; // 0=day, 1=night [Yor] map->foreachpc(pc->daynight_timer_sub); safestrncpy(tmp_soutput, (data == 0) ? msg_txt(502) : msg_txt(60), sizeof(tmp_soutput)); // The day has arrived! - intif->broadcast(tmp_soutput, strlen(tmp_soutput) + 1, BC_DEFAULT); + clif->broadcast(NULL, tmp_soutput, (int)strlen(tmp_soutput) + 1, BC_DEFAULT, ALL_CLIENT); return 0; } @@ -10343,7 +11193,8 @@ int map_day_timer(int tid, int64 tick, int id, intptr_t data) { * timer to do the night [Yor] * data: 0 = called by timer, 1 = gmcommand/script *------------------------------------------------*/ -int map_night_timer(int tid, int64 tick, int id, intptr_t data) { +static int map_night_timer(int tid, int64 tick, int id, intptr_t data) +{ char tmp_soutput[1024]; if (data == 0 && battle_config.night_duration <= 0) // if we want a night @@ -10355,27 +11206,35 @@ int map_night_timer(int tid, int64 tick, int id, intptr_t data) { map->night_flag = 1; // 0=day, 1=night [Yor] map->foreachpc(pc->daynight_timer_sub); safestrncpy(tmp_soutput, (data == 0) ? msg_txt(503) : msg_txt(59), sizeof(tmp_soutput)); // The night has fallen... - intif->broadcast(tmp_soutput, strlen(tmp_soutput) + 1, BC_DEFAULT); + clif->broadcast(NULL, tmp_soutput, (int)strlen(tmp_soutput) + 1, BC_DEFAULT, ALL_CLIENT); return 0; } -void pc_setstand(struct map_session_data *sd) { +static void pc_setstand(struct map_session_data *sd) +{ nullpo_retv(sd); status_change_end(&sd->bl, SC_TENSIONRELAX, INVALID_TIMER); - clif->sc_end(&sd->bl,sd->bl.id,SELF,SI_SIT); + clif->sc_end(&sd->bl, sd->bl.id, SELF, status->get_sc_icon(SC_SIT)); //Reset sitting tick. sd->ssregen.tick.hp = sd->ssregen.tick.sp = 0; - sd->state.dead_sit = sd->vd.dead_sit = 0; + if (pc_isdead(sd)) { + sd->state.dead_sit = sd->vd.dead_sit = 0; + clif->party_dead_notification(sd); + } else { + sd->state.dead_sit = sd->vd.dead_sit = 0; + } } /** * Mechanic (MADO GEAR) **/ -void pc_overheat(struct map_session_data *sd, int val) { +static void pc_overheat(struct map_session_data *sd, int val) +{ int heat = val, skill_lv, limit[] = { 10, 20, 28, 46, 66 }; + nullpo_retv(sd); if( !pc_ismadogear(sd) || sd->sc.data[SC_OVERHEAT] ) return; // already burning @@ -10397,10 +11256,11 @@ void pc_overheat(struct map_session_data *sd, int val) { /** * Check if player is autolooting given itemID. */ -bool pc_isautolooting(struct map_session_data *sd, int nameid) +static bool pc_isautolooting(struct map_session_data *sd, int nameid) { int i = 0; + nullpo_ret(sd); if (sd->state.autoloottype && sd->state.autoloottype&(1<<itemdb_type(nameid))) return true; @@ -10417,7 +11277,8 @@ bool pc_isautolooting(struct map_session_data *sd, int nameid) * @param sd Player map session data * @param command Command name with @/# and without params */ -bool pc_can_use_command(struct map_session_data *sd, const char *command) { +static bool pc_can_use_command(struct map_session_data *sd, const char *command) +{ return atcommand->can_use(sd,command); } @@ -10426,7 +11287,7 @@ bool pc_can_use_command(struct map_session_data *sd, const char *command) { * * @see TimerFunc */ -int pc_charm_timer(int tid, int64 tick, int id, intptr_t data) +static int pc_charm_timer(int tid, int64 tick, int id, intptr_t data) { struct map_session_data *sd = map->id2sd(id); int i; @@ -10467,7 +11328,7 @@ int pc_charm_timer(int tid, int64 tick, int id, intptr_t data) * @param max Maximum amount of charms to add. * @param type Charm type (@see spirit_charm_types) */ -void pc_add_charm(struct map_session_data *sd, int interval, int max, int type) +static void pc_add_charm(struct map_session_data *sd, int interval, int max, enum spirit_charm_types type) { int tid, i; @@ -10509,7 +11370,7 @@ void pc_add_charm(struct map_session_data *sd, int interval, int max, int type) * @param count Amount of charms to remove. * @param type Type of charm to remove. */ -void pc_del_charm(struct map_session_data *sd, int count, int type) +static void pc_del_charm(struct map_session_data *sd, int count, enum spirit_charm_types type) { int i; @@ -10556,7 +11417,7 @@ void pc_del_charm(struct map_session_data *sd, int count, int type) * @param type Modifier type (1=exp 2=itemdrop) * @return The percent rate modifier (100 = 100%) */ -int pc_level_penalty_mod(int diff, unsigned char race, uint32 mode, int type) +static int pc_level_penalty_mod(int diff, unsigned char race, uint32 mode, int type) { #if defined(RENEWAL_DROP) || defined(RENEWAL_EXP) int rate = 100, i; @@ -10585,61 +11446,10 @@ int pc_level_penalty_mod(int diff, unsigned char race, uint32 mode, int type) return 100; #endif } -int pc_split_str(char *str,char **val,int num) -{ - int i; - - for (i=0; i<num && str; i++){ - val[i] = str; - str = strchr(str,','); - if (str && i<num-1) //Do not remove a trailing comma. - *str++=0; - } - return i; -} -int pc_split_atoi(char* str, int* val, char sep, int max) +static bool pc_read_skill_job_skip(short skill_id, int job_id) { - int i,j; - for (i=0; i<max; i++) { - if (!str) break; - val[i] = atoi(str); - str = strchr(str,sep); - if (str) - *str++=0; - } - //Zero up the remaining. - for(j=i; j < max; j++) - val[j] = 0; - return i; -} - -int pc_split_atoui(char* str, unsigned int* val, char sep, int max) -{ - static int warning=0; - int i,j; - for (i=0; i<max; i++) { - double f; - if (!str) break; - f = atof(str); - if (f < 0) - val[i] = 0; - else if (f > UINT_MAX) { - val[i] = UINT_MAX; - if (!warning) { - warning = 1; - ShowWarning("pc_readdb (exp.txt): Required exp per level is capped to %u\n", UINT_MAX); - } - } else - val[i] = (unsigned int)f; - str = strchr(str,sep); - if (str) - *str++=0; - } - //Zero up the remaining. - for(j=i; j < max; j++) - val[j] = 0; - return i; + return skill_id == NV_TRICKDEAD && ((pc->jobid2mapid(job_id) & (MAPID_BASEMASK | JOBL_2)) != MAPID_NOVICE); // skip trickdead for non-novices } /** @@ -10655,7 +11465,7 @@ int pc_split_atoui(char* str, unsigned int* val, char sep, int max) * * @author [Ind/Hercules] */ -void pc_read_skill_tree(void) +static void pc_read_skill_tree(void) { struct config_t skill_tree_conf; struct config_setting_t *skt = NULL; @@ -10722,8 +11532,8 @@ void pc_read_skill_tree(void) ShowWarning("pc_read_skill_tree: '%s' can't inherit '%s', skill tree is full!\n", job_name, ijob_name); break; } - if (src->id == NV_TRICKDEAD && ((pc->jobid2mapid(job_id)&(MAPID_BASEMASK | JOBL_2)) != MAPID_NOVICE)) - continue; // skip trickdead for non-novices + if (pc->read_skill_job_skip(src->id, job_id)) + continue; dst = &pc->skill_tree[job_idx][cur]; dst->inherited = 1; if (dst->id == 0) { @@ -10852,7 +11662,7 @@ void pc_read_skill_tree(void) /** * Clears the skill tree and frees any allocated memory. */ -void pc_clear_skill_tree(void) +static void pc_clear_skill_tree(void) { int i; for (i = 0; i < CLASS_COUNT; i++) { @@ -10866,10 +11676,12 @@ void pc_clear_skill_tree(void) memset(pc->skill_tree, 0, sizeof(pc->skill_tree)); } -bool pc_readdb_levelpenalty(char* fields[], int columns, int current) { +static bool pc_readdb_levelpenalty(char *fields[], int columns, int current) +{ #if defined(RENEWAL_DROP) || defined(RENEWAL_EXP) int type, race, diff; + nullpo_retr(false, fields); type = atoi(fields[0]); race = atoi(fields[1]); diff = atoi(fields[2]); @@ -10894,93 +11706,131 @@ bool pc_readdb_levelpenalty(char* fields[], int columns, int current) { return true; } -/*========================================== - * pc DB reading. - * exp.txt - required experience values - * skill_tree.txt - skill tree for every class - * attr_fix.txt - elemental adjustment table - *------------------------------------------*/ -int pc_readdb(void) { - int i,j,k; - unsigned int count = 0; - FILE *fp; - char line[24000],*p; +static bool pc_read_exp_db_sub_class(struct config_setting_t *t, bool base) +{ + struct class_exp_group entry = { { 0 } }; + struct config_setting_t *exp_t = NULL; + int maxlv = 0; - //reset - memset(pc->exp_table,0,sizeof(pc->exp_table)); - memset(pc->max_level,0,sizeof(pc->max_level)); + nullpo_retr(false, t); - sprintf(line, "%s/"DBPATH"exp.txt", map->db_path); + safestrncpy(entry.name, config_setting_name(t), SCRIPT_VARNAME_LENGTH); - fp=fopen(line, "r"); - if(fp==NULL){ - ShowError("can't read %s\n", line); - return 1; + if (libconfig->setting_lookup_int(t, "MaxLevel", &maxlv) == 0 + || (maxlv <= 0 || maxlv > MAX_LEVEL)) { + ShowError("pc_read_exp_db_sub_class: Invalid max %s level '%d' set for entry '%s'. Defaulting to %d...", base ? "base" : "job", maxlv, entry.name, MAX_LEVEL); + maxlv = MAX_LEVEL; } - while(fgets(line, sizeof(line), fp)) { - int jobs[CLASS_COUNT], job_count, job, job_id; - int type; - unsigned int ui,maxlv; - char *split[4]; - if(line[0]=='/' && line[1]=='/') - continue; - if (pc_split_str(line,split,4) < 4) - continue; - job_count = pc_split_atoi(split[1],jobs,':',CLASS_COUNT); - if (job_count < 1) - continue; - job_id = jobs[0]; - if (!pc->db_checkid(job_id)) { - ShowError("pc_readdb: Invalid job ID %d.\n", job_id); - continue; - } - type = atoi(split[2]); - if (type < 0 || type > 1) { - ShowError("pc_readdb: Invalid type %d (must be 0 for base levels, 1 for job levels).\n", type); - continue; - } - maxlv = atoi(split[0]); - if (maxlv > MAX_LEVEL) { - ShowWarning("pc_readdb: Specified max level %u for job %d is beyond server's limit (%d).\n ", maxlv, job_id, MAX_LEVEL); - maxlv = MAX_LEVEL; + entry.max_level = maxlv; + + if ((exp_t = libconfig->setting_lookup(t, "Exp")) != NULL && config_setting_is_array(exp_t)) { + int j = 0; + + VECTOR_ENSURE(entry.exp, maxlv - 2, 10); + + if (libconfig->setting_length(exp_t) > maxlv - 1) { + ShowWarning("pc_read_exp_db_sub_class: Exp table length (%d) for %s exp group '%s' exceeds specified max level %d. Skipping remaining entries...\n", libconfig->setting_length(exp_t), base ? "base" : "job", entry.name, maxlv); } - count++; - job = jobs[0] = pc->class2idx(job_id); - //We send one less and then one more because the last entry in the exp array should hold 0. - pc->max_level[job][type] = pc_split_atoui(split[3], pc->exp_table[job][type],',',maxlv-1)+1; - //Reverse check in case the array has a bunch of trailing zeros... [Skotlex] - //The reasoning behind the -2 is this... if the max level is 5, then the array - //should look like this: - //0: x, 1: x, 2: x: 3: x 4: 0 <- last valid value is at 3. - while ((ui = pc->max_level[job][type]) >= 2 && pc->exp_table[job][type][ui-2] <= 0) - pc->max_level[job][type]--; - if (pc->max_level[job][type] < maxlv) { - ShowWarning("pc_readdb: Specified max %u for job %d, but that job's exp table only goes up to level %u.\n", maxlv, job_id, pc->max_level[job][type]); + + while (j < libconfig->setting_length(exp_t) && j <= maxlv - 2) + VECTOR_PUSH(entry.exp, libconfig->setting_get_int64_elem(exp_t, j++)); + + if (j - 1 < maxlv - 2) { + ShowError("pc_read_exp_db_sub_class: Specified max %d for group '%s', but that group's %s exp table only goes up to level %d.\n", maxlv, entry.name, base ? "base" : "job", VECTOR_LENGTH(entry.exp)); ShowInfo("Filling the missing values with the last exp entry.\n"); - //Fill the requested values with the last entry. - ui = (pc->max_level[job][type] <= 2? 0: pc->max_level[job][type]-2); - for (; ui+2 < maxlv; ui++) - pc->exp_table[job][type][ui] = pc->exp_table[job][type][ui-1]; - pc->max_level[job][type] = maxlv; - } - //ShowDebug("%s - Class %d: %d\n", type?"Job":"Base", job_id, pc->max_level[job][type]); - for (i = 1; i < job_count; i++) { - job_id = jobs[i]; - if (!pc->db_checkid(job_id)) { - ShowError("pc_readdb: Invalid job ID %d.\n", job_id); - continue; - } - job = pc->class2idx(job_id); - memcpy(pc->exp_table[job][type], pc->exp_table[jobs[0]][type], sizeof(pc->exp_table[0][0])); - pc->max_level[job][type] = maxlv; - //ShowDebug("%s - Class %d: %u\n", type?"Job":"Base", job_id, pc->max_level[job][type]); + while (j++ <= maxlv - 2) + VECTOR_PUSH(entry.exp, VECTOR_LAST(entry.exp)); } + } else { + ShowError("pc_read_exp_db_sub_class: Invalid or non-existent 'Exp' field set for %s level entry '%s'. Skipping...\n", entry.name, base ? "base" : "job"); + return false; } - fclose(fp); - pc->validate_levels(); - ShowStatus("Done reading '"CL_WHITE"%u"CL_RESET"' entries in '"CL_WHITE"%s/"DBPATH"%s"CL_RESET"'.\n",count,map->db_path,"exp.txt"); - count = 0; + + VECTOR_ENSURE(pc->class_exp_groups[base ? CLASS_EXP_TABLE_BASE : CLASS_EXP_TABLE_JOB], 1, 1); + VECTOR_PUSH(pc->class_exp_groups[base ? CLASS_EXP_TABLE_BASE : CLASS_EXP_TABLE_JOB], entry); + return true; +} + +/** + * Description: Helper function to read a root configuration in the exp_group_db.conf file. + * @param[in] t pointer to the root config setting + * @param[in] base boolean switch determining whether to read either base or job exp. + * @return total number of valid entries read from the setting. + */ +static int pc_read_exp_db_sub(struct config_setting_t *t, bool base) +{ + int i = 0, entry_count = 0; + struct config_setting_t *tt = NULL; + + nullpo_ret(t); + + while ((tt = libconfig->setting_get_elem(t, i++)) != NULL) { + pc->read_exp_db_sub_class(tt, base); + entry_count++; + } + + return entry_count; +} + +/** + * Description: Initiates reading of the exp_group_db.conf. + * @return true success, false on failure. + */ +static bool pc_read_exp_db(void) +{ + struct config_t exp_db_conf; + struct config_setting_t *edb = NULL; + int entry_count = 0; + char config_filename[256]; + + libconfig->format_db_path(DBPATH"exp_group_db.conf", config_filename, sizeof(config_filename)); + + if (!libconfig->load_file(&exp_db_conf, config_filename)) + return false; + + if ((edb = libconfig->setting_lookup(exp_db_conf.root, "base_exp_group_db")) != NULL) { + entry_count += pc->read_exp_db_sub(edb, true); + } else { + ShowError("pc_read_exp_db: Error reading base exp group db in '%s'.\n", config_filename); + libconfig->destroy(&exp_db_conf); + return false; + } + + if ((edb = libconfig->setting_lookup(exp_db_conf.root, "job_exp_group_db")) != NULL) { + entry_count += pc->read_exp_db_sub(edb, false); + } else { + ShowError("pc_read_exp_db: Error reading job exp group db in '%s'.\n", config_filename); + libconfig->destroy(&exp_db_conf); + return false; + } + + libconfig->destroy(&exp_db_conf); + + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", entry_count, config_filename); + + return true; +} + +/*========================================== + * PC DB reading. + * exp_group_db.conf - required experience values + * skill_tree.txt - skill tree for every class + * attr_fix.txt - elemental adjustment table + *------------------------------------------*/ +static int pc_readdb(void) +{ + int i,j,k; + unsigned int count = 0; + FILE *fp; + char line[24000],*p; + + /** + * Read and load into memory, the exp_group_db.conf file. + */ + pc->clear_exp_groups(); + pc->read_exp_db(); + // Reset and read skilltree pc->clear_skill_tree(); pc->read_skill_tree(); @@ -11014,18 +11864,18 @@ int pc_readdb(void) { ShowError("can't read %s\n", line); return 1; } - while(fgets(line, sizeof(line), fp)) - { + while (fgets(line, sizeof(line), fp)) { char *split[10]; int lv,n; - if(line[0]=='/' && line[1]=='/') + if (line[0]=='/' && line[1]=='/') continue; - for(j=0,p=line;j<3 && p;j++){ - split[j]=p; - p=strchr(p,','); - if(p) *p++=0; + for (j = 0, p = line; j < 3 && p != NULL; j++) { + split[j] = p; + p = strchr(p,','); + if (p != NULL) + *p++ = 0; } - if( j < 2 ) + if (j < 2) continue; lv=atoi(split[0]); @@ -11037,8 +11887,8 @@ int pc_readdb(void) { if(line[0]=='/' && line[1]=='/') continue; - for ( j = ELE_NEUTRAL, p = line; j<n && j<ELE_MAX && p; j++ ) { - while(*p==32 && *p>0) + for (j = ELE_NEUTRAL, p = line; j < n && j < ELE_MAX && p != NULL; j++) { + while (*p == ' ') p++; battle->attr_fix_table[lv-1][i][j]=atoi(p); #ifndef RENEWAL @@ -11046,7 +11896,8 @@ int pc_readdb(void) { battle->attr_fix_table[lv-1][i][j] = 0; #endif p=strchr(p,','); - if(p) *p++=0; + if (p != NULL) + *p++ = 0; } i++; @@ -11093,25 +11944,41 @@ int pc_readdb(void) { return 0; } -void pc_validate_levels(void) { +static bool pc_job_is_dummy(int job) +{ + if (job == JOB_KNIGHT2 || job == JOB_CRUSADER2 + || job == JOB_WEDDING || job == JOB_XMAS || job == JOB_SUMMER + || job == JOB_LORD_KNIGHT2 || job == JOB_PALADIN2 + || job == JOB_BABY_KNIGHT2 || job == JOB_BABY_CRUSADER2 + || job == JOB_STAR_GLADIATOR2 + || (job >= JOB_RUNE_KNIGHT2 && job <= JOB_MECHANIC_T2) + || (job >= JOB_BABY_RUNE2 && job <= JOB_BABY_MECHANIC2)) + return true; + return false; +} + +static void pc_validate_levels(void) +{ int i; int j; for (i = 0; i < JOB_MAX; i++) { if (!pc->db_checkid(i)) continue; - if (i == JOB_WEDDING || i == JOB_XMAS || i == JOB_SUMMER) + if (pc->job_is_dummy(i)) continue; //Classes that do not need exp tables. j = pc->class2idx(i); - if (!pc->max_level[j][0]) - ShowWarning("Class %s (%d) does not has a base exp table.\n", pc->job_name(i), i); - if (!pc->max_level[j][1]) - ShowWarning("Class %s (%d) does not has a job exp table.\n", pc->job_name(i), i); + if (pc->dbs->class_exp_table[j][CLASS_EXP_TABLE_BASE] == NULL) + ShowWarning("Class %s (%d - %d) does not have a base exp table.\n", pc->job_name(i), i, j); + if (pc->dbs->class_exp_table[j][CLASS_EXP_TABLE_JOB] == NULL) + ShowWarning("Class %s (%d - %d) does not have a job exp table.\n", pc->job_name(i), i, j); } } -void pc_itemcd_do(struct map_session_data *sd, bool load) { +static void pc_itemcd_do(struct map_session_data *sd, bool load) +{ int i,cursor = 0; struct item_cd* cd = NULL; + nullpo_retv(sd); if( load ) { if( !(cd = idb_get(pc->itemcd_db, sd->status.char_id)) ) { // no skill cooldown is associated with this character @@ -11142,8 +12009,12 @@ void pc_itemcd_do(struct map_session_data *sd, bool load) { return; } -void pc_bank_deposit(struct map_session_data *sd, int money) { - unsigned int limit_check = money+sd->status.bank_vault; +static void pc_bank_deposit(struct map_session_data *sd, int money) +{ + unsigned int limit_check; + + nullpo_retv(sd); + limit_check = money + sd->status.bank_vault; if( money <= 0 || limit_check > MAX_BANK_ZENY ) { clif->bank_deposit(sd,BDA_OVERFLOW); @@ -11162,9 +12033,12 @@ void pc_bank_deposit(struct map_session_data *sd, int money) { clif->bank_deposit(sd,BDA_SUCCESS); } } -void pc_bank_withdraw(struct map_session_data *sd, int money) { - unsigned int limit_check = money+sd->status.zeny; +static void pc_bank_withdraw(struct map_session_data *sd, int money) +{ + unsigned int limit_check; + nullpo_retv(sd); + limit_check = money + sd->status.zeny; if (money <= 0) { clif->bank_withdraw(sd,BWA_UNKNOWN_ERROR); return; @@ -11187,7 +12061,9 @@ void pc_bank_withdraw(struct map_session_data *sd, int money) { } } /* status change data arrived from char-server */ -void pc_scdata_received(struct map_session_data *sd) { +static void pc_scdata_received(struct map_session_data *sd) +{ + nullpo_retv(sd); pc->inventory_rentals(sd); clif->show_modifiers(sd); @@ -11195,7 +12071,7 @@ void pc_scdata_received(struct map_session_data *sd) { time_t exp_time = sd->expiration_time; char tmpstr[1024]; strftime(tmpstr, sizeof(tmpstr) - 1, msg_sd(sd,501), localtime(&exp_time)); // "Your account time limit is: %d-%m-%Y %H:%M:%S." - clif->wis_message(sd->fd, map->wisp_server_name, tmpstr, strlen(tmpstr)+1); + clif->wis_message(sd->fd, map->wisp_server_name, tmpstr, (int)strlen(tmpstr)); pc->expire_check(sd); } @@ -11206,7 +12082,8 @@ void pc_scdata_received(struct map_session_data *sd) { pc->autotrade_start(sd); } } -int pc_expiration_timer(int tid, int64 tick, int id, intptr_t data) { +static int pc_expiration_timer(int tid, int64 tick, int id, intptr_t data) +{ struct map_session_data *sd = map->id2sd(id); if( !sd ) return 0; @@ -11222,7 +12099,8 @@ int pc_expiration_timer(int tid, int64 tick, int id, intptr_t data) { } /* This timer exists only when a character with an expire timer > 24h is online */ /* It loops through online players once an hour to check whether a new < 24h is available */ -int pc_global_expiration_timer(int tid, int64 tick, int id, intptr_t data) { +static int pc_global_expiration_timer(int tid, int64 tick, int id, intptr_t data) +{ struct s_mapiterator* iter; struct map_session_data* sd; @@ -11235,7 +12113,9 @@ int pc_global_expiration_timer(int tid, int64 tick, int id, intptr_t data) { return 0; } -void pc_expire_check(struct map_session_data *sd) { +static void pc_expire_check(struct map_session_data *sd) +{ + nullpo_retv(sd); /* ongoing timer */ if( sd->expiration_tid != INVALID_TIMER ) return; @@ -11255,7 +12135,7 @@ void pc_expire_check(struct map_session_data *sd) { /** * Loads autotraders ***/ -void pc_autotrade_load(void) +static void pc_autotrade_load(void) { char *data; @@ -11288,11 +12168,13 @@ void pc_autotrade_load(void) /** * Loads vending data and sets it up, is triggered when char server data that pc_autotrade_load requested arrives **/ -void pc_autotrade_start(struct map_session_data *sd) { +static void pc_autotrade_start(struct map_session_data *sd) +{ unsigned int count = 0; int i; char *data; + nullpo_retv(sd); if (SQL_ERROR == SQL->Query(map->mysql_handle, "SELECT `itemkey`,`amount`,`price` FROM `%s` WHERE `char_id` = '%d'",map->autotrade_data_db,sd->status.char_id)) Sql_ShowDebug(map->mysql_handle); @@ -11334,9 +12216,11 @@ void pc_autotrade_start(struct map_session_data *sd) { /** * Perform a autotrade action **/ -void pc_autotrade_update(struct map_session_data *sd, enum e_pc_autotrade_update_action action) { +static void pc_autotrade_update(struct map_session_data *sd, enum e_pc_autotrade_update_action action) +{ int i; + nullpo_retv(sd); /* either way, this goes down */ if( action != PAUC_START ) { if (SQL_ERROR == SQL->Query(map->mysql_handle, "DELETE FROM `%s` WHERE `char_id` = '%d'",map->autotrade_data_db,sd->status.char_id)) @@ -11362,7 +12246,7 @@ void pc_autotrade_update(struct map_session_data *sd, enum e_pc_autotrade_update )) Sql_ShowDebug(map->mysql_handle); } - /* yes we want it to fall */ + FALLTHROUGH case PAUC_REFRESH: for( i = 0; i < sd->vend_num; i++ ) { if( sd->vending[i].amount == 0 ) @@ -11383,13 +12267,15 @@ void pc_autotrade_update(struct map_session_data *sd, enum e_pc_autotrade_update /** * Handles characters upon @autotrade usage **/ -void pc_autotrade_prepare(struct map_session_data *sd) { +static void pc_autotrade_prepare(struct map_session_data *sd) +{ struct autotrade_vending *data; int i, cursor = 0; int account_id, char_id; char title[MESSAGE_SIZE]; unsigned char sex; + nullpo_retv(sd); CREATE(data, struct autotrade_vending, 1); memcpy(data->vending, sd->vending, sizeof(sd->vending)); @@ -11432,10 +12318,12 @@ void pc_autotrade_prepare(struct map_session_data *sd) { /** * Prepares autotrade data from pc->at_db from a player that has already returned from char server **/ -void pc_autotrade_populate(struct map_session_data *sd) { +static void pc_autotrade_populate(struct map_session_data *sd) +{ struct autotrade_vending *data; int i, j, k, cursor = 0; + nullpo_retv(sd); if( !(data = idb_get(pc->at_db,sd->status.char_id)) ) return; @@ -11475,14 +12363,15 @@ void pc_autotrade_populate(struct map_session_data *sd) { /** * @see DBApply */ -int pc_autotrade_final(union DBKey key, struct DBData *data, va_list ap) +static int pc_autotrade_final(union DBKey key, struct DBData *data, va_list ap) { struct autotrade_vending* at_v = DB->data2ptr(data); + nullpo_ret(at_v); HPM->data_store_destroy(&at_v->hdata); return 0; } -void pc_update_idle_time(struct map_session_data* sd, enum e_battle_config_idletime type) +static void pc_update_idle_time(struct map_session_data *sd, enum e_battle_config_idletime type) { nullpo_retv(sd); if (battle_config.idletime_criteria&type) @@ -11490,16 +12379,17 @@ void pc_update_idle_time(struct map_session_data* sd, enum e_battle_config_idlet } //Checks if the given class value corresponds to a player class. [Skotlex] -//JOB_NOVICE isn't checked for class_ is supposed to be unsigned -bool pc_db_checkid(unsigned int class_) +//JOB_NOVICE isn't checked for class is supposed to be unsigned +static bool pc_db_checkid(int class) { - return class_ < JOB_MAX_BASIC - || (class_ >= JOB_NOVICE_HIGH && class_ <= JOB_DARK_COLLECTOR ) - || (class_ >= JOB_RUNE_KNIGHT && class_ <= JOB_MECHANIC_T2 ) - || (class_ >= JOB_BABY_RUNE && class_ <= JOB_BABY_MECHANIC2 ) - || (class_ >= JOB_SUPER_NOVICE_E && class_ <= JOB_SUPER_BABY_E ) - || (class_ >= JOB_KAGEROU && class_ <= JOB_OBORO ) - || (class_ >= JOB_REBELLION && class_ < JOB_MAX ); + return class < JOB_MAX_BASIC + || (class >= JOB_NOVICE_HIGH && class <= JOB_DARK_COLLECTOR ) + || (class >= JOB_RUNE_KNIGHT && class <= JOB_MECHANIC_T2 ) + || (class >= JOB_BABY_RUNE && class <= JOB_BABY_MECHANIC2 ) + || (class >= JOB_SUPER_NOVICE_E && class <= JOB_SUPER_BABY_E ) + || (class >= JOB_KAGEROU && class <= JOB_OBORO ) + || (class == JOB_REBELLION) + || (class >= JOB_SUMMONER && class < JOB_MAX ); } /** @@ -11507,16 +12397,266 @@ bool pc_db_checkid(unsigned int class_) * @param sd map_session_data of Player * @return index of magnifer, INDEX_NOT_FOUND if it is not found */ -int pc_have_magnifier(struct map_session_data *sd) +static int pc_have_magnifier(struct map_session_data *sd) { int n; - n = pc->search_inventory(sd, ITEMID_MAGNIFIER); + n = pc->search_inventory(sd, ITEMID_SPECTACLES); if (n == INDEX_NOT_FOUND) - n = pc->search_inventory(sd, ITEMID_NOVICE_MAGNIFIER); + n = pc->search_inventory(sd, ITEMID_N_MAGNIFIER); return n; } -void do_final_pc(void) { +/** + * checks if player have any item that listed in item chain + * @param sd map_session_data of Player + * @param chain_cache_id cache id of item chain + * @return index of inventory, INDEX_NOT_FOUND if it is not found + */ +static int pc_have_item_chain(struct map_session_data *sd, enum e_chain_cache chain_cache_id) +{ + nullpo_retr(INDEX_NOT_FOUND, sd); + Assert_retr(INDEX_NOT_FOUND, chain_cache_id >= ECC_ORE && chain_cache_id < ECC_MAX); + + int chain_id = itemdb->chain_cache[chain_cache_id]; + + for (int n = 0; n < itemdb->chains[chain_id].qty; n++) { + struct item_chain_entry *entry = &itemdb->chains[chain_id].items[n]; + int index = pc->search_inventory(sd, entry->id); + if (index != INDEX_NOT_FOUND) + return index; + } + + return INDEX_NOT_FOUND; +} + +/** + * Checks if player have basic skills learned. + * @param sd Player Data + * @param level Required Level of Novice Skill + * @return bool true, if requirement is satisfied + */ +static bool pc_check_basicskill(struct map_session_data *sd, int level) +{ + if (pc->checkskill(sd, NV_BASIC) >= level || pc->checkskill(sd, SU_BASIC_SKILL)) + return true; + return false; +} + +/** + * Verifies a chat message, searching for atcommands, checking if the sender + * character can chat, and updating the idle timer. + * + * @param sd The sender character. + * @param message The message text. + * @return Whether the message is a valid chat message. + */ +static bool pc_process_chat_message(struct map_session_data *sd, const char *message) +{ + nullpo_retr(false, sd); + if (atcommand->exec(sd->fd, sd, message, true)) { + return false; + } + + if (!pc->can_talk(sd)) { + return false; + } + + if (battle_config.min_chat_delay != 0) { + if (DIFF_TICK(sd->cantalk_tick, timer->gettick()) > 0) { + return false; + } + sd->cantalk_tick = timer->gettick() + battle_config.min_chat_delay; + } + + pc->update_idle_time(sd, BCIDLE_CHAT); + + return true; +} + +/** + * Checks a chat message, scanning for the Super Novice prayer sequence. + * + * If a match is found, the angel is invoked or the counter is incremented as + * appropriate. + * + * @param sd The sender character. + * @param message The message text. + */ +static void pc_check_supernovice_call(struct map_session_data *sd, const char *message) +{ + uint64 next = pc->nextbaseexp(sd); + int percent = 0; + + nullpo_retv(sd); + nullpo_retv(message); + if ((sd->job & MAPID_UPPERMASK) != MAPID_SUPER_NOVICE) + return; + if (next == 0) + next = pc->thisbaseexp(sd); + if (next == 0) + return; + + // 0%, 10%, 20%, ... + percent = (int)( ( (float)sd->status.base_exp/(float)next )*1000. ); + if ((battle_config.snovice_call_type != 0 || percent != 0) && (percent%100) == 0) { + // 10.0%, 20.0%, ..., 90.0% + switch (sd->state.snovice_call_flag) { + case 0: + if (strstr(message, msg_txt(1479))) // "Dear angel, can you hear my voice?" + sd->state.snovice_call_flag = 1; + break; + case 1: + { + char buf[256]; + snprintf(buf, 256, msg_txt(1480), sd->status.name); + if (strstr(message, buf)) // "I am %s Super Novice~" + sd->state.snovice_call_flag = 2; + } + break; + case 2: + if (strstr(message, msg_txt(1481))) // "Help me out~ Please~ T_T" + sd->state.snovice_call_flag = 3; + break; + case 3: + sc_start(NULL, &sd->bl, status->skill2sc(MO_EXPLOSIONSPIRITS), 100, 17, skill->get_time(MO_EXPLOSIONSPIRITS, 5)); //Lv17-> +50 critical (noted by Poki) [Skotlex] + clif->skill_nodamage(&sd->bl, &sd->bl, MO_EXPLOSIONSPIRITS, 5, 1); // prayer always shows successful Lv5 cast and disregards noskill restrictions + sd->state.snovice_call_flag = 0; + break; + } + } +} + +/** + * Sends a message t all online GMs having the specified permission. + * + * @param sender_name Sender character name. + * @param permission The required permission to receive this message. + * @param message The message body. + * + * @return The amount of characters the message was delivered to. + */ +// The transmission of GM only Wisp/Page from server to inter-server +static int pc_wis_message_to_gm(const char *sender_name, int permission, const char *message) +{ + nullpo_ret(sender_name); + nullpo_ret(message); + int mes_len = (int)strlen(message) + 1; // + null + int count = 0; + + // information is sent to all online GM + map->foreachpc(pc->wis_message_to_gm_sub, permission, sender_name, message, mes_len, &count); + + return count; +} + +/** + * Helper function for pc_wis_message_to_gm(). + */ +static int pc_wis_message_to_gm_sub(struct map_session_data *sd, va_list va) +{ + nullpo_ret(sd); + + int permission = va_arg(va, int); + if (!pc_has_permission(sd, permission)) + return 0; + + const char *sender_name = va_arg(va, const char *); + const char *message = va_arg(va, const char *); + int len = va_arg(va, int); + int *count = va_arg(va, int *); + + nullpo_ret(sender_name); + nullpo_ret(message); + nullpo_ret(count); + + clif->wis_message(sd->fd, sender_name, message, len); + ++*count; + return 1; +} + +static void pc_update_job_and_level(struct map_session_data *sd) +{ + nullpo_retv(sd); + + if (sd->status.party_id) { + struct party_data *p; + int i; + + if ((p = party->search(sd->status.party_id)) != NULL) { + ARR_FIND(0, MAX_PARTY, i, p->party.member[i].char_id == sd->status.char_id); + if (i < MAX_PARTY) { + p->party.member[i].lv = sd->status.base_level; + clif->party_job_and_level(sd); + } + } + } +} + +static void pc_clear_exp_groups(void) +{ + int i, k, size; + for (k = 0; k < 2; k++) { + size = VECTOR_LENGTH(pc->class_exp_groups[k]); + + for (i = 0; i < size; i++) + VECTOR_CLEAR(VECTOR_INDEX(pc->class_exp_groups[k], i).exp); + VECTOR_CLEAR(pc->class_exp_groups[k]); + } +} + +static void pc_init_exp_groups(void) +{ + int i; + for (i = 0; i < 2; i++) { + VECTOR_INIT(pc->class_exp_groups[i]); + } +} + +static bool pc_has_second_costume(struct map_session_data *sd) +{ + nullpo_retr(false, sd); + +// FIXME: JOB_SUPER_NOVICE_E(4190) is not supposed to be 3rd Job. (Issue#2383) + if ((sd->job & JOBL_THIRD) != 0 && (sd->job & MAPID_BASEMASK) != MAPID_NOVICE) + return true; + return false; +} + +static bool pc_expandInventory(struct map_session_data *sd, int adjustSize) +{ + nullpo_retr(false, sd); + const int invSize = sd->status.inventorySize; + if (adjustSize > MAX_INVENTORY || invSize + adjustSize <= FIXED_INVENTORY_SIZE || invSize + adjustSize > MAX_INVENTORY) { + clif->inventoryExpandResult(sd, EXPAND_INVENTORY_RESULT_MAX_SIZE); + return false; + } + if (pc_isdead(sd) || sd->state.vending || sd->state.prevend || sd->state.buyingstore || sd->chat_id != 0 || sd->state.trading || sd->state.storage_flag || sd->state.prevend) { + clif->inventoryExpandResult(sd, EXPAND_INVENTORY_RESULT_OTHER_WORK); + return false; + } + sd->status.inventorySize += adjustSize; + clif->inventoryExpansionInfo(sd); + return true; +} + +static bool pc_auto_exp_insurance(struct map_session_data *sd) +{ + nullpo_retr(false, sd); + + int item_position = pc->have_item_chain(sd, ECC_NEO_INSURANCE); + if (item_position == INDEX_NOT_FOUND) + return false; + + pc->delitem(sd, item_position, 1, 0, DELITEM_SKILLUSE, LOG_TYPE_CONSUME); +#if PACKETVER >= 20100914 + clif->msgtable(sd, MSG_NOTIFY_NEO_INSURANCE_ITEM_USE); +#endif + return true; +} + +static void do_final_pc(void) +{ + db_destroy(pc->itemcd_db); pc->at_db->destroy(pc->at_db,pc->autotrade_final); @@ -11524,6 +12664,8 @@ void do_final_pc(void) { pc->clear_skill_tree(); + pc->clear_exp_groups(); + ers_destroy(pc->sc_display_ers); ers_destroy(pc->num_reg_ers); ers_destroy(pc->str_reg_ers); @@ -11531,13 +12673,15 @@ void do_final_pc(void) { return; } -void do_init_pc(bool minimal) { +static void do_init_pc(bool minimal) +{ if (minimal) return; pc->itemcd_db = idb_alloc(DB_OPT_RELEASE_DATA); pc->at_db = idb_alloc(DB_OPT_RELEASE_DATA); + pc->init_exp_groups(); pc->readdb(); timer->add_func_list(pc->invincible_timer, "pc_invincible_timer"); @@ -11578,12 +12722,14 @@ void do_init_pc(bool minimal) { ers_chunk_size(pc->num_reg_ers, 300); ers_chunk_size(pc->str_reg_ers, 50); } + /*===================================== -* Default Functions : pc.h -* Generated by HerculesInterfaceMaker -* created by Susu -*-------------------------------------*/ -void pc_defaults(void) { + * Default Functions : pc.h + * Generated by HerculesInterfaceMaker + * created by Susu + *-------------------------------------*/ +void pc_defaults(void) +{ const struct sg_data sg_info[MAX_PC_FEELHATE] = { { SG_SUN_ANGER, SG_SUN_BLESS, SG_SUN_COMFORT, "PC_FEEL_SUN", "PC_HATE_MOB_SUN", is_day_of_sun }, { SG_MOON_ANGER, SG_MOON_BLESS, SG_MOON_COMFORT, "PC_FEEL_MOON", "PC_HATE_MOB_MOON", is_day_of_moon }, @@ -11592,6 +12738,7 @@ void pc_defaults(void) { unsigned int equip_pos[EQI_MAX]={EQP_ACC_L,EQP_ACC_R,EQP_SHOES,EQP_GARMENT,EQP_HEAD_LOW,EQP_HEAD_MID,EQP_HEAD_TOP,EQP_ARMOR,EQP_HAND_L,EQP_HAND_R,EQP_COSTUME_HEAD_TOP,EQP_COSTUME_HEAD_MID,EQP_COSTUME_HEAD_LOW,EQP_COSTUME_GARMENT,EQP_AMMO, EQP_SHADOW_ARMOR, EQP_SHADOW_WEAPON, EQP_SHADOW_SHIELD, EQP_SHADOW_SHOES, EQP_SHADOW_ACC_R, EQP_SHADOW_ACC_L }; pc = &pc_s; + pc->dbs = &exptables; /* vars */ pc->at_db = NULL; @@ -11637,6 +12784,7 @@ void pc_defaults(void) { pc->isequip = pc_isequip; pc->equippoint = pc_equippoint; + pc->item_equippoint = pc_item_equippoint; pc->setinventorydata = pc_setinventorydata; pc->checkskill = pc_checkskill; @@ -11645,6 +12793,8 @@ void pc_defaults(void) { pc->checkequip = pc_checkequip; pc->calc_skilltree = pc_calc_skilltree; + pc->calc_skilltree_bonus = pc_calc_skilltree_bonus; + pc->calc_skilltree_clear = pc_calc_skilltree_clear; pc->calc_skilltree_normalize_job = pc_calc_skilltree_normalize_job; pc->clean_skilltree = pc_clean_skilltree; @@ -11707,6 +12857,7 @@ void pc_defaults(void) { pc->maxbaselv = pc_maxbaselv; pc->maxjoblv = pc_maxjoblv; pc->checkbaselevelup = pc_checkbaselevelup; + pc->checkbaselevelup_sc = pc_checkbaselevelup_sc; pc->checkjoblevelup = pc_checkjoblevelup; pc->gainexp = pc_gainexp; pc->nextbaseexp = pc_nextbaseexp; @@ -11723,6 +12874,7 @@ void pc_defaults(void) { pc->resetlvl = pc_resetlvl; pc->resetstate = pc_resetstate; pc->resetskill = pc_resetskill; + pc->resetskill_job = pc_resetskill_job; pc->resetfeel = pc_resetfeel; pc->resethate = pc_resethate; pc->equipitem = pc_equipitem; @@ -11731,6 +12883,7 @@ void pc_defaults(void) { pc->unequipitem_pos = pc_unequipitem_pos; pc->checkitem = pc_checkitem; pc->useitem = pc_useitem; + pc->autocast_clear = pc_autocast_clear; pc->skillatk_bonus = pc_skillatk_bonus; pc->skillheal_bonus = pc_skillheal_bonus; @@ -11743,6 +12896,8 @@ void pc_defaults(void) { pc->itemheal = pc_itemheal; pc->percentheal = pc_percentheal; pc->jobchange = pc_jobchange; + pc->hide = pc_hide; + pc->unhide = pc_unhide; pc->setoption = pc_setoption; pc->setcart = pc_setcart; pc->setfalcon = pc_setfalcon; @@ -11797,13 +12952,19 @@ void pc_defaults(void) { pc->delinvincibletimer = pc_delinvincibletimer; pc->addspiritball = pc_addspiritball; + pc->addspiritball_sub = pc_addspiritball_sub; pc->delspiritball = pc_delspiritball; + pc->delspiritball_sub = pc_delspiritball_sub; pc->addfame = pc_addfame; - pc->famerank = pc_famerank; + pc->fame_rank = pc_fame_rank; + pc->famelist_type = pc_famelist_type; pc->set_hate_mob = pc_set_hate_mob; pc->getmaxspiritball = pc_getmaxspiritball; pc->readdb = pc_readdb; + pc->read_exp_db = pc_read_exp_db; + pc->read_exp_db_sub = pc_read_exp_db_sub; + pc->read_exp_db_sub_class = pc_read_exp_db_sub_class; pc->map_day_timer = map_day_timer; // by [yor] pc->map_night_timer = map_night_timer; // by [yor] // Rental System @@ -11850,12 +13011,17 @@ void pc_defaults(void) { pc->autosave = pc_autosave; pc->follow_timer = pc_follow_timer; pc->read_skill_tree = pc_read_skill_tree; + pc->read_skill_job_skip = pc_read_skill_job_skip; pc->clear_skill_tree = pc_clear_skill_tree; pc->isUseitem = pc_isUseitem; pc->show_steal = pc_show_steal; pc->checkcombo = pc_checkcombo; pc->calcweapontype = pc_calcweapontype; pc->removecombo = pc_removecombo; + pc->update_job_and_level = pc_update_job_and_level; + pc->clear_exp_groups = pc_clear_exp_groups; + pc->init_exp_groups = pc_init_exp_groups; + pc->job_is_dummy = pc_job_is_dummy; pc->bank_withdraw = pc_bank_withdraw; pc->bank_deposit = pc_bank_deposit; @@ -11871,6 +13037,11 @@ void pc_defaults(void) { pc->db_checkid = pc_db_checkid; pc->validate_levels = pc_validate_levels; + pc->check_supernovice_call = pc_check_supernovice_call; + pc->process_chat_message = pc_process_chat_message; + pc->wis_message_to_gm = pc_wis_message_to_gm; + pc->wis_message_to_gm_sub = pc_wis_message_to_gm_sub; + /** * Autotrade persistency [Ind/Hercules <3] **/ @@ -11883,6 +13054,14 @@ void pc_defaults(void) { pc->check_job_name = pc_check_job_name; pc->update_idle_time = pc_update_idle_time; - + pc->have_magnifier = pc_have_magnifier; + pc->have_item_chain = pc_have_item_chain; + + pc->check_basicskill = pc_check_basicskill; + + pc->isDeathPenaltyJob = pc_isDeathPenaltyJob; + pc->has_second_costume = pc_has_second_costume; + pc->expandInventory = pc_expandInventory; + pc->auto_exp_insurance = pc_auto_exp_insurance; } |