diff options
Diffstat (limited to 'src/map/skill.c')
-rw-r--r-- | src/map/skill.c | 5251 |
1 files changed, 3765 insertions, 1486 deletions
diff --git a/src/map/skill.c b/src/map/skill.c index 79f965f67..0ead96472 100644 --- a/src/map/skill.c +++ b/src/map/skill.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 @@ -26,6 +26,7 @@ #include "map/battle.h" #include "map/battleground.h" #include "map/chrif.h" +#include "map/clan.h" #include "map/clif.h" #include "map/date.h" #include "map/elemental.h" @@ -36,12 +37,14 @@ #include "map/log.h" #include "map/map.h" #include "map/mercenary.h" +#include "map/messages.h" #include "map/mob.h" #include "map/npc.h" #include "map/party.h" #include "map/path.h" #include "map/pc.h" #include "map/pet.h" +#include "map/refine.h" #include "map/script.h" #include "map/status.h" #include "map/unit.h" @@ -54,6 +57,7 @@ #include "common/strlib.h" #include "common/timer.h" #include "common/utils.h" +#include "common/conf.h" #include <math.h> #include <stdio.h> @@ -63,27 +67,43 @@ #define SKILLUNITTIMER_INTERVAL 100 -// ranges reserved for mapping skill ids to skilldb offsets -#define HM_SKILLRANGEMIN 750 -#define HM_SKILLRANGEMAX (HM_SKILLRANGEMIN + MAX_HOMUNSKILL) -#define MC_SKILLRANGEMIN (HM_SKILLRANGEMAX + 1) -#define MC_SKILLRANGEMAX (MC_SKILLRANGEMIN + MAX_MERCSKILL) -#define EL_SKILLRANGEMIN (MC_SKILLRANGEMAX + 1) -#define EL_SKILLRANGEMAX (EL_SKILLRANGEMIN + MAX_ELEMENTALSKILL) -#define GD_SKILLRANGEMIN (EL_SKILLRANGEMAX + 1) -#define GD_SKILLRANGEMAX (GD_SKILLRANGEMIN + MAX_GUILDSKILL) - -#if GD_SKILLRANGEMAX > 999 - #error GD_SKILLRANGEMAX is greater than 999 -#endif - -struct skill_interface skill_s; -struct s_skill_dbs skilldbs; +static struct skill_interface skill_s; +static struct s_skill_dbs skilldbs; struct skill_interface *skill; +static const struct { + int start; + int end; +} skill_idx_ranges[] = { + { NV_BASIC, NPC_LEX_AETERNA }, + { KN_CHARGEATK, SA_ELEMENTWIND }, + { RK_ENCHANTBLADE, AB_SILENTIUM }, + { WL_WHITEIMPRISON, SC_FEINTBOMB }, + { LG_CANNONSPEAR, SR_GENTLETOUCH_REVITALIZE }, + { WA_SWING_DANCE, WA_MOONLIT_SERENADE }, + { MI_RUSH_WINDMILL, MI_HARMONIZE }, + { WM_LESSON, WM_UNLIMITED_HUMMING_VOICE }, + { SO_FIREWALK, SO_EARTH_INSIGNIA }, + { GN_TRAINING_SWORD, GN_SLINGITEM_RANGEMELEEATK }, + { AB_SECRAMENT, LG_OVERBRAND_PLUSATK }, + { ALL_ODINS_RECALL, ALL_LIGHTGUARD }, + { RL_GLITTERING_GREED, RL_GLITTERING_GREED_ATK }, + { KO_YAMIKUMO, OB_AKAITSUKI }, + { ECL_SNOWFLIP, ALL_THANATOS_RECALL }, + { GC_DARKCROW, NC_MAGMA_ERUPTION_DOTDAMAGE }, + { SU_BASIC_SKILL, SU_SPIRITOFSEA }, + { HLIF_HEAL, MH_VOLCANIC_ASH }, + { MS_BASH, MER_INVINCIBLEOFF2 }, + { EL_CIRCLE_OF_FIRE, EL_STONE_RAIN }, + { GD_APPROVAL, GD_DEVELOPMENT }, + CUSTOM_SKILL_RANGES +}; + //Since only mob-casted splash skills can hit ice-walls -static inline int splash_target(struct block_list* bl) { +static int skill_splash_target(struct block_list *bl) +{ + nullpo_retr(BL_CHAR, bl); #ifndef RENEWAL return ( bl->type == BL_MOB ) ? BL_SKILL|BL_CHAR : BL_CHAR; #else // Some skills can now hit ground skills(traps, ice wall & etc.) @@ -92,7 +112,8 @@ static inline int splash_target(struct block_list* bl) { } /// Returns the id of the skill, or 0 if not found. -int skill_name2id(const char* name) { +static int skill_name2id(const char *name) +{ if( name == NULL ) return 0; @@ -101,131 +122,635 @@ int skill_name2id(const char* name) { /// Maps skill ids to skill db offsets. /// Returns the skill's array index, or 0 (Unknown Skill). -int skill_get_index( uint16 skill_id ) { - // avoid ranges reserved for mapping guild/homun/mercenary skills - if( (skill_id >= GD_SKILLRANGEMIN && skill_id <= GD_SKILLRANGEMAX) - || (skill_id >= HM_SKILLRANGEMIN && skill_id <= HM_SKILLRANGEMAX) - || (skill_id >= MC_SKILLRANGEMIN && skill_id <= MC_SKILLRANGEMAX) - || (skill_id >= EL_SKILLRANGEMIN && skill_id <= EL_SKILLRANGEMAX) ) +static int skill_get_index(int skill_id) +{ + int length = ARRAYLENGTH(skill_idx_ranges); + + + if (skill_id < skill_idx_ranges[0].start || skill_id > skill_idx_ranges[length - 1].end) { + ShowWarning("skill_get_index: skill id '%d' is not being handled!\n", skill_id); + Assert_report(0); return 0; + } - // map skill id to skill db index - if( skill_id >= GD_SKILLBASE ) - skill_id = GD_SKILLRANGEMIN + skill_id - GD_SKILLBASE; - else if( skill_id >= EL_SKILLBASE ) - skill_id = EL_SKILLRANGEMIN + skill_id - EL_SKILLBASE; - else if( skill_id >= MC_SKILLBASE ) - skill_id = MC_SKILLRANGEMIN + skill_id - MC_SKILLBASE; - else if( skill_id >= HM_SKILLBASE ) - skill_id = HM_SKILLRANGEMIN + skill_id - HM_SKILLBASE; - //[Ind/Hercules] GO GO GO LESS! - http://herc.ws/board/topic/512-skill-id-processing-overhaul/ - else if( skill_id > 1019 && skill_id < 8001 ) { - if( skill_id < 2058 ) // 1020 - 2000 are empty - skill_id = 1020 + skill_id - 2001; - else if( skill_id < 2549 ) // 2058 - 2200 are empty - 1020+57 - skill_id = (1077) + skill_id - 2201; - else if ( skill_id < 3036 ) // 2549 - 3000 are empty - 1020+57+348 - skill_id = (1425) + skill_id - 3001; - else if ( skill_id < 5019 ) // 3036 - 5000 are empty - 1020+57+348+35 - skill_id = (1460) + skill_id - 5001; - else - ShowWarning("skill_get_index: skill id '%d' is not being handled!\n",skill_id); + int skill_idx = 0; + bool found = false; + // Map Skill ID to Skill Indexes (in reverse order) + for (int i = 0; i < length; i++) { + // Check if SkillID belongs to this range. + if (skill_id <= skill_idx_ranges[i].end && skill_id >= skill_idx_ranges[i].start) { + skill_idx += (skill_idx_ranges[i].end - skill_id); + found = true; + break; + } + // Add the difference of current range + skill_idx += (skill_idx_ranges[i].end - skill_idx_ranges[i].start + 1); } - // validate result - if( !skill_id || skill_id >= MAX_SKILL_DB ) + if (!found) { + ShowWarning("skill_get_index: skill id '%d' (idx: %d) is not handled as it lies outside the defined ranges!\n", skill_id, skill_idx); + Assert_report(0); return 0; + } + if (skill_idx >= MAX_SKILL_DB) { + ShowWarning("skill_get_index: skill id '%d'(idx: %d) is not being handled as it exceeds MAX_SKILL_DB!\n", skill_id, skill_idx); + Assert_report(0); + return 0; + } - return skill_id; + return skill_idx; } -const char* skill_get_name( uint16 skill_id ) { +static const char *skill_get_name(int skill_id) +{ return skill->dbs->db[skill->get_index(skill_id)].name; } -const char* skill_get_desc( uint16 skill_id ) { +static const char *skill_get_desc(int skill_id) +{ return skill->dbs->db[skill->get_index(skill_id)].desc; } -// out of bounds error checking [celest] -void skill_chk(uint16* skill_id) { - *skill_id = skill->get_index(*skill_id); // checks/adjusts id -} +#define skill_get_lvl_idx(lv) (min((lv), MAX_SKILL_LEVEL) - 1) +#define skill_adjust_over_level(val, lv, max_lv) ((val) > 1 ? ((val) + ((lv) - (max_lv)) / 2) : (val)) -#define skill_get(var,id) do { skill->chk(&(id)); if(!(id)) return 0; return (var); } while(0) -#define skill_get2(var,id,lv) do { \ - skill->chk(&(id)); \ - if(!(id)) return 0; \ - if( (lv) > MAX_SKILL_LEVEL && (var) > 1 ) { \ - int lv2__ = (lv); (lv) = skill->dbs->db[(id)].max; \ - return (var) + ((lv2__-(lv))/2);\ - } \ - return (var);\ -} while(0) -#define skill_glv(lv) min((lv),MAX_SKILL_LEVEL-1) // Skill DB -int skill_get_hit( uint16 skill_id ) { skill_get (skill->dbs->db[skill_id].hit, skill_id); } -int skill_get_inf( uint16 skill_id ) { skill_get (skill->dbs->db[skill_id].inf, skill_id); } -int skill_get_ele( uint16 skill_id , uint16 skill_lv ) { Assert_ret(skill_lv > 0); skill_get (skill->dbs->db[skill_id].element[skill_glv(skill_lv-1)], skill_id); } -int skill_get_nk( uint16 skill_id ) { skill_get (skill->dbs->db[skill_id].nk, skill_id); } -int skill_get_max( uint16 skill_id ) { skill_get (skill->dbs->db[skill_id].max, skill_id); } -int skill_get_range( uint16 skill_id , uint16 skill_lv ) { Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].range[skill_glv(skill_lv-1)], skill_id, skill_lv); } -int skill_get_splash( uint16 skill_id , uint16 skill_lv ) { Assert_ret(skill_lv > 0); skill_get2 ( (skill->dbs->db[skill_id].splash[skill_glv(skill_lv-1)]>=0?skill->dbs->db[skill_id].splash[skill_glv(skill_lv-1)]:AREA_SIZE), skill_id, skill_lv); } -int skill_get_hp( uint16 skill_id ,uint16 skill_lv ) { Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].hp[skill_glv(skill_lv-1)], skill_id, skill_lv); } -int skill_get_sp( uint16 skill_id ,uint16 skill_lv ) { Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].sp[skill_glv(skill_lv-1)], skill_id, skill_lv); } -int skill_get_hp_rate(uint16 skill_id, uint16 skill_lv ) { Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].hp_rate[skill_glv(skill_lv-1)], skill_id, skill_lv); } -int skill_get_sp_rate(uint16 skill_id, uint16 skill_lv ) { Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].sp_rate[skill_glv(skill_lv-1)], skill_id, skill_lv); } -int skill_get_state(uint16 skill_id) { skill_get (skill->dbs->db[skill_id].state, skill_id); } -int skill_get_spiritball(uint16 skill_id, uint16 skill_lv) { Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].spiritball[skill_glv(skill_lv-1)], skill_id, skill_lv); } -int skill_get_itemid(uint16 skill_id, int idx) { skill_get (skill->dbs->db[skill_id].itemid[idx], skill_id); } -int skill_get_itemqty(uint16 skill_id, int idx) { skill_get (skill->dbs->db[skill_id].amount[idx], skill_id); } -int skill_get_zeny( uint16 skill_id ,uint16 skill_lv ) { Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].zeny[skill_glv(skill_lv-1)], skill_id, skill_lv); } -int skill_get_num( uint16 skill_id ,uint16 skill_lv ) { Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].num[skill_glv(skill_lv-1)], skill_id, skill_lv); } -int skill_get_cast( uint16 skill_id ,uint16 skill_lv ) { Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].cast[skill_glv(skill_lv-1)], skill_id, skill_lv); } -int skill_get_delay( uint16 skill_id ,uint16 skill_lv ) { Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].delay[skill_glv(skill_lv-1)], skill_id, skill_lv); } -int skill_get_walkdelay( uint16 skill_id ,uint16 skill_lv ) { Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].walkdelay[skill_glv(skill_lv-1)], skill_id, skill_lv); } -int skill_get_time( uint16 skill_id ,uint16 skill_lv ) { Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].upkeep_time[skill_glv(skill_lv-1)], skill_id, skill_lv); } -int skill_get_time2( uint16 skill_id ,uint16 skill_lv ) { Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].upkeep_time2[skill_glv(skill_lv-1)], skill_id, skill_lv); } -int skill_get_castdef( uint16 skill_id ) { skill_get (skill->dbs->db[skill_id].cast_def_rate, skill_id); } -int skill_get_weapontype( uint16 skill_id ) { skill_get (skill->dbs->db[skill_id].weapon, skill_id); } -int skill_get_ammotype( uint16 skill_id ) { skill_get (skill->dbs->db[skill_id].ammo, skill_id); } -int skill_get_ammo_qty( uint16 skill_id, uint16 skill_lv ) { Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].ammo_qty[skill_glv(skill_lv-1)], skill_id, skill_lv); } -int skill_get_inf2( uint16 skill_id ) { skill_get (skill->dbs->db[skill_id].inf2, skill_id); } -int skill_get_castcancel( uint16 skill_id ) { skill_get (skill->dbs->db[skill_id].castcancel, skill_id); } -int skill_get_maxcount( uint16 skill_id ,uint16 skill_lv ) { Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].maxcount[skill_glv(skill_lv-1)], skill_id, skill_lv); } -int skill_get_blewcount( uint16 skill_id ,uint16 skill_lv ) { Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].blewcount[skill_glv(skill_lv-1)], skill_id, skill_lv); } -int skill_get_mhp( uint16 skill_id ,uint16 skill_lv ) { Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].mhp[skill_glv(skill_lv-1)], skill_id, skill_lv); } -int skill_get_castnodex( uint16 skill_id ,uint16 skill_lv ) { Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].castnodex[skill_glv(skill_lv-1)], skill_id, skill_lv); } -int skill_get_delaynodex( uint16 skill_id ,uint16 skill_lv ){ Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].delaynodex[skill_glv(skill_lv-1)], skill_id, skill_lv); } -int skill_get_type( uint16 skill_id ) { skill_get (skill->dbs->db[skill_id].skill_type, skill_id); } -int skill_get_unit_id ( uint16 skill_id, int flag ){ skill_get (skill->dbs->db[skill_id].unit_id[flag], skill_id); } -int skill_get_unit_interval( uint16 skill_id ) { skill_get (skill->dbs->db[skill_id].unit_interval, skill_id); } -int skill_get_unit_range( uint16 skill_id, uint16 skill_lv ) { Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].unit_range[skill_glv(skill_lv-1)], skill_id, skill_lv); } -int skill_get_unit_target( uint16 skill_id ) { skill_get (skill->dbs->db[skill_id].unit_target&BCT_ALL, skill_id); } -int skill_get_unit_bl_target( uint16 skill_id ) { skill_get (skill->dbs->db[skill_id].unit_target&BL_ALL, skill_id); } -int skill_get_unit_flag( uint16 skill_id ) { skill_get (skill->dbs->db[skill_id].unit_flag, skill_id); } -int skill_get_unit_layout_type( uint16 skill_id ,uint16 skill_lv ){ Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].unit_layout_type[skill_glv(skill_lv-1)], skill_id, skill_lv); } -int skill_get_cooldown( uint16 skill_id, uint16 skill_lv ) { Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].cooldown[skill_glv(skill_lv-1)], skill_id, skill_lv); } -int skill_get_fixed_cast( uint16 skill_id ,uint16 skill_lv ) { + +static int skill_get_hit(int skill_id) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + return skill->dbs->db[idx].hit; +} + +static int skill_get_inf(int skill_id) +{ + int idx; + if (skill_id == 0) + return INF_NONE; + idx = skill->get_index(skill_id); + Assert_retr(INF_NONE, idx != 0); + return skill->dbs->db[idx].inf; +} + +static int skill_get_ele(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return ELE_NEUTRAL; + idx = skill->get_index(skill_id); + Assert_retr(ELE_NEUTRAL, idx != 0); + Assert_retr(ELE_NEUTRAL, skill_lv > 0); + return skill->dbs->db[idx].element[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_nk(int skill_id) +{ + int idx; + if (skill_id == 0) + return NK_NONE; + idx = skill->get_index(skill_id); + Assert_retr(NK_NONE, idx != 0); + return skill->dbs->db[idx].nk; +} + +static int skill_get_max(int skill_id) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + return skill->dbs->db[idx].max; +} + +static int skill_get_range(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].range[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].range[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_splash(int skill_id, int skill_lv) +{ + int idx, val; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + val = skill->dbs->db[idx].splash[skill_get_lvl_idx(skill_lv)]; + if (val < 0) { + val = AREA_SIZE; + } + if (skill_lv > MAX_SKILL_LEVEL) { + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return val; +} + +static int skill_get_hp(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].hp[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].hp[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_sp(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].sp[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].sp[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_hp_rate(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].hp_rate[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].hp_rate[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_sp_rate(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].sp_rate[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].sp_rate[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_state(int skill_id) +{ + int idx; + if (skill_id == 0) + return ST_NONE; + idx = skill->get_index(skill_id); + Assert_retr(ST_NONE, idx != 0); + return skill->dbs->db[idx].state; +} + +static int skill_get_spiritball(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].spiritball[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].spiritball[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_itemid(int skill_id, int item_idx) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(item_idx >= 0 && item_idx < MAX_SKILL_ITEM_REQUIRE); + return skill->dbs->db[idx].itemid[item_idx]; +} + +static int skill_get_itemqty(int skill_id, int item_idx) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(item_idx >= 0 && item_idx < MAX_SKILL_ITEM_REQUIRE); + return skill->dbs->db[idx].amount[item_idx]; +} + +static int skill_get_zeny(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].zeny[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].zeny[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_num(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].num[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].num[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_cast(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].cast[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].cast[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_delay(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].delay[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].delay[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_walkdelay(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].walkdelay[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].walkdelay[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_time(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].upkeep_time[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].upkeep_time[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_time2(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].upkeep_time2[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].upkeep_time2[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_castdef(int skill_id) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + return skill->dbs->db[idx].cast_def_rate; +} + +static int skill_get_weapontype(int skill_id) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + return skill->dbs->db[idx].weapon; +} + +static int skill_get_ammotype(int skill_id) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + return skill->dbs->db[idx].ammo; +} + +static int skill_get_ammo_qty(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].ammo_qty[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].ammo_qty[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_inf2(int skill_id) +{ + int idx; + if (skill_id == 0) + return INF2_NONE; + idx = skill->get_index(skill_id); + Assert_retr(INF2_NONE, idx != 0); + return skill->dbs->db[idx].inf2; +} + +static int skill_get_castcancel(int skill_id) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + return skill->dbs->db[idx].castcancel; +} + +static int skill_get_maxcount(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].maxcount[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].maxcount[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_blewcount(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].blewcount[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].blewcount[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_mhp(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].mhp[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].mhp[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_castnodex(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].castnodex[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].castnodex[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_delaynodex(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].delaynodex[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].delaynodex[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_type(int skill_id) +{ + int idx; + if (skill_id == 0) + return BF_NONE; + idx = skill->get_index(skill_id); + Assert_retr(BF_NONE, idx != 0); + return skill->dbs->db[idx].skill_type; +} + +static int skill_get_unit_id(int skill_id, int flag) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(flag >= 0 && flag < ARRAYLENGTH(skill->dbs->db[0].unit_id)); + return skill->dbs->db[idx].unit_id[flag]; +} + +static int skill_get_unit_interval(int skill_id) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + return skill->dbs->db[idx].unit_interval; +} + +static int skill_get_unit_range(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].unit_range[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].unit_range[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_unit_target(int skill_id) +{ + int idx; + if (skill_id == 0) + return BCT_NOONE; + idx = skill->get_index(skill_id); + Assert_retr(BCT_NOONE, idx != 0); + return skill->dbs->db[idx].unit_target & BCT_ALL; +} + +static int skill_get_unit_bl_target(int skill_id) +{ + int idx; + if (skill_id == 0) + return BL_NUL; + idx = skill->get_index(skill_id); + Assert_retr(BL_NUL, idx != 0); + return skill->dbs->db[idx].unit_target & BL_ALL; +} + +static int skill_get_unit_flag(int skill_id) +{ + int idx; + if (skill_id == 0) + return UF_NONE; + idx = skill->get_index(skill_id); + Assert_retr(UF_NONE, idx != 0); + return skill->dbs->db[idx].unit_flag; +} + +static int skill_get_unit_layout_type(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].unit_layout_type[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].unit_layout_type[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_cooldown(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].cooldown[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].cooldown[skill_get_lvl_idx(skill_lv)]; +} + +static int skill_get_fixed_cast(int skill_id, int skill_lv) +{ + int idx; + if (skill_id == 0) + return 0; + idx = skill->get_index(skill_id); + Assert_ret(idx != 0); + Assert_ret(skill_lv > 0); #ifdef RENEWAL_CAST - Assert_ret(skill_lv > 0); skill_get2 (skill->dbs->db[skill_id].fixed_cast[skill_glv(skill_lv-1)], skill_id, skill_lv); + if (skill_lv > MAX_SKILL_LEVEL) { + int val = skill->dbs->db[idx].fixed_cast[skill_get_lvl_idx(skill_lv)]; + return skill_adjust_over_level(val, skill_lv, skill->dbs->db[idx].max); + } + return skill->dbs->db[idx].fixed_cast[skill_get_lvl_idx(skill_lv)]; #else return 0; #endif } -int skill_tree_get_max(uint16 skill_id, int b_class) + +static int skill_tree_get_max(int skill_id, int class) { int i; - b_class = pc->class2idx(b_class); + int class_idx = pc->class2idx(class); - ARR_FIND( 0, MAX_SKILL_TREE, i, pc->skill_tree[b_class][i].id == 0 || pc->skill_tree[b_class][i].id == skill_id ); - if( i < MAX_SKILL_TREE && pc->skill_tree[b_class][i].id == skill_id ) - return pc->skill_tree[b_class][i].max; + ARR_FIND( 0, MAX_SKILL_TREE, i, pc->skill_tree[class_idx][i].id == 0 || pc->skill_tree[class_idx][i].id == skill_id ); + if( i < MAX_SKILL_TREE && pc->skill_tree[class_idx][i].id == skill_id ) + return pc->skill_tree[class_idx][i].max; else return skill->get_max(skill_id); } -int skill_get_casttype (uint16 skill_id) { +static int skill_get_casttype(int skill_id) +{ int inf = skill->get_inf(skill_id); if (inf&(INF_GROUND_SKILL)) return CAST_GROUND; @@ -241,8 +766,11 @@ int skill_get_casttype (uint16 skill_id) { return CAST_DAMAGE; } -int skill_get_casttype2 (uint16 index) { - int inf = skill->dbs->db[index].inf; +static int skill_get_casttype2(int index) +{ + int inf; + Assert_retr(CAST_NODAMAGE, index < MAX_SKILL_DB); + inf = skill->dbs->db[index].inf; if (inf&(INF_GROUND_SKILL)) return CAST_GROUND; if (inf&INF_SUPPORT_SKILL) @@ -258,7 +786,8 @@ int skill_get_casttype2 (uint16 index) { } //Returns actual skill range taking into account attack range and AC_OWL [Skotlex] -int skill_get_range2 (struct block_list *bl, uint16 skill_id, uint16 skill_lv) { +static int skill_get_range2(struct block_list *bl, int skill_id, int skill_lv) +{ int range; struct map_session_data *sd = BL_CAST(BL_PC, bl); if( bl->type == BL_MOB && battle_config.mob_ai&0x400 ) @@ -292,7 +821,7 @@ int skill_get_range2 (struct block_list *bl, uint16 skill_id, uint16 skill_lv) { if (sd != NULL) range += pc->checkskill(sd, AC_VULTURE); else - range += 10; //Assume level 10? + range += battle->bc->mob_eye_range_bonus; break; // added to allow GS skills to be effected by the range of Snake Eyes [Reddozen] case GS_RAPIDSHOWER: @@ -303,7 +832,7 @@ int skill_get_range2 (struct block_list *bl, uint16 skill_id, uint16 skill_lv) { if (sd != NULL) range += pc->checkskill(sd, GS_SNAKEEYE); else - range += 10; //Assume level 10? + range += battle->bc->mob_eye_range_bonus; break; case NJ_KIRIKAGE: if (sd != NULL) @@ -346,7 +875,8 @@ int skill_get_range2 (struct block_list *bl, uint16 skill_id, uint16 skill_lv) { return range; } -int skill_calc_heal(struct block_list *src, struct block_list *target, uint16 skill_id, uint16 skill_lv, bool heal) { +static int skill_calc_heal(struct block_list *src, struct block_list *target, uint16 skill_id, uint16 skill_lv, bool heal) +{ int skill2_lv, hp; struct map_session_data *sd = BL_CAST(BL_PC, src); struct map_session_data *tsd = BL_CAST(BL_PC, target); @@ -354,7 +884,10 @@ int skill_calc_heal(struct block_list *src, struct block_list *target, uint16 sk nullpo_ret(src); - switch( skill_id ) { + switch (skill_id) { + case SU_TUNABELLY: + hp = status_get_max_hp(target) * ((20 * skill_lv) - 10) / 100; + break; case BA_APPLEIDUN: #ifdef RENEWAL hp = 100+5*skill_lv+5*(status_get_vit(src)/10); // HP recovery @@ -386,6 +919,11 @@ int skill_calc_heal(struct block_list *src, struct block_list *target, uint16 sk hp += hp * skill2_lv * 2 / 100; else if (src->type == BL_HOM && (skill2_lv = homun->checkskill(BL_UCAST(BL_HOM, src), HLIF_BRAIN)) > 0) hp += hp * skill2_lv * 2 / 100; + if (sd != NULL && ((skill2_lv = pc->checkskill(sd, SU_POWEROFSEA)) > 0)) { + hp += hp * 10 / 100; + if (pc->checkskill(sd, SU_TUNABELLY) == 5 && pc->checkskill(sd, SU_TUNAPARTY) == 5 && pc->checkskill(sd, SU_BUNCHOFSHRIMP) == 5 && pc->checkskill(sd, SU_FRESHSHRIMP) == 5) + hp += hp * 20 / 100; + } break; } @@ -411,6 +949,8 @@ int skill_calc_heal(struct block_list *src, struct block_list *target, uint16 sk hp -= hp * 20/100; if(sc->data[SC_HEALPLUS] && skill_id != NPC_EVILLAND && skill_id != BA_APPLEIDUN) hp += hp * sc->data[SC_HEALPLUS]->val1/100; // Only affects Heal, Sanctuary and PotionPitcher.(like bHealPower) [Inkfish] + if (sc->data[SC_VITALIZE_POTION] != NULL && skill_id != NPC_EVILLAND && skill_id != BA_APPLEIDUN) + hp += hp * sc->data[SC_VITALIZE_POTION]->val3 / 100; if(sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 2) hp += hp / 10; if (sc->data[SC_VITALITYACTIVATION]) @@ -433,19 +973,21 @@ int skill_calc_heal(struct block_list *src, struct block_list *target, uint16 sk } // Making plagiarize check its own function [Aru] -int can_copy (struct map_session_data *sd, uint16 skill_id, struct block_list* bl) +static int can_copy(struct map_session_data *sd, uint16 skill_id, struct block_list *bl) { + nullpo_ret(sd); // Never copy NPC/Wedding Skills if (skill->get_inf2(skill_id)&(INF2_NPC_SKILL|INF2_WEDDING_SKILL)) return 0; - // High-class skills - if((skill_id >= LK_AURABLADE && skill_id <= ASC_CDP) || (skill_id >= ST_PRESERVE && skill_id <= CR_CULTIVATION)) - { - if(battle_config.copyskill_restrict == 2) + // Transcendent-class skills + if((skill_id >= LK_AURABLADE && skill_id <= ASC_CDP) || (skill_id >= ST_PRESERVE && skill_id <= CR_CULTIVATION)) { + if (battle_config.copyskill_restrict == 2) { return 0; - else if(battle_config.copyskill_restrict) - return (sd->status.class_ == JOB_STALKER); + } else if (battle_config.copyskill_restrict == 1) { + if ((sd->job & (MAPID_UPPERMASK | JOBL_UPPER)) != MAPID_STALKER) + return 0; + } } //Added so plagarize can't copy agi/bless if you're undead since it damages you @@ -454,18 +996,21 @@ int can_copy (struct map_session_data *sd, uint16 skill_id, struct block_list* b skill_id == MER_INCAGI || skill_id == MER_BLESSING)) return 0; - // Couldn't preserve 3rd Class skills except only when using Reproduce skill. [Jobbie] - if( !(sd->sc.data[SC__REPRODUCE]) && ((skill_id >= RK_ENCHANTBLADE && skill_id <= LG_OVERBRAND_PLUSATK) || (skill_id >= RL_GLITTERING_GREED && skill_id <= OB_AKAITSUKI) || (skill_id >= GC_DARKCROW && skill_id <= NC_MAGMA_ERUPTION_DOTDAMAGE))) + // Couldn't preserve 3rd Class/Summoner skills except only when using Reproduce skill. [Jobbie] + if (!(sd->sc.data[SC__REPRODUCE]) && + ((skill_id >= RK_ENCHANTBLADE && skill_id <= LG_OVERBRAND_PLUSATK) || + (skill_id >= RL_GLITTERING_GREED && skill_id <= OB_AKAITSUKI) || + (skill_id >= GC_DARKCROW && skill_id <= SU_FRESHSHRIMP))) return 0; // Reproduce will only copy skills according on the list. [Jobbie] - else if( sd->sc.data[SC__REPRODUCE] && !skill->dbs->reproduce_db[skill->get_index(skill_id)] ) + else if (sd->sc.data[SC__REPRODUCE] && (skill->get_inf2(skill_id) & INF2_ALLOW_REPRODUCE) == 0) return 0; return 1; } // [MouseJstr] - skill ok to cast? and when? -int skillnotok (uint16 skill_id, struct map_session_data *sd) +static int skillnotok(uint16 skill_id, struct map_session_data *sd) { int16 idx,m; nullpo_retr (1, sd); @@ -481,21 +1026,21 @@ int skillnotok (uint16 skill_id, struct map_session_data *sd) if (pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL)) return 0; // can do any damn thing they want - if( skill_id == AL_TELEPORT && sd->skillitem == skill_id && sd->skillitemlv > 2 ) - return 0; // Teleport lv 3 bypasses this check.[Inkfish] + if (skill_id == AL_TELEPORT && sd->autocast.type == AUTOCAST_ITEM && sd->autocast.skill_lv > 2) + return 0; // Teleport level 3 and higher bypasses this check if cast by itemskill() script commands. // Epoque: // This code will compare the player's attack motion value which is influenced by ASPD before // allowing a skill to be cast. This is to prevent no-delay ACT files from spamming skills such as // AC_DOUBLE which do not have a skill delay and are not regarded in terms of attack motion. - if( !sd->state.autocast && sd->skillitem != skill_id && sd->canskill_tick && + if (sd->autocast.type == AUTOCAST_NONE && sd->canskill_tick != 0 && DIFF_TICK(timer->gettick(), sd->canskill_tick) < (sd->battle_status.amotion * (battle_config.skill_amotion_leniency) / 100) ) {// attempted to cast a skill before the attack motion has finished return 1; } if (sd->blockskill[idx]) { - clif->skill_fail(sd, skill_id, USESKILL_FAIL_SKILLINTERVAL, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_SKILLINTERVAL, 0, 0); return 1; } @@ -503,7 +1048,7 @@ int skillnotok (uint16 skill_id, struct map_session_data *sd) * It has been confirmed on a official server (thanks to Yommy) that item-cast skills bypass all the restrictions below * Also, without this check, an exploit where an item casting + healing (or any other kind buff) isn't deleted after used on a restricted map **/ - if( sd->skillitem == skill_id ) + if (sd->autocast.type == AUTOCAST_ITEM) return 0; if( sd->sc.data[SC_ALL_RIDING] ) @@ -542,21 +1087,22 @@ int skillnotok (uint16 skill_id, struct map_session_data *sd) //char output[150]; //sprintf(output, msg_txt(862), battle_config.min_npc_vendchat_distance); // "You're too close to a NPC, you must be at least %d cells away from any NPC." //clif->message(sd->fd, output); - clif->skill_fail(sd,skill_id,USESKILL_FAIL_THERE_ARE_NPC_AROUND,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_THERE_ARE_NPC_AROUND, 0, 0); return 1; } + FALLTHROUGH case MC_IDENTIFY: return 0; // always allowed case WZ_ICEWALL: // noicewall flag [Valaris] if (map->list[m].flag.noicewall) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 1; } break; case GC_DARKILLUSION: if( map_flag_gvg2(m) ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 1; } break; @@ -565,7 +1111,7 @@ int skillnotok (uint16 skill_id, struct map_session_data *sd) || !(battle_config.emergency_call&(map->list[m].flag.gvg || map->list[m].flag.gvg_castle?8:4)) || (battle_config.emergency_call&16 && map->list[m].flag.nowarpto && !map->list[m].flag.gvg_castle) ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 1; } break; @@ -579,12 +1125,21 @@ int skillnotok (uint16 skill_id, struct map_session_data *sd) return 1; } break; - + default: + return skill->not_ok_unknown(skill_id, sd); } return (map->list[m].flag.noskill); } -int skillnotok_hom(uint16 skill_id, struct homun_data *hd) +static int skill_notok_unknown(uint16 skill_id, struct map_session_data *sd) +{ + int16 m; + nullpo_retr (1, sd); + m = sd->bl.m; + return (map->list[m].flag.noskill); +} + +static int skillnotok_hom(uint16 skill_id, struct homun_data *hd) { uint16 idx = skill->get_index(skill_id); nullpo_retr(1,hd); @@ -598,7 +1153,7 @@ int skillnotok_hom(uint16 skill_id, struct homun_data *hd) case MH_LIGHT_OF_REGENE: if( homun->get_intimacy_grade(hd) != 4 ){ if( hd->master ) - clif->skill_fail(hd->master, skill_id, USESKILL_FAIL_RELATIONGRADE, 0); + clif->skill_fail(hd->master, skill_id, USESKILL_FAIL_RELATIONGRADE, 0, 0); return 1; } break; @@ -610,13 +1165,22 @@ int skillnotok_hom(uint16 skill_id, struct homun_data *hd) if(hd->sc.data[SC_GOLDENE_FERSE]) return 1; break; + default: + return skill->not_ok_hom_unknown(skill_id, hd); } //Use master's criteria. return skill->not_ok(skill_id, hd->master); } -int skillnotok_mercenary(uint16 skill_id, struct mercenary_data *md) +static int skillnotok_hom_unknown(uint16 skill_id, struct homun_data *hd) +{ + nullpo_retr(1, hd); + //Use master's criteria. + return skill->not_ok(skill_id, hd->master); +} + +static int skillnotok_mercenary(uint16 skill_id, struct mercenary_data *md) { uint16 idx = skill->get_index(skill_id); nullpo_retr(1,md); @@ -629,10 +1193,36 @@ int skillnotok_mercenary(uint16 skill_id, struct mercenary_data *md) return skill->not_ok(skill_id, md->master); } -struct s_skill_unit_layout* skill_get_unit_layout (uint16 skill_id, uint16 skill_lv, struct block_list* src, int x, int y) { +/** + * Validates the plausibility of auto-cast related data and calls pc_autocast_clear() if necessary. + * + * @param sd The character who cast the skill. + * @param skill_id The cast skill's ID. + * @param skill_lv The cast skill's level. (clif_parse_UseSkillMap() passes 0.) + * + **/ +static void skill_validate_autocast_data(struct map_session_data *sd, int skill_id, int skill_lv) +{ + nullpo_retv(sd); + + // Determine if called by clif_parse_UseSkillMap(). + bool use_skill_map = (skill_lv == 0 && (skill_id == AL_WARP || skill_id == AL_TELEPORT)); + + if (sd->autocast.type == AUTOCAST_NONE) + pc->autocast_clear(sd); // No auto-cast type set. Preventively unset all auto-cast related data. + else if (sd->autocast.type == AUTOCAST_TEMP) + pc->autocast_clear(sd); // AUTOCAST_TEMP should have been unset straight after usage. + else if (sd->autocast.skill_id == 0 || skill_id == 0 || sd->autocast.skill_id != skill_id) + pc->autocast_clear(sd); // Implausible skill ID. + else if (sd->autocast.skill_lv == 0 || (!use_skill_map && (skill_lv == 0 || sd->autocast.skill_lv != skill_lv))) + pc->autocast_clear(sd); // Implausible skill level. +} + +static struct s_skill_unit_layout *skill_get_unit_layout(uint16 skill_id, uint16 skill_lv, struct block_list *src, int x, int y) +{ int pos = skill->get_unit_layout_type(skill_id,skill_lv); - uint8 dir; + nullpo_retr(&skill->dbs->unit_layout[0], src); if (pos < -1 || pos >= MAX_SKILL_UNIT_LAYOUT) { ShowError("skill_get_unit_layout: unsupported layout type %d for skill %d (level %d)\n", pos, skill_id, skill_lv); pos = cap_value(pos, 0, MAX_SQUARE_LAYOUT); // cap to nearest square layout @@ -641,7 +1231,9 @@ struct s_skill_unit_layout* skill_get_unit_layout (uint16 skill_id, uint16 skill if (pos != -1) // simple single-definition layout return &skill->dbs->unit_layout[pos]; - dir = (src->x == x && src->y == y) ? 6 : map->calc_dir(src,x,y); // 6 - default aegis direction + enum unit_dir dir = UNIT_DIR_EAST; // default aegis direction + if (src->x != x || src->y != y) + dir = map->calc_dir(src, x, y); if (skill_id == MG_FIREWALL) return &skill->dbs->unit_layout [skill->firewall_unit_pos + dir]; @@ -657,7 +1249,8 @@ struct s_skill_unit_layout* skill_get_unit_layout (uint16 skill_id, uint16 skill /*========================================== * *------------------------------------------*/ -int skill_additional_effect(struct block_list* src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int attack_type, int dmg_lv, int64 tick) { +static int skill_additional_effect(struct block_list *src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int attack_type, int dmg_lv, int64 tick) +{ struct map_session_data *sd, *dstsd; struct mob_data *md, *dstmd; struct status_data *sstatus, *tstatus; @@ -761,7 +1354,7 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 break; // If a normal attack is a skill, it's splash damage. [Inkfish] if(sd) { // Automatic trigger of Blitz Beat - if (pc_isfalcon(sd) && sd->status.weapon == W_BOW && (temp=pc->checkskill(sd,HT_BLITZBEAT))>0 && + if (pc_isfalcon(sd) && sd->weapontype == W_BOW && (temp=pc->checkskill(sd,HT_BLITZBEAT))>0 && rnd()%1000 <= sstatus->luk*3 ) { rate = sd->status.job_level / 10 + 1; skill->castend_damage_id(src,bl,HT_BLITZBEAT,(temp<rate)?temp:rate,tick,SD_LEVEL); @@ -770,13 +1363,13 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 if( pc_iswug(sd) && (temp=pc->checkskill(sd,RA_WUGSTRIKE)) > 0 && rnd()%1000 <= sstatus->luk*3 ) skill->castend_damage_id(src,bl,RA_WUGSTRIKE,temp,tick,0); // Gank - if(dstmd && sd->status.weapon != W_BOW && + if(dstmd && sd->weapontype != W_BOW && (temp=pc->checkskill(sd,RG_SNATCHER)) > 0 && (temp*15 + 55) + pc->checkskill(sd,TF_STEAL)*10 > rnd()%1000) { if(pc->steal_item(sd,bl,pc->checkskill(sd,TF_STEAL))) clif->skill_nodamage(src,bl,TF_STEAL,temp,1); else - clif->skill_fail(sd,RG_SNATCHER,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, RG_SNATCHER, USESKILL_FAIL_LEVEL, 0, 0); } // Chance to trigger Taekwon kicks [Dralnu] if(sc && !sc->data[SC_COMBOATTACK]) { @@ -842,7 +1435,7 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 if (!sc_start2(src,bl,SC_POISON,(4*skill_lv+10),skill_lv,src->id,skill->get_time2(skill_id,skill_lv)) && sd && skill_id==TF_POISON ) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; case AS_SONICBLOW: @@ -860,7 +1453,7 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 if (!sc_start(src,bl,SC_FREEZE,skill_lv*3+35,skill_lv,skill->get_time2(skill_id,skill_lv)) && sd && skill_id == MG_FROSTDIVER ) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; #ifdef RENEWAL @@ -869,6 +1462,10 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 break; #endif + case WZ_HEAVENDRIVE: + status_change_end(bl, SC_SV_ROOTTWIST, INVALID_TIMER); + break; + case WZ_STORMGUST: /** * Storm Gust counter was dropped in renewal @@ -1102,8 +1699,8 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 break; case TK_JUMPKICK: - if( dstsd && dstsd->class_ != MAPID_SOUL_LINKER && !tsc->data[SC_PRESERVE] ) - {// debuff the following statuses + if (dstsd != NULL && (dstsd->job & MAPID_UPPERMASK) != MAPID_SOUL_LINKER && tsc->data[SC_PRESERVE] == NULL) { + // debuff the following statuses status_change_end(bl, SC_SOULLINK, INVALID_TIMER); status_change_end(bl, SC_ADRENALINE2, INVALID_TIMER); status_change_end(bl, SC_KAITE, INVALID_TIMER); @@ -1382,6 +1979,25 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 case MH_XENO_SLASHER: sc_start2(src, bl, SC_BLOODING, 10 * skill_lv, skill_lv, src->id, skill->get_time(skill_id,skill_lv)); break; + /** + * Summoner + */ + case SU_SCRATCH: + sc_start2(src, bl, SC_BLOODING, (skill_lv * 3), skill_lv, src->id, skill->get_time(skill_id, skill_lv)); // TODO: What's the chance/time? + break; + case SU_SV_STEMSPEAR: + sc_start2(src, bl, SC_BLOODING, 10, skill_lv, src->id, skill->get_time(skill_id, skill_lv)); + break; + case SU_CN_METEOR: + sc_start(src, bl, SC_CURSE, 10, skill_lv, skill->get_time2(skill_id, skill_lv)); // TODO: What's the chance/time? + break; + case SU_SCAROFTAROU: + sc_start(src, bl, SC_STUN, 10, skill_lv, skill->get_time2(skill_id, skill_lv)); // TODO: What's the chance/time? + break; + case SU_LUNATICCARROTBEAT: + if (skill->area_temp[3] == 1) + sc_start(src, bl, SC_STUN, 10, skill_lv, skill_get_time(skill_id, skill_lv)); // TODO: What's the chance/time? + break; default: skill->additional_effect_unknown(src, bl, &skill_id, &skill_lv, &attack_type, &dmg_lv, &tick); break; @@ -1469,7 +2085,7 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 if (DIFF_TICK(ud->canact_tick, tick + rate) < 0){ ud->canact_tick = tick+rate; if ( battle_config.display_status_timers ) - clif->status_change(src, SI_POSTDELAY, 1, rate, 0, 0, 0); + clif->status_change(src, status->get_sc_icon(SC_POSTDELAY), status->get_sc_relevant_bl_types(SC_POSTDELAY), 1, rate, 0, 0, 0); } } } @@ -1490,9 +2106,9 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 temp = (sd->autospell[i].id > 0) ? sd->autospell[i].id : -sd->autospell[i].id; - sd->state.autocast = 1; + sd->autocast.type = AUTOCAST_TEMP; notok = skill->not_ok(temp, sd); - sd->state.autocast = 0; + sd->autocast.type = AUTOCAST_NONE; if ( notok ) continue; @@ -1543,21 +2159,12 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 else if (temp == PF_SPIDERWEB) //Special case, due to its nature of coding. type = CAST_GROUND; - sd->state.autocast = 1; + sd->autocast.type = AUTOCAST_TEMP; skill->consume_requirement(sd,temp,auto_skill_lv,1); skill->toggle_magicpower(src, temp); - switch (type) { - case CAST_GROUND: - skill->castend_pos2(src, tbl->x, tbl->y, temp, auto_skill_lv, tick, 0); - break; - case CAST_NODAMAGE: - skill->castend_nodamage_id(src, tbl, temp, auto_skill_lv, tick, 0); - break; - case CAST_DAMAGE: - skill->castend_damage_id(src, tbl, temp, auto_skill_lv, tick, 0); - break; - } - sd->state.autocast = 0; + skill->castend_type(type, src, tbl, temp, auto_skill_lv, tick, 0); + sd->autocast.type = AUTOCAST_NONE; + //Set canact delay. [Skotlex] ud = unit->bl2ud(src); if (ud) { @@ -1565,7 +2172,7 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 if (DIFF_TICK(ud->canact_tick, tick + rate) < 0){ ud->canact_tick = tick+rate; if (battle_config.display_status_timers) - clif->status_change(src, SI_POSTDELAY, 1, rate, 0, 0, 0); + clif->status_change(src, status->get_sc_icon(SC_POSTDELAY), status->get_sc_relevant_bl_types(SC_POSTDELAY), 1, rate, 0, 0, 0); } } } @@ -1614,16 +2221,21 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 return 0; } -void skill_additional_effect_unknown(struct block_list* src, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int *attack_type, int *dmg_lv, int64 *tick) { +static void skill_additional_effect_unknown(struct block_list *src, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int *attack_type, int *dmg_lv, int64 *tick) +{ } -int skill_onskillusage(struct map_session_data *sd, struct block_list *bl, uint16 skill_id, int64 tick) { +static int skill_onskillusage(struct map_session_data *sd, struct block_list *bl, uint16 skill_id, int64 tick) +{ int temp, skill_lv, i, type, notok; struct block_list *tbl; if( sd == NULL || !skill_id ) return 0; + // Preserve auto-cast type if bAutoSpellOnSkill was triggered by a skill which was cast by Abracadabra, Improvised Song or an item. + enum autocast_type ac_type = sd->autocast.type; + for( i = 0; i < ARRAYLENGTH(sd->autospell3) && sd->autospell3[i].flag; i++ ) { if( sd->autospell3[i].flag != skill_id ) continue; @@ -1633,9 +2245,9 @@ int skill_onskillusage(struct map_session_data *sd, struct block_list *bl, uint1 temp = (sd->autospell3[i].id > 0) ? sd->autospell3[i].id : -sd->autospell3[i].id; - sd->state.autocast = 1; + sd->autocast.type = AUTOCAST_TEMP; notok = skill->not_ok(temp, sd); - sd->state.autocast = 0; + sd->autocast.type = AUTOCAST_NONE; if ( notok ) continue; @@ -1681,18 +2293,16 @@ int skill_onskillusage(struct map_session_data *sd, struct block_list *bl, uint1 !battle->check_range(&sd->bl, tbl, skill->get_range2(&sd->bl, temp,skill_lv) + (temp == RG_CLOSECONFINE?0:1)) ) continue; - sd->state.autocast = 1; sd->autospell3[i].lock = true; + sd->autocast.type = AUTOCAST_TEMP; skill->consume_requirement(sd,temp,skill_lv,1); - switch( type ) { - case CAST_GROUND: skill->castend_pos2(&sd->bl, tbl->x, tbl->y, temp, skill_lv, tick, 0); break; - case CAST_NODAMAGE: skill->castend_nodamage_id(&sd->bl, tbl, temp, skill_lv, tick, 0); break; - case CAST_DAMAGE: skill->castend_damage_id(&sd->bl, tbl, temp, skill_lv, tick, 0); break; - } + skill->castend_type(type, &sd->bl, tbl, temp, skill_lv, tick, 0); + sd->autocast.type = AUTOCAST_NONE; sd->autospell3[i].lock = false; - sd->state.autocast = 0; } + sd->autocast.type = ac_type; + if (sd->autobonus3[0].rate) { for( i = 0; i < ARRAYLENGTH(sd->autobonus3); i++ ) { if( rnd()%1000 >= sd->autobonus3[i].rate ) @@ -1715,7 +2325,8 @@ int skill_onskillusage(struct map_session_data *sd, struct block_list *bl, uint1 * type of skills, so not every instance of skill->additional_effect needs a call * to this one. */ -int skill_counter_additional_effect(struct block_list* src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int attack_type, int64 tick) { +static int skill_counter_additional_effect(struct block_list *src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int attack_type, int64 tick) +{ int rate; struct map_session_data *sd=NULL; struct map_session_data *dstsd=NULL; @@ -1789,7 +2400,7 @@ int skill_counter_additional_effect(struct block_list* src, struct block_list *b break; } - if( sd && (sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR + if (sd != NULL && (sd->job & MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR && rnd()%10000 < battle_config.sg_miracle_skill_ratio) // SG_MIRACLE [Komurka] sc_start(src,src,SC_MIRACLE,100,1,battle_config.sg_miracle_skill_duration); @@ -1799,11 +2410,11 @@ int skill_counter_additional_effect(struct block_list* src, struct block_list *b ) { // Soul Drain should only work on targeted spells [Skotlex] if( pc_issit(sd) ) pc->setstand(sd); // Character stuck in attacking animation while 'sitting' fix. [Skotlex] - if( skill->get_nk(skill_id)&NK_SPLASH && skill->area_temp[1] != bl->id ) + if (skill->get_nk(skill_id)&NK_SPLASH && skill->area_temp[1] != bl->id) { ; - else { + } else { clif->skill_nodamage(src,bl,HW_SOULDRAIN,rate,1); - status->heal(src, 0, status->get_lv(bl)*(95+15*rate)/100, 2); + status->heal(src, 0, status->get_lv(bl)*(95+15*rate)/100, STATUS_HEAL_SHOWEFFECT); } } @@ -1826,9 +2437,9 @@ int skill_counter_additional_effect(struct block_list* src, struct block_list *b sc->data[SC_SOULLINK]->val3 = 0; //Clear bounced spell check. } } - if( hp || sp ) { + if (hp != 0 || sp != 0) { // updated to force healing to allow healing through berserk - status->heal(src, hp, sp, battle_config.show_hp_sp_gain ? 3 : 1); + status->heal(src, hp, sp, STATUS_HEAL_FORCED | (battle_config.show_hp_sp_gain ? STATUS_HEAL_SHOWEFFECT : STATUS_HEAL_DEFAULT)); } } @@ -1838,6 +2449,9 @@ int skill_counter_additional_effect(struct block_list* src, struct block_list *b struct unit_data *ud; int i, auto_skill_id, auto_skill_lv, type, notok; + // Preserve auto-cast type if bAutoSpellWhenHit was triggered during cast of a skill which was cast by Abracadabra, Improvised Song or an item. + enum autocast_type ac_type = dstsd->autocast.type; + for (i = 0; i < ARRAYLENGTH(dstsd->autospell2) && dstsd->autospell2[i].id; i++) { if(!(dstsd->autospell2[i].flag&attack_type&BF_WEAPONMASK && @@ -1853,9 +2467,9 @@ int skill_counter_additional_effect(struct block_list* src, struct block_list *b if (attack_type&BF_LONG) rate>>=1; - dstsd->state.autocast = 1; + dstsd->autocast.type = AUTOCAST_TEMP; notok = skill->not_ok(auto_skill_id, dstsd); - dstsd->state.autocast = 0; + dstsd->autocast.type = AUTOCAST_NONE; if ( notok ) continue; @@ -1896,20 +2510,11 @@ int skill_counter_additional_effect(struct block_list* src, struct block_list *b if( !battle->check_range(src, tbl, skill->get_range2(src, auto_skill_id,auto_skill_lv) + (auto_skill_id == RG_CLOSECONFINE?0:1)) && battle_config.autospell_check_range ) continue; - dstsd->state.autocast = 1; + dstsd->autocast.type = AUTOCAST_TEMP; skill->consume_requirement(dstsd,auto_skill_id,auto_skill_lv,1); - switch (type) { - case CAST_GROUND: - skill->castend_pos2(bl, tbl->x, tbl->y, auto_skill_id, auto_skill_lv, tick, 0); - break; - case CAST_NODAMAGE: - skill->castend_nodamage_id(bl, tbl, auto_skill_id, auto_skill_lv, tick, 0); - break; - case CAST_DAMAGE: - skill->castend_damage_id(bl, tbl, auto_skill_id, auto_skill_lv, tick, 0); - break; - } - dstsd->state.autocast = 0; + skill->castend_type(type, bl, tbl, auto_skill_id, auto_skill_lv, tick, 0); + dstsd->autocast.type = AUTOCAST_NONE; + // Set canact delay. [Skotlex] ud = unit->bl2ud(bl); if (ud) { @@ -1917,10 +2522,12 @@ int skill_counter_additional_effect(struct block_list* src, struct block_list *b if (DIFF_TICK(ud->canact_tick, tick + rate) < 0){ ud->canact_tick = tick+rate; if (battle_config.display_status_timers) - clif->status_change(bl, SI_POSTDELAY, 1, rate, 0, 0, 0); + clif->status_change(bl, status->get_sc_icon(SC_POSTDELAY), status->get_sc_relevant_bl_types(SC_POSTDELAY), 1, rate, 0, 0, 0); } } } + + dstsd->autocast.type = ac_type; } //Autobonus when attacked @@ -1942,7 +2549,8 @@ int skill_counter_additional_effect(struct block_list* src, struct block_list *b return 0; } -void skill_counter_additional_effect_unknown(struct block_list* src, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int *attack_type, int64 *tick) { +static void skill_counter_additional_effect_unknown(struct block_list *src, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int *attack_type, int64 *tick) +{ } /*========================================================================= @@ -1951,7 +2559,7 @@ void skill_counter_additional_effect_unknown(struct block_list* src, struct bloc * - flag is a BCT_ flag to indicate which type of adjustment should be used * (BCT_ENEMY/BCT_PARTY/BCT_SELF) are the valid values. *------------------------------------------------------------------------*/ -int skill_break_equip (struct block_list *bl, unsigned short where, int rate, int flag) +static int skill_break_equip(struct block_list *bl, unsigned short where, int rate, int flag) { const int where_list[4] = {EQP_WEAPON, EQP_ARMOR, EQP_SHIELD, EQP_HELM}; const enum sc_type scatk[4] = {SC_NOEQUIPWEAPON, SC_NOEQUIPARMOR, SC_NOEQUIPSHIELD, SC_NOEQUIPHELM}; @@ -1968,7 +2576,7 @@ int skill_break_equip (struct block_list *bl, unsigned short where, int rate, in if (sd->bonus.unbreakable) rate -= rate*sd->bonus.unbreakable/100; if (where&EQP_WEAPON) { - switch (sd->status.weapon) { + switch (sd->weapontype) { case W_FIST: //Bare fists should not break :P case W_1HAXE: case W_2HAXE: @@ -2005,7 +2613,7 @@ int skill_break_equip (struct block_list *bl, unsigned short where, int rate, in if (sd) { for (i = 0; i < EQI_MAX; i++) { int j = sd->equip_index[i]; - if (j < 0 || sd->status.inventory[j].attribute == 1 || !sd->inventory_data[j]) + if (j < 0 || (sd->status.inventory[j].attribute & ATTR_BROKEN) != 0 || !sd->inventory_data[j]) continue; switch(i) { @@ -2031,17 +2639,18 @@ int skill_break_equip (struct block_list *bl, unsigned short where, int rate, in continue; } if (flag) { - sd->status.inventory[j].attribute = 1; + sd->status.inventory[j].attribute |= ATTR_BROKEN; pc->unequipitem(sd, j, PCUNEQUIPITEM_RECALC|PCUNEQUIPITEM_FORCE); } } - clif->equiplist(sd); + clif->equipList(sd); } return where; //Return list of pieces broken. } -int skill_strip_equip(struct block_list *bl, unsigned short where, int rate, int lv, int time) { +static int skill_strip_equip(struct block_list *bl, unsigned short where, int rate, int lv, int time) +{ struct status_change *sc; const int pos[5] = {EQP_WEAPON, EQP_SHIELD, EQP_ARMOR, EQP_HELM, EQP_ACC}; const enum sc_type sc_atk[5] = {SC_NOEQUIPWEAPON, SC_NOEQUIPSHIELD, SC_NOEQUIPARMOR, SC_NOEQUIPHELM, SC__STRIPACCESSARY}; @@ -2067,21 +2676,25 @@ int skill_strip_equip(struct block_list *bl, unsigned short where, int rate, int } return where?1:0; } + /*========================================================================= - Used to knock back players, monsters, traps, etc - - 'count' is the number of squares to knock back - - 'direction' indicates the way OPPOSITE to the knockback direction (or -1 for default behavior) - - if 'flag&0x1', position update packets must not be sent. - - if 'flag&0x2', skill blown ignores players' special_state.no_knockback - -------------------------------------------------------------------------*/ -int skill_blown(struct block_list* src, struct block_list* target, int count, int8 dir, int flag) + * Used to knock back players, monsters, traps, etc + * 'count' is the number of squares to knock back + * 'direction' indicates the way OPPOSITE to the knockback direction (or UNIT_DIR_UNDEFINED for default behavior) + * if 'flag&0x1', position update packets must not be sent. + * if 'flag&0x2', skill blown ignores players' special_state.no_knockback + */ +static int skill_blown(struct block_list *src, struct block_list *target, int count, enum unit_dir dir, int flag) { int dx = 0, dy = 0; + struct status_change *tsc = status->get_sc(target); nullpo_ret(src); if (src != target && map->list[src->m].flag.noknockback) return 0; // No knocking + + nullpo_ret(target); if (count == 0) return 0; // Actual knockback distance is 0. @@ -2113,15 +2726,18 @@ int skill_blown(struct block_list* src, struct block_list* target, int count, in break; } - if (dir == -1) // <optimized>: do the computation here instead of outside + if (dir == UNIT_DIR_UNDEFINED) // <optimized>: do the computation here instead of outside dir = map->calc_dir(target, src->x, src->y); // direction from src to target, reversed - if (dir >= 0 && dir < 8) { + if (dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX) { // take the reversed 'direction' and reverse it dx = -dirx[dir]; dy = -diry[dir]; } + if (tsc != NULL && tsc->data[SC_SU_STOOP]) // Any knockback will cancel it. + status_change_end(target, SC_SU_STOOP, INVALID_TIMER); + return unit->blown(target, dx, dy, count, flag); // send over the proper flag } @@ -2132,10 +2748,12 @@ int skill_blown(struct block_list* src, struct block_list* target, int count, in 1 - Regular reflection (Maya) 2 - SL_KAITE reflection */ -int skill_magic_reflect(struct block_list* src, struct block_list* bl, int type) { +static int skill_magic_reflect(struct block_list *src, struct block_list *bl, int type) +{ struct status_change *sc = status->get_sc(bl); struct map_session_data* sd = BL_CAST(BL_PC, bl); + nullpo_ret(src); if( sc && sc->data[SC_KYOMU] ) // Nullify reflecting ability return 0; @@ -2179,9 +2797,14 @@ int skill_magic_reflect(struct block_list* src, struct block_list* bl, int type) * client (causes player characters to not scream skill name) * flag&0x4000 - Return 0 if damage was reflected *-------------------------------------------------------------------------*/ -int skill_attack(int attack_type, struct block_list* src, struct block_list *dsrc, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int64 tick, int flag) { +static int skill_attack(int attack_type, struct block_list *src, struct block_list *dsrc, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int64 tick, int flag) +{ struct Damage dmg; +#if MAGIC_REFLECTION_TYPE struct status_data *sstatus, *tstatus; +#else + struct status_data *tstatus; +#endif struct status_change *sc; struct map_session_data *sd, *tsd; int type; @@ -2216,7 +2839,9 @@ int skill_attack(int attack_type, struct block_list* src, struct block_list *dsr ) return 0; +#if MAGIC_REFLECTION_TYPE sstatus = status->get_status_data(src); +#endif tstatus = status->get_status_data(bl); sc = status->get_sc(bl); if (sc && !sc->count) sc = NULL; //Don't need it. @@ -2322,14 +2947,28 @@ int skill_attack(int attack_type, struct block_list* src, struct block_list *dsr } #endif /* MAGIC_REFLECTION_TYPE */ } - if(sc && sc->data[SC_MAGICROD] && src == dsrc) { - int sp = skill->get_sp(skill_id,skill_lv); + if (sc && sc->data[SC_MAGICROD] && src == dsrc) { + int sp = skill->get_sp(skill_id, skill_lv); dmg.damage = dmg.damage2 = 0; dmg.dmg_lv = ATK_MISS; //This will prevent skill additional effect from taking effect. [Skotlex] sp = sp * sc->data[SC_MAGICROD]->val2 / 100; - if(skill_id == WZ_WATERBALL && skill_lv > 1) - sp = sp/((skill_lv|1)*(skill_lv|1)); //Estimate SP cost of a single water-ball - status->heal(bl, 0, sp, 2); + if (skill_id == WZ_WATERBALL && skill_lv > 1) + sp = sp / ((skill_lv | 1) * (skill_lv | 1)); //Estimate SP cost of a single water-ball + status->heal(bl, 0, sp, STATUS_HEAL_SHOWEFFECT); + if (battle->bc->magicrod_type == 1) + clif->skill_nodamage(bl, bl, SA_MAGICROD, sc->data[SC_MAGICROD]->val1, 1); // Animation used here in eAthena [Wolfie] + } + } + + if (bl->type == BL_MOB) { + struct mob_data *md = BL_CAST(BL_MOB, bl); + if (md != NULL) { + if (md->db->dmg_taken_rate != 100) { + if (dmg.damage > 0) + dmg.damage = apply_percentrate64(dmg.damage, md->db->dmg_taken_rate, 100); + if (dmg.damage2 > 0) + dmg.damage2 = apply_percentrate64(dmg.damage2, md->db->dmg_taken_rate, 100); + } } } @@ -2375,7 +3014,7 @@ int skill_attack(int attack_type, struct block_list* src, struct block_list *dsr case TK_STORMKICK: case TK_DOWNKICK: case TK_COUNTER: - if (pc->famerank(sd->status.char_id,MAPID_TAEKWON)) {//Extend combo time. + if (pc->fame_rank(sd->status.char_id, RANKTYPE_TAEKWON) > 0) { //Extend combo time. sce->val1 = skill_id; //Update combo-skill sce->val3 = skill_id; if( sce->timer != INVALID_TIMER ) @@ -2493,18 +3132,20 @@ int skill_attack(int attack_type, struct block_list* src, struct block_list *dsr case KO_MUCHANAGE: if( dmg.dmg_lv == ATK_FLEE ) break; + FALLTHROUGH case WL_SOULEXPANSION: case WL_COMET: case NJ_HUUMA: dmg.dmotion = clif->skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,skill_lv,BDT_MULTIHIT); break; case WL_CHAINLIGHTNING_ATK: - dmg.dmotion = clif->skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,1,WL_CHAINLIGHTNING,-2,BDT_SKILL); + dmg.dmotion = clif->skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,1,WL_CHAINLIGHTNING_ATK,-2,BDT_SKILL); break; case LG_OVERBRAND_BRANDISH: case LG_OVERBRAND: /* Fall through */ dmg.amotion = status_get_amotion(src) * 2; + FALLTHROUGH case LG_OVERBRAND_PLUSATK: dmg.dmotion = clif->skill_damage(dsrc,bl,tick,status_get_amotion(src),dmg.dmotion,damage,dmg.div_,skill_id,-1,BDT_SPLASH); break; @@ -2563,6 +3204,7 @@ int skill_attack(int attack_type, struct block_list* src, struct block_list *dsr dmg.dmotion = clif->skill_damage(src,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, (flag&SD_LEVEL) ? -1 : skill_lv, BDT_SPLASH); if( dsrc != src ) // avoid damage display redundancy break; + FALLTHROUGH case HT_LANDMINE: dmg.dmotion = clif->skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, -1, type); break; @@ -2575,6 +3217,7 @@ int skill_attack(int attack_type, struct block_list* src, struct block_list *dsr case AB_DUPLELIGHT_MELEE: case AB_DUPLELIGHT_MAGIC: dmg.amotion = 300;/* makes the damage value not overlap with previous damage (when displayed by the client) */ + FALLTHROUGH default: skill->attack_display_unknown(&attack_type, src, dsrc, bl, &skill_id, &skill_lv, &tick, &flag, &type, &dmg, &damage); break; @@ -2644,8 +3287,8 @@ int skill_attack(int attack_type, struct block_list* src, struct block_list *dsr } tsd->reproduceskill_id = copy_skill; - pc_setglobalreg(tsd, script->add_str("REPRODUCE_SKILL"), copy_skill); - pc_setglobalreg(tsd, script->add_str("REPRODUCE_SKILL_LV"), lv); + pc_setglobalreg(tsd, script->add_variable("REPRODUCE_SKILL"), copy_skill); + pc_setglobalreg(tsd, script->add_variable("REPRODUCE_SKILL_LV"), lv); tsd->status.skill[cidx].id = copy_skill; tsd->status.skill[cidx].lv = lv; @@ -2668,8 +3311,8 @@ int skill_attack(int attack_type, struct block_list* src, struct block_list *dsr lv = plagiarismlvl; tsd->cloneskill_id = copy_skill; - pc_setglobalreg(tsd, script->add_str("CLONE_SKILL"), copy_skill); - pc_setglobalreg(tsd, script->add_str("CLONE_SKILL_LV"), lv); + pc_setglobalreg(tsd, script->add_variable("CLONE_SKILL"), copy_skill); + pc_setglobalreg(tsd, script->add_variable("CLONE_SKILL_LV"), lv); tsd->status.skill[cidx].id = copy_skill; tsd->status.skill[cidx].lv = lv; @@ -2708,7 +3351,7 @@ int skill_attack(int attack_type, struct block_list* src, struct block_list *dsr //Only knockback if it's still alive, otherwise a "ghost" is left behind. [Skotlex] //Reflected spells do not bounce back (bl == dsrc since it only happens for direct skills) if (dmg.blewcount > 0 && bl!=dsrc && !status->isdead(bl)) { - int8 dir = -1; // default + enum unit_dir dir = UNIT_DIR_UNDEFINED; // default switch(skill_id) {//direction case MG_FIREWALL: case PR_SANCTUARY: @@ -2721,13 +3364,13 @@ int skill_attack(int attack_type, struct block_list* src, struct block_list *dsr // This ensures the storm randomly pushes instead of exactly a cell backwards per official mechanics. case WZ_STORMGUST: if(!battle_config.stormgust_knockback) - dir = rnd()%8; + dir = rnd() % UNIT_DIR_MAX; break; case WL_CRIMSONROCK: dir = map->calc_dir(bl,skill->area_temp[4],skill->area_temp[5]); break; case MC_CARTREVOLUTION: - dir = 6; // Official servers push target to the West + dir = UNIT_DIR_EAST; // Official servers push target to the West break; default: dir = skill->attack_dir_unknown(&attack_type, src, dsrc, bl, &skill_id, &skill_lv, &tick, &flag); @@ -2748,8 +3391,12 @@ int skill_attack(int attack_type, struct block_list* src, struct block_list *dsr case SR_KNUCKLEARROW: if( skill->blown(dsrc,bl,dmg.blewcount,dir,0) && !(flag&4) ) { short dir_x, dir_y; - dir_x = dirx[(dir+4)%8]; - dir_y = diry[(dir+4)%8]; + if (Assert_chk(dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX)) { + map->freeblock_unlock(); // unblock before assert-returning + return 0; + } + dir_x = dirx[unit_get_opposite_dir(dir)]; + dir_y = diry[unit_get_opposite_dir(dir)]; if (map->getcell(bl->m, bl, bl->x + dir_x, bl->y + dir_y, CELL_CHKNOPASS) != 0) skill->addtimerskill(src, tick + 300 * ((flag&2) ? 1 : 2), bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag|4); } @@ -2870,15 +3517,27 @@ int skill_attack(int attack_type, struct block_list* src, struct block_list *dsr return (int)cap_value(damage,INT_MIN,INT_MAX); } -void skill_attack_combo1_unknown(int *attack_type, struct block_list* src, struct block_list *dsrc, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag, struct status_change_entry *sce, int *combo) { +static void skill_attack_combo1_unknown(int *attack_type, struct block_list *src, struct block_list *dsrc, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag, struct status_change_entry *sce, int *combo) +{ if (src == dsrc) // Ground skills are exceptions. [Inkfish] status_change_end(src, SC_COMBOATTACK, INVALID_TIMER); } -void skill_attack_combo2_unknown(int *attack_type, struct block_list* src, struct block_list *dsrc, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag, int *combo) { +static void skill_attack_combo2_unknown(int *attack_type, struct block_list *src, struct block_list *dsrc, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag, int *combo) +{ } -void skill_attack_display_unknown(int *attack_type, struct block_list* src, struct block_list *dsrc, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag, int *type, struct Damage *dmg, int64 *damage) { +static void skill_attack_display_unknown(int *attack_type, struct block_list *src, struct block_list *dsrc, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag, int *type, struct Damage *dmg, int64 *damage) +{ + nullpo_retv(bl); + nullpo_retv(dmg); + nullpo_retv(tick); + nullpo_retv(flag); + nullpo_retv(damage); + nullpo_retv(skill_id); + nullpo_retv(skill_lv); + nullpo_retv(type); + if (*flag & SD_ANIMATION && dmg->div_ < 2) //Disabling skill animation doesn't works on multi-hit. *type = BDT_SPLASH; if (bl->type == BL_SKILL) { @@ -2889,24 +3548,36 @@ void skill_attack_display_unknown(int *attack_type, struct block_list* src, stru dmg->dmotion = clif->skill_damage(dsrc, bl, *tick, dmg->amotion, dmg->dmotion, *damage, dmg->div_, *skill_id, (*flag & SD_LEVEL) ? -1 : *skill_lv, *type); } -int skill_attack_copy_unknown(int *attack_type, struct block_list* src, struct block_list *dsrc, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag) { - return *skill_id; +static int skill_attack_copy_unknown(int *attack_type, struct block_list *src, struct block_list *dsrc, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag) +{ + nullpo_ret(skill_id); + return *skill_id; } -int skill_attack_dir_unknown(int *attack_type, struct block_list* src, struct block_list *dsrc, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag) { - return -1; +static int skill_attack_dir_unknown(int *attack_type, struct block_list *src, struct block_list *dsrc, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag) +{ + return UNIT_DIR_UNDEFINED; } -void skill_attack_blow_unknown(int *attack_type, struct block_list* src, struct block_list *dsrc, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag, int *type, struct Damage *dmg, int64 *damage, int8 *dir) { +static void skill_attack_blow_unknown(int *attack_type, struct block_list *src, struct block_list *dsrc, struct block_list *bl, + uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag, int *type, + struct Damage *dmg, int64 *damage, enum unit_dir *dir) +{ + nullpo_retv(bl); + nullpo_retv(dmg); + nullpo_retv(dir); + nullpo_retv(damage); + skill->blown(dsrc, bl, dmg->blewcount, *dir, 0x0); if (!dmg->blewcount && bl->type == BL_SKILL && *damage > 0){ struct skill_unit *su = BL_UCAST(BL_SKILL, bl); if (su->group && su->group->skill_id == HT_BLASTMINE) - skill->blown(src, bl, 3, -1, 0); + skill->blown(src, bl, 3, UNIT_DIR_UNDEFINED, 0); } } -void skill_attack_post_unknown(int *attack_type, struct block_list* src, struct block_list *dsrc, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag) { +static void skill_attack_post_unknown(int *attack_type, struct block_list *src, struct block_list *dsrc, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag) +{ } /*========================================== @@ -2914,7 +3585,8 @@ void skill_attack_post_unknown(int *attack_type, struct block_list* src, struct * Checking bl battle flag and display damage * then call func with source,target,skill_id,skill_lv,tick,flag *------------------------------------------*/ -int skill_area_sub(struct block_list *bl, va_list ap) { +static int skill_area_sub(struct block_list *bl, va_list ap) +{ struct block_list *src; uint16 skill_id,skill_lv; int flag; @@ -2943,7 +3615,7 @@ int skill_area_sub(struct block_list *bl, va_list ap) { return 0; } -int skill_check_unit_range_sub(struct block_list *bl, va_list ap) +static int skill_check_unit_range_sub(struct block_list *bl, va_list ap) { const struct skill_unit *su = NULL; uint16 skill_id,g_skill_id; @@ -2965,6 +3637,7 @@ int skill_check_unit_range_sub(struct block_list *bl, va_list ap) case AL_PNEUMA: if(g_skill_id == SA_LANDPROTECTOR) break; + FALLTHROUGH case MG_SAFETYWALL: case MH_STEINWAND: case SC_MAELSTROM: @@ -3013,7 +3686,8 @@ int skill_check_unit_range_sub(struct block_list *bl, va_list ap) return 1; } -int skill_check_unit_range (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv) { +static int skill_check_unit_range(struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv) +{ //Non players do not check for the skill's splash-trigger area. int range = bl->type == BL_PC ? skill->get_unit_range(skill_id, skill_lv):0; int layout_type = skill->get_unit_layout_type(skill_id,skill_lv); @@ -3026,7 +3700,8 @@ int skill_check_unit_range (struct block_list *bl, int x, int y, uint16 skill_id return map->foreachinarea(skill->check_unit_range_sub,bl->m,x-range,y-range,x+range,y+range,BL_SKILL,skill_id); } -int skill_check_unit_range2_sub (struct block_list *bl, va_list ap) { +static int skill_check_unit_range2_sub(struct block_list *bl, va_list ap) +{ uint16 skill_id; if(bl->prev == NULL) @@ -3045,7 +3720,8 @@ int skill_check_unit_range2_sub (struct block_list *bl, va_list ap) { return 1; } -int skill_check_unit_range2 (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv) { +static int skill_check_unit_range2(struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv) +{ int range, type; switch (skill_id) { @@ -3088,12 +3764,13 @@ int skill_check_unit_range2 (struct block_list *bl, int x, int y, uint16 skill_i * &1: finished casting the skill (invoke hp/sp/item consumption) * &2: picked menu entry (Warp Portal, Teleport and other menu based skills) *------------------------------------------*/ -int skill_check_condition_mercenary(struct block_list *bl, int skill_id, int lv, int type) { +static int skill_check_condition_mercenary(struct block_list *bl, int skill_id, int lv, int type) +{ struct status_data *st; struct map_session_data *sd = NULL; int i, hp, sp, hp_rate, sp_rate, state, mhp; - uint16 idx; - int itemid[MAX_SKILL_ITEM_REQUIRE],amount[ARRAYLENGTH(itemid)],index[ARRAYLENGTH(itemid)]; + int idx; + int itemid[MAX_SKILL_ITEM_REQUIRE], amount[MAX_SKILL_ITEM_REQUIRE], index[MAX_SKILL_ITEM_REQUIRE]; if( lv < 1 || lv > MAX_SKILL_LEVEL ) return 0; @@ -3109,8 +3786,7 @@ int skill_check_condition_mercenary(struct block_list *bl, int skill_id, int lv, return 0; // Requirements - for( i = 0; i < ARRAYLENGTH(itemid); i++ ) - { + for (i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++) { itemid[i] = skill->dbs->db[idx].itemid[i]; amount[i] = skill->dbs->db[idx].amount[i]; } @@ -3146,11 +3822,11 @@ int skill_check_condition_mercenary(struct block_list *bl, int skill_id, int lv, if( !(type&2) ) { if( hp > 0 && st->hp <= (unsigned int)hp ) { - clif->skill_fail(sd, skill_id, USESKILL_FAIL_HP_INSUFFICIENT, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_HP_INSUFFICIENT, 0, 0); return 0; } if( sp > 0 && st->sp <= (unsigned int)sp ) { - clif->skill_fail(sd, skill_id, USESKILL_FAIL_SP_INSUFFICIENT, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_SP_INSUFFICIENT, 0, 0); return 0; } } @@ -3159,7 +3835,7 @@ int skill_check_condition_mercenary(struct block_list *bl, int skill_id, int lv, switch( state ) { case ST_MOVE_ENABLE: if( !unit->can_move(bl) ) { - clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; @@ -3173,7 +3849,7 @@ int skill_check_condition_mercenary(struct block_list *bl, int skill_id, int lv, if (itemid[i] < 1) continue; // No item index[i] = pc->search_inventory(sd, itemid[i]); if (index[i] == INDEX_NOT_FOUND || sd->status.inventory[index[i]].amount < amount[i]) { - clif->skill_fail(sd, skill_id, USESKILL_FAIL_NEED_ITEM, amount[i]|(itemid[i] << 16)); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_NEED_ITEM, amount[i], itemid[i]); return 0; } } @@ -3196,14 +3872,16 @@ int skill_check_condition_mercenary(struct block_list *bl, int skill_id, int lv, /*========================================== * what the hell it doesn't need to receive this many params, it doesn't do anything ~_~ *------------------------------------------*/ -int skill_area_sub_count(struct block_list *src, struct block_list *target, uint16 skill_id, uint16 skill_lv, int64 tick, int flag) { +static int skill_area_sub_count(struct block_list *src, struct block_list *target, uint16 skill_id, uint16 skill_lv, int64 tick, int flag) +{ return 1; } /*========================================== * *------------------------------------------*/ -int skill_timerskill(int tid, int64 tick, int id, intptr_t data) { +static int skill_timerskill(int tid, int64 tick, int id, intptr_t data) +{ struct block_list *src = map->id2bl(id),*target = NULL; struct unit_data *ud = unit->bl2ud(src); struct skill_timerskill *skl; @@ -3387,6 +4065,7 @@ int skill_timerskill(int tid, int64 tick, int id, intptr_t data) { skill->blown(src,target,skill->get_blewcount(skl->skill_id, skl->skill_lv), -1, 0x0 ); break; } + FALLTHROUGH } default: skill->timerskill_target_unknown(tid, tick, src, target, ud, skl); @@ -3397,7 +4076,8 @@ int skill_timerskill(int tid, int64 tick, int id, intptr_t data) { break; switch( skl->skill_id ) { case WZ_METEOR: - if( skl->type >= 0 ) { + case SU_CN_METEOR: + if (skl->type >= 0) { int x = skl->type>>16, y = skl->type&0xFFFF; if( path->search_long(NULL, src, src->m, src->x, src->y, x, y, CELL_CHKWALL) ) skill->unitsetting(src,skl->skill_id,skl->skill_lv,x,y,skl->flag); @@ -3413,6 +4093,7 @@ int skill_timerskill(int tid, int64 tick, int id, intptr_t data) { map->foreachinarea(skill->cell_overlap,src->m,skl->x-i,skl->y-i,skl->x+i,skl->y+i,BL_SKILL,skl->skill_id,&dummy,src); } + FALLTHROUGH // fall through ... case WL_EARTHSTRAIN: skill->unitsetting(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,(skl->type<<16)|skl->flag); @@ -3433,24 +4114,26 @@ int skill_timerskill(int tid, int64 tick, int id, intptr_t data) { return 0; } -bool skill_timerskill_dead_unknown(struct block_list *src, struct unit_data *ud, struct skill_timerskill *skl) +static bool skill_timerskill_dead_unknown(struct block_list *src, struct unit_data *ud, struct skill_timerskill *skl) { return false; } -void skill_timerskill_target_unknown(int tid, int64 tick, struct block_list *src, struct block_list *target, struct unit_data *ud, struct skill_timerskill *skl) +static void skill_timerskill_target_unknown(int tid, int64 tick, struct block_list *src, struct block_list *target, struct unit_data *ud, struct skill_timerskill *skl) { + nullpo_retv(skl); skill->attack(skl->type, src, src, target, skl->skill_id, skl->skill_lv, tick, skl->flag); } -void skill_timerskill_notarget_unknown(int tid, int64 tick, struct block_list *src, struct unit_data *ud, struct skill_timerskill *skl) +static void skill_timerskill_notarget_unknown(int tid, int64 tick, struct block_list *src, struct unit_data *ud, struct skill_timerskill *skl) { } /*========================================== * *------------------------------------------*/ -int skill_addtimerskill(struct block_list *src, int64 tick, int target, int x,int y, uint16 skill_id, uint16 skill_lv, int type, int flag) { +static int skill_addtimerskill(struct block_list *src, int64 tick, int target, int x, int y, uint16 skill_id, uint16 skill_lv, int type, int flag) +{ int i; struct unit_data *ud; nullpo_retr(1, src); @@ -3479,7 +4162,7 @@ int skill_addtimerskill(struct block_list *src, int64 tick, int target, int x,in /*========================================== * *------------------------------------------*/ -int skill_cleartimerskill (struct block_list *src) +static int skill_cleartimerskill(struct block_list *src) { int i; struct unit_data *ud; @@ -3512,12 +4195,12 @@ int skill_cleartimerskill (struct block_list *src) return 1; } -bool skill_cleartimerskill_exception(int skill_id) +static bool skill_cleartimerskill_exception(int skill_id) { return false; } -int skill_activate_reverberation(struct block_list *bl, va_list ap) +static int skill_activate_reverberation(struct block_list *bl, va_list ap) { struct skill_unit *su = NULL; struct skill_unit_group *sg = NULL; @@ -3530,14 +4213,14 @@ int skill_activate_reverberation(struct block_list *bl, va_list ap) if( su->alive && (sg = su->group) != NULL && sg->skill_id == WM_REVERBERATION && sg->unit_id == UNT_REVERBERATION ) { int64 tick = timer->gettick(); clif->changetraplook(bl,UNT_USED_TRAPS); - map->foreachinrange(skill->trap_splash, bl, skill->get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, bl, tick); + skill->trap_do_splash(bl, sg->skill_id, sg->skill_lv, sg->bl_flag, tick); su->limit = DIFF_TICK32(tick,sg->tick)+1500; sg->unit_id = UNT_USED_TRAPS; } return 0; } -int skill_reveal_trap(struct block_list *bl, va_list ap) +static int skill_reveal_trap(struct block_list *bl, va_list ap) { struct skill_unit *su = NULL; @@ -3545,20 +4228,36 @@ int skill_reveal_trap(struct block_list *bl, va_list ap) Assert_ret(bl->type == BL_SKILL); su = BL_UCAST(BL_SKILL, bl); - if (su->alive && su->group && skill->get_inf2(su->group->skill_id)&INF2_TRAP) { //Reveal trap. - //Change look is not good enough, the client ignores it as an actual trap still. [Skotlex] - //clif->changetraplook(bl, su->group->unit_id); - clif->getareachar_skillunit(&su->bl,su,AREA); + if (su->alive && su->group && skill->get_inf2(su->group->skill_id) & INF2_HIDDEN_TRAP) { //Reveal trap. + su->visible = true; + clif->skillunit_update(bl); return 1; } return 0; } +static void skill_castend_type(int type, struct block_list *src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int64 tick, int flag) +{ + switch (type) { + case CAST_GROUND: + nullpo_retv(bl); + skill->castend_pos2(src, bl->x, bl->y, skill_id, skill_lv, tick, flag); + break; + case CAST_NODAMAGE: + skill->castend_nodamage_id(src, bl, skill_id, skill_lv, tick, flag); + break; + case CAST_DAMAGE: + skill->castend_damage_id(src, bl, skill_id, skill_lv, tick, flag); + break; + } +} + /*========================================== * * *------------------------------------------*/ -int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int64 tick, int flag) { +static int skill_castend_damage_id(struct block_list *src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int64 tick, int flag) +{ struct map_session_data *sd = NULL; struct status_data *tstatus; struct status_change *sc; @@ -3744,7 +4443,7 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 if (!(flag&1) && sc && sc->data[SC_SOULLINK] && sc->data[SC_SOULLINK]->val2 == SL_MONK) { //Becomes a splash attack when Soul Linked. map->foreachinrange(skill->area_sub, bl, - skill->get_splash(skill_id, skill_lv),splash_target(src), + skill->get_splash(skill_id, skill_lv),skill->splash_target(src), src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1, skill->castend_damage_id); } else @@ -3755,14 +4454,14 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 clif->skill_nodamage(src,bl,skill_id,skill_lv,1); skill->area_temp[1] = 0; map->foreachinrange(skill->attack_area, src, - skill->get_splash(skill_id, skill_lv), splash_target(src), + skill->get_splash(skill_id, skill_lv), skill->splash_target(src), BF_WEAPON, src, src, skill_id, skill_lv, tick, flag, BCT_ENEMY); break; case KN_CHARGEATK: { bool path_exists = path->search_long(NULL, src, src->m, src->x, src->y, bl->x, bl->y,CELL_CHKWALL); unsigned int dist = distance_bl(src, bl); - uint8 dir = map->calc_dir(bl, src->x, src->y); + enum unit_dir dir = map->calc_dir(bl, src->x, src->y); // teleport to target (if not on WoE grounds) if( !map_flag_gvg2(src->m) && !map->list[src->m].flag.battleground && unit->movepos(src, bl->x, bl->y, 0, 1) ) @@ -3774,7 +4473,7 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 skill->blown(src, bl, dist, dir, 0); //HACK: since knockback officially defaults to the left, the client also turns to the left... therefore, // make the caster look in the direction of the target - unit->setdir(src, (dir+4)%8); + unit->set_dir(src, unit_get_opposite_dir(dir)); } } @@ -3791,7 +4490,7 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 //line of sight between caster and target. skill->area_temp[1] = bl->id; map->foreachinpath(skill->attack_area,src->m,src->x,src->y,bl->x,bl->y, - skill->get_splash(skill_id, skill_lv),skill->get_maxcount(skill_id,skill_lv), splash_target(src), + skill->get_splash(skill_id, skill_lv),skill->get_maxcount(skill_id,skill_lv), skill->splash_target(src), skill->get_type(skill_id),src,src,skill_id,skill_lv,tick,flag,BCT_ENEMY); break; @@ -3802,7 +4501,7 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 case NPC_THUNDERBREATH: skill->area_temp[1] = bl->id; map->foreachinpath(skill->attack_area,src->m,src->x,src->y,bl->x,bl->y, - skill->get_splash(skill_id, skill_lv),skill->get_maxcount(skill_id,skill_lv), splash_target(src), + skill->get_splash(skill_id, skill_lv),skill->get_maxcount(skill_id,skill_lv), skill->splash_target(src), skill->get_type(skill_id),src,src,skill_id,skill_lv,tick,flag,BCT_ENEMY); break; @@ -3813,15 +4512,16 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 case RG_BACKSTAP: { - uint8 dir = map->calc_dir(src, bl->x, bl->y), t_dir = unit->getdir(bl); - if ((!check_distance_bl(src, bl, 0) && !map->check_dir(dir, t_dir)) || bl->type == BL_SKILL) { + enum unit_dir dir = map->calc_dir(src, bl->x, bl->y); + enum unit_dir t_dir = unit->getdir(bl); + if ((!check_distance_bl(src, bl, 0) && map->check_dir(dir, t_dir) == 0) || bl->type == BL_SKILL) { status_change_end(src, SC_HIDING, INVALID_TIMER); skill->attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag); - dir = dir < 4 ? dir+4 : dir-4; // change direction [Celest] - unit->setdir(bl,dir); + dir = unit_get_opposite_dir(dir); // change direction [Celest] + unit->set_dir(bl, dir); } else if (sd) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); } break; @@ -3845,14 +4545,13 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 { short x, y, i = 2; // Move 2 cells for Issen(from target) struct block_list *mbl = bl; - short dir = 0; skill->attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); if( skill_id == MO_EXTREMITYFIST ) { mbl = src; i = 3; // for Asura(from caster) - status->set_sp(src, 0, 0); + status->set_sp(src, 0, STATUS_HEAL_DEFAULT); status_change_end(src, SC_EXPLOSIONSPIRITS, INVALID_TIMER); status_change_end(src, SC_BLADESTOP, INVALID_TIMER); #ifdef RENEWAL @@ -3862,18 +4561,18 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 status_change_end(src, SC_NJ_NEN, INVALID_TIMER); status_change_end(src, SC_HIDING, INVALID_TIMER); #ifdef RENEWAL - status->set_hp(src, max(status_get_max_hp(src)/100, 1), 0); + status->set_hp(src, max(status_get_max_hp(src)/100, 1), STATUS_HEAL_DEFAULT); #else // not RENEWAL - status->set_hp(src, 1, 0); + status->set_hp(src, 1, STATUS_HEAL_DEFAULT); #endif // RENEWAL } - dir = map->calc_dir(src,bl->x,bl->y); - if( dir > 0 && dir < 4) x = -i; - else if( dir > 4 ) x = i; - else x = 0; - if( dir > 2 && dir < 6 ) y = -i; - else if( dir == 7 || dir < 2 ) y = i; - else y = 0; + enum unit_dir dir = map->calc_dir(src, bl->x, bl->y); + if (Assert_chk(dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX)) { + map->freeblock_unlock(); // unblock before assert-returning + return 0; + } + x = i * dirx[dir]; + y = i * diry[dir]; if ((mbl == src || (!map_flag_gvg2(src->m) && !map->list[src->m].flag.battleground))) { // only NJ_ISSEN don't have slide effect in GVG if (!(unit->movepos(src, mbl->x+x, mbl->y+y, 1, 1))) { // The cell is not reachable (wall, object, ...), move next to the target @@ -3896,11 +4595,22 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 skill->attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); break; - //Splash attack skills. + case SU_BITE: + skill->attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag); + if (status->get_lv(src) >= 30 && (rnd() % 100 < (int)(status->get_lv(src) / 30) + 10)) // TODO: Need activation chance. + skill->addtimerskill(src, tick + skill->get_delay(skill_id, skill_lv), bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag); + break; + + case SU_PICKYPECK: + clif->skill_nodamage(src, bl, skill_id, skill_lv, 1); + break; + + // Splash attack skills. case AS_GRIMTOOTH: case MC_CARTREVOLUTION: case NPC_SPLASHATTACK: flag |= SD_PREAMBLE; // a fake packet will be sent for the first target to be hit + FALLTHROUGH case AS_SPLASHER: case HT_BLITZBEAT: case AC_SHOWER: @@ -3948,7 +4658,9 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 case KO_BAKURETSU: case GN_ILLUSIONDOPING: case MH_XENO_SLASHER: - if( flag&1 ) {//Recursive invocation + case SU_SCRATCH: + case SU_LUNATICCARROTBEAT: + if (flag&1) { //Recursive invocation // skill->area_temp[0] holds number of targets in area // skill->area_temp[1] holds the id of the original target // skill->area_temp[2] counts how many targets have already been processed @@ -3963,15 +4675,19 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 break; heal = skill->attack(skill->get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, sflag); - if( skill_id == NPC_VAMPIRE_GIFT && heal > 0 ) { + if (skill_id == NPC_VAMPIRE_GIFT && heal > 0) { clif->skill_nodamage(NULL, src, AL_HEAL, heal, 1); - status->heal(src,heal,0,0); + status->heal(src, heal, 0, STATUS_HEAL_DEFAULT); } + if (skill_id == SU_SCRATCH && status->get_lv(src) >= 30 && (rnd() % 100 < (int)(status->get_lv(src) / 30) + 10)) // TODO: Need activation chance. + skill->addtimerskill(src, tick + skill->get_delay(skill_id, skill_lv), bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag); } else { switch ( skill_id ) { case NJ_BAKUENRYU: case LG_EARTHDRIVE: case GN_CARTCANNON: + case SU_SCRATCH: + case SU_LUNATICCARROTBEAT: clif->skill_nodamage(src,bl,skill_id,skill_lv,1); break; case SR_TIGERCANNON: @@ -3990,13 +4706,19 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 skill->area_temp[0] = 0; skill->area_temp[1] = bl->id; skill->area_temp[2] = 0; - if( skill_id == WL_CRIMSONROCK ) { + if (skill_id == WL_CRIMSONROCK) { skill->area_temp[4] = bl->x; skill->area_temp[5] = bl->y; } + if (skill_id == SU_LUNATICCARROTBEAT) { + skill->area_temp[3] = 0; + } - if( skill_id == NC_VULCANARM ) - if (sd) pc->overheat(sd,1); + if (skill_id == NC_VULCANARM) { + if (sd != NULL) { + pc->overheat(sd,1); + } + } // if skill damage should be split among targets, count them //SD_LEVEL -> Forced splash damage for Auto Blitz-Beat -> count targets @@ -4005,7 +4727,21 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 skill->area_temp[0] = map->foreachinrange(skill->area_sub, bl, (skill_id == AS_SPLASHER)?1:skill->get_splash(skill_id, skill_lv), BL_CHAR, src, skill_id, skill_lv, tick, BCT_ENEMY, skill->area_sub_count); // recursive invocation of skill->castend_damage_id() with flag|1 - map->foreachinrange(skill->area_sub, bl, skill->get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill->castend_damage_id); + map->foreachinrange(skill->area_sub, bl, skill->get_splash(skill_id, skill_lv), skill->splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill->castend_damage_id); + + if (skill_id == AS_SPLASHER) { + // Prevent double item consumption when the target explodes (item requirements have already been processed in skill_castend_nodamage_id) + flag |= 1; + } + + if (sd && skill_id == SU_LUNATICCARROTBEAT) { + short item_idx = pc->search_inventory(sd, ITEMID_CARROT); + + if (item_idx >= 0) { + pc->delitem(sd, item_idx, 1, 0, 1, LOG_TYPE_CONSUME); + skill->area_temp[3] = 1; + } + } } break; @@ -4063,12 +4799,12 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 if(idb_exists(skill->bowling_db, bl->id)) break; // Random direction - dir = rnd()%8; + dir = rnd() % UNIT_DIR_MAX; } else { // Create an empty list of already hit targets db_clear(skill->bowling_db); // Direction is walkpath - dir = (unit->getdir(src)+4)%8; + dir = unit_get_opposite_dir(unit->getdir(src)); } // Add current target to the list of already hit targets idb_put(skill->bowling_db, bl->id, bl); @@ -4077,6 +4813,10 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 ty = bl->y; for(i=0;i<c;i++) { // Target coordinates (get changed even if knockback fails) + if (Assert_chk(dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX)) { + map->freeblock_unlock(); // unblock before assert-returning + return 0; + } tx -= dirx[dir]; ty -= diry[dir]; // If target cell is a wall then break @@ -4086,9 +4826,9 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 // Splash around target cell, but only cells inside area; we first have to check the area is not negative if((max(min_x,tx-1) <= min(max_x,tx+1)) && (max(min_y,ty-1) <= min(max_y,ty+1)) && - (map->foreachinarea(skill->area_sub, bl->m, max(min_x,tx-1), max(min_y,ty-1), min(max_x,tx+1), min(max_y,ty+1), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY, skill->area_sub_count))) { + (map->foreachinarea(skill->area_sub, bl->m, max(min_x,tx-1), max(min_y,ty-1), min(max_x,tx+1), min(max_y,ty+1), skill->splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY, skill->area_sub_count))) { // Recursive call - map->foreachinarea(skill->area_sub, bl->m, max(min_x,tx-1), max(min_y,ty-1), min(max_x,tx+1), min(max_y,ty+1), splash_target(src), src, skill_id, skill_lv, tick, (flag|BCT_ENEMY)+1, skill->castend_damage_id); + map->foreachinarea(skill->area_sub, bl->m, max(min_x,tx-1), max(min_y,ty-1), min(max_x,tx+1), min(max_y,ty+1), skill->splash_target(src), src, skill_id, skill_lv, tick, (flag|BCT_ENEMY)+1, skill->castend_damage_id); // Self-collision if(bl->x >= min_x && bl->x <= max_x && bl->y >= min_y && bl->y <= max_y) skill->attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,(flag&0xFFF)>0?SD_ANIMATION:0); @@ -4105,18 +4845,24 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 if (bl->id==skill->area_temp[1]) break; if (skill->attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,SD_ANIMATION)) - skill->blown(src,bl,skill->area_temp[2],-1,0); + skill->blown(src, bl, skill->area_temp[2], UNIT_DIR_UNDEFINED, 0); } else { - int x=bl->x,y=bl->y,i,dir; - dir = map->calc_dir(bl,src->x,src->y); + int x = bl->x; + int y = bl->y; + int i; + enum unit_dir dir = map->calc_dir(bl, src->x, src->y); skill->area_temp[1] = bl->id; skill->area_temp[2] = skill->get_blewcount(skill_id,skill_lv); // all the enemies between the caster and the target are hit, as well as the target if (skill->attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,0)) - skill->blown(src,bl,skill->area_temp[2],-1,0); + skill->blown(src, bl, skill->area_temp[2], UNIT_DIR_UNDEFINED, 0); for (i=0;i<4;i++) { map->foreachincell(skill->area_sub,bl->m,x,y,BL_CHAR,src,skill_id,skill_lv, tick,flag|BCT_ENEMY|1,skill->castend_damage_id); + if (Assert_chk(dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX)) { + map->freeblock_unlock(); // unblock before assert-returning + return 0; + } x += dirx[dir]; y += diry[dir]; } @@ -4240,7 +4986,7 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 case SL_STUN: if (sd && !battle_config.allow_es_magic_pc && bl->type != BL_MOB) { status->change_start(src,src,SC_STUN,10000,skill_lv,0,0,0,500,SCFLAG_FIXEDTICK|SCFLAG_FIXEDRATE); - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } skill->attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag); @@ -4281,6 +5027,7 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 if( (tsc = status->get_sc(bl)) && tsc->data[SC_HIDING] ) break; } + FALLTHROUGH case HVAN_EXPLOSION: if (src != bl) skill->attack(BF_MISC,src,src,bl,skill_id,skill_lv,tick,flag); @@ -4308,7 +5055,7 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 src, src, bl, skill_id, skill_lv, tick, flag); if (heal > 0){ clif->skill_nodamage(NULL, src, AL_HEAL, heal, 1); - status->heal(src, heal, 0, 0); + status->heal(src, heal, 0, STATUS_HEAL_DEFAULT); } } break; @@ -4337,7 +5084,7 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 if(rnd()%100 < (10 + 3*skill_lv)) { if( !sd || pc->checkskill(sd,KN_SPEARBOOMERANG) == 0 ) break; // Spear Boomerang auto cast chance only works if you have mastered Spear Boomerang. - skill->blown(src,bl,6,-1,0); + skill->blown(src, bl, 6, UNIT_DIR_UNDEFINED, 0); skill->addtimerskill(src,tick+800,bl->id,0,0,skill_id,skill_lv,BF_WEAPON,flag); skill->castend_damage_id(src,bl,KN_SPEARBOOMERANG,1,tick,0); } @@ -4345,7 +5092,7 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 case RK_PHANTOMTHRUST: { struct map_session_data *tsd = BL_CAST(BL_PC, bl); - unit->setdir(src,map->calc_dir(src, bl->x, bl->y)); + unit->set_dir(src, map->calc_dir(src, bl->x, bl->y)); clif->skill_nodamage(src,bl,skill_id,skill_lv,1); skill->blown(src,bl,distance_bl(src,bl)-1,unit->getdir(src),0); @@ -4359,16 +5106,13 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 case KO_JYUMONJIKIRI: case GC_DARKILLUSION: { - short x, y; - short dir = map->calc_dir(bl, src->x, src->y); - - if ( dir < 4 ) { - x = bl->x + 2 * (dir > 0) - 3 * (dir > 0); - y = bl->y + 1 - (dir / 2) - (dir > 2); - } else { - x = bl->x + 2 * (dir > 4) - 1 * (dir > 4); - y = bl->y + (dir / 6) - 1 + (dir > 6); + enum unit_dir dir = map->calc_dir(bl, src->x, src->y); + if (Assert_chk(dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX)) { + map->freeblock_unlock(); // unblock before assert-returning + return 0; } + short x = bl->x + dirx[dir]; + short y = bl->y + diry[dir]; if ( unit->movepos(src, x, y, 1, 1) ) { clif->slide(src, x, y); @@ -4383,12 +5127,12 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 if( sc && sc->data[SC_COMBOATTACK] && sc->data[SC_COMBOATTACK]->val1 == GC_WEAPONBLOCKING ) skill->attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); else if( sd ) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_GC_WEAPONBLOCKING,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_GC_WEAPONBLOCKING, 0, 0); break; case GC_CROSSRIPPERSLASHER: if( sd && !(sc && sc->data[SC_ROLLINGCUTTER]) ) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_CONDITION,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_CONDITION, 0, 0); else { skill->attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); @@ -4419,7 +5163,7 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 heal = 0; // Don't absorb heal from Ice Walls or other skill units. if( heal && rnd()%100 < rate ) { - status->heal(src, heal, 0, 0); + status->heal(src, heal, 0, STATUS_HEAL_DEFAULT); clif->skill_nodamage(NULL, src, AL_HEAL, heal, 1); } } @@ -4489,20 +5233,9 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 if( !skill->check_condition_castbegin(sd, spell_skill_id, spell_skill_lv) ) break; - switch( skill->get_casttype(spell_skill_id) ) { - case CAST_GROUND: - skill->castend_pos2(src, bl->x, bl->y, spell_skill_id, spell_skill_lv, tick, 0); - break; - case CAST_NODAMAGE: - skill->castend_nodamage_id(src, bl, spell_skill_id, spell_skill_lv, tick, 0); - break; - case CAST_DAMAGE: - skill->castend_damage_id(src, bl, spell_skill_id, spell_skill_lv, tick, 0); - break; - } - + skill->castend_type(skill->get_casttype(spell_skill_id), src, bl, spell_skill_id, spell_skill_lv, tick, 0); sd->ud.canact_tick = tick + skill->delay_fix(src, spell_skill_id, spell_skill_lv); - clif->status_change(src, SI_POSTDELAY, 1, skill->delay_fix(src, spell_skill_id, spell_skill_lv), 0, 0, 0); + clif->status_change(src, status->get_sc_icon(SC_POSTDELAY), status->get_sc_relevant_bl_types(SC_POSTDELAY), 1, skill->delay_fix(src, spell_skill_id, spell_skill_lv), 0, 0, 0); cooldown = skill->get_cooldown(spell_skill_id, spell_skill_lv); for (i = 0; i < ARRAYLENGTH(sd->skillcooldown) && sd->skillcooldown[i].id; i++) { @@ -4536,24 +5269,27 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 skill->attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag|ELE_DARK); break; case RA_WUGSTRIKE: - if( sd && pc_isridingwug(sd) ){ - short x[8]={0,-1,-1,-1,0,1,1,1}; - short y[8]={1,1,0,-1,-1,-1,0,1}; - uint8 dir = map->calc_dir(bl, src->x, src->y); - - if( unit->movepos(src, bl->x+x[dir], bl->y+y[dir], 1, 1) ) - { - clif->slide(src, bl->x+x[dir], bl->y+y[dir]); + if (sd != NULL && pc_isridingwug(sd)) { + enum unit_dir dir = map->calc_dir(bl, src->x, src->y); + if (Assert_chk(dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX)) { + map->freeblock_unlock(); // unblock before assert-returning + return 0; + } + short x = bl->x + dirx[dir]; + short y = bl->y + diry[dir]; + if (unit->movepos(src, x, y, 1, 1) != 0) { + clif->slide(src, x, y); clif->fixpos(src); skill->attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag); } break; } + FALLTHROUGH case RA_WUGBITE: if( path->search(NULL,src,src->m,src->x,src->y,bl->x,bl->y,1,CELL_CHKNOREACH) ) { skill->attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); }else if( sd && skill_id == RA_WUGBITE ) // Only RA_WUGBITE has the skill fail message. - clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; @@ -4572,10 +5308,10 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 if( !(sg->unit_id == UNT_USED_TRAPS || (sg->unit_id == UNT_ANKLESNARE && sg->val2 != 0 )) ) { struct item item_tmp; memset(&item_tmp,0,sizeof(item_tmp)); - item_tmp.nameid = sg->item_id?sg->item_id:ITEMID_TRAP; + item_tmp.nameid = sg->item_id ? sg->item_id : ITEMID_BOOBY_TRAP; item_tmp.identify = 1; if( item_tmp.nameid ) - map->addflooritem(bl, &item_tmp, 1, bl->m, bl->x, bl->y, 0, 0, 0, 0); + map->addflooritem(bl, &item_tmp, 1, bl->m, bl->x, bl->y, 0, 0, 0, 0, false); } skill->delunit(su); } @@ -4589,7 +5325,7 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 status_change_end(bl, SC_CLOAKING, INVALID_TIMER); status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER); // Need confirm it. } else { - map->foreachinrange(skill->area_sub, bl, skill->get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill->castend_damage_id); + map->foreachinrange(skill->area_sub, bl, skill->get_splash(skill_id, skill_lv), skill->splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill->castend_damage_id); clif->skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, BDT_SKILL); if( sd ) pc->overheat(sd,1); } @@ -4607,7 +5343,7 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 // Destination area skill->area_temp[4] = x; skill->area_temp[5] = y; - map->foreachinrange(skill->area_sub, bl, skill->get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill->castend_damage_id); + map->foreachinrange(skill->area_sub, bl, skill->get_splash(skill_id, skill_lv), skill->splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill->castend_damage_id); skill->addtimerskill(src,tick + 800,src->id,x,y,skill_id,skill_lv,0,flag); // To teleport Self clif->skill_damage(src,src,tick,status_get_amotion(src),0,-30000,1,skill_id,skill_lv,BDT_SKILL); } @@ -4666,7 +5402,7 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 status_change_end(bl, SC_HIDING, INVALID_TIMER); status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER); } else{ - map->foreachinrange(skill->area_sub, bl, skill->get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill->castend_damage_id); + map->foreachinrange(skill->area_sub, bl, skill->get_splash(skill_id, skill_lv), skill->splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill->castend_damage_id); clif->skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, BDT_SKILL); } break; @@ -4711,7 +5447,7 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 skill->attack(skill->get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag); status_change_end(bl, SC_POISON, INVALID_TIMER); } else if( sd ) - clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); } break; @@ -4803,7 +5539,7 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 if(flag & 1) skill->attack(skill->get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag); else { - map->foreachinrange(skill->area_sub, bl, skill->get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag | BCT_ENEMY | SD_SPLASH | 1, skill->castend_damage_id); + map->foreachinrange(skill->area_sub, bl, skill->get_splash(skill_id, skill_lv), skill->splash_target(src), src, skill_id, skill_lv, tick, flag | BCT_ENEMY | SD_SPLASH | 1, skill->castend_damage_id); } break; @@ -4824,6 +5560,15 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 skill->attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag); break; + case SU_SV_STEMSPEAR: + skill->attack(skill->get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag); + if (status->get_lv(src) >= 30 && (rnd() % 100 < (int)(status->get_lv(src) / 30) + 10)) // TODO: Need activation chance. + skill->addtimerskill(src, tick + skill->get_delay(skill_id, skill_lv), bl->id, 0, 0, skill_id, skill_lv, (skill_id == SU_SV_STEMSPEAR) ? BF_MAGIC : BF_WEAPON, flag); + break; + case SU_SCAROFTAROU: + sc_start(src, bl, status->skill2sc(skill_id), 10, skill_lv, skill->get_time(skill_id, skill_lv)); // TODO: What's the activation chance for the effect? + break; + case 0:/* no skill - basic/normal attack */ if(sd) { if (flag & 3){ @@ -4867,8 +5612,12 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 return 0; } -bool skill_castend_damage_id_unknown(struct block_list* src, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag, struct status_data *tstatus, struct status_change *sc) +static bool skill_castend_damage_id_unknown(struct block_list *src, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag, struct status_data *tstatus, struct status_change *sc) { + nullpo_retr(true, skill_id); + nullpo_retr(true, skill_lv); + nullpo_retr(true, tick); + nullpo_retr(true, tstatus); ShowWarning("skill_castend_damage_id: Unknown skill used:%d\n", *skill_id); clif->skill_damage(src, bl, *tick, status_get_amotion(src), tstatus->dmotion, 0, abs(skill->get_num(*skill_id, *skill_lv)), @@ -4880,7 +5629,8 @@ bool skill_castend_damage_id_unknown(struct block_list* src, struct block_list * /*========================================== * *------------------------------------------*/ -int skill_castend_id(int tid, int64 tick, int id, intptr_t data) { +static int skill_castend_id(int tid, int64 tick, int id, intptr_t data) +{ struct block_list *target, *src; struct map_session_data *sd; struct mob_data *md; @@ -4917,12 +5667,11 @@ int skill_castend_id(int tid, int64 tick, int id, intptr_t data) { return 0; } - if( sd && ud->skilltimer != INVALID_TIMER && (pc->checkskill(sd,SA_FREECAST) > 0 || ud->skill_id == LG_EXEEDBREAK) ) + if (sd && ud->skilltimer != INVALID_TIMER && (pc->checkskill(sd, SA_FREECAST) > 0 || ud->skill_id == LG_EXEEDBREAK || (skill->get_inf2(ud->skill_id) & INF2_FREE_CAST_REDUCED) != 0)) {// restore original walk speed ud->skilltimer = INVALID_TIMER; status_calc_bl(&sd->bl, SCB_SPEED|SCB_ASPD); } - ud->skilltimer = INVALID_TIMER; } @@ -4933,6 +5682,8 @@ int skill_castend_id(int tid, int64 tick, int id, intptr_t data) { // Use a do so that you can break out of it when the skill fails. do { + bool is_asura = (ud->skill_id == MO_EXTREMITYFIST); + if(!target || target->prev==NULL) break; if(src->m != target->m || status->isdead(src)) break; @@ -4957,6 +5708,8 @@ int skill_castend_id(int tid, int64 tick, int id, intptr_t data) { ud->skilltimer=tid; return skill->castend_pos(tid,tick,id,data); case GN_WALLOFTHORN: + case SU_CN_POWDERING: + case SU_SV_ROOTTWIST: ud->skillx = target->x; ud->skilly = target->y; ud->skilltimer = tid; @@ -4968,8 +5721,9 @@ int skill_castend_id(int tid, int64 tick, int id, intptr_t data) { } if(ud->skill_id == RG_BACKSTAP) { - uint8 dir = map->calc_dir(src,target->x,target->y),t_dir = unit->getdir(target); - if(check_distance_bl(src, target, 0) || map->check_dir(dir,t_dir)) { + enum unit_dir dir = map->calc_dir(src, target->x, target->y); + enum unit_dir t_dir = unit->getdir(target); + if (check_distance_bl(src, target, 0) || map->check_dir(dir, t_dir) != 0) { break; } } @@ -5017,7 +5771,7 @@ int skill_castend_id(int tid, int64 tick, int id, intptr_t data) { } if( sd && (inf2&INF2_CHORUS_SKILL) && skill->check_pc_partner(sd, ud->skill_id, &ud->skill_lv, 1, 0) < 1 ) { - clif->skill_fail(sd, ud->skill_id, USESKILL_FAIL_NEED_HELPER, 0); + clif->skill_fail(sd, ud->skill_id, USESKILL_FAIL_NEED_HELPER, 0, 0); break; } @@ -5025,7 +5779,7 @@ int skill_castend_id(int tid, int64 tick, int id, intptr_t data) { if (BL_UCCAST(BL_MOB, target)->class_ == MOBID_EMPELIUM) break; } else if (inf && battle->check_target(src, target, inf) <= 0) { - if (sd) clif->skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0); + if (sd) clif->skill_fail(sd, ud->skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } else if (ud->skill_id == RK_PHANTOMTHRUST && target->type != BL_MOB) { if( !map_flag_vs(src->m) && battle->check_target(src,target,BCT_PARTY) <= 0 ) @@ -5037,7 +5791,7 @@ int skill_castend_id(int tid, int64 tick, int id, intptr_t data) { && rnd() % 100 < 75 ) { // Fogwall makes all offensive-type targeted skills fail at 75% - if (sd) clif->skill_fail(sd, ud->skill_id, USESKILL_FAIL_LEVEL, 0); + if (sd) clif->skill_fail(sd, ud->skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } } @@ -5056,7 +5810,7 @@ int skill_castend_id(int tid, int64 tick, int id, intptr_t data) { !check_distance_bl(src, target, skill->get_range2(src,ud->skill_id,ud->skill_lv)+battle_config.skill_add_range)) { if (sd) { - clif->skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, ud->skill_id, USESKILL_FAIL_LEVEL, 0, 0); if(battle_config.skill_out_range_consume) //Consume items anyway. [Skotlex] skill->consume_requirement(sd,ud->skill_id,ud->skill_lv,3); } @@ -5086,7 +5840,7 @@ int skill_castend_id(int tid, int64 tick, int id, intptr_t data) { if (ud->walktimer != INVALID_TIMER && ud->skill_id != TK_RUN && ud->skill_id != RA_WUGDASH) unit->stop_walking(src, STOPWALKING_FLAG_FIXPOS); - if( !sd || sd->skillitem != ud->skill_id || skill->get_delay(ud->skill_id,ud->skill_lv) ) + if (sd == NULL || sd->autocast.skill_id != ud->skill_id || skill->get_delay(ud->skill_id,ud->skill_lv) != 0) ud->canact_tick = tick + skill->delay_fix(src, ud->skill_id, ud->skill_lv); // Tests show wings don't overwrite the delay but skill scrolls do. [Inkfish] if (sd) { // Cooldown application int i, cooldown = skill->get_cooldown(ud->skill_id, ud->skill_lv); @@ -5100,7 +5854,7 @@ int skill_castend_id(int tid, int64 tick, int id, intptr_t data) { skill->blockpc_start(sd, ud->skill_id, cooldown); } if( battle_config.display_status_timers && sd ) - clif->status_change(src, SI_POSTDELAY, 1, skill->delay_fix(src, ud->skill_id, ud->skill_lv), 0, 0, 0); + clif->status_change(src, status->get_sc_icon(SC_POSTDELAY), status->get_sc_relevant_bl_types(SC_POSTDELAY), 1, skill->delay_fix(src, ud->skill_id, ud->skill_lv), 0, 0, 0); if( sd ) { switch( ud->skill_id ) @@ -5155,7 +5909,7 @@ int skill_castend_id(int tid, int64 tick, int id, intptr_t data) { } if( sd && ud->skill_id != SA_ABRACADABRA && ud->skill_id != WM_RANDOMIZESPELL ) // they just set the data so leave it as it is.[Inkfish] - sd->skillitem = sd->skillitemlv = 0; + pc->autocast_clear(sd); if (ud->skilltimer == INVALID_TIMER) { if(md) md->skill_idx = -1; @@ -5163,8 +5917,9 @@ int skill_castend_id(int tid, int64 tick, int id, intptr_t data) { ud->skill_lv = ud->skilltarget = 0; } - if (src->id != target->id) - unit->setdir(src, map->calc_dir(src, target->x, target->y)); + // Asura Strike caster doesn't look to their target in the end + if (src->id != target->id && !is_asura) + unit->set_dir(src, map->calc_dir(src, target->x, target->y)); map->freeblock_unlock(); return 1; @@ -5175,7 +5930,7 @@ int skill_castend_id(int tid, int64 tick, int id, intptr_t data) { //When Asura fails... (except when it fails from Fog of Wall) //Consume SP/spheres skill->consume_requirement(sd,ud->skill_id, ud->skill_lv,1); - status->set_sp(src, 0, 0); + status->set_sp(src, 0, STATUS_HEAL_DEFAULT); sc = &sd->sc; if (sc->count) { //End states @@ -5187,45 +5942,45 @@ int skill_castend_id(int tid, int64 tick, int id, intptr_t data) { } if (target && target->m == src->m) { //Move character to target anyway. - int dir, x, y; - dir = map->calc_dir(src,target->x,target->y); - if( dir > 0 && dir < 4) x = -2; - else if( dir > 4 ) x = 2; - else x = 0; - if( dir > 2 && dir < 6 ) y = -2; - else if( dir == 7 || dir < 2 ) y = 2; - else y = 0; - if (unit->movepos(src, src->x+x, src->y+y, 1, 1)) { + enum unit_dir dir = map->calc_dir(src, target->x, target->y); + Assert_ret(dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX); + int dist = 3; // number of cells that asura caster will walk + int x = dist * dirx[dir]; + int y = dist * diry[dir]; + + if (unit->movepos(src, src->x + x, src->y + y, 1, 1) != 0) { //Display movement + animation. - clif->slide(src,src->x,src->y); + clif->slide(src, src->x, src->y); clif->spiritball(src); } - clif->skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0); + // "Skill Failed" message was already shown when checking that target is invalid + //clif->skill_fail(sd, ud->skill_id, USESKILL_FAIL_LEVEL, 0, 0); } } - if( !sd || sd->skillitem != ud->skill_id || skill->get_delay(ud->skill_id,ud->skill_lv) ) + if (sd == NULL || sd->autocast.skill_id != ud->skill_id || skill->get_delay(ud->skill_id,ud->skill_lv) != 0) ud->canact_tick = tick; ud->skill_id = ud->skill_lv = ud->skilltarget = 0; //You can't place a skill failed packet here because it would be //sent in ALL cases, even cases where skill_check_condition fails //which would lead to double 'skill failed' messages u.u [Skotlex] if(sd) - sd->skillitem = sd->skillitemlv = 0; + pc->autocast_clear(sd); else if(md) md->skill_idx = -1; return 0; } -bool skill_castend_id_unknown(struct unit_data *ud, struct block_list *src, struct block_list *target) +static bool skill_castend_id_unknown(struct unit_data *ud, struct block_list *src, struct block_list *target) { - return false; + return false; } /*========================================== * *------------------------------------------*/ -int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int64 tick, int flag) { +static int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int64 tick, int flag) +{ struct map_session_data *sd, *dstsd; struct mob_data *md, *dstmd; struct homun_data *hd; @@ -5283,7 +6038,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case AL_DECAGI: case AB_RENOVATIO: case AB_HIGHNESSHEAL: - clif->skill_fail(sd,skill_id,USESKILL_FAIL_TOTARGET,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_TOTARGET, 0, 0); return 0; default: if (skill->castend_nodamage_id_mado_unknown(src, bl, &skill_id, &skill_lv, &tick, &flag)) @@ -5299,9 +6054,10 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin switch (skill_id) { case HLIF_HEAL: // [orn] if (bl->type != BL_HOM) { - if (sd) clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0) ; + if (sd) clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0) ; break ; } + FALLTHROUGH case AL_HEAL: /** @@ -5316,7 +6072,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if (sd && battle->check_undead(tstatus->race,tstatus->def_ele) && skill_id != AL_INCAGI) { if (battle->check_target(src, bl, BCT_ENEMY) < 1) { //Offensive heal does not works on non-enemies. [Skotlex] - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } return skill->castend_damage_id(src, bl, skill_id, skill_lv, tick, flag); @@ -5417,6 +6173,10 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin * Arch Bishop **/ case AB_HIGHNESSHEAL: + /** + * Summoner + */ + case SU_TUNABELLY: { int heal = skill->calc_heal(src, bl, (skill_id == AB_HIGHNESSHEAL)?AL_HEAL:skill_id, (skill_id == AB_HIGHNESSHEAL)?10:skill_lv, true); int heal_get_jobexp; @@ -5427,7 +6187,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if (status->isimmune(bl) || (dstmd != NULL && (dstmd->class_ == MOBID_EMPELIUM || mob_is_battleground(dstmd)))) heal = 0; - if (sd && dstsd && sd->status.partner_id == dstsd->status.char_id && (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.sex == 0) + if (sd != NULL && dstsd != NULL && sd->status.partner_id == dstsd->status.char_id && (sd->job & MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.sex == 0) heal = heal * 2; if (tsc && tsc->count) @@ -5446,16 +6206,19 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin else if (tsc->data[SC_BERSERK]) heal = 0; //Needed so that it actually displays 0 when healing. } + if (skill_id == AL_HEAL) { + status_change_end(bl, SC_BITESCAR, INVALID_TIMER); + } clif->skill_nodamage (src, bl, skill_id, heal, 1); if( tsc && tsc->data[SC_AKAITSUKI] && heal && skill_id != HLIF_HEAL ) heal = ~heal + 1; - heal_get_jobexp = status->heal(bl,heal,0,0); + heal_get_jobexp = status->heal(bl, heal, 0, STATUS_HEAL_DEFAULT); if(sd && dstsd && heal > 0 && sd != dstsd && battle_config.heal_exp > 0){ heal_get_jobexp = heal_get_jobexp * battle_config.heal_exp / 100; if (heal_get_jobexp <= 0) heal_get_jobexp = 1; - pc->gainexp (sd, bl, 0, heal_get_jobexp, false); + pc->gainexp(sd, bl, 0, heal_get_jobexp, false); } } break; @@ -5463,7 +6226,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case PR_REDEMPTIO: if (sd && !(flag&1)) { if (sd->status.party_id == 0) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } skill->area_temp[0] = 0; @@ -5472,7 +6235,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin src,skill_id,skill_lv,tick, flag|BCT_PARTY|1, skill->castend_nodamage_id); if (skill->area_temp[0] == 0) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } skill->area_temp[0] = 5 - skill->area_temp[0]; // The actual penalty... @@ -5482,8 +6245,8 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin clif->updatestatus(sd,SP_BASEEXP); clif->updatestatus(sd,SP_JOBEXP); } - status->set_hp(src, 1, 0); - status->set_sp(src, 0, 0); + status->set_hp(src, 1, STATUS_HEAL_DEFAULT); + status->set_sp(src, 0, STATUS_HEAL_DEFAULT); break; } else if (status->isdead(bl) && flag&1) { //Revive skill->area_temp[0]++; //Count it in, then fall-through to the Resurrection code. @@ -5494,7 +6257,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case ALL_RESURRECTION: if(sd && (map_flag_gvg2(bl->m) || map->list[bl->m].flag.battleground)) { //No reviving in WoE grounds! - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } if (!status->isdead(bl)) @@ -5531,7 +6294,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if (jexp < 1) jexp = 1; } if(exp > 0 || jexp > 0) - pc->gainexp (sd, bl, exp, jexp, false); + pc->gainexp(sd, bl, exp, jexp, false); } } } @@ -5584,9 +6347,9 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if (sd) { // player-casted - sd->state.abra_flag = 1; - sd->skillitem = abra_skill_id; - sd->skillitemlv = abra_skill_lv; + sd->autocast.type = AUTOCAST_ABRA; + sd->autocast.skill_id = abra_skill_id; + sd->autocast.skill_lv = abra_skill_lv; clif->item_skill(sd, abra_skill_id, abra_skill_lv); } else { // mob-casted @@ -5647,7 +6410,8 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin break; case SA_SUMMONMONSTER: clif->skill_nodamage(src,bl,skill_id,skill_lv,1); - if (sd) mob->once_spawn(sd, src->m, src->x, src->y," --ja--", -1, 1, "", SZ_SMALL, AI_NONE); + if (sd != NULL) + mob->once_spawn(sd, src->m, src->x, src->y, DEFAULT_MOB_JNAME, -1, 1, "", SZ_SMALL, AI_NONE); break; case SA_LEVELUP: clif->skill_nodamage(src,bl,skill_id,skill_lv,1); @@ -5655,7 +6419,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin break; case SA_INSTANTDEATH: clif->skill_nodamage(src,bl,skill_id,skill_lv,1); - status->set_hp(bl,1,0); + status->set_hp(bl, 1, STATUS_HEAL_DEFAULT); break; case SA_QUESTION: case SA_GRAVITY: @@ -5668,7 +6432,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin int class_; if ( sd && dstmd->status.mode&MD_BOSS ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } class_ = skill_id == SA_MONOCELL ? MOBID_PORING : mob->get_random_id(4, 1, 0); @@ -5688,7 +6452,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case SA_DEATH: if ( sd && dstmd && dstmd->status.mode&MD_BOSS ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } clif->skill_nodamage(src,bl,skill_id,skill_lv,1); @@ -5715,8 +6479,8 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case CR_PROVIDENCE: if(sd && dstsd){ //Check they are not another crusader [Skotlex] - if ((dstsd->class_&MAPID_UPPERMASK) == MAPID_CRUSADER) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + if ((dstsd->job & MAPID_UPPERMASK) == MAPID_CRUSADER) { + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); map->freeblock_unlock(); return 1; } @@ -5729,9 +6493,9 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin { struct status_change* sc = status->get_sc(src); - if( sd && dstsd && (dstsd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER && dstsd->status.sex == sd->status.sex ) { + if (sd != NULL && dstsd != NULL && (dstsd->job & MAPID_UPPERMASK) == MAPID_BARDDANCER && dstsd->status.sex == sd->status.sex) { // Cannot cast on another bard/dancer-type class of the same gender as caster - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); map->freeblock_unlock(); return 1; } @@ -5748,7 +6512,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin status_change_end(bl, SC_MARIONETTE, INVALID_TIMER); } else { if( sd ) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); map->freeblock_unlock(); return 1; } @@ -5765,7 +6529,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case SA_LIGHTNINGLOADER: case SA_SEISMICWEAPON: if (dstsd) { - if(dstsd->status.weapon == W_FIST || + if (dstsd->weapontype == W_FIST || (dstsd->sc.count && !dstsd->sc.data[type] && ( //Allow re-enchanting to lengthen time. [Skotlex] dstsd->sc.data[SC_PROPERTYFIRE] || @@ -5777,7 +6541,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin dstsd->sc.data[SC_ENCHANTPOISON] )) ) { - if (sd) clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + if (sd) clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); clif->skill_nodamage(src,bl,skill_id,skill_lv,0); break; } @@ -5785,7 +6549,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin // 100% success rate at lv4 & 5, but lasts longer at lv5 if(!clif->skill_nodamage(src,bl,skill_id,skill_lv, sc_start(src,bl,type,(60+skill_lv*10),skill_lv, skill->get_time(skill_id,skill_lv)))) { if (sd) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); if (skill->break_equip(bl, EQP_WEAPON, 10000, BCT_PARTY) && sd && sd != dstsd) clif->message(sd->fd, msg_sd(sd,869)); // "You broke the target's weapon." } @@ -5825,6 +6589,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case PR_KYRIE: case MER_KYRIE: + case SU_TUNAPARTY: clif->skill_nodamage(bl, bl, skill_id, -1, sc_start(src, bl, type, 100, skill_lv, skill->get_time(skill_id, skill_lv))); break; @@ -5837,24 +6602,18 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin clif->skill_nodamage (src,src,skill_id,skill_lv,1); // Initiate 10% of your damage becomes fire element. sc_start4(src,src,SC_SUB_WEAPONPROPERTY,100,3,20,0,0,skill->get_time2(skill_id, skill_lv)); - if( sd ) - skill->blockpc_start(sd, skill_id, skill->get_time(skill_id, skill_lv)); - else if( bl->type == BL_MER ) - skill->blockmerc_start(BL_UCAST(BL_MER, bl), skill_id, skill->get_time(skill_id, skill_lv)); break; case TK_JUMPKICK: /* Check if the target is an enemy; if not, skill should fail so the character doesn't unit->movepos (exploitable) */ - if( battle->check_target(src, bl, BCT_ENEMY) > 0 ) - { - if( unit->movepos(src, bl->x, bl->y, 1, 1) ) - { - skill->attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); - clif->slide(src,bl->x,bl->y); + if (battle->check_target(src, bl, BCT_ENEMY) > 0) { + if (unit->movepos(src, bl->x, bl->y, 1, 1)) { + skill->attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag); + clif->slide(src, bl->x, bl->y); } + } else if (sd != NULL) { + clif->skill_fail(sd, skill_id, USESKILL_FAIL, 0, 0); } - else - clif->skill_fail(sd,skill_id,USESKILL_FAIL,0); break; case AL_INCAGI: @@ -5941,9 +6700,18 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case RK_ABUNDANCE: case RK_CRUSHSTRIKE: case ALL_ODINS_POWER: + case SU_FRESHSHRIMP: + case SU_ARCLOUSEDASH: clif->skill_nodamage(src,bl,skill_id,skill_lv, sc_start(src,bl,type,100,skill_lv,skill->get_time(skill_id,skill_lv))); break; + // Works just like the above list of skills, except animation caused by + // status must trigger AFTER the skill cast animation or it will cancel + // out the status's animation. + case SU_STOOP: + clif->skill_nodamage(src,bl,skill_id,skill_lv,1); + sc_start(src,bl,type,100,skill_lv,skill->get_time(skill_id,skill_lv)); + break; case KN_AUTOCOUNTER: sc_start(src,bl,type,100,skill_lv,skill->get_time(skill_id,skill_lv)); skill->addtimerskill(src, tick + 100, bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag); @@ -5966,7 +6734,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin break; case HP_ASSUMPTIO: if( sd && dstmd ) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); else clif->skill_nodamage(src,bl,skill_id,skill_lv, sc_start(src,bl,type,100,skill_lv,skill->get_time(skill_id,skill_lv))); @@ -6019,7 +6787,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin clif->skill_nodamage(src,bl,skill_id,skill_lv, sc_start(src,bl,type,100,skill_lv,skill->get_time(skill_id,skill_lv))); else if( sd ) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; case AS_ENCHANTPOISON: // Prevent spamming [Valaris] if (sd && dstsd && dstsd->sc.count) { @@ -6032,7 +6800,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin //dstsd->sc.data[SC_ENCHANTPOISON] //People say you should be able to recast to lengthen the timer. [Skotlex] ) { clif->skill_nodamage(src,bl,skill_id,skill_lv,0); - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } } @@ -6062,17 +6830,17 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin int id; if (sd->mission_mobid && (sd->mission_count || rnd()%100)) { //Cannot change target when already have one clif->mission_info(sd, sd->mission_mobid, sd->mission_count); - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } id = mob->get_random_id(0,0xF, sd->status.base_level); if (!id) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } sd->mission_mobid = id; sd->mission_count = 0; - pc_setglobalreg(sd,script->add_str("TK_MISSION_ID"), id); + pc_setglobalreg(sd,script->add_variable("TK_MISSION_ID"), id); clif->mission_info(sd, id, 0); clif->skill_nodamage(src,bl,skill_id,skill_lv,1); } @@ -6102,7 +6870,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin (failure = sc_start(src,bl,type, skill_id == SM_SELFPROVOKE ? 100:( 50 + 3*skill_lv + status->get_lv(src) - status->get_lv(bl)), skill_lv, skill->get_time(skill_id,skill_lv)))); if( !failure ) { if( sd ) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); map->freeblock_unlock(); return 0; } @@ -6132,7 +6900,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if( !dstsd || (!sd && !mer) ) { // Only players can be devoted if( sd ) - clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } @@ -6141,11 +6909,11 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if( lv > battle_config.devotion_level_difference || // Level difference requeriments (dstsd->sc.data[type] && dstsd->sc.data[type]->val1 != src->id) || // Cannot Devote a player devoted from another source (skill_id == ML_DEVOTION && (!mer || mer != dstsd->md)) || // Mercenary only can devote owner - (dstsd->class_&MAPID_UPPERMASK) == MAPID_CRUSADER || // Crusader Cannot be devoted + (dstsd->job & MAPID_UPPERMASK) == MAPID_CRUSADER || // Crusader Cannot be devoted (dstsd->sc.data[SC_HELLPOWER])) // Players affected by SC_HELLPOWERR cannot be devoted. { if( sd ) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); map->freeblock_unlock(); return 1; } @@ -6160,7 +6928,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin ARR_FIND(0, count, i, sd->devotion[i] == 0 ); if( i == count ) { // No free slots, skill Fail - clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); map->freeblock_unlock(); return 1; } @@ -6168,7 +6936,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin sd->devotion[i] = bl->id; } - else + else if (mer != NULL) mer->devotion_flag = 1; // Mercenary Devoting Owner clif->skill_nodamage(src, bl, skill_id, skill_lv, @@ -6194,7 +6962,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin break; case MO_KITRANSLATION: - if(dstsd && ((dstsd->class_&MAPID_BASEMASK)!=MAPID_GUNSLINGER || (dstsd->class_&MAPID_UPPERMASK)!=MAPID_REBELLION)) { + if (dstsd != NULL && (dstsd->job & MAPID_BASEMASK) != MAPID_GUNSLINGER) { pc->addspiritball(dstsd,skill->get_time(skill_id,skill_lv),5); } break; @@ -6210,10 +6978,10 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case MO_ABSORBSPIRITS: { int sp = 0; - if ( dstsd && dstsd->spiritball - && (sd == dstsd || map_flag_vs(src->m) || (sd && sd->duel_group && sd->duel_group == dstsd->duel_group)) - && ((dstsd->class_&MAPID_BASEMASK) != MAPID_GUNSLINGER || (dstsd->class_&MAPID_UPPERMASK) != MAPID_REBELLION) - ) { + if (dstsd != NULL && dstsd->spiritball != 0 + && (sd == dstsd || map_flag_vs(src->m) || (sd && sd->duel_group && sd->duel_group == dstsd->duel_group)) + && (dstsd->job & MAPID_BASEMASK) != MAPID_GUNSLINGER + ) { // split the if for readability, and included gunslingers in the check so that their coins cannot be removed [Reddozen] sp = dstsd->spiritball * 7; pc->delspiritball(dstsd, dstsd->spiritball, 0); @@ -6225,7 +6993,8 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if (dstsd && dstsd->charm_type != CHARM_TYPE_NONE && dstsd->charm_count > 0) { pc->del_charm(dstsd, dstsd->charm_count, dstsd->charm_type); } - if (sp) status->heal(src, 0, sp, 3); + if (sp != 0) + status->heal(src, 0, sp, STATUS_HEAL_FORCED | STATUS_HEAL_SHOWEFFECT); clif->skill_nodamage(src,bl,skill_id,skill_lv,sp?1:0); } break; @@ -6259,7 +7028,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin skill->area_temp[1] = 0; clif->skill_nodamage(src,bl,skill_id,skill_lv,1); map->foreachinrange(skill->area_sub, bl, - skill->get_splash(skill_id, skill_lv), splash_target(src), + skill->get_splash(skill_id, skill_lv), skill->splash_target(src), src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1, skill->castend_damage_id); status_change_end(src, SC_HIDING, INVALID_TIMER); @@ -6278,7 +7047,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin int count = 0; skill->area_temp[1] = 0; clif->skill_nodamage(src,bl,skill_id,skill_lv,1); - count = map->foreachinrange(skill->area_sub, bl, skill->get_splash(skill_id, skill_lv), splash_target(src), + count = map->foreachinrange(skill->area_sub, bl, skill->get_splash(skill_id, skill_lv), skill->splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill->castend_damage_id); if( !count && ( skill_id == NC_AXETORNADO || skill_id == SR_SKYNETBLOW || skill_id == KO_HAPPOKUNAI ) ) clif->skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, BDT_SKILL); @@ -6324,7 +7093,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin clif->skill_nodamage(src,bl,skill_id,skill_lv,1); skill->area_temp[1] = 0; map->foreachinrange(skill->attack_area, src, - skill->get_splash(skill_id, skill_lv), splash_target(src), + skill->get_splash(skill_id, skill_lv), skill->splash_target(src), BF_MAGIC, src, src, skill_id, skill_lv, tick, flag, BCT_ENEMY); break; @@ -6338,7 +7107,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin clif->skill_nodamage(src, src, skill_id, -1, 1); map->delblock(src); //Required to prevent chain-self-destructions hitting back. map->foreachinrange(skill->area_sub, bl, - skill->get_splash(skill_id, skill_lv), splash_target(src), + skill->get_splash(skill_id, skill_lv), skill->splash_target(src), src, skill_id, skill_lv, tick, flag|targetmask, skill->castend_damage_id); map->addblock(src); @@ -6414,14 +7183,14 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if (sd) { if (!dstsd || !( (sd->sc.data[SC_SOULLINK] && sd->sc.data[SC_SOULLINK]->val2 == SL_SOULLINKER) - || (dstsd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER + || (dstsd->job & MAPID_UPPERMASK) == MAPID_SOUL_LINKER || dstsd->status.char_id == sd->status.char_id || dstsd->status.char_id == sd->status.partner_id || dstsd->status.char_id == sd->status.child ) ) { status->change_start(src,src,SC_STUN,10000,skill_lv,0,0,0,500,SCFLAG_FIXEDRATE); - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } } @@ -6448,7 +7217,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin return 0; } else if( tsc && tsc->option&OPTION_MADOGEAR ) { //Mado Gear cannot hide - if( sd ) clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + if( sd ) clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); map->freeblock_unlock(); return 0; } @@ -6474,7 +7243,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if( failure ) clif->skill_nodamage(src,bl,skill_id,( skill_id == LG_FORCEOFVANGUARD ) ? skill_lv : -1,failure); else if( sd ) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); if ( skill_id == LG_FORCEOFVANGUARD || skill_id == RA_CAMOUFLAGE ) break; map->freeblock_unlock(); @@ -6484,7 +7253,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if( failure ) clif->skill_nodamage(src,bl,skill_id,( skill_id == LG_FORCEOFVANGUARD ) ? skill_lv : -1,failure); else if( sd ) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); } break; @@ -6504,8 +7273,8 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin // custom hack to make the mob display the skill, because these skills don't show the skill use text themselves //NOTE: mobs don't have the sprite animation that is used when performing this skill (will cause glitches) char temp[70]; - snprintf(temp, sizeof(temp), "%s : %s !!",md->name,skill->dbs->db[skill_id].desc); - clif->disp_overhead(&md->bl,temp); + snprintf(temp, sizeof(temp), msg_txt(882), md->name, skill->get_desc(skill_id)); // %s : %s !! + clif->disp_overhead(&md->bl, temp, AREA_CHAT_WOC, NULL); } break; @@ -6524,7 +7293,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin clif->skill_nodamage(src,bl,skill_id,skill_lv, sc_start2(src,bl,type,70,skill_lv,src->id,skill->get_time(skill_id,skill_lv))); } else { clif->skill_nodamage(src,bl,skill_id,skill_lv,0); - if(sd) clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + if(sd) clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); } } break; @@ -6534,20 +7303,20 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if(pc->steal_item(sd,bl,skill_lv)) clif->skill_nodamage(src,bl,skill_id,skill_lv,1); else - clif->skill_fail(sd,skill_id,USESKILL_FAIL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL, 0, 0); } break; case RG_STEALCOIN: if(sd) { - int amount = pc->steal_coin(sd, bl); - if( amount > 0 ) { + int amount = pc->steal_coin(sd, bl, skill_lv); + if (amount > 0 && dstmd != NULL) { dstmd->state.provoke_flag = src->id; mob->target(dstmd, src, skill->get_range2(src, skill_id, skill_lv)); clif->skill_nodamage(src, bl, skill_id, amount, 1); } else - clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); } break; @@ -6555,7 +7324,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin { int brate = 0; if (tstatus->mode&MD_BOSS) { - if (sd) clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + if (sd) clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } if(status->isimmune(bl) || !tsc) @@ -6574,7 +7343,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin skill->get_time2(skill_id,skill_lv))) clif->skill_nodamage(src,bl,skill_id,skill_lv,1); else if(sd) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); // Level 6-10 doesn't consume a red gem if it fails [celest] if (skill_lv > 5) { // not to consume items @@ -6587,7 +7356,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case NV_FIRSTAID: clif->skill_nodamage(src,bl,skill_id,5,1); - status->heal(bl,5,0,0); + status->heal(bl, 5, 0, STATUS_HEAL_DEFAULT); break; case AL_CURE: @@ -6598,7 +7367,8 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin status_change_end(bl, SC_SILENCE, INVALID_TIMER); status_change_end(bl, SC_BLIND, INVALID_TIMER); status_change_end(bl, SC_CONFUSION, INVALID_TIMER); - clif->skill_nodamage(src,bl,skill_id,skill_lv,1); + status_change_end(bl, SC_BITESCAR, INVALID_TIMER); + clif->skill_nodamage(src, bl, skill_id, skill_lv, 1); break; case TF_DETOXIFY: @@ -6608,27 +7378,34 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin break; case PR_STRECOVERY: - if(status->isimmune(bl)) { - clif->skill_nodamage(src,bl,skill_id,skill_lv,0); + if (status->isimmune(bl) != 0) { + clif->skill_nodamage(src, bl, skill_id, skill_lv, 0); break; } - if (tsc && tsc->opt1) { - status_change_end(bl, SC_FREEZE, INVALID_TIMER); - status_change_end(bl, SC_STONE, INVALID_TIMER); - status_change_end(bl, SC_SLEEP, INVALID_TIMER); - status_change_end(bl, SC_STUN, INVALID_TIMER); - status_change_end(bl, SC_WHITEIMPRISON, INVALID_TIMER); - } - status_change_end(bl, SC_NETHERWORLD, INVALID_TIMER); - //Is this equation really right? It looks so... special. - if( battle->check_undead(tstatus->race,tstatus->def_ele) ) { - status->change_start(src, bl, SC_BLIND, - 100*(100-(tstatus->int_/2+tstatus->vit/3+tstatus->luk/10)), 1,0,0,0, - skill->get_time2(skill_id, skill_lv) * (100-(tstatus->int_+tstatus->vit)/2)/100,SCFLAG_NONE); + + if (!battle->check_undead(tstatus->race, tstatus->def_ele)) { + if (tsc != NULL && tsc->opt1 != 0) { + status_change_end(bl, SC_FREEZE, INVALID_TIMER); + status_change_end(bl, SC_STONE, INVALID_TIMER); + status_change_end(bl, SC_SLEEP, INVALID_TIMER); + status_change_end(bl, SC_STUN, INVALID_TIMER); + status_change_end(bl, SC_WHITEIMPRISON, INVALID_TIMER); + } + + status_change_end(bl, SC_NETHERWORLD, INVALID_TIMER); + } else { + int rate = 100 * (100 - (tstatus->int_ / 2 + tstatus->vit / 3 + tstatus->luk / 10)); + int duration = skill->get_time2(skill_id, skill_lv); + + duration = duration * (100 - (tstatus->int_ + tstatus->vit) / 2) / 100; + status->change_start(src, bl, SC_BLIND, rate, 1, 0, 0, 0, duration, SCFLAG_NONE); } - clif->skill_nodamage(src,bl,skill_id,skill_lv,1); - if(dstmd) - mob->unlocktarget(dstmd,tick); + + clif->skill_nodamage(src, bl, skill_id, skill_lv, 1); + + if (dstmd != NULL) + mob->unlocktarget(dstmd, tick); + break; // Mercenary Supportive Skills @@ -6663,7 +7440,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case MER_SCAPEGOAT: if( mer && mer->master ) { - status->heal(&mer->master->bl, mer->battle_status.hp, 0, 2); + status->heal(&mer->master->bl, mer->battle_status.hp, 0, STATUS_HEAL_SHOWEFFECT); status->damage(src, src, mer->battle_status.max_hp, 0, 0, 1); } break; @@ -6677,7 +7454,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin break; if( dstsd ) { // Fail on Players - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } if (dstmd && dstmd->class_ == MOBID_EMPELIUM) @@ -6701,8 +7478,8 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin map->freeblock_unlock(); return 1; } - if( sd->skillitem != skill_id ) - status_zap(src,0,skill->dbs->db[skill->get_index(skill_id)].sp[skill_lv]); // consume sp only if succeeded + if (sd->autocast.type == AUTOCAST_NONE) + status_zap(src, 0, skill->get_sp(skill_id, skill_lv)); // consume sp only if succeeded } break; @@ -6718,7 +7495,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if (sd) { //Prevent vending of GMs with unnecessary Level to trade/drop. [Skotlex] if ( !pc_can_give_items(sd) ) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); else { sd->state.prevend = sd->state.workinprogress = 3; clif->openvendingreq(sd,2+skill_lv); @@ -6738,7 +7515,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin break; } - if( sd->state.autocast || ( (sd->skillitem == AL_TELEPORT || battle_config.skip_teleport_lv1_menu) && skill_lv == 1 ) || skill_lv == 3 ) + if (sd->autocast.type == AUTOCAST_TEMP || ((sd->autocast.skill_id == AL_TELEPORT || battle_config.skip_teleport_lv1_menu) && skill_lv == 1) || skill_lv == 3) { if( skill_lv == 1 ) pc->randomwarp(sd,CLR_TELEPORT); @@ -6766,7 +7543,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if (skill->produce_mix(sd, skill_id, ITEMID_HOLY_WATER, 0, 0, 0, 1)) clif->skill_nodamage(src,bl,skill_id,skill_lv,1); else - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); } break; @@ -6785,7 +7562,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin eflag = pc->additem(sd,&item_tmp,1,LOG_TYPE_PRODUCE); if(eflag) { clif->additem(sd,0,0,eflag); - map->addflooritem(&sd->bl, &item_tmp, 1, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0); + map->addflooritem(&sd->bl, &item_tmp, 1, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0, false); } } break; @@ -6864,7 +7641,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin //Nothing stripped. if( sd && !rate ) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } @@ -6878,22 +7655,23 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin return 1; } if( sd ) { - int x,bonus=100, potion = min(500+skill_lv,505); - x = skill_lv%11 - 1; - i = pc->search_inventory(sd,skill->dbs->db[skill_id].itemid[x]); - if (i == INDEX_NOT_FOUND || skill->dbs->db[skill_id].itemid[x] <= 0) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + int bonus = 100, potion = min(500+skill_lv,505); + int item_idx = (skill_lv - 1) % MAX_SKILL_ITEM_REQUIRE; + int item_id = skill->get_itemid(skill_id, item_idx); + int inventory_idx = pc->search_inventory(sd, item_id); + if (inventory_idx == INDEX_NOT_FOUND || item_id <= 0) { + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); map->freeblock_unlock(); return 1; } - if(sd->inventory_data[i] == NULL || sd->status.inventory[i].amount < skill->dbs->db[skill_id].amount[x]) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + if (sd->inventory_data[inventory_idx] == NULL || sd->status.inventory[inventory_idx].amount < skill->get_itemqty(skill_id, item_idx)) { + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); map->freeblock_unlock(); return 1; } if( skill_id == AM_BERSERKPITCHER ) { - if( dstsd && dstsd->status.base_level < (unsigned int)sd->inventory_data[i]->elv ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + if (dstsd != NULL && dstsd->status.base_level < sd->inventory_data[inventory_idx]->elv) { + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); map->freeblock_unlock(); return 1; } @@ -6901,7 +7679,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin script->potion_flag = 1; script->potion_hp = script->potion_sp = script->potion_per_hp = script->potion_per_sp = 0; script->potion_target = bl->id; - script->run_use_script(sd, sd->inventory_data[i], 0); + script->run_use_script(sd, sd->inventory_data[inventory_idx], 0); script->potion_flag = script->potion_target = 0; if( sd->sc.data[SC_SOULLINK] && sd->sc.data[SC_SOULLINK]->val2 == SL_ALCHEMIST ) bonus += sd->status.base_level; @@ -6979,7 +7757,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if( tsc && tsc->data[SC_EXTREMITYFIST2] ) sp = 0; #endif - status->heal(bl,(int)hp,sp,0); + status->heal(bl, (int)hp, sp, STATUS_HEAL_DEFAULT); } break; case AM_CP_WEAPON: @@ -6993,7 +7771,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin (dstsd && equip[skill_id - AM_CP_WEAPON] == EQP_SHIELD && pc->checkequip(dstsd, EQP_SHIELD) > 0 && (index = dstsd->equip_index[EQI_HAND_L]) >= 0 && dstsd->inventory_data[index] && dstsd->inventory_data[index]->type != IT_ARMOR)) ) { - clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); map->freeblock_unlock(); // Don't consume item requirements return 0; } @@ -7006,7 +7784,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin clif->skill_nodamage(src,bl,skill_id,skill_lv,1); //Prepare 200 White Potions. if (!skill->produce_mix(sd, skill_id, ITEMID_WHITE_POTION, 0, 0, 0, 200)) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); } break; case AM_TWILIGHT2: @@ -7014,7 +7792,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin clif->skill_nodamage(src,bl,skill_id,skill_lv,1); //Prepare 200 Slim White Potions. if (!skill->produce_mix(sd, skill_id, ITEMID_WHITE_SLIM_POTION, 0, 0, 0, 200)) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); } break; case AM_TWILIGHT3: @@ -7028,7 +7806,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin || !skill->can_produce_mix(sd,ITEMID_FIRE_BOTTLE,-1, 50) //50 Flame Bottle || ebottle < 200 //200 empty bottle are required at total. ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } clif->skill_nodamage(src,bl,skill_id,skill_lv,1); @@ -7048,13 +7826,13 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin break; } clif->skill_nodamage(src,bl,skill_id,skill_lv,1); - if((dstsd && (dstsd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER) + if ((dstsd != NULL && (dstsd->job & MAPID_UPPERMASK) == MAPID_SOUL_LINKER) || (tsc && tsc->data[SC_SOULLINK] && tsc->data[SC_SOULLINK]->val2 == SL_ROGUE) //Rogue's spirit defends against dispel. || (dstsd && pc_ismadogear(dstsd)) || rnd()%100 >= 50+10*skill_lv ) { if (sd) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } if(status->isimmune(bl) || !tsc || !tsc->count) @@ -7110,7 +7888,9 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case TK_HIGHJUMP: { - int x,y, dir = unit->getdir(src); + int x; + int y; + enum unit_dir dir = unit->getdir(src); //Fails on noteleport maps, except for GvG and BG maps [Skotlex] if( map->list[src->m].flag.noteleport @@ -7154,7 +7934,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin sp = skill->get_sp(skill_id,skill_lv); sp = sp * tsc->data[SC_MAGICROD]->val2 / 100; if(sp < 1) sp = 1; - status->heal(bl,0,sp,2); + status->heal(bl, 0, sp, STATUS_HEAL_SHOWEFFECT); status_percent_damage(bl, src, 0, -20, false); //20% max SP damage. } else { struct unit_data *ud = unit->bl2ud(bl); @@ -7167,7 +7947,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin //Only 10% success chance against bosses. [Skotlex] if (rnd()%100 < 90) { - if (sd) clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + if (sd) clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } } else if (!dstsd || map_flag_vs(bl->m)) //HP damage only on pvp-maps when against players. @@ -7186,14 +7966,15 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if (sp) //Recover some of the SP used sp = sp*(25*(skill_lv-1))/100; - if(hp || sp) - status->heal(src, hp, sp, 2); + if (hp != 0 || sp != 0) + status->heal(src, hp, sp, STATUS_HEAL_SHOWEFFECT); } } break; case SA_MAGICROD: - clif->skill_nodamage(src,src,SA_MAGICROD,skill_lv,1); - sc_start(src,bl,type,100,skill_lv,skill->get_time(skill_id,skill_lv)); + if (battle->bc->magicrod_type == 0) + clif->skill_nodamage(src, src, SA_MAGICROD, skill_lv, 1); // Animation used here in official [Wolfie] + sc_start(src, bl, type, 100, skill_lv, skill->get_time(skill_id, skill_lv)); break; case SA_AUTOSPELL: clif->skill_nodamage(src,bl,skill_id,skill_lv,1); @@ -7221,7 +8002,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin maxlv = skill_lv - 4; } else if(skill_lv >=2) { - int i = rnd()%3; + int i = rnd() % ARRAYLENGTH(spellarray); spellid = spellarray[i]; maxlv = skill_lv - 1; } @@ -7307,8 +8088,8 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case NPC_LICK: status_zap(bl, 0, 100); - clif->skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(src,bl,type,(skill_lv*5),skill_lv,skill->get_time2(skill_id,skill_lv))); + clif->skill_nodamage(src, bl, skill_id, skill_lv, + sc_start(src, bl, type, (skill_lv * 20), skill_lv, skill->get_time2(skill_id, skill_lv))); break; case NPC_SUICIDE: @@ -7358,11 +8139,19 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case NPC_RUN: { - const int mask[8][2] = {{0,-1},{1,-1},{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1}}; - uint8 dir = (bl == src)?unit->getdir(src):map->calc_dir(src,bl->x,bl->y); //If cast on self, run forward, else run away. + enum unit_dir dir; + if (bl == src) //If cast on self, run forward, else run away. + dir = unit->getdir(src); + else + dir = map->calc_dir(src, bl->x, bl->y); + if (Assert_chk(dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX)) { + map->freeblock_unlock(); // unblock before assert-returning + return 0; + } unit->stop_attack(src); //Run skillv tiles overriding the can-move check. - if (unit->walktoxy(src, src->x + skill_lv * mask[dir][0], src->y + skill_lv * mask[dir][1], 2) && md) + if (unit->walk_toxy(src, src->x + skill_lv * -dirx[dir], src->y + skill_lv * -diry[dir], 2) == 0 + && md != NULL) md->state.skillstate = MSS_WALK; //Otherwise it isn't updated in the AI. } break; @@ -7428,16 +8217,16 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case WE_MALE: { - int hp_rate = (!skill_lv)? 0:skill->dbs->db[skill_id].hp_rate[skill_lv-1]; + int hp_rate = skill_lv == 0 ? 0 : skill->get_hp_rate(skill_id, skill_lv); int gain_hp = tstatus->max_hp*abs(hp_rate)/100; // The earned is the same % of the target HP than it cost the caster. [Skotlex] - clif->skill_nodamage(src,bl,skill_id,status->heal(bl, gain_hp, 0, 0),1); + clif->skill_nodamage(src, bl, skill_id, status->heal(bl, gain_hp, 0, STATUS_HEAL_DEFAULT), 1); } break; case WE_FEMALE: { - int sp_rate = (!skill_lv)? 0:skill->dbs->db[skill_id].sp_rate[skill_lv-1]; + int sp_rate = skill_lv == 0 ? 0 : skill->get_sp_rate(skill_id, skill_lv); int gain_sp = tstatus->max_sp*abs(sp_rate)/100;// The earned is the same % of the target SP than it cost the caster. [Skotlex] - clif->skill_nodamage(src,bl,skill_id,status->heal(bl, 0, gain_sp, 0),1); + clif->skill_nodamage(src, bl, skill_id, status->heal(bl, 0, gain_sp, STATUS_HEAL_DEFAULT), 1); } break; @@ -7458,7 +8247,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin we_baby_parents = true; } if (!we_baby_parents) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); map->freeblock_unlock(); return 0; } @@ -7473,11 +8262,11 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin hp = sstatus->max_hp/10; sp = hp * 10 * skill_lv / 100; if (!status->charge(src,hp,0)) { - if (sd) clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + if (sd) clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } clif->skill_nodamage(src, bl, skill_id, skill_lv, 1); - status->heal(bl,0,sp,2); + status->heal(bl, 0, sp, STATUS_HEAL_SHOWEFFECT); } break; @@ -7498,16 +8287,17 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if( battle_config.skill_removetrap_type ) { int i; // get back all items used to deploy the trap - for( i = 0; i < 10; i++ ) { - if( skill->dbs->db[su->group->skill_id].itemid[i] > 0 ) { + for (i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++) { + int nameid = skill->get_itemid(su->group->skill_id, i); + if (nameid > 0) { int success; - struct item item_tmp; - memset(&item_tmp,0,sizeof(item_tmp)); - item_tmp.nameid = skill->dbs->db[su->group->skill_id].itemid[i]; + struct item item_tmp = { 0 }; + int amount = skill->get_itemqty(su->group->skill_id, i); + item_tmp.nameid = nameid; item_tmp.identify = 1; - if (item_tmp.nameid && (success=pc->additem(sd,&item_tmp,skill->dbs->db[su->group->skill_id].amount[i],LOG_TYPE_SKILL)) != 0) { + if ((success = pc->additem(sd, &item_tmp, amount, LOG_TYPE_SKILL)) != 0) { clif->additem(sd,0,0,success); - map->addflooritem(&sd->bl, &item_tmp, skill->dbs->db[su->group->skill_id].amount[i], sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0); + map->addflooritem(&sd->bl, &item_tmp, amount, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0, false); } } } @@ -7515,17 +8305,17 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin // get back 1 trap struct item item_tmp; memset(&item_tmp,0,sizeof(item_tmp)); - item_tmp.nameid = su->group->item_id?su->group->item_id:ITEMID_TRAP; + item_tmp.nameid = su->group->item_id ? su->group->item_id : ITEMID_BOOBY_TRAP; item_tmp.identify = 1; if (item_tmp.nameid && (flag=pc->additem(sd,&item_tmp,1,LOG_TYPE_SKILL)) != 0) { clif->additem(sd,0,0,flag); - map->addflooritem(&sd->bl, &item_tmp, 1, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0); + map->addflooritem(&sd->bl, &item_tmp, 1, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0, false); } } } skill->delunit(su); }else if(sd) - clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); } break; @@ -7541,6 +8331,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin // remove trap should be used instead break; // otherwise fall through to below + FALLTHROUGH case UNT_BLASTMINE: case UNT_SKIDTRAP: case UNT_LANDMINE: @@ -7571,14 +8362,15 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin || tstatus-> hp > tstatus->max_hp*3/4 #endif // RENEWAL ) { - if (sd) clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + if (sd) clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); map->freeblock_unlock(); return 1; } clif->skill_nodamage(src,bl,skill_id,skill_lv, sc_start4(src,bl,type,100,skill_lv,skill_id,src->id,skill->get_time(skill_id,skill_lv),1000)); #ifndef RENEWAL - if (sd) skill->blockpc_start (sd, skill_id, skill->get_time(skill_id, skill_lv)+3000); + if (sd) + skill->blockpc_start(sd, skill_id, skill->get_time(skill_id, skill_lv) + 3000); #endif break; @@ -7600,7 +8392,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if (!clif->skill_nodamage(src,bl,skill_id,skill_lv, sc_start(src,bl,type,55+5*skill_lv,skill_lv,skill->get_time(skill_id,skill_lv))) ) { - if (sd) clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + if (sd) clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); map->freeblock_unlock(); return 0; } @@ -7624,12 +8416,12 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin unsigned int sp1 = 0, sp2 = 0; if (dstmd) { if (dstmd->state.soul_change_flag) { - if(sd) clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + if(sd) clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } dstmd->state.soul_change_flag = 1; sp2 = sstatus->max_sp * 3 /100; - status->heal(src, 0, sp2, 2); + status->heal(src, 0, sp2, STATUS_HEAL_SHOWEFFECT); clif->skill_nodamage(src,bl,skill_id,skill_lv,1); break; } @@ -7641,8 +8433,8 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if( tsc && tsc->data[SC_EXTREMITYFIST2] ) sp1 = tstatus->sp; #endif // RENEWAL - status->set_sp(src, sp2, 3); - status->set_sp(bl, sp1, 3); + status->set_sp(src, sp2, STATUS_HEAL_FORCED | STATUS_HEAL_SHOWEFFECT); + status->set_sp(bl, sp1, STATUS_HEAL_FORCED | STATUS_HEAL_SHOWEFFECT); clif->skill_nodamage(src,bl,skill_id,skill_lv,1); } break; @@ -7680,7 +8472,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin clif->skill_nodamage(NULL,bl,AL_HEAL,hp,1); if(sp > 0) clif->skill_nodamage(NULL,bl,MG_SRECOVERY,sp,1); - status->heal(bl,hp,sp,0); + status->heal(bl, hp, sp, STATUS_HEAL_DEFAULT); } break; // Full Chemical Protection @@ -7700,7 +8492,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin s++; } if ( sd && !s ) { - clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); map->freeblock_unlock(); // Don't consume item requirements return 0; } @@ -7732,12 +8524,12 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin } if (rnd() % 100 > skill_lv * 8 || (dstmd && ((dstmd->guardian_data && dstmd->class_ == MOBID_EMPELIUM) || mob_is_battleground(dstmd)))) { if (sd != NULL) - clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); map->freeblock_unlock(); return 0; } - status_zap(src,0,skill->dbs->db[skill->get_index(skill_id)].sp[skill_lv]); // consume sp only if succeeded [Inkfish] + status_zap(src, 0, skill->get_sp(skill_id, skill_lv)); // consume sp only if succeeded [Inkfish] do { int eff = rnd() % 14; if( eff == 5 ) @@ -7760,8 +8552,8 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin status_fix_damage(src, bl, 1000, 0); clif->damage(src,bl,0,0,1000,0,BDT_NORMAL,0); if( !status->isdead(bl) ) { - int where[] = { EQP_ARMOR, EQP_SHIELD, EQP_HELM, EQP_SHOES, EQP_GARMENT }; - skill->break_equip(bl, where[rnd()%5], 10000, BCT_ENEMY); + int where[] = {EQP_ARMOR, EQP_SHIELD, EQP_HELM}; + skill->break_equip(bl, where[rnd() % ARRAYLENGTH(where)], 10000, BCT_ENEMY); } } break; @@ -7769,7 +8561,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin sc_start(src,bl,SC_INCATKRATE,100,-50,skill->get_time2(skill_id,skill_lv)); break; case 5: // 2000HP heal, random teleported - status->heal(src, 2000, 0, 0); + status->heal(src, 2000, 0, STATUS_HEAL_DEFAULT); if( !map_flag_vs(bl->m) ) unit->warp(bl, -1,-1,-1, CLR_TELEPORT); break; @@ -7782,7 +8574,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case 7: // stop freeze or stoned { enum sc_type sc[] = { SC_STOP, SC_FREEZE, SC_STONE }; - sc_start(src,bl,sc[rnd()%3],100,skill_lv,skill->get_time2(skill_id,skill_lv)); + sc_start(src,bl,sc[rnd() % ARRAYLENGTH(sc)],100,skill_lv,skill->get_time2(skill_id,skill_lv)); } break; case 8: // curse coma and poison @@ -7839,14 +8631,14 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case SL_SUPERNOVICE: case SL_WIZARD: //NOTE: here, 'type' has the value of the associated MAPID, not of the SC_SOULLINK constant. - if (sd && !(dstsd && (dstsd->class_&MAPID_UPPERMASK) == type)) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + if (sd != NULL && !(dstsd != NULL && (dstsd->job & MAPID_UPPERMASK) == type)) { + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } if (skill_id == SL_SUPERNOVICE && dstsd && dstsd->die_counter && !(rnd()%100)) { //Erase death count 1% of the casts dstsd->die_counter = 0; - pc_setglobalreg(dstsd,script->add_str("PC_DIE_COUNTER"), 0); + pc_setglobalreg(dstsd,script->add_variable("PC_DIE_COUNTER"), 0); clif->specialeffect(bl, 0x152, AREA); //SC_SOULLINK invokes status_calc_pc for us. } @@ -7855,8 +8647,8 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin sc_start(src,src,SC_SMA_READY,100,skill_lv,skill->get_time(SL_SMA,skill_lv)); break; case SL_HIGH: - if (sd && !(dstsd && (dstsd->class_&JOBL_UPPER) && !(dstsd->class_&JOBL_2) && dstsd->status.base_level < 70)) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + if (sd != NULL && !(dstsd != NULL && (dstsd->job & JOBL_UPPER) != 0 && (dstsd->job & JOBL_2) == 0 && dstsd->status.base_level < 70)) { + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } clif->skill_nodamage(src,bl,skill_id,skill_lv, @@ -7867,7 +8659,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case SL_SWOO: if (tsce) { if(sd) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); status->change_start(src,src,SC_STUN,10000,skill_lv,0,0,0,10000,SCFLAG_FIXEDRATE); status_change_end(bl, SC_SWOO, INVALID_TIMER); break; @@ -7875,7 +8667,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case SL_SKA: // [marquis007] case SL_SKE: if (sd && !battle_config.allow_es_magic_pc && bl->type != BL_MOB) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); status->change_start(src,src,SC_STUN,10000,skill_lv,0,0,0,500,SCFLAG_FIXEDTICK|SCFLAG_FIXEDRATE); break; } @@ -7967,7 +8759,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if (sd) { clif->skill_nodamage(src,bl,skill_id,skill_lv,1); if (!pc->set_hate_mob(sd, skill_lv-1, bl)) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); } break; @@ -7997,16 +8789,16 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if (homun->call(sd)) clif->skill_nodamage(src, bl, skill_id, skill_lv, 1); else - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); } break; case AM_REST: if (sd) { - if (homun->vaporize(sd,HOM_ST_REST)) + if (homun->vaporize(sd, HOM_ST_REST, false)) clif->skill_nodamage(src, bl, skill_id, skill_lv, 1); else - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); } break; @@ -8016,8 +8808,14 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin int x,y; x = src->x; y = src->y; - if (hd) - skill->blockhomun_start(hd, skill_id, skill->get_time2(skill_id,skill_lv)); + if (hd) { +#ifdef RENEWAL + skill->blockhomun_start(hd, skill_id, skill->get_cooldown(skill_id, skill_lv)); +#else + skill->blockhomun_start(hd, skill_id, skill->get_time2(skill_id, skill_lv)); +#endif + } + if (unit->movepos(src,bl->x,bl->y,0,0)) { clif->skill_nodamage(src,src,skill_id,skill_lv,1); // Homun @@ -8035,9 +8833,9 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin } // Failed else if (hd && hd->master) - clif->skill_fail(hd->master, skill_id, USESKILL_FAIL_LEVEL, 0); + clif->skill_fail(hd->master, skill_id, USESKILL_FAIL_LEVEL, 0, 0); else if (sd) - clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; case HVAN_CHAOTIC: // [orn] { @@ -8045,19 +8843,27 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin int r = rnd()%100; int target = (skill_lv-1)%5; int hp; - if(r<per[target][0]) //Self + if (r < per[target][0]) { //Self bl = src; - else if(r<per[target][1]) //Master + } else if (r < per[target][1]) { //Master bl = battle->get_master(src); - else //Enemy - bl = map->id2bl(battle->get_target(src)); + } else if ((per[target][1] - per[target][0]) < per[target][0] + && bl == battle->get_master(src)) { + /** + * Skill rolled for enemy, but there's nothing the Homunculus is attacking. + * So bl has been set to its master in unit->skilluse_id2. + * If it's more likely that it will heal itself, + * we let it heal itself. + */ + bl = src; + } if (!bl) bl = src; hp = skill->calc_heal(src, bl, skill_id, 1+rnd()%skill_lv, true); //Eh? why double skill packet? clif->skill_nodamage(src,bl,AL_HEAL,hp,1); clif->skill_nodamage(src,bl,skill_id,hp,1); - status->heal(bl, hp, 0, 0); + status->heal(bl, hp, 0, STATUS_HEAL_DEFAULT); } break; // Homun single-target support skills [orn] @@ -8140,7 +8946,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin { if( !sd->status.party_id ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } party->foreachsamemap(skill->area_sub, sd, skill->get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill->castend_nodamage_id); @@ -8196,7 +9002,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if( status->charge(bl,heal,0) ) clif->skill_nodamage(src,bl,skill_id,skill_lv,sc_start2(src,bl,type,100,skill_lv,heal,skill->get_time(skill_id,skill_lv))); else - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); } break; case RK_REFRESH: @@ -8204,7 +9010,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin int heal = status_get_max_hp(bl) * 25 / 100; clif->skill_nodamage(src,bl,skill_id,skill_lv, sc_start(src,bl,type,100,skill_lv,skill->get_time(skill_id,skill_lv))); - status->heal(bl,heal,0,1); + status->heal(bl, heal, 0, STATUS_HEAL_FORCED); status->change_clear_buffs(bl,4); } break; @@ -8258,7 +9064,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin value = status_get_max_hp(bl) * 25 / 100; status->change_clear_buffs(bl,4); skill->area_temp[5] &= ~0x20; - status->heal(bl,value,0,1); + status->heal(bl, value, 0, STATUS_HEAL_FORCED); type = SC_REFRESH; }else if( skill->area_temp[5]&0x40 ){ skill->area_temp[5] &= ~0x40; @@ -8374,11 +9180,11 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin { int heal = status_get_max_hp(bl) * ( 18 - 2 * skill_lv ) / 100; if( status_get_hp(bl) < heal ) { // if you haven't enough HP skill fails. - if( sd ) clif->skill_fail(sd,skill_id,USESKILL_FAIL_HP_INSUFFICIENT,0); + if (sd) clif->skill_fail(sd, skill_id, USESKILL_FAIL_HP_INSUFFICIENT, 0, 0); break; } if( !status->charge(bl,heal,0) ) { - if( sd ) clif->skill_fail(sd,skill_id,USESKILL_FAIL_HP_INSUFFICIENT,0); + if (sd) clif->skill_fail(sd, skill_id, USESKILL_FAIL_HP_INSUFFICIENT, 0, 0); break; } clif->skill_nodamage(src,bl,skill_id,skill_lv,sc_start(src,bl,type,100,skill_lv,skill->get_time(skill_id,skill_lv))); @@ -8390,7 +9196,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case AB_ANCILLA: if( sd ) { clif->skill_nodamage(src,bl,skill_id,skill_lv,1); - skill->produce_mix(sd, skill_id, ITEMID_ANCILLA, 0, 0, 0, 1); + skill->produce_mix(sd, skill_id, ITEMID_ANSILA, 0, 0, 0, 1); } break; @@ -8404,7 +9210,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin clif->skill_nodamage(bl, bl, skill_id, skill_lv, sc_start(src, bl, type, 100, level + (sd?(sd->status.job_level / 10):0), skill->get_time(skill_id,skill_lv))); else if( sd ) { if( !level ) - clif->skill_fail(sd,skill_id,USESKILL_FAIL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL, 0, 0); else party->foreachsamemap(skill->area_sub, sd, skill->get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill->castend_nodamage_id); } @@ -8443,7 +9249,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin clif->skill_nodamage(bl, bl, skill_id, heal, 1); if( tsc && tsc->data[SC_AKAITSUKI] && heal ) heal = ~heal + 1; - status->heal(bl, heal, 0, 1); + status->heal(bl, heal, 0, STATUS_HEAL_FORCED); } } else if( sd ) party->foreachsamemap(skill->area_sub, sd, skill->get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill->castend_nodamage_id); @@ -8508,9 +9314,9 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin clif->skill_nodamage(src,bl,skill_id,skill_lv,1); - if((dstsd && (dstsd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER) || rnd()%100 >= 60 + 8 * skill_lv) { + if ((dstsd != NULL && (dstsd->job & MAPID_UPPERMASK) == MAPID_SOUL_LINKER) || rnd()%100 >= 60 + 8 * skill_lv) { if (sd) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } if(status->isimmune(bl) || !tsc || !tsc->count) @@ -8573,11 +9379,11 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin int failure = sc_start2(src,bl,type,rate,skill_lv,src->id,(src == bl)?5000:(bl->type == BL_PC)?skill->get_time(skill_id,skill_lv):skill->get_time2(skill_id, skill_lv)); clif->skill_nodamage(src,bl,skill_id,skill_lv,failure); if( sd && !failure ) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); } }else if( sd ) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_TOTARGET,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_TOTARGET, 0, 0); break; case WL_FROSTMISTY: @@ -8613,7 +9419,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin clif->skill_nodamage(src, bl, skill_id, skill_lv, 1); map->foreachinrange(skill->area_sub,bl,skill->get_splash(skill_id,skill_lv),BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill->castend_nodamage_id); }else if( sd ) // Failure on Rate - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); } break; @@ -8643,7 +9449,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if( sc && !sc->data[i] ) break; if( i == SC_SPELLBOOK7 ) { - clif->skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_READING, 0); + clif->skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_READING, 0, 0); break; } @@ -8706,7 +9512,9 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case NC_F_SIDESLIDE: case NC_B_SIDESLIDE: { - uint8 dir = (skill_id == NC_F_SIDESLIDE) ? (unit->getdir(src)+4)%8 : unit->getdir(src); + enum unit_dir dir = unit->getdir(src); + if (skill_id == NC_F_SIDESLIDE) + dir = unit_get_opposite_dir(dir); skill->blown(src,bl,skill->get_blewcount(skill_id,skill_lv),dir,0); clif->slide(src,src->x,src->y); clif->skill_nodamage(src,bl,skill_id,skill_lv,1); @@ -8716,10 +9524,10 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case NC_SELFDESTRUCTION: if (sd) { if (pc_ismadogear(sd)) - pc->setmadogear(sd, false); + pc->setmadogear(sd, false, MADO_ROBOT); clif->skill_nodamage(src, bl, skill_id, skill_lv, 1); skill->castend_damage_id(src, src, skill_id, skill_lv, tick, flag); - status->set_sp(src, 0, 0); + status->set_sp(src, 0, STATUS_HEAL_DEFAULT); } break; @@ -8735,7 +9543,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin int failure; if( (failure = sc_start2(src,bl,type,100,skill_lv,src->id,skill->get_time(skill_id,skill_lv))) ) { - map->foreachinrange(skill->area_sub,src,skill->get_splash(skill_id,skill_lv),splash_target(src),src,skill_id,skill_lv,tick,flag|BCT_ENEMY|SD_SPLASH|1,skill->castend_damage_id);; + map->foreachinrange(skill->area_sub,src,skill->get_splash(skill_id,skill_lv),skill->splash_target(src),src,skill_id,skill_lv,tick,flag|BCT_ENEMY|SD_SPLASH|1,skill->castend_damage_id);; clif->skill_damage(src,src,tick,status_get_amotion(src),0,-30000,1,skill_id,skill_lv,BDT_SKILL); if (sd) pc->overheat(sd,1); } @@ -8747,7 +9555,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if( sd ) { int heal, hp = 0; // % of max hp regen if( !dstsd || !pc_ismadogear(dstsd) ) { - clif->skill_fail(sd, skill_id,USESKILL_FAIL_TOTARGET,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_TOTARGET, 0, 0); break; } switch (cap_value(skill_lv, 1, 5)) { @@ -8758,7 +9566,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case 5: hp = 23; break; } heal = tstatus->max_hp * hp / 100; - status->heal(bl,heal,0,2); + status->heal(bl, heal, 0, STATUS_HEAL_SHOWEFFECT); clif->skill_nodamage(src, bl, skill_id, skill_lv, heal); } break; @@ -8781,7 +9589,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin clif->skill_nodamage(src,bl,skill_id,1,1); } else - clif->skill_fail(sd,skill_id,USESKILL_FAIL_IMITATION_SKILL_NONE,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_IMITATION_SKILL_NONE, 0, 0); } break; @@ -8791,7 +9599,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin dstsd->shadowform_id = src->id; } else if( sd ) - clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; case SC_BODYPAINT: @@ -8825,9 +9633,9 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if (is_boss(bl)) break; joblvbonus = ( sd ? sd->status.job_level : 50 ); //First we set the success chance based on the caster's build which increases the chance. - rate = 10 * skill_lv + rnd_value( sstatus->dex / 12, sstatus->dex / 4 ) + joblvbonus + status->get_lv(src) / 10; + rate = 10 * skill_lv + rnd->value( sstatus->dex / 12, sstatus->dex / 4 ) + joblvbonus + status->get_lv(src) / 10; // We then reduce the success chance based on the target's build. - rate -= rnd_value( tstatus->agi / 6, tstatus->agi / 3 ) + tstatus->luk / 10 + ( dstsd ? (dstsd->max_weight / 10 - dstsd->weight / 10 ) / 100 : 0 ) + status->get_lv(bl) / 10; + rate -= rnd->value( tstatus->agi / 6, tstatus->agi / 3 ) + tstatus->luk / 10 + ( dstsd ? (dstsd->max_weight / 10 - dstsd->weight / 10 ) / 100 : 0 ) + status->get_lv(bl) / 10; //Finally we set the minimum success chance cap based on the caster's skill level and DEX. rate = cap_value( rate, skill_lv + sstatus->dex / 20, 100); clif->skill_nodamage(src,bl,skill_id,0,sc_start(src,bl,type,rate,skill_lv,skill->get_time(skill_id,skill_lv))); @@ -8836,7 +9644,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin int sp = 100 * skill_lv; if( dstmd ) sp = dstmd->level * 2; if( status_zap(bl,0,sp) ) - status->heal(src,0,sp/2,3);//What does flag 3 do? [Rytech] + status->heal(src, 0, sp / 2, STATUS_HEAL_FORCED | STATUS_HEAL_SHOWEFFECT); } if ( tsc && tsc->data[SC__UNLUCKY] && skill_id == SC_UNLUCKY) { //If the target was successfully inflected with the Unlucky status, give 1 of 3 random status's. @@ -8852,12 +9660,13 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin } } } else if( sd ) - clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; case LG_TRAMPLE: clif->skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, BDT_SKILL); map->foreachinrange(skill->destroy_trap,bl,skill->get_splash(skill_id,skill_lv),BL_SKILL,tick); + status_change_end(bl, SC_SV_ROOTTWIST, INVALID_TIMER); break; case LG_REFLECTDAMAGE: @@ -8878,7 +9687,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin struct item_data *shield_data = NULL; if( sd->equip_index[EQI_HAND_L] < 0 || !( shield_data = sd->inventory_data[sd->equip_index[EQI_HAND_L]] ) || shield_data->type != IT_ARMOR ) { //Skill will first check if a shield is equipped. If none is found on the caster the skill will fail. - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } //Generates a number between 1 - 3. The number generated will determine which effect will be triggered. @@ -8959,7 +9768,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case 3: sc_start(src, bl, SC_SHIELDSPELL_REF, 100, opt, INFINITE_DURATION); // HP Recovery val = sstatus->max_hp * ((status->get_lv(src) / 10) + (shield->refine + 1)) / 100; - status->heal(bl, val, 0, 2); + status->heal(bl, val, 0, STATUS_HEAL_SHOWEFFECT); status_change_end(bl,SC_SHIELDSPELL_REF,INVALID_TIMER); break; } @@ -9064,8 +9873,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case SR_ASSIMILATEPOWER: if( flag&1 ) { int sp = 0; - if( dstsd && dstsd->spiritball && (sd == dstsd || map_flag_vs(src->m)) && (dstsd->class_&MAPID_BASEMASK)!=MAPID_GUNSLINGER ) - { + if (dstsd != NULL && dstsd->spiritball != 0 && (sd == dstsd || map_flag_vs(src->m)) && (dstsd->job & MAPID_BASEMASK) != MAPID_GUNSLINGER) { sp = dstsd->spiritball; //1%sp per spiritball. pc->delspiritball(dstsd, dstsd->spiritball, 0); status_percent_heal(src, 0, sp); @@ -9076,14 +9884,14 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin clif->skill_nodamage(src, bl, skill_id, skill_lv, sp ? 1:0); } else { clif->skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, BDT_SKILL); - map->foreachinrange(skill->area_sub, bl, skill->get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|BCT_SELF|SD_SPLASH|1, skill->castend_nodamage_id); + map->foreachinrange(skill->area_sub, bl, skill->get_splash(skill_id, skill_lv), skill->splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|BCT_SELF|SD_SPLASH|1, skill->castend_nodamage_id); } break; case SR_POWERVELOCITY: if( !dstsd ) break; - if ( sd && (dstsd->class_&MAPID_BASEMASK) != MAPID_GUNSLINGER ) { + if (sd != NULL && (dstsd->job & MAPID_BASEMASK) != MAPID_GUNSLINGER) { int i, max = pc->getmaxspiritball(dstsd, 5); for ( i = 0; i < max; i++ ) { pc->addspiritball(dstsd, skill->get_time(MO_CALLSPIRITS, 1), max); @@ -9103,7 +9911,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin } heal = 120 * skill_lv + status_get_max_hp(bl) * (2 + skill_lv) / 100; - status->heal(bl, heal, 0, 0); + status->heal(bl, heal, 0, STATUS_HEAL_DEFAULT); if( (tsc && tsc->opt1) && (rnd()%100 < ((skill_lv * 5) + (status_get_dex(src) + status->get_lv(src)) / 4) - (1 + (rnd() % 10))) ) { status_change_end(bl, SC_STONE, INVALID_TIMER); @@ -9174,7 +9982,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin heal = 1; status->fixed_revive(bl, heal, 0); clif->skill_nodamage(src,bl,skill_id,skill_lv,1); - status->set_sp(bl, 0, 0); + status->set_sp(bl, 0, STATUS_HEAL_DEFAULT); } } break; @@ -9213,7 +10021,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case WM_GLOOMYDAY: if ( tsc && tsc->data[type] ) { - clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } // val4 indicates caster's voice lesson level @@ -9280,10 +10088,10 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin improv_skill_lv = 4 + skill_lv; clif->skill_nodamage (src, bl, skill_id, skill_lv, 1); - if (sd == NULL) { - sd->state.abra_flag = 2; - sd->skillitem = improv_skill_id; - sd->skillitemlv = improv_skill_lv; + if (sd != NULL) { + sd->autocast.type = AUTOCAST_IMPROVISE; + sd->autocast.skill_id = improv_skill_id; + sd->autocast.skill_lv = improv_skill_lv; clif->item_skill(sd, improv_skill_id, improv_skill_lv); } else { struct unit_data *ud = unit->bl2ud(src); @@ -9345,7 +10153,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin break; } if( !mapindex ) { //Given map not found? - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); map->freeblock_unlock(); return 0; } @@ -9387,6 +10195,25 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin clif->skill_damage(src,bl,tick, status_get_amotion(src), 0, 0, 1, skill_id, -2, BDT_SKILL); break; + case SU_HIDE: + if (tsce != NULL) { + clif->skill_nodamage(src, bl, skill_id, skill_lv, 1); + status_change_end(bl, type, INVALID_TIMER); + map->freeblock_unlock(); + return 0; + } + clif->skill_nodamage(src, bl, skill_id, skill_lv, 1); + sc_start(src, bl, type, 100, skill_lv, skill->get_time(skill_id, skill_lv)); + break; + + case SU_BUNCHOFSHRIMP: + if (sd == NULL || sd->status.party_id == 0 || flag&1) { + clif->skill_nodamage(bl, bl, skill_id, skill_lv, sc_start(src, bl, type, 100, skill_lv, skill->get_time(skill_id, skill_lv))); + } else if (sd != NULL) { + party->foreachsamemap(skill->area_sub, sd, skill->get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill->castend_nodamage_id); + } + break; + case GM_SANDMAN: if( tsc ) { if( tsc->opt1 == OPT1_SLEEP ) @@ -9421,7 +10248,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin // Summoning the new one. if( !elemental->create(sd,elemental_class,skill->get_time(skill_id,skill_lv)) ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } clif->skill_nodamage(src,bl,skill_id,skill_lv,1); @@ -9443,7 +10270,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case 3: mode = EL_MODE_AGGRESSIVE; break; } if (!elemental->change_mode(sd->ed, mode)) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } clif->skill_nodamage(src,bl,skill_id,skill_lv,1); @@ -9487,12 +10314,12 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if( !ed ) break; if( !status->charge(&sd->bl,s_hp,s_sp) ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } e_hp = ed->battle_status.max_hp * 10 / 100; e_sp = ed->battle_status.max_sp * 10 / 100; - status->heal(&ed->bl,e_hp,e_sp,3); + status->heal(&ed->bl, e_hp, e_sp, STATUS_HEAL_FORCED | STATUS_HEAL_SHOWEFFECT); clif->skill_nodamage(src,&ed->bl,skill_id,skill_lv,1); } break; @@ -9518,7 +10345,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin sc_start2(src,bl, type, 100, skill_lv, src->id, skill->get_time(skill_id,skill_lv)); (sc->bs_counter)++; } else if( sd ) { - clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } } @@ -9541,7 +10368,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case GN_SLINGITEM: if( sd ) { - short ammo_id; + int ammo_id; int equip_idx = sd->equip_index[EQI_AMMO]; if( equip_idx <= 0 ) break; // No ammo. @@ -9556,7 +10383,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin else skill->attack(BF_WEAPON,src,src,bl,GN_SLINGITEM_RANGEMELEEATK,skill_lv,tick,flag); } else //Otherwise, it fails, shows animation and removes items. - clif->skill_fail(sd,GN_SLINGITEM_RANGEMELEEATK,0xa,0); + clif->skill_fail(sd, GN_SLINGITEM_RANGEMELEEATK, 0xa, 0, 0); } else if( itemdb_is_GNthrowable(ammo_id) ) { struct script_code *scriptroot = sd->inventory_data[equip_idx]->script; if( !scriptroot ) @@ -9657,7 +10484,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case KO_KAZEHU_SEIRAN: case KO_DOHU_KOUKAI: if(sd) { - int ttype = skill->get_ele(skill_id, skill_lv); + enum spirit_charm_types ttype = skill->get_ele(skill_id, skill_lv); clif->skill_nodamage(src, bl, skill_id, skill_lv, 1); pc->add_charm(sd, skill->get_time(skill_id, skill_lv), MAX_SPIRITCHARM, ttype); // replace existing charms of other type } @@ -9667,7 +10494,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if(sd) { struct mob_data *summon_md; - summon_md = mob->once_spawn_sub(src, src->m, src->x, src->y, clif->get_bl_name(src), MOBID_KO_KAGE, "", SZ_SMALL, AI_NONE); + summon_md = mob->once_spawn_sub(src, src->m, src->x, src->y, clif->get_bl_name(src), MOBID_KO_KAGE, "", SZ_SMALL, AI_NONE, 0); if( summon_md ) { summon_md->master_id = src->id; summon_md->special_state.ai = AI_ZANZOU; @@ -9684,7 +10511,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case KO_KYOUGAKU: if (!map_flag_vs(src->m) || !dstsd) { - if (sd) clif->skill_fail(sd, skill_id, USESKILL_FAIL_SIZE, 0); + if (sd) clif->skill_fail(sd, skill_id, USESKILL_FAIL_SIZE, 0, 0); break; } else { int time; @@ -9706,14 +10533,14 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if( status->get_lv(bl) <= status->get_lv(src) ) status->change_start(src, bl, SC_COMA, skill_lv, skill_lv, 0, src->id, 0, 0, SCFLAG_NONE); } else if( sd ) - clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; case KO_GENWAKU: if ( !map_flag_gvg2(src->m) && ( dstsd || dstmd ) && !(tstatus->mode&MD_PLANT) && battle->check_target(src,bl,BCT_ENEMY) > 0 ) { int x = src->x, y = src->y; if( sd && rnd()%100 > max(5, (45 + 5 * skill_lv) - status_get_int(bl) / 10) ){//[(Base chance of success) - ( target's int / 10)]%. - clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } @@ -9735,7 +10562,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case OB_OBOROGENSOU: if( sd && ( (skill_id == OB_OBOROGENSOU && bl->type == BL_MOB) // This skill does not work on monsters. || is_boss(bl) ) ){ // Does not work on Boss monsters. - clif->skill_fail(sd, skill_id, USESKILL_FAIL_TOTARGET_PLAYER, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_TOTARGET_PLAYER, 0, 0); break; } case KO_IZAYOI: @@ -9768,7 +10595,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin } } else { skill->area_temp[2] = 0; - map->foreachinrange(skill->area_sub, bl, skill->get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill->castend_nodamage_id); + map->foreachinrange(skill->area_sub, bl, skill->get_splash(skill_id, skill_lv), skill->splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill->castend_nodamage_id); } break; @@ -9793,6 +10620,8 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin SC_MANDRAGORA, SC_HARMONIZE, SC_DEEP_SLEEP, SC_SIREN, SC_SLEEP, SC_CONFUSION, SC_ILLUSION }; int heal; + if (hd == NULL) + break; if(tsc){ int i; for (i = 0; i < ARRAYLENGTH(scs); i++) { @@ -9800,7 +10629,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin } } heal = 5 * status->get_lv(&hd->bl) + status->base_matk(&hd->bl, &hd->battle_status, status->get_lv(&hd->bl)); - status->heal(bl, heal, 0, 0); + status->heal(bl, heal, 0, STATUS_HEAL_DEFAULT); clif->skill_nodamage(src, src, skill_id, skill_lv, clif->skill_nodamage(src, bl, AL_HEAL, heal, 1)); status->change_start(src, src, type, 1000, skill_lv, 0, 0, 0, skill->get_time(skill_id,skill_lv), SCFLAG_NOAVOID|SCFLAG_FIXEDTICK|SCFLAG_FIXEDRATE); status->change_start(src, bl, type, 1000, skill_lv, 0, 0, 0, skill->get_time(skill_id,skill_lv), SCFLAG_NOAVOID|SCFLAG_FIXEDTICK|SCFLAG_FIXEDRATE); @@ -9848,7 +10677,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin for (i = 0; i < summons[skill_lv-1].quantity; i++) { struct mob_data *summon_md = mob->once_spawn_sub(src, src->m, src->x, src->y, clif->get_bl_name(src), - summons[skill_lv-1].mob_id, "", SZ_SMALL, AI_ATTACK); + summons[skill_lv-1].mob_id, "", SZ_SMALL, AI_ATTACK, 0); if (summon_md != NULL) { summon_md->master_id = src->id; if (summon_md->deletetimer != INVALID_TIMER) @@ -9897,23 +10726,25 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin return 0; } -bool skill_castend_nodamage_id_dead_unknown(struct block_list *src, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag) +static bool skill_castend_nodamage_id_dead_unknown(struct block_list *src, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag) { - return true; + return true; } -bool skill_castend_nodamage_id_undead_unknown(struct block_list *src, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag) +static bool skill_castend_nodamage_id_undead_unknown(struct block_list *src, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag) { - return true; + return true; } -bool skill_castend_nodamage_id_mado_unknown(struct block_list *src, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag) +static bool skill_castend_nodamage_id_mado_unknown(struct block_list *src, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag) { - return false; + return false; } -bool skill_castend_nodamage_id_unknown(struct block_list *src, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag) +static bool skill_castend_nodamage_id_unknown(struct block_list *src, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag) { + nullpo_retr(true, skill_id); + nullpo_retr(true, skill_lv); ShowWarning("skill_castend_nodamage_id: Unknown skill used:%d\n", *skill_id); clif->skill_nodamage(src, bl, *skill_id, *skill_lv, 1); map->freeblock_unlock(); @@ -9923,13 +10754,14 @@ bool skill_castend_nodamage_id_unknown(struct block_list *src, struct block_list /*========================================== * *------------------------------------------*/ -int skill_castend_pos(int tid, int64 tick, int id, intptr_t data) +static int skill_castend_pos(int tid, int64 tick, int id, intptr_t data) { struct block_list* src = map->id2bl(id); struct map_session_data *sd; struct unit_data *ud = unit->bl2ud(src); struct mob_data *md; + nullpo_ret(src); nullpo_ret(ud); sd = BL_CAST(BL_PC , src); @@ -9947,7 +10779,7 @@ int skill_castend_pos(int tid, int64 tick, int id, intptr_t data) return 0; } - if( sd && ud->skilltimer != INVALID_TIMER && ( pc->checkskill(sd,SA_FREECAST) > 0 || ud->skill_id == LG_EXEEDBREAK ) ) + if (sd && ud->skilltimer != INVALID_TIMER && (pc->checkskill(sd, SA_FREECAST) > 0 || ud->skill_id == LG_EXEEDBREAK || (skill->get_inf2(ud->skill_id) & INF2_FREE_CAST_REDUCED) != 0)) {// restore original walk speed ud->skilltimer = INVALID_TIMER; status_calc_bl(&sd->bl, SCB_SPEED|SCB_ASPD); @@ -9964,7 +10796,7 @@ int skill_castend_pos(int tid, int64 tick, int id, intptr_t data) skill->check_unit_range(src,ud->skillx,ud->skilly,ud->skill_id,ud->skill_lv) ) { - if (sd) clif->skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0); + if (sd) clif->skill_fail(sd, ud->skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } if( src->type&battle_config.skill_nofootset && @@ -9972,7 +10804,7 @@ int skill_castend_pos(int tid, int64 tick, int id, intptr_t data) skill->check_unit_range2(src,ud->skillx,ud->skilly,ud->skill_id,ud->skill_lv) ) { - if (sd) clif->skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0); + if (sd) clif->skill_fail(sd, ud->skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } if( src->type&battle_config.land_skill_limit && @@ -9985,7 +10817,7 @@ int skill_castend_pos(int tid, int64 tick, int id, intptr_t data) } if( maxcount == 0 ) { - if (sd) clif->skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0); + if (sd) clif->skill_fail(sd, ud->skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } } @@ -10028,7 +10860,7 @@ int skill_castend_pos(int tid, int64 tick, int id, intptr_t data) if (ud->walktimer != INVALID_TIMER) unit->stop_walking(src, STOPWALKING_FLAG_FIXPOS); - if( !sd || sd->skillitem != ud->skill_id || skill->get_delay(ud->skill_id,ud->skill_lv) ) + if (sd == NULL || sd->autocast.skill_id != ud->skill_id || skill->get_delay(ud->skill_id,ud->skill_lv) != 0) ud->canact_tick = tick + skill->delay_fix(src, ud->skill_id, ud->skill_lv); if (sd) { //Cooldown application int i, cooldown = skill->get_cooldown(ud->skill_id, ud->skill_lv); @@ -10042,7 +10874,7 @@ int skill_castend_pos(int tid, int64 tick, int id, intptr_t data) skill->blockpc_start(sd, ud->skill_id, cooldown); } if( battle_config.display_status_timers && sd ) - clif->status_change(src, SI_POSTDELAY, 1, skill->delay_fix(src, ud->skill_id, ud->skill_lv), 0, 0, 0); + clif->status_change(src, status->get_sc_icon(SC_POSTDELAY), status->get_sc_relevant_bl_types(SC_POSTDELAY), 1, skill->delay_fix(src, ud->skill_id, ud->skill_lv), 0, 0, 0); #if 0 if (sd) { switch (ud->skill_id) { @@ -10057,10 +10889,10 @@ int skill_castend_pos(int tid, int64 tick, int id, intptr_t data) map->freeblock_lock(); skill->castend_pos2(src,ud->skillx,ud->skilly,ud->skill_id,ud->skill_lv,tick,0); - if( sd && sd->skillitem != AL_WARP ) // Warp-Portal thru items will clear data in skill_castend_map. [Inkfish] - sd->skillitem = sd->skillitemlv = 0; + if (sd != NULL && sd->autocast.skill_id != AL_WARP) // Warp-Portal thru items will clear data in skill_castend_map. [Inkfish] + pc->autocast_clear(sd); - unit->setdir(src, map->calc_dir(src, ud->skillx, ud->skilly)); + unit->set_dir(src, map->calc_dir(src, ud->skillx, ud->skilly)); if (ud->skilltimer == INVALID_TIMER) { if (md) md->skill_idx = -1; @@ -10072,18 +10904,18 @@ int skill_castend_pos(int tid, int64 tick, int id, intptr_t data) return 1; } while(0); - if( !sd || sd->skillitem != ud->skill_id || skill->get_delay(ud->skill_id,ud->skill_lv) ) + if (sd == NULL || sd->autocast.skill_id != ud->skill_id || skill->get_delay(ud->skill_id,ud->skill_lv) != 0) ud->canact_tick = tick; ud->skill_id = ud->skill_lv = 0; if(sd) - sd->skillitem = sd->skillitemlv = 0; + pc->autocast_clear(sd); else if(md) md->skill_idx = -1; return 0; } -static int check_npc_chaospanic(struct block_list *bl, va_list args) +static int skill_check_npc_chaospanic(struct block_list *bl, va_list args) { const struct npc_data *nd = NULL; @@ -10096,20 +10928,57 @@ static int check_npc_chaospanic(struct block_list *bl, va_list args) return 1; } + /* skill count without self */ -static int skill_count_wos(struct block_list *bl,va_list ap) { +static int skill_count_wos(struct block_list *bl, va_list ap) +{ struct block_list* src = va_arg(ap, struct block_list*); + nullpo_retr(1, bl); + nullpo_retr(1, src); if( src->id != bl->id ) { return 1; } return 0; } +/** + * Returns the linked song/dance skill ID, if any (for the Bard/Dancer Soul Link). + * + * @param skill_id The skill ID to look up + * + * @return The linked song or dance's skill ID if any + * @retval 0 if the given skill_id doesn't have a linked skill ID + */ +static int skill_get_linked_song_dance_id(int skill_id) +{ + switch (skill_id) { + case BA_WHISTLE: + return DC_HUMMING; + case BA_ASSASSINCROSS: + return DC_DONTFORGETME; + case BA_POEMBRAGI: + return DC_FORTUNEKISS; + case BA_APPLEIDUN: + return DC_SERVICEFORYOU; + case DC_HUMMING: + return BA_WHISTLE; + case DC_DONTFORGETME: + return BA_ASSASSINCROSS; + case DC_FORTUNEKISS: + return BA_POEMBRAGI; + case DC_SERVICEFORYOU: + return BA_APPLEIDUN; + } + return 0; +} + /*========================================== * *------------------------------------------*/ -int skill_castend_map (struct map_session_data *sd, uint16 skill_id, const char *mapname) { +static int skill_castend_map(struct map_session_data *sd, uint16 skill_id, const char *mapname) +{ nullpo_ret(sd); + nullpo_ret(mapname); //Simplify skill_failed code. #define skill_failed(sd) ( (sd)->menuskill_id = (sd)->menuskill_val = 0 ) @@ -10179,7 +11048,7 @@ int skill_castend_map (struct map_session_data *sd, uint16 skill_id, const char map_index = mapindex->name2id(mapname); if(!map_index) { //Given map not found? - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); skill_failed(sd); return 0; } @@ -10194,13 +11063,13 @@ int skill_castend_map (struct map_session_data *sd, uint16 skill_id, const char maxcount--; } if(!maxcount) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); skill_failed(sd); return 0; } } - lv = sd->skillitem==skill_id?sd->skillitemlv:pc->checkskill(sd,skill_id); + lv = (sd->autocast.type > AUTOCAST_TEMP) ? sd->autocast.skill_lv : pc->checkskill(sd, skill_id); wx = sd->menuskill_val>>16; wy = sd->menuskill_val&0xffff; @@ -10223,7 +11092,7 @@ int skill_castend_map (struct map_session_data *sd, uint16 skill_id, const char } skill->consume_requirement(sd,sd->menuskill_id,lv,2); - sd->skillitem = sd->skillitemlv = 0; // Clear data that's skipped in 'skill_castend_pos' [Inkfish] + pc->autocast_clear(sd); // Clear data which was skipped in skill_castend_pos(). if((group=skill->unitsetting(&sd->bl,skill_id,lv,wx,wy,0))==NULL) { skill_failed(sd); @@ -10246,7 +11115,8 @@ int skill_castend_map (struct map_session_data *sd, uint16 skill_id, const char /*========================================== * *------------------------------------------*/ -int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, uint16 skill_lv, int64 tick, int flag) { +static int skill_castend_pos2(struct block_list *src, int x, int y, uint16 skill_id, uint16 skill_lv, int64 tick, int flag) +{ struct map_session_data* sd; struct status_change* sc; struct status_change_entry *sce; @@ -10275,6 +11145,7 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui case HW_GANBANTEIN: case LG_EARTHDRIVE: case SC_ESCAPE: + case SU_CN_METEOR: break; //Effect is displayed on respective switch case. default: skill->castend_pos2_effect_unknown(src, &x, &y, &skill_id, &skill_lv, &tick, &flag); @@ -10311,9 +11182,10 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui map->foreachinarea(status->change_timer_sub, src->m, x-r, y-r, x+r,y+r,BL_CHAR, src,NULL,SC_SIGHT,tick); - if(battle_config.traps_setting&1) - map->foreachinarea(skill_reveal_trap, - src->m, x-r, y-r, x+r, y+r, BL_SKILL); + if (battle_config.trap_visibility != 0) { + map->foreachinarea(skill_reveal_trap, + src->m, x - r, y - r, x + r, y + r, BL_SKILL); + } break; case SR_RIDEINLIGHTNING: @@ -10339,10 +11211,11 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui case SC_CHAOSPANIC: case SC_MAELSTROM: - if (sd && map->foreachinarea(&check_npc_chaospanic,src->m, x-3, y-3, x+3, y+3, BL_NPC) > 0 ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + if (sd && map->foreachinarea(skill->check_npc_chaospanic, src->m, x-3, y-3, x+3, y+3, BL_NPC) > 0 ) { + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } + FALLTHROUGH case MG_SAFETYWALL: { @@ -10352,6 +11225,7 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui return 0; // Don't consume gems if cast on LP } } + FALLTHROUGH case MG_FIREWALL: case MG_THUNDERSTORM: @@ -10461,10 +11335,13 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui case SO_ELEMENTAL_SHIELD: case RL_B_TRAP: case MH_XENO_SLASHER: - flag|=1;//Set flag to 1 to prevent deleting ammo (it will be deleted on group-delete). + case SU_CN_POWDERING: + case SU_SV_ROOTTWIST: + flag |= 1; // Set flag to 1 to prevent deleting ammo (it will be deleted on group-delete). + FALLTHROUGH case GS_GROUNDDRIFT: //Ammo should be deleted right away. if ( skill_id == WM_SEVERE_RAINSTORM ) - sc_start(src,src,SC_NO_SWITCH_EQUIP,100,0,skill->get_time(skill_id,skill_lv)); + sc_start(src, src, type, 100, 0, skill->get_time(skill_id, skill_lv)); skill->unitsetting(src,skill_id,skill_lv,x,y,0); break; case WZ_ICEWALL: @@ -10481,9 +11358,9 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui if( sc && sc->data[SC_BASILICA] ) status_change_end(src, SC_BASILICA, INVALID_TIMER); // Cancel Basilica else { // Create Basilica. Start SC on caster. Unit timer start SC on others. - if( map->foreachinrange(skill_count_wos, src, 2, BL_MOB|BL_PC, src) ) { + if( map->foreachinrange(skill->count_wos, src, 2, BL_MOB|BL_PC, src) ) { if( sd ) - clif->skill_fail(sd,skill_id,USESKILL_FAIL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL, 0, 0); return 1; } @@ -10513,11 +11390,24 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui break; case WZ_METEOR: + case SU_CN_METEOR: { int area = skill->get_splash(skill_id, skill_lv); short tmpx = 0, tmpy = 0, x1 = 0, y1 = 0; int i; +#if 0 + // The Meteor should inflict curse if Catnip fruit is consumed. + // Currently Catnip fruit is added as requirement. + if (sd && skill_id == SU_CN_METEOR) { + short item_idx = pc->search_inventory(sd, ITEMID_CATNIP_FRUIT); + if (item_idx >= 0) { + pc->delitem(sd, item_idx, 1, 0, 1, LOG_TYPE_SKILL); + flag |= 1; + } + } +#endif + for( i = 0; i < 2 + (skill_lv>>1); i++ ) { // Creates a random Cell in the Splash Area tmpx = x - area + rnd()%(area * 2 + 1); @@ -10569,6 +11459,19 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui } status_change_end(src, SC_HIDING, INVALID_TIMER); break; + case SU_LOPE: + { + if (map->list[src->m].flag.noteleport && !(map->list[src->m].flag.battleground || map_flag_gvg2(src->m))) { + x = src->x; + y = src->y; + } + clif->skill_nodamage(src, src, SU_LOPE, skill_lv, 1); + if(!map->count_oncell(src->m, x, y, BL_PC | BL_NPC | BL_MOB, 0) && map->getcell(src->m, src, x, y, CELL_CHKREACH)) { + clif->slide(src, x, y); + unit->movepos(src, x, y, 1, 0); + } + } + break; case AM_SPHEREMINE: case AM_CANNIBALIZE: { @@ -10588,7 +11491,7 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui } // Correct info, don't change any of this! [Celest] - md = mob->once_spawn_sub(src, src->m, x, y, clif->get_bl_name(src), class_, "", SZ_SMALL, AI_NONE); + md = mob->once_spawn_sub(src, src->m, x, y, clif->get_bl_name(src), class_, "", SZ_SMALL, AI_NONE, 0); if (md) { md->master_id = src->id; md->special_state.ai = (skill_id == AM_SPHEREMINE) ? AI_SPHERE : AI_FLORA; @@ -10603,55 +11506,58 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui // Slim Pitcher [Celest] case CR_SLIMPITCHER: if (sd) { - int i = skill_lv%11 - 1; - int j = pc->search_inventory(sd,skill->dbs->db[skill_id].itemid[i]); - if (j == INDEX_NOT_FOUND || skill->dbs->db[skill_id].itemid[i] <= 0 - || sd->inventory_data[j] == NULL || sd->status.inventory[j].amount < skill->dbs->db[skill_id].amount[i] + int item_idx = (skill_lv - 1) % MAX_SKILL_ITEM_REQUIRE; + int item_id = skill->get_itemid(skill_id, item_idx); + int inventory_idx = pc->search_inventory(sd, item_id); + int bonus; + if (inventory_idx == INDEX_NOT_FOUND || item_id <= 0 + || sd->inventory_data[inventory_idx] == NULL + || sd->status.inventory[inventory_idx].amount < skill->get_itemqty(skill_id, item_idx) ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 1; } script->potion_flag = 1; script->potion_hp = 0; script->potion_sp = 0; - script->run_use_script(sd, sd->inventory_data[j], 0); + script->run_use_script(sd, sd->inventory_data[inventory_idx], 0); script->potion_flag = 0; //Apply skill bonuses - i = pc->checkskill(sd,CR_SLIMPITCHER)*10 + bonus = pc->checkskill(sd,CR_SLIMPITCHER)*10 + pc->checkskill(sd,AM_POTIONPITCHER)*10 + pc->checkskill(sd,AM_LEARNINGPOTION)*5 + pc->skillheal_bonus(sd, skill_id); - script->potion_hp = script->potion_hp * (100+i)/100; - script->potion_sp = script->potion_sp * (100+i)/100; + script->potion_hp = script->potion_hp * (100 + bonus) / 100; + script->potion_sp = script->potion_sp * (100 + bonus) / 100; - if(script->potion_hp > 0 || script->potion_sp > 0) { - i = skill->get_splash(skill_id, skill_lv); + if (script->potion_hp > 0 || script->potion_sp > 0) { + r = skill->get_splash(skill_id, skill_lv); map->foreachinarea(skill->area_sub, - src->m,x-i,y-i,x+i,y+i,BL_CHAR, - src,skill_id,skill_lv,tick,flag|BCT_PARTY|BCT_GUILD|1, + src->m, x - r, y - r, x + r, y + r, BL_CHAR, + src, skill_id, skill_lv, tick, flag|BCT_PARTY|BCT_GUILD|1, skill->castend_nodamage_id); } } else { - int i = skill_lv%11 - 1; - struct item_data *item; - i = skill->dbs->db[skill_id].itemid[i]; - item = itemdb->search(i); + int item_idx = (skill_lv - 1) % MAX_SKILL_ITEM_REQUIRE; + int item_id = skill->get_itemid(skill_id, item_idx); + struct item_data *item = itemdb->search(item_id); + int bonus; script->potion_flag = 1; script->potion_hp = 0; script->potion_sp = 0; script->run(item->script,0,src->id,0); script->potion_flag = 0; - i = skill->get_max(CR_SLIMPITCHER)*10; + bonus = skill->get_max(CR_SLIMPITCHER)*10; - script->potion_hp = script->potion_hp * (100+i)/100; - script->potion_sp = script->potion_sp * (100+i)/100; + script->potion_hp = script->potion_hp * (100 + bonus)/100; + script->potion_sp = script->potion_sp * (100 + bonus)/100; - if(script->potion_hp > 0 || script->potion_sp > 0) { - i = skill->get_splash(skill_id, skill_lv); + if (script->potion_hp > 0 || script->potion_sp > 0) { + r = skill->get_splash(skill_id, skill_lv); map->foreachinarea(skill->area_sub, - src->m,x-i,y-i,x+i,y+i,BL_CHAR, - src,skill_id,skill_lv,tick,flag|BCT_PARTY|BCT_GUILD|1, + src->m, x - r, y - r, x + r, y + r, BL_CHAR, + src, skill_id, skill_lv, tick, flag|BCT_PARTY|BCT_GUILD|1, skill->castend_nodamage_id); } } @@ -10664,7 +11570,7 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui r = skill->get_splash(skill_id, skill_lv); map->foreachinarea(skill->cell_overlap, src->m, x-r, y-r, x+r, y+r, BL_SKILL, HW_GANBANTEIN, &dummy, src); } else { - if (sd) clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + if (sd) clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 1; } break; @@ -10679,15 +11585,15 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui case CR_CULTIVATION: if (sd) { if( map->count_oncell(src->m,x,y,BL_CHAR,0) > 0 ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 1; } clif->skill_poseffect(src,skill_id,skill_lv,x,y,tick); if (rnd()%100 < 50) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); } else { int mob_id = skill_lv < 2 ? MOBID_BLACK_MUSHROOM + rnd()%2 : MOBID_RED_PLANT + rnd()%6; - struct mob_data *md = mob->once_spawn_sub(src, src->m, x, y, "--ja--", mob_id, "", SZ_SMALL, AI_NONE); + struct mob_data *md = mob->once_spawn_sub(src, src->m, x, y, DEFAULT_MOB_JNAME, mob_id, "", SZ_SMALL, AI_NONE, 0); int i; if (md == NULL) break; @@ -10733,7 +11639,7 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui case AM_RESURRECTHOMUN: // [orn] if (sd) { if (!homun->ressurect(sd, 20*skill_lv, x, y)) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); break; } } @@ -10746,7 +11652,7 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui case RK_DRAGONBREATH: case RK_DRAGONBREATH_WATER: r = skill->get_splash(skill_id,skill_lv); - map->foreachinarea(skill->area_sub,src->m,x-r,y-r,x+r,y+r,splash_target(src), + map->foreachinarea(skill->area_sub,src->m,x-r,y-r,x+r,y+r,skill->splash_target(src), src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill->castend_damage_id); break; case WM_GREAT_ECHO: @@ -10768,7 +11674,7 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui break; case SO_ARRULLO: r = skill->get_splash(skill_id,skill_lv); - map->foreachinarea(skill->area_sub,src->m,x-r,y-r,x+r,y+r,splash_target(src), + map->foreachinarea(skill->area_sub,src->m,x-r,y-r,x+r,y+r,skill->splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill->castend_nodamage_id); break; /** @@ -10777,7 +11683,7 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui case GC_POISONSMOKE: if( !(sc && sc->data[SC_POISONINGWEAPON]) ) { if( sd ) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_GC_POISONINGWEAPON,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_GC_POISONINGWEAPON, 0, 0); return 0; } clif->skill_damage(src,src,tick,status_get_amotion(src),0,-30000,1,skill_id,skill_lv,BDT_SKILL); @@ -10796,17 +11702,16 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui case WL_EARTHSTRAIN: { - int i, wave = skill_lv + 4, dir = map->calc_dir(src,x,y); + int i; + int wave = skill_lv + 4; + enum unit_dir dir = map->calc_dir(src, x, y); + Assert_ret(dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX); int sx = x = src->x, sy = y = src->y; // Store first caster's location to avoid glitch on unit setting - for( i = 1; i <= wave; i++ ) - { - switch( dir ){ - case 0: case 1: case 7: sy = y + i; break; - case 3: case 4: case 5: sy = y - i; break; - case 2: sx = x - i; break; - case 6: sx = x + i; break; - } + for (i = 1; i <= wave; i++) { + sy = y + i * diry[dir]; + if (dir == UNIT_DIR_WEST || dir == UNIT_DIR_EAST) + sx = x + i * dirx[dir]; skill->addtimerskill(src,timer->gettick() + (140 * i),0,sx,sy,skill_id,skill_lv,dir,flag&2); } } @@ -10833,7 +11738,7 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui case NC_SILVERSNIPER: { - struct mob_data *md = mob->once_spawn_sub(src, src->m, x, y, clif->get_bl_name(src), MOBID_SILVERSNIPER, "", SZ_SMALL, AI_NONE); + struct mob_data *md = mob->once_spawn_sub(src, src->m, x, y, clif->get_bl_name(src), MOBID_SILVERSNIPER, "", SZ_SMALL, AI_NONE, 0); if (md) { md->master_id = src->id; md->special_state.ai = AI_FLORA; @@ -10883,10 +11788,10 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui case LG_RAYOFGENESIS: if( status->charge(src,status_get_max_hp(src)*3*skill_lv / 100,0) ) { r = skill->get_splash(skill_id,skill_lv); - map->foreachinarea(skill->area_sub,src->m,x-r,y-r,x+r,y+r,splash_target(src), + map->foreachinarea(skill->area_sub,src->m,x-r,y-r,x+r,y+r,skill->splash_target(src), src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill->castend_damage_id); } else if( sd ) - clif->skill_fail(sd,skill_id,USESKILL_FAIL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL, 0, 0); break; case WM_DOMINION_IMPULSE: @@ -10995,20 +11900,22 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui return 0; } -void skill_castend_pos2_effect_unknown(struct block_list* src, int *x, int *y, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag) { +static void skill_castend_pos2_effect_unknown(struct block_list *src, int *x, int *y, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag) +{ if (skill->get_inf(*skill_id) & INF_SELF_SKILL) clif->skill_nodamage(src, src, *skill_id, *skill_lv, 1); else clif->skill_poseffect(src, *skill_id, *skill_lv, *x, *y, *tick); } -bool skill_castend_pos2_unknown(struct block_list* src, int *x, int *y, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag) { +static bool skill_castend_pos2_unknown(struct block_list *src, int *x, int *y, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag) +{ ShowWarning("skill_castend_pos2: Unknown skill used:%d\n", *skill_id); return true; } /// transforms 'target' skill unit into dissonance (if conditions are met) -int skill_dance_overlap_sub(struct block_list *bl, va_list ap) +static int skill_dance_overlap_sub(struct block_list *bl, va_list ap) { struct skill_unit *target = NULL; struct skill_unit *src = va_arg(ap, struct skill_unit*); @@ -11038,7 +11945,8 @@ int skill_dance_overlap_sub(struct block_list *bl, va_list ap) //Does the song/dance overlapping -> dissonance check. [Skotlex] //When flag is 0, this unit is about to be removed, cancel the dissonance effect //When 1, this unit has been positioned, so start the cancel effect. -int skill_dance_overlap(struct skill_unit* su, int flag) { +static int skill_dance_overlap(struct skill_unit *su, int flag) +{ if (!su || !su->group || !(su->group->state.song_dance&0x1)) return 0; @@ -11060,7 +11968,8 @@ int skill_dance_overlap(struct skill_unit* su, int flag) { * @param flag 1 Revert * @retval true success **/ -bool skill_dance_switch(struct skill_unit* su, int flag) { +static bool skill_dance_switch(struct skill_unit *su, int flag) +{ static int prevflag = 1; // by default the backup is empty static struct skill_unit_group backup; struct skill_unit_group* group; @@ -11116,7 +12025,8 @@ bool skill_dance_switch(struct skill_unit* su, int flag) { * Initializes and sets a ground skill. * flag&1 is used to determine when the skill 'morphs' (Warp portal becomes active, or Fire Pillar becomes active) *------------------------------------------*/ -struct skill_unit_group* skill_unitsetting(struct block_list *src, uint16 skill_id, uint16 skill_lv, int16 x, int16 y, int flag) { +static struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 skill_id, uint16 skill_lv, int16 x, int16 y, int flag) +{ struct skill_unit_group *group; int i,limit,val1=0,val2=0,val3=0; int target,interval,range,unit_flag,req_item=0; @@ -11146,9 +12056,10 @@ struct skill_unit_group* skill_unitsetting(struct block_list *src, uint16 skill_ sd = BL_CAST(BL_PC, src); st = status->get_status_data(src); + nullpo_retr(NULL, st); sc = status->get_sc(src); // for traps, firewall and fogwall - celest - switch( skill_id ) { + switch (skill_id) { case SO_ELEMENTAL_SHIELD: val2 = 300 * skill_lv + 65 * (st->int_ + status->get_lv(src)) + st->max_sp; break; @@ -11217,8 +12128,10 @@ struct skill_unit_group* skill_unitsetting(struct block_list *src, uint16 skill_ case HT_ANKLESNARE: if( flag&2 ) val3 = SC_ESCAPE; + FALLTHROUGH case HT_SHOCKWAVE: val1=skill_lv*15+10; + FALLTHROUGH case HT_SANDMAN: case MA_SANDMAN: case HT_CLAYMORETRAP: @@ -11243,7 +12156,7 @@ struct skill_unit_group* skill_unitsetting(struct block_list *src, uint16 skill_ case RA_ICEBOUNDTRAP: { struct skill_condition req = skill->get_requirement(sd,skill_id,skill_lv); - ARR_FIND(0, MAX_SKILL_ITEM_REQUIRE, i, req.itemid[i] && (req.itemid[i] == ITEMID_TRAP || req.itemid[i] == ITEMID_TRAP_ALLOY)); + ARR_FIND(0, MAX_SKILL_ITEM_REQUIRE, i, req.itemid[i] && (req.itemid[i] == ITEMID_BOOBY_TRAP || req.itemid[i] == ITEMID_SPECIAL_ALLOY_TRAP)); if( i != MAX_SKILL_ITEM_REQUIRE && req.itemid[i] ) req_item = req.itemid[i]; if( map_flag_gvg2(src->m) || map->list[src->m].flag.battleground ) @@ -11396,23 +12309,27 @@ struct skill_unit_group* skill_unitsetting(struct block_list *src, uint16 skill_ int element[5]={ELE_WIND,ELE_DARK,ELE_POISON,ELE_WATER,ELE_FIRE}; val1 = st->rhw.ele; - if (!val1) - val1=element[rnd()%5]; + if (val1 == ELE_NEUTRAL) + val1 = element[rnd() % ARRAYLENGTH(element)]; - switch (val1) - { + switch (val1) { case ELE_FIRE: - subunt++; + subunt = 4; + break; case ELE_WATER: - subunt++; + subunt = 3; + break; case ELE_POISON: - subunt++; + subunt = 2; + break; case ELE_DARK: - subunt++; + subunt = 1; + break; case ELE_WIND: + subunt = 0; break; default: - subunt=rnd()%5; + subunt = rnd() % 5; break; } @@ -11445,6 +12362,7 @@ struct skill_unit_group* skill_unitsetting(struct block_list *src, uint16 skill_ target = BCT_ALL; val1 = skill_lv + 1; val2 = 1; + FALLTHROUGH case WM_POEMOFNETHERWORLD: // Can't be placed on top of Land Protector. case SO_WATER_INSIGNIA: case SO_FIRE_INSIGNIA: @@ -11487,6 +12405,7 @@ struct skill_unit_group* skill_unitsetting(struct block_list *src, uint16 skill_ break; } + nullpo_retr(NULL, layout); nullpo_retr(NULL, group=skill->init_unitgroup(src,layout->count,skill_id,skill_lv,skill->get_unit_id(skill_id,flag&1)+subunt, limit, interval)); group->val1=val1; group->val2=val2; @@ -11627,10 +12546,15 @@ struct skill_unit_group* skill_unitsetting(struct block_list *src, uint16 skill_ return group; } -void skill_unitsetting1_unknown(struct block_list *src, uint16 *skill_id, uint16 *skill_lv, int16 *x, int16 *y, int *flag, int *val1, int *val2, int *val3) { +static void skill_unitsetting1_unknown(struct block_list *src, uint16 *skill_id, uint16 *skill_lv, int16 *x, int16 *y, int *flag, int *val1, int *val2, int *val3) +{ } -void skill_unitsetting2_unknown(struct block_list *src, uint16 *skill_id, uint16 *skill_lv, int16 *x, int16 *y, int *flag, int *unit_flag, int *val1, int *val2, int *val3, struct skill_unit_group *group) { +static void skill_unitsetting2_unknown(struct block_list *src, uint16 *skill_id, uint16 *skill_lv, int16 *x, int16 *y, int *flag, int *unit_flag, int *val1, int *val2, int *val3, struct skill_unit_group *group) +{ + nullpo_retv(group); + nullpo_retv(val2); + nullpo_retv(unit_flag); if (group->state.song_dance & 0x1) *val2 = *unit_flag & (UF_DANCE | UF_SONG); //Store whether this is a song/dance } @@ -11638,7 +12562,8 @@ void skill_unitsetting2_unknown(struct block_list *src, uint16 *skill_id, uint16 /*========================================== * *------------------------------------------*/ -int skill_unit_onplace(struct skill_unit *src, struct block_list *bl, int64 tick) { +static int skill_unit_onplace(struct skill_unit *src, struct block_list *bl, int64 tick) +{ struct skill_unit_group *sg; struct block_list *ss; struct status_change *sc; @@ -11716,7 +12641,7 @@ int skill_unit_onplace(struct skill_unit *src, struct block_list *bl, int64 tick if (bl->type == BL_PC && !working) { struct map_session_data *sd = BL_UCAST(BL_PC, bl); - if ((!sd->chatID || battle_config.chat_warpportal) && sd->ud.to_x == src->bl.x && sd->ud.to_y == src->bl.y) { + if ((sd->chat_id == 0 || battle_config.chat_warpportal) && sd->ud.to_x == src->bl.x && sd->ud.to_y == src->bl.y) { int x = sg->val2>>16; int y = sg->val2&0xffff; int count = sg->val1>>16; @@ -11762,6 +12687,7 @@ int skill_unit_onplace(struct skill_unit *src, struct block_list *bl, int64 tick case UNT_HERMODE: if (sg->src_id!=bl->id && battle->check_target(&src->bl,bl,BCT_PARTY|BCT_GUILD) > 0) status->change_clear_buffs(bl,1); //Should dispell only allies. + FALLTHROUGH case UNT_RICHMANKIM: case UNT_ETERNALCHAOS: case UNT_DRUMBATTLEFIELD: @@ -11780,6 +12706,7 @@ int skill_unit_onplace(struct skill_unit *src, struct block_list *bl, int64 tick if (!battle_config.song_timer_reset && sc && sce) return 0; // Let it fall through + FALLTHROUGH case UNT_WHISTLE: case UNT_ASSASSINCROSS: case UNT_POEMBRAGI: @@ -11859,7 +12786,7 @@ int skill_unit_onplace(struct skill_unit *src, struct block_list *bl, int64 tick if (sg->src_id == bl->id) break; //Does not affect the caster. clif->changetraplook(&src->bl,UNT_USED_TRAPS); - map->foreachinrange(skill->trap_splash,&src->bl, skill->get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &src->bl,tick); + skill->trap_do_splash(&src->bl, sg->skill_id, sg->skill_lv, sg->bl_flag, tick); sg->unit_id = UNT_USED_TRAPS; sg->limit = DIFF_TICK32(tick,sg->tick) + 1500; break; @@ -11869,6 +12796,13 @@ int skill_unit_onplace(struct skill_unit *src, struct block_list *bl, int64 tick sc_start(ss, bl, SC_VOLCANIC_ASH, 100, sg->skill_lv, skill->get_time(MH_VOLCANIC_ASH, sg->skill_lv)); break; + case UNT_CATNIPPOWDER: + if (sg->src_id == bl->id || (status_get_mode(bl)&MD_BOSS)) + break; // Does not affect the caster or Boss. + if (sce == NULL && battle->check_target(&src->bl, bl, BCT_ENEMY) > 0) + sc_start(ss, bl, type, 100, sg->skill_lv, skill->get_time(sg->skill_id, sg->skill_lv)); + break; + case UNT_GD_LEADERSHIP: case UNT_GD_GLORYWOUNDS: case UNT_GD_SOULCOLD: @@ -11883,17 +12817,19 @@ int skill_unit_onplace(struct skill_unit *src, struct block_list *bl, int64 tick return skill_id; } -void skill_unit_onplace_unknown(struct skill_unit *src, struct block_list *bl, int64 *tick) { +static void skill_unit_onplace_unknown(struct skill_unit *src, struct block_list *bl, int64 *tick) +{ } /*========================================== * *------------------------------------------*/ -int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int64 tick) { +static int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int64 tick) +{ struct skill_unit_group *sg; struct block_list *ss; struct map_session_data *tsd; - struct status_data *tstatus, *bst; + struct status_data *tstatus; struct status_change *tsc, *ssc; struct skill_unit_group_tickset *ts; enum sc_type type; @@ -11917,7 +12853,7 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 return 0; tstatus = status->get_status_data(bl); - bst = status->get_base_status(bl); + nullpo_ret(tstatus); type = status->skill2sc(sg->skill_id); skill_id = sg->skill_id; @@ -11965,6 +12901,13 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 ts->tick += sg->interval*(map->count_oncell(bl->m,bl->x,bl->y,BL_CHAR,0)-1); } + if (sg->skill_id == HT_ANKLESNARE + || (battle_config.trap_trigger == 1 && skill->get_inf2(sg->skill_id) & INF2_HIDDEN_TRAP) + ) { + src->visible = true; + clif->skillunit_update(&src->bl); + } + switch (sg->unit_id) { case UNT_FIREWALL: case UNT_KAEN: { @@ -12006,7 +12949,7 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 clif->skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1); if (tsc && tsc->data[SC_AKAITSUKI] && heal) heal = ~heal + 1; - status->heal(bl, heal, 0, 0); + status->heal(bl, heal, 0, STATUS_HEAL_DEFAULT); if (diff >= 500) sg->val1--; } @@ -12027,7 +12970,7 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 if (status->isimmune(bl)) heal = 0; clif->skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1); - status->heal(bl, heal, 0, 0); + status->heal(bl, heal, 0, STATUS_HEAL_DEFAULT); } break; @@ -12074,12 +13017,12 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 tsc->sg_counter++; //SG hit counter. if (skill->attack(skill->get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0) <= 0 && tsc) tsc->sg_counter=0; //Attack absorbed. - break; + break; #endif case GS_DESPERADO: if (rnd()%100 < src->val1) skill->attack(BF_WEAPON,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); - break; + break; default: skill->attack(skill->get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); } @@ -12117,10 +13060,11 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 clif->fixpos(bl); } sg->val2 = bl->id; - } else + } else { sec = 3000; //Couldn't trap it? + } + if( sg->unit_id == UNT_ANKLESNARE ) { - clif->skillunit_update(&src->bl); /** * If you're snared from a trap that was invisible this makes the trap be * visible again -- being you stepped on it (w/o this the trap remains invisible and you go "WTF WHY I CANT MOVE") @@ -12144,7 +13088,7 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 } - map->foreachinrange(skill->trap_splash, &src->bl, skill->get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &src->bl, tick); + skill->trap_do_splash(&src->bl, sg->skill_id, sg->skill_lv, sg->bl_flag, tick); sg->unit_id = UNT_USED_TRAPS; //Changed ID so it does not invoke a for each in area again. } break; @@ -12160,11 +13104,13 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 case UNT_VERDURETRAP: if( bl->type == BL_PC )// it won't work on players break; + FALLTHROUGH case UNT_FIRINGTRAP: case UNT_ICEBOUNDTRAP: case UNT_CLUSTERBOMB: if( bl->id == ss->id )// it won't trigger on caster break; + FALLTHROUGH case UNT_LANDMINE: case UNT_BLASTMINE: case UNT_SHOCKWAVE: @@ -12173,10 +13119,10 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 case UNT_FREEZINGTRAP: case UNT_FIREPILLAR_ACTIVE: case UNT_CLAYMORETRAP: - if( sg->unit_id == UNT_FIRINGTRAP || sg->unit_id == UNT_ICEBOUNDTRAP || sg->unit_id == UNT_CLAYMORETRAP ) - map->foreachinrange(skill->trap_splash,&src->bl, skill->get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag|BL_SKILL|~BCT_SELF, &src->bl,tick); + if (sg->unit_id == UNT_FIRINGTRAP || sg->unit_id == UNT_ICEBOUNDTRAP || sg->unit_id == UNT_CLAYMORETRAP) + skill->trap_do_splash(&src->bl, sg->skill_id, sg->skill_lv, sg->bl_flag | BL_SKILL | ~BCT_SELF, tick); else - map->foreachinrange(skill->trap_splash,&src->bl, skill->get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &src->bl,tick); + skill->trap_do_splash(&src->bl, sg->skill_id, sg->skill_lv, sg->bl_flag, tick); if (sg->unit_id != UNT_FIREPILLAR_ACTIVE) clif->changetraplook(&src->bl, sg->unit_id==UNT_LANDMINE?UNT_FIREPILLAR_ACTIVE:UNT_USED_TRAPS); sg->limit=DIFF_TICK32(tick,sg->tick)+1500 + @@ -12236,7 +13182,7 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 if( tsc && tsc->data[SC_AKAITSUKI] && heal ) heal = ~heal + 1; clif->skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1); - status->heal(bl, heal, 0, 0); + status->heal(bl, heal, 0, STATUS_HEAL_DEFAULT); } } break; @@ -12290,7 +13236,7 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 case 0: // Heal 1~9999 HP heal = rnd() %9999+1; clif->skill_nodamage(ss,bl,AL_HEAL,heal,1); - status->heal(bl,heal,0,0); + status->heal(bl, heal, 0, STATUS_HEAL_DEFAULT); break; case 1: // End all negative status status->change_clear_buffs(bl,2); @@ -12407,9 +13353,7 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 case UNT_GROUNDDRIFT_POISON: case UNT_GROUNDDRIFT_WATER: case UNT_GROUNDDRIFT_FIRE: - map->foreachinrange(skill->trap_splash,&src->bl, - skill->get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, - &src->bl,tick); + skill->trap_do_splash(&src->bl, sg->skill_id, sg->skill_lv, sg->bl_flag, tick); sg->unit_id = UNT_USED_TRAPS; //clif->changetraplook(&src->bl, UNT_FIREPILLAR_ACTIVE); sg->limit=DIFF_TICK32(tick,sg->tick)+1500; @@ -12437,7 +13381,7 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 } hp = tstatus->max_hp * hp / 100; sp = tstatus->max_sp * sp / 100; - status->heal(bl, hp, sp, 2); + status->heal(bl, hp, sp, STATUS_HEAL_SHOWEFFECT); sc_start(ss, bl, type, 100, sg->skill_lv, (sg->interval * 3) + 100); } // Reveal hidden players every 5 seconds. @@ -12456,6 +13400,7 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 case UNT_STEALTHFIELD: if( bl->id == sg->src_id ) break; // Don't work on Self (video shows that) + FALLTHROUGH case UNT_NEUTRALBARRIER: sc_start(ss,bl,type,100,sg->skill_lv,sg->interval + 100); break; @@ -12469,7 +13414,7 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 case UNT_REVERBERATION: clif->changetraplook(&src->bl,UNT_USED_TRAPS); - map->foreachinrange(skill->trap_splash,&src->bl, skill->get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &src->bl,tick); + skill->trap_do_splash(&src->bl, sg->skill_id, sg->skill_lv, sg->bl_flag, tick); sg->limit = DIFF_TICK32(tick,sg->tick)+1500; sg->unit_id = UNT_USED_TRAPS; break; @@ -12558,7 +13503,7 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 clif->skill_nodamage(&src->bl, bl, AL_HEAL, hp, 0); if( tsc && tsc->data[SC_AKAITSUKI] && hp ) hp = ~hp + 1; - status->heal(bl, hp, 0, 0); + status->heal(bl, hp, 0, STATUS_HEAL_DEFAULT); sc_start(ss, bl, type, 100, sg->skill_lv, sg->interval + 100); } break; @@ -12572,13 +13517,13 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 int hp = tstatus->max_hp / 100; //+1% each 5s if ((sg->val3) % 5) { //each 5s if (tstatus->def_ele == skill->get_ele(sg->skill_id,sg->skill_lv)) { - status->heal(bl, hp, 0, 2); + status->heal(bl, hp, 0, STATUS_HEAL_SHOWEFFECT); } else if( (sg->unit_id == UNT_FIRE_INSIGNIA && tstatus->def_ele == ELE_EARTH) || (sg->unit_id == UNT_WATER_INSIGNIA && tstatus->def_ele == ELE_FIRE) || (sg->unit_id == UNT_WIND_INSIGNIA && tstatus->def_ele == ELE_WATER) || (sg->unit_id == UNT_EARTH_INSIGNIA && tstatus->def_ele == ELE_WIND) ) { - status->heal(bl, -hp, 0, 0); + status->heal(bl, -hp, 0, STATUS_HEAL_DEFAULT); } } sg->val3++; //timer @@ -12590,6 +13535,8 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 if (tsc && (tsc->data[SC_HALLUCINATIONWALK] || tsc->data[SC_VACUUM_EXTREME])) { return 0; } else { + struct status_data *bst = status->get_base_status(bl); + nullpo_ret(bst); sg->limit -= 1000 * bst->str/20; sc_start(ss, bl, SC_VACUUM_EXTREME, 100, sg->skill_lv, sg->limit); @@ -12646,6 +13593,33 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 status->change_start(ss, bl, SC_BLIND, rnd() % 100 > sg->skill_lv * 10, sg->skill_lv, sg->skill_id, 0, 0, skill->get_time2(sg->skill_id, sg->skill_lv), SCFLAG_FIXEDTICK|SCFLAG_FIXEDRATE); break; + case UNT_SV_ROOTTWIST: + if (status_get_mode(bl)&MD_BOSS) { + break; + } + if (tsc) { + if (!sg->val2) { + int sec = skill->get_time(sg->skill_id, sg->skill_lv); + + if (sc_start2(ss, bl, type, 100, sg->skill_lv, sg->group_id, sec)) { + const struct TimerData* td = ((tsc->data[type])? timer->get(tsc->data[type]->timer) : NULL); + + if (td != NULL) + sec = DIFF_TICK32(td->tick, tick); + clif->fixpos(bl); + sg->val2 = bl->id; + } else { // Couldn't trap it? + sec = 7000; + } + sg->limit = DIFF_TICK32(tick, sg->tick) + sec; + } else if (tsc->data[type] && bl->id == sg->val2) { + skill->attack(skill->get_type(SU_SV_ROOTTWIST_ATK), ss, &src->bl, bl, SU_SV_ROOTTWIST_ATK, sg->skill_lv, tick, SD_LEVEL|SD_ANIMATION); + } + } + break; + default: + skill->unit_onplace_timer_unknown(src, bl, &tick); + break; } if (bl->type == BL_MOB && ss != bl) @@ -12653,10 +13627,16 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 return skill_id; } + +static void skill_unit_onplace_timer_unknown(struct skill_unit *src, struct block_list *bl, int64 *tick) +{ +} + /*========================================== * Triggered when a char steps out of a skill cell *------------------------------------------*/ -int skill_unit_onout(struct skill_unit *src, struct block_list *bl, int64 tick) { +static int skill_unit_onout(struct skill_unit *src, struct block_list *bl, int64 tick) +{ struct skill_unit_group *sg; struct status_change *sc; struct status_change_entry *sce; @@ -12721,7 +13701,8 @@ int skill_unit_onout(struct skill_unit *src, struct block_list *bl, int64 tick) /*========================================== * Triggered when a char steps out of a skill group (entirely) [Skotlex] *------------------------------------------*/ -int skill_unit_onleft(uint16 skill_id, struct block_list *bl, int64 tick) { +static int skill_unit_onleft(uint16 skill_id, struct block_list *bl, int64 tick) +{ struct status_change *sc; struct status_change_entry *sce; enum sc_type type; @@ -12806,7 +13787,8 @@ int skill_unit_onleft(uint16 skill_id, struct block_list *bl, int64 tick) { case PF_FOGWALL: if (sce) { status_change_end(bl, type, INVALID_TIMER); - if ((sce=sc->data[SC_BLIND])) { + nullpo_retb(sc); + if ((sce = sc->data[SC_BLIND])) { if (bl->type == BL_PC) //Players get blind ended immediately, others have it still for 30 secs. [Skotlex] status_change_end(bl, SC_BLIND, INVALID_TIMER); else { @@ -12834,14 +13816,19 @@ int skill_unit_onleft(uint16 skill_id, struct block_list *bl, int64 tick) { * flag&1: Invoke onplace function (otherwise invoke onout) * flag&4: Invoke a onleft call (the unit might be scheduled for deletion) *------------------------------------------*/ -int skill_unit_effect(struct block_list* bl, va_list ap) { +static int skill_unit_effect(struct block_list *bl, va_list ap) +{ struct skill_unit* su = va_arg(ap,struct skill_unit*); - struct skill_unit_group* group = su->group; + struct skill_unit_group* group; int64 tick = va_arg(ap,int64); unsigned int flag = va_arg(ap,unsigned int); uint16 skill_id; bool dissonance; + nullpo_ret(bl); + nullpo_ret(su); + group = su->group; + if( (!su->alive && !(flag&4)) || bl->prev == NULL ) return 0; @@ -12873,7 +13860,8 @@ int skill_unit_effect(struct block_list* bl, va_list ap) { /*========================================== * *------------------------------------------*/ -int skill_unit_ondamaged(struct skill_unit *src, struct block_list *bl, int64 damage, int64 tick) { +static int skill_unit_ondamaged(struct skill_unit *src, struct block_list *bl, int64 damage, int64 tick) +{ struct skill_unit_group *sg; nullpo_ret(src); @@ -12907,7 +13895,7 @@ int skill_unit_ondamaged(struct skill_unit *src, struct block_list *bl, int64 da /*========================================== * *------------------------------------------*/ -int skill_check_condition_char_sub (struct block_list *bl, va_list ap) +static int skill_check_condition_char_sub(struct block_list *bl, va_list ap) { struct map_session_data *tsd = NULL; struct block_list *src = va_arg(ap, struct block_list *); @@ -12936,33 +13924,34 @@ int skill_check_condition_char_sub (struct block_list *bl, va_list ap) return 0; if( skill->get_inf2(skill_id)&INF2_CHORUS_SKILL ) { - if( tsd->status.party_id == sd->status.party_id && (tsd->class_&MAPID_THIRDMASK) == MAPID_MINSTRELWANDERER ) + if (tsd->status.party_id == sd->status.party_id && (tsd->job & MAPID_THIRDMASK) == MAPID_MINSTRELWANDERER) p_sd[(*c)++] = tsd->bl.id; return 1; } else { switch(skill_id) { - case PR_BENEDICTIO: { - uint8 dir = map->calc_dir(&sd->bl,tsd->bl.x,tsd->bl.y); - dir = (unit->getdir(&sd->bl) + dir)%8; //This adjusts dir to account for the direction the sd is facing. - if ((tsd->class_&MAPID_BASEMASK) == MAPID_ACOLYTE && (dir == 2 || dir == 6) //Must be standing to the left/right of Priest. - && sd->status.sp >= 10) + case PR_BENEDICTIO: + { + enum unit_dir dir = map->calc_dir(&sd->bl, tsd->bl.x, tsd->bl.y); + dir = (unit->getdir(&sd->bl) + dir) % UNIT_DIR_MAX; //This adjusts dir to account for the direction the sd is facing. + if ((tsd->job & MAPID_BASEMASK) == MAPID_ACOLYTE && (dir == UNIT_DIR_WEST || dir == UNIT_DIR_EAST) //Must be standing to the left/right of Priest. + && sd->status.sp >= 10) { p_sd[(*c)++]=tsd->bl.id; + } return 1; } case AB_ADORAMUS: // Adoramus does not consume Blue Gemstone when there is at least 1 Priest class next to the caster - if( (tsd->class_&MAPID_UPPERMASK) == MAPID_PRIEST ) + if ((tsd->job & MAPID_UPPERMASK) == MAPID_PRIEST) p_sd[(*c)++] = tsd->bl.id; return 1; case WL_COMET: // Comet does not consume Red Gemstones when there is at least 1 Warlock class next to the caster - if( ( tsd->class_&MAPID_THIRDMASK ) == MAPID_WARLOCK ) + if ((tsd->job & MAPID_THIRDMASK) == MAPID_WARLOCK) p_sd[(*c)++] = tsd->bl.id; return 1; case LG_RAYOFGENESIS: - if( tsd->status.party_id == sd->status.party_id && (tsd->class_&MAPID_THIRDMASK) == MAPID_ROYAL_GUARD && - tsd->sc.data[SC_BANDING] ) + if (tsd->status.party_id == sd->status.party_id && (tsd->job & MAPID_THIRDMASK) == MAPID_ROYAL_GUARD && tsd->sc.data[SC_BANDING]) p_sd[(*c)++] = tsd->bl.id; return 1; default: //Warning: Assuming Ensemble Dance/Songs for code speed. [Skotlex] @@ -12971,7 +13960,7 @@ int skill_check_condition_char_sub (struct block_list *bl, va_list ap) if(pc_issit(tsd) || !unit->can_move(&tsd->bl)) return 0; if (sd->status.sex != tsd->status.sex && - (tsd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER && + (tsd->job & MAPID_UPPERMASK) == MAPID_BARDDANCER && (skill_lv = pc->checkskill(tsd, skill_id)) > 0 && (tsd->weapontype1==W_MUSICAL || tsd->weapontype1==W_WHIP) && sd->status.party_id && tsd->status.party_id && @@ -12994,12 +13983,16 @@ int skill_check_condition_char_sub (struct block_list *bl, va_list ap) /*========================================== * Checks and stores partners for ensemble skills [Skotlex] *------------------------------------------*/ -int skill_check_pc_partner (struct map_session_data *sd, uint16 skill_id, uint16* skill_lv, int range, int cast_flag) { +static int skill_check_pc_partner(struct map_session_data *sd, uint16 skill_id, uint16 *skill_lv, int range, int cast_flag) +{ static int c=0; static int p_sd[2] = { 0, 0 }; int i; bool is_chorus = ( skill->get_inf2(skill_id)&INF2_CHORUS_SKILL ); + nullpo_ret(sd); + nullpo_ret(skill_lv); + if (!battle_config.player_skill_partner_check || pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL)) return is_chorus ? MAX_PARTY : 99; //As if there were infinite partners. @@ -13049,7 +14042,7 @@ int skill_check_pc_partner (struct map_session_data *sd, uint16 skill_id, uint16 /*========================================== * *------------------------------------------*/ -int skill_check_condition_mob_master_sub (struct block_list *bl, va_list ap) +static int skill_check_condition_mob_master_sub(struct block_list *bl, va_list ap) { const struct mob_data *md = NULL; int src_id = va_arg(ap, int); @@ -13074,11 +14067,12 @@ int skill_check_condition_mob_master_sub (struct block_list *bl, va_list ap) * Determines if a given skill should be made to consume ammo * when used by the player. [Skotlex] *------------------------------------------*/ -int skill_isammotype (struct map_session_data *sd, int skill_id) +static int skill_isammotype(struct map_session_data *sd, int skill_id) { + nullpo_ret(sd); return ( battle_config.arrow_decrement==2 && - (sd->status.weapon == W_BOW || (sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE)) && + (sd->weapontype == W_BOW || (sd->weapontype >= W_REVOLVER && sd->weapontype <= W_GRENADE)) && skill_id != HT_PHANTASMIC && skill->get_type(skill_id) == BF_WEAPON && !(skill->get_nk(skill_id)&NK_NO_DAMAGE) && @@ -13089,43 +14083,35 @@ int skill_isammotype (struct map_session_data *sd, int skill_id) /** * Checks whether a skill can be used in combos or not **/ -bool skill_is_combo( int skill_id ) +static bool skill_is_combo(int skill_id) { - switch( skill_id ) - { - case MO_CHAINCOMBO: - case MO_COMBOFINISH: - case CH_TIGERFIST: - case CH_CHAINCRUSH: - case MO_EXTREMITYFIST: - case TK_TURNKICK: - case TK_STORMKICK: - case TK_DOWNKICK: - case TK_COUNTER: - case TK_JUMPKICK: - case HT_POWER: - case GC_COUNTERSLASH: - case GC_WEAPONCRUSH: - case SR_FALLENEMPIRE: - case SR_DRAGONCOMBO: - case SR_TIGERCANNON: - case SR_GATEOFHELL: - return true; - } + if (skill->get_inf2(skill_id) & INF2_IS_COMBO_SKILL) + return true; + return false; } -int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id, uint16 skill_lv) { +static int skill_check_condition_castbegin(struct map_session_data *sd, uint16 skill_id, uint16 skill_lv) +{ struct status_data *st; struct status_change *sc; struct skill_condition require; nullpo_ret(sd); - if (sd->chatID) return 0; + if (skill_lv < 1 || skill_lv > MAX_SKILL_LEVEL) + return 0; + + if (sd->chat_id != 0) + return 0; - if (pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL) && sd->skillitem != skill_id) { - //GMs don't override the skillItem check, otherwise they can use items without them being consumed! [Skotlex] + if (((sd->autocast.itemskill_conditions_checked || !sd->autocast.itemskill_check_conditions) + && sd->autocast.type == AUTOCAST_ITEM) || sd->autocast.type == AUTOCAST_IMPROVISE) { + return 1; + } + + if (pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL) && sd->autocast.type != AUTOCAST_ITEM) { + // GMs don't override the AUTOCAST_ITEM check, otherwise they can use items without them being consumed! sd->state.arrow_atk = skill->get_ammotype(skill_id)?1:0; //Need to do arrow state check. sd->spiritball_old = sd->spiritball; //Need to do Spiritball check. return 1; @@ -13156,34 +14142,8 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id if( !sc->count ) sc = NULL; - if( sd->skillitem == skill_id ) { - if( sd->state.abra_flag ) // Hocus-Pocus was used. [Inkfish] - sd->state.abra_flag = 0; - else { - int i; - // When a target was selected, consume items that were skipped in pc_use_item [Skotlex] - if( (i = sd->itemindex) == -1 || - sd->status.inventory[i].nameid != sd->itemid || - sd->inventory_data[i] == NULL || - !sd->inventory_data[i]->flag.delay_consume || - sd->status.inventory[i].amount < 1 - ) { - //Something went wrong, item exploit? - sd->itemid = sd->itemindex = -1; - return 0; - } - //Consume - sd->itemid = sd->itemindex = -1; - if( skill_id == WZ_EARTHSPIKE && sc && sc->data[SC_EARTHSCROLL] && rnd()%100 > sc->data[SC_EARTHSCROLL]->val2 ) // [marquis007] - ; //Do not consume item. - else if( sd->status.inventory[i].expire_time == 0 ) // Rental usable items are not consumed until expiration - pc->delitem(sd, i, 1, 0, DELITEM_NORMAL, LOG_TYPE_CONSUME); - } - return 1; - } - - if( pc_is90overweight(sd) ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_WEIGHTOVER,0); + if (pc_is90overweight(sd) && sd->autocast.type != AUTOCAST_ITEM) { // Skill casting items ignore the overweight restriction. + clif->skill_fail(sd, skill_id, USESKILL_FAIL_WEIGHTOVER, 0, 0); return 0; } @@ -13211,8 +14171,10 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id case SG_FUSION: case RA_WUGDASH: case KO_YAMIKUMO: - if( sc && sc->data[status->skill2sc(skill_id)] ) + case SU_HIDE: + if (sc && sc->data[status->skill2sc(skill_id)]) return 1; + FALLTHROUGH default: { int ret = skill->check_condition_castbegin_off_unknown(sc, &skill_id); @@ -13304,9 +14266,6 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id } } - if( skill_lv < 1 || skill_lv > MAX_SKILL_LEVEL ) - return 0; - require = skill->get_requirement(sd,skill_id,skill_lv); //Can only update state when weapon/arrow info is checked. @@ -13314,14 +14273,28 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id // perform skill-specific checks (and actions) switch( skill_id ) { + case MC_VENDING: + case ALL_BUYING_STORE: + if (map->list[sd->bl.m].flag.novending) { + clif->message(sd->fd, msg_sd(sd, 276)); // "You can't open a shop on this map" + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); + return 0; + } + if (map->getcell(sd->bl.m, &sd->bl, sd->bl.x, sd->bl.y, CELL_CHKNOVENDING)) { + clif->message(sd->fd, msg_sd(sd, 204)); // "You can't open a shop on this cell." + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); + return 0; + } + break; case SO_SPELLFIST: if(sd->skill_id_old != MG_FIREBOLT && sd->skill_id_old != MG_COLDBOLT && sd->skill_id_old != MG_LIGHTNINGBOLT){ - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } + FALLTHROUGH case SA_CASTCANCEL: if(sd->ud.skilltimer == INVALID_TIMER) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; @@ -13336,7 +14309,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id if(sc && sc->data[SC_RAISINGDRAGON]) skill_lv += sc->data[SC_RAISINGDRAGON]->val1; if(sd->spiritball >= skill_lv) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; @@ -13357,7 +14330,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id if (sc->data[SC_COMBOATTACK]) { if( sc->data[SC_COMBOATTACK]->val1 == MO_TRIPLEATTACK ) break; - clif->skill_fail(sd, skill_id, USESKILL_FAIL_COMBOSKILL, MO_TRIPLEATTACK); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_COMBOSKILL, MO_TRIPLEATTACK, 0); } return 0; case MO_COMBOFINISH: @@ -13366,7 +14339,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id if( sc && sc->data[SC_COMBOATTACK] ) { if ( sc->data[SC_COMBOATTACK]->val1 == MO_CHAINCOMBO ) break; - clif->skill_fail(sd, skill_id, USESKILL_FAIL_COMBOSKILL, MO_CHAINCOMBO); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_COMBOSKILL, MO_CHAINCOMBO, 0); } return 0; case CH_TIGERFIST: @@ -13375,7 +14348,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id if( sc && sc->data[SC_COMBOATTACK] ) { if ( sc->data[SC_COMBOATTACK]->val1 == MO_COMBOFINISH ) break; - clif->skill_fail(sd, skill_id, USESKILL_FAIL_COMBOSKILL, MO_COMBOFINISH); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_COMBOSKILL, MO_COMBOFINISH, 0); } return 0; case CH_CHAINCRUSH: @@ -13384,7 +14357,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id if( sc && sc->data[SC_COMBOATTACK] ) { if( sc->data[SC_COMBOATTACK]->val1 == CH_TIGERFIST ) break; - clif->skill_fail(sd, skill_id, USESKILL_FAIL_COMBOSKILL, CH_TIGERFIST); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_COMBOSKILL, CH_TIGERFIST, 0); } return 0; case MO_EXTREMITYFIST: @@ -13405,15 +14378,15 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id } } else if (!unit->can_move(&sd->bl)) { //Placed here as ST_MOVE_ENABLE should not apply if rooted or on a combo. [Skotlex] - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; case TK_MISSION: - if( (sd->class_&MAPID_UPPERMASK) != MAPID_TAEKWON ) { + if ((sd->job & MAPID_UPPERMASK) != MAPID_TAEKWON) { // Cannot be used by Non-Taekwon classes - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; @@ -13423,9 +14396,9 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id case TK_READYSTORM: case TK_READYTURN: case TK_JUMPKICK: - if( (sd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER ) { + if ((sd->job & MAPID_UPPERMASK) == MAPID_SOUL_LINKER) { // Soul Linkers cannot use this skill - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; @@ -13434,7 +14407,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id case TK_STORMKICK: case TK_DOWNKICK: case TK_COUNTER: - if ((sd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER) + if ((sd->job & MAPID_UPPERMASK) == MAPID_SOUL_LINKER) return 0; //Anti-Soul Linker check in case you job-changed with Stances active. if(!(sc && sc->data[SC_COMBOATTACK]) || sc->data[SC_COMBOATTACK]->val1 == TK_JUMPKICK) return 0; //Combo needs to be ready @@ -13446,7 +14419,8 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id status_change_end(&sd->bl, SC_COMBOATTACK, INVALID_TIMER); return 0; } - if(sc->data[SC_COMBOATTACK]->val1 != skill_id && !( sd && sd->status.base_level >= 90 && pc->famerank(sd->status.char_id, MAPID_TAEKWON) )) { + if (sc->data[SC_COMBOATTACK]->val1 != skill_id + && !(sd != NULL && sd->status.base_level >= 90 && pc->fame_rank(sd->status.char_id, RANKTYPE_TAEKWON) > 0)) { //Cancel combo wait. unit->cancel_combo(&sd->bl); return 0; @@ -13457,7 +14431,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id int time; if(!(sc && sc->data[SC_DANCING])) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } time = 1000*(sc->data[SC_DANCING]->val3>>16); @@ -13466,7 +14440,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id (sc->data[SC_DANCING]->val1>>16)) //Dance Skill LV - time < skill->get_time2(skill_id,skill_lv)) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } } @@ -13475,7 +14449,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id case PR_BENEDICTIO: if (skill->check_pc_partner(sd, skill_id, &skill_lv, 1, 0) < 2) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; @@ -13493,7 +14467,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id case CG_HERMODE: if(!npc->check_areanpc(1,sd->bl.m,sd->bl.x,sd->bl.y,skill->get_splash(skill_id, skill_lv))) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; @@ -13505,7 +14479,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id int x = sd->bl.x+(i%size-range); int y = sd->bl.y+(i/size-range); if (map->getcell(sd->bl.m, &sd->bl, x, y, CELL_CHKWALL)) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } } @@ -13513,10 +14487,10 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id break; case PR_REDEMPTIO: { - int exp; - if( ((exp = pc->nextbaseexp(sd)) > 0 && get_percentage(sd->status.base_exp, exp) < 1) || - ((exp = pc->nextjobexp(sd)) > 0 && get_percentage(sd->status.job_exp, exp) < 1)) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); //Not enough exp. + int64 exp; + if (((exp = pc->nextbaseexp(sd)) > 0 && get_percentage64(sd->status.base_exp, exp) < 1) || + ((exp = pc->nextjobexp(sd)) > 0 && get_percentage64(sd->status.job_exp, exp) < 1)) { + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); //Not enough exp. return 0; } break; @@ -13525,7 +14499,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id case AM_TWILIGHT3: if (!party->skill_check(sd, sd->status.party_id, skill_id, skill_lv)) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; @@ -13536,7 +14510,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id break; if (sd->bl.m == sd->feel_map[skill_id-SG_SUN_WARM].m) break; - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; break; case SG_SUN_COMFORT: @@ -13547,7 +14521,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id if (sd->bl.m == sd->feel_map[skill_id-SG_SUN_COMFORT].m && (battle_config.allow_skill_without_day || pc->sg_info[skill_id-SG_SUN_COMFORT].day_func())) break; - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; case SG_FUSION: if (sc && sc->data[SC_SOULLINK] && sc->data[SC_SOULLINK]->val2 == SL_STAR) @@ -13556,7 +14530,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id //Only invoke on skill begin cast (instant cast skill). [Kevin] if( require.sp > 0 ) { if (st->sp < (unsigned int)require.sp) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_SP_INSUFFICIENT,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_SP_INSUFFICIENT, 0, 0); else status_zap(&sd->bl, 0, require.sp); } @@ -13565,9 +14539,10 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id case GD_REGENERATION: case GD_RESTORE: if (!map_flag_gvg2(sd->bl.m)) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } + FALLTHROUGH case GD_EMERGENCYCALL: // other checks were already done in skillnotok() if (!sd->status.guild_id || !sd->state.gmaster_flag) @@ -13576,7 +14551,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id case GS_GLITTERING: if(sd->spiritball >= 10) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; @@ -13587,12 +14562,13 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id #else if (st->hp < 2) { #endif - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } + FALLTHROUGH case NJ_BUNSINJYUTSU: if (!(sc && sc->data[SC_NJ_NEN])) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; @@ -13600,7 +14576,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id case NJ_ZENYNAGE: case KO_MUCHANAGE: if(sd->status.zeny < require.zeny) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_MONEY,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_MONEY, 0, 0); return 0; } break; @@ -13610,14 +14586,14 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id break; case AM_CALLHOMUN: //Can't summon if a hom is already out if (sd->status.hom_id && sd->hd && !sd->hd->homunculus.vaporize) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; case AM_REST: //Can't vapo homun if you don't have an active homun or it's hp is < 80% if (!homun_alive(sd->hd) || sd->hd->battle_status.hp < (sd->hd->battle_status.max_hp*80/100)) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; @@ -13626,12 +14602,12 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id **/ case AB_ANCILLA: { - int count = 0, i; - for( i = 0; i < MAX_INVENTORY; i ++ ) - if( sd->status.inventory[i].nameid == ITEMID_ANCILLA ) + int count = 0; + for (int i = 0; i < sd->status.inventorySize; i ++) + if (sd->status.inventory[i].nameid == ITEMID_ANSILA) count += sd->status.inventory[i].amount; if( count >= 3 ) { - clif->skill_fail(sd, skill_id, USESKILL_FAIL_ANCILLA_NUMOVER, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_ANCILLA_NUMOVER, 0, 0); return 0; } } @@ -13651,8 +14627,8 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id && ((idx = pc->search_inventory(sd,require.itemid[0])) == INDEX_NOT_FOUND || sd->status.inventory[idx].amount < require.amount[0]) ) { - //clif->skill_fail(sd,skill_id,USESKILL_FAIL_NEED_ITEM,require.amount[0],require.itemid[0]); - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + //clif->skill_fail(sd, skill_id, USESKILL_FAIL_NEED_ITEM, require.amount[0], 0, require.itemid[0]); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; @@ -13672,7 +14648,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id switch(skill_id){ case WL_TETRAVORTEX: if( i < 4 ){ - clif->skill_fail(sd,skill_id,USESKILL_FAIL_CONDITION,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_CONDITION, 0, 0); return 0; } break; @@ -13681,13 +14657,13 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id if( sc && sc->data[j] ) i++; if( i == 0 ){ - clif->skill_fail(sd,skill_id,USESKILL_FAIL_SUMMON_NONE,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_SUMMON_NONE, 0, 0); return 0; } break; default: if( i == 5 ){ - clif->skill_fail(sd,skill_id,USESKILL_FAIL_SUMMON,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_SUMMON, 0, 0); return 0; } } @@ -13698,14 +14674,14 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id **/ case GC_HALLUCINATIONWALK: if( sc && (sc->data[SC_HALLUCINATIONWALK] || sc->data[SC_HALLUCINATIONWALK_POSTDELAY]) ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; case GC_COUNTERSLASH: case GC_WEAPONCRUSH: if( !(sc && sc->data[SC_COMBOATTACK] && sc->data[SC_COMBOATTACK]->val1 == GC_WEAPONBLOCKING) ) { - clif->skill_fail(sd, skill_id, USESKILL_FAIL_GC_WEAPONBLOCKING, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_GC_WEAPONBLOCKING, 0, 0); return 0; } break; @@ -13714,25 +14690,25 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id **/ case RA_WUGMASTERY: if( pc_isfalcon(sd) || pc_isridingwug(sd) || sd->sc.data[SC__GROOMY] ) { - clif->skill_fail(sd,skill_id,sd->sc.data[SC__GROOMY]?USESKILL_FAIL_MANUAL_NOTIFY:USESKILL_FAIL_CONDITION,0); + clif->skill_fail(sd, skill_id, sd->sc.data[SC__GROOMY] ? USESKILL_FAIL_MANUAL_NOTIFY : USESKILL_FAIL_CONDITION, 0, 0); return 0; } break; case RA_WUGSTRIKE: if( !pc_iswug(sd) && !pc_isridingwug(sd) ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_CONDITION,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_CONDITION, 0, 0); return 0; } break; case RA_WUGRIDER: if( pc_isfalcon(sd) || ( !pc_isridingwug(sd) && !pc_iswug(sd) ) ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_CONDITION,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_CONDITION, 0, 0); return 0; } break; case RA_WUGDASH: if(!pc_isridingwug(sd)) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_CONDITION,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_CONDITION, 0, 0); return 0; } break; @@ -13741,13 +14717,13 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id **/ case LG_BANDING: if( sc && sc->data[SC_INSPIRATION] ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; case LG_PRESTIGE: if( sc && (sc->data[SC_BANDING] || sc->data[SC_INSPIRATION]) ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; @@ -13756,7 +14732,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id if( sc && sc->data[SC_INSPIRATION] ) return 1; // Don't check for partner. if( !(sc && sc->data[SC_BANDING]) ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL, 0, 0); return 0; } if( sc->data[SC_BANDING] && @@ -13767,12 +14743,12 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id if( sc && sc->data[SC_COMBOATTACK] ) { if( sc->data[SC_COMBOATTACK]->val1 == SR_DRAGONCOMBO ) break; - clif->skill_fail(sd, skill_id, USESKILL_FAIL_COMBOSKILL, SR_DRAGONCOMBO); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_COMBOSKILL, SR_DRAGONCOMBO, 0); } return 0; case SR_CRESCENTELBOW: if( sc && sc->data[SC_CRESCENTELBOW] ) { - clif->skill_fail(sd, skill_id, USESKILL_FAIL_DUPLICATE, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_DUPLICATE, 0, 0); return 0; } break; @@ -13781,7 +14757,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id if (map->foreachinrange(mob->count_sub, &sd->bl, skill->get_splash(skill_id, skill_lv), BL_MOB, MOBID_EMPELIUM, MOBID_S_EMPEL_1, MOBID_S_EMPEL_2)) { char output[128]; - sprintf(output, "You're too close to a stone or emperium to do this skill"); /* TODO official response? or message.conf it */ + sprintf(output, "%s", msg_txt(883)); /* TODO official response */ // You are too close to a stone or emperium to do this skill clif->messagecolor_self(sd->fd, COLOR_RED, output); return 0; } @@ -13789,7 +14765,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id if( sd->spiritball > 0 ) sd->spiritball_old = require.spiritball = sd->spiritball; else { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; @@ -13800,7 +14776,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id case SC_MANHOLE: case SC_DIMENSIONDOOR: if( sc && sc->data[SC_MAGNETICFIELD] ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; @@ -13808,7 +14784,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id int count; count = skill->check_pc_partner(sd, skill_id, &skill_lv, skill->get_splash(skill_id,skill_lv), 0); if( count < 1 ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_NEED_HELPER,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_NEED_HELPER, 0, 0); return 0; } else require.sp -= require.sp * 20 * count / 100; // -20% each W/M in the party. @@ -13818,7 +14794,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id if (sd->equip_index[EQI_HAND_R] < 0 || !itemid_is_pilebunker(sd->status.inventory[sd->equip_index[EQI_HAND_R]].nameid) ) { - clif->skill_fail(sd, skill_id, USESKILL_FAIL_THIS_WEAPON, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_THIS_WEAPON, 0, 0); return 0; } break; @@ -13826,7 +14802,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id if (( sd->equip_index[EQI_ACC_L] >= 0 && sd->status.inventory[sd->equip_index[EQI_ACC_L]].nameid == ITEMID_HOVERING_BOOSTER ) || ( sd->equip_index[EQI_ACC_R] >= 0 && sd->status.inventory[sd->equip_index[EQI_ACC_R]].nameid == ITEMID_HOVERING_BOOSTER )); else { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; @@ -13834,25 +14810,25 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id case SO_ELECTRICWALK: // Can't be casted until you've walked all cells. if( sc && sc->data[SC_PROPERTYWALK] && sc->data[SC_PROPERTYWALK]->val3 < skill->get_maxcount(sc->data[SC_PROPERTYWALK]->val1,sc->data[SC_PROPERTYWALK]->val2) ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; case SO_EL_CONTROL: if( !sd->status.ele_id || !sd->ed ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_EL_SUMMON,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_EL_SUMMON, 0, 0); return 0; } break; case RETURN_TO_ELDICASTES: if( pc_ismadogear(sd) ) { //Cannot be used if Mado is equipped. - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; case CR_REFLECTSHIELD: if( sc && sc->data[SC_KYOMU] && rnd()%100 < 5 * sc->data[SC_KYOMU]->val1 ){ - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; @@ -13861,14 +14837,14 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id case KO_KAZEHU_SEIRAN: case KO_DOHU_KOUKAI: if (sd->charm_type == skill->get_ele(skill_id, skill_lv) && sd->charm_count >= MAX_SPIRITCHARM) { - clif->skill_fail(sd, skill_id, USESKILL_FAIL_SUMMON, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_SUMMON, 0, 0); return 0; } break; case KO_KAIHOU: case KO_ZENKAI: if (sd->charm_type == CHARM_TYPE_NONE || sd->charm_count <= 0) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_SUMMON,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_SUMMON, 0, 0); return 0; } break; @@ -13883,66 +14859,67 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id switch(require.state) { case ST_HIDING: if(!(sc && sc->option&OPTION_HIDE)) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; case ST_CLOAKING: if(!pc_iscloaking(sd)) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; case ST_HIDDEN: if(!pc_ishiding(sd)) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; case ST_RIDING: if (!pc_isridingpeco(sd) && !pc_isridingdragon(sd)) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; case ST_FALCON: if(!pc_isfalcon(sd)) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; case ST_CARTBOOST: if(!(sc && sc->data[SC_CARTBOOST])) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } + FALLTHROUGH case ST_CART: if(!pc_iscarton(sd)) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_CART,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_CART, 0, 0); return 0; } break; case ST_SHIELD: - if(sd->status.shield <= 0) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + if (!sd->has_shield) { + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; case ST_SIGHT: if(!(sc && sc->data[SC_SIGHT])) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; case ST_EXPLOSIONSPIRITS: if(!(sc && sc->data[SC_EXPLOSIONSPIRITS])) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_EXPLOSIONSPIRITS,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_EXPLOSIONSPIRITS, 0, 0); return 0; } break; case ST_RECOV_WEIGHT_RATE: if(battle_config.natural_heal_weight_rate <= 100 && sd->weight*100/sd->max_weight >= (unsigned int)battle_config.natural_heal_weight_rate) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; @@ -13951,7 +14928,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id sd->ud.canmove_tick = timer->gettick(); //When using a combo, cancel the can't move delay to enable the skill. [Skotlex] if (!unit->can_move(&sd->bl)) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; @@ -13960,63 +14937,65 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id break; if (map->getcell(sd->bl.m, &sd->bl, sd->bl.x, sd->bl.y, CELL_CHKWATER)) break; - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; case ST_RIDINGDRAGON: if( !pc_isridingdragon(sd) ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_DRAGON,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_DRAGON, 0, 0); return 0; } break; case ST_WUG: if( !pc_iswug(sd) ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; case ST_RIDINGWUG: if( !pc_isridingwug(sd) ){ - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; case ST_MADO: if( !pc_ismadogear(sd) ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_MADOGEAR,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_MADOGEAR, 0, 0); return 0; } break; case ST_ELEMENTALSPIRIT: if(!sd->ed) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_EL_SUMMON,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_EL_SUMMON, 0, 0); return 0; } break; case ST_POISONINGWEAPON: if (!(sc && sc->data[SC_POISONINGWEAPON])) { - clif->skill_fail(sd, skill_id, USESKILL_FAIL_GC_POISONINGWEAPON, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_GC_POISONINGWEAPON, 0, 0); return 0; } break; case ST_ROLLINGCUTTER: if (!(sc && sc->data[SC_ROLLINGCUTTER])) { - clif->skill_fail(sd, skill_id, USESKILL_FAIL_CONDITION, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_CONDITION, 0, 0); return 0; } break; case ST_MH_FIGHTING: if (!(sc && sc->data[SC_STYLE_CHANGE] && sc->data[SC_STYLE_CHANGE]->val2 == MH_MD_FIGHTING)){ - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } + FALLTHROUGH case ST_MH_GRAPPLING: if (!(sc && sc->data[SC_STYLE_CHANGE] && sc->data[SC_STYLE_CHANGE]->val2 == MH_MD_GRAPPLING)){ - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } + FALLTHROUGH case ST_PECO: if (!pc_isridingpeco(sd)) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } break; @@ -14025,27 +15004,27 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id if(require.mhp > 0 && get_percentage(st->hp, st->max_hp) > require.mhp) { //mhp is the max-hp-requirement, that is, //you must have this % or less of HP to cast it. - clif->skill_fail(sd,skill_id,USESKILL_FAIL_HP_INSUFFICIENT,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_HP_INSUFFICIENT, 0, 0); return 0; } if( require.weapon && !pc_check_weapontype(sd,require.weapon) ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_THIS_WEAPON,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_THIS_WEAPON, 0, 0); return 0; } - if( require.sp > 0 && st->sp < (unsigned int)require.sp) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_SP_INSUFFICIENT,0); + if (require.sp > 0 && st->sp < (unsigned int)require.sp && sd->autocast.type == AUTOCAST_NONE) { // Auto-cast skills don't consume SP. + clif->skill_fail(sd, skill_id, USESKILL_FAIL_SP_INSUFFICIENT, 0, 0); return 0; } if( require.zeny > 0 && sd->status.zeny < require.zeny ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_MONEY,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_MONEY, 0, 0); return 0; } if( require.spiritball > 0 && sd->spiritball < require.spiritball) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_SPIRITS,require.spiritball); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_SPIRITS, require.spiritball, 0); return 0; } @@ -14060,27 +15039,28 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id return 1; } -int skill_check_condition_castbegin_off_unknown(struct status_change *sc, uint16 *skill_id) +static int skill_check_condition_castbegin_off_unknown(struct status_change *sc, uint16 *skill_id) { - return -1; + return -1; } -int skill_check_condition_castbegin_mount_unknown(struct status_change *sc, uint16 *skill_id) +static int skill_check_condition_castbegin_mount_unknown(struct status_change *sc, uint16 *skill_id) { - return 0; + return 0; } -int skill_check_condition_castbegin_madogear_unknown(struct status_change *sc, uint16 *skill_id) +static int skill_check_condition_castbegin_madogear_unknown(struct status_change *sc, uint16 *skill_id) { - return 0; + return 0; } -int skill_check_condition_castbegin_unknown(struct status_change *sc, uint16 *skill_id) +static int skill_check_condition_castbegin_unknown(struct status_change *sc, uint16 *skill_id) { - return -1; + return -1; } -int skill_check_condition_castend(struct map_session_data* sd, uint16 skill_id, uint16 skill_lv) { +static int skill_check_condition_castend(struct map_session_data *sd, uint16 skill_id, uint16 skill_lv) +{ struct skill_condition require; struct status_data *st; int i; @@ -14088,11 +15068,16 @@ int skill_check_condition_castend(struct map_session_data* sd, uint16 skill_id, nullpo_ret(sd); - if( sd->chatID ) + if (sd->chat_id != 0) return 0; - if( pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL) && sd->skillitem != skill_id ) { - //GMs don't override the skillItem check, otherwise they can use items without them being consumed! [Skotlex] + if (((sd->autocast.itemskill_conditions_checked || !sd->autocast.itemskill_check_conditions) + && sd->autocast.type == AUTOCAST_ITEM) || sd->autocast.type == AUTOCAST_IMPROVISE) { + return 1; + } + + if (pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL) && sd->autocast.type != AUTOCAST_ITEM) { + // GMs don't override the AUTOCAST_ITEM check, otherwise they can use items without them being consumed! sd->state.arrow_atk = skill->get_ammotype(skill_id)?1:0; //Need to do arrow state check. sd->spiritball_old = sd->spiritball; //Need to do Spiritball check. return 1; @@ -14118,15 +15103,9 @@ int skill_check_condition_castend(struct map_session_data* sd, uint16 skill_id, return 0; break; } - /* temporarily disabled, awaiting for kenpachi to detail this so we can make it work properly */ -#if 0 - if( sd->state.abra_flag ) // Casting finished (Hocus-Pocus) - return 1; -#endif - if( sd->skillitem == skill_id ) - return 1; - if( pc_is90overweight(sd) ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_WEIGHTOVER,0); + + if (pc_is90overweight(sd) && sd->autocast.type != AUTOCAST_ITEM) { // Skill casting items ignore the overweight restriction. + clif->skill_fail(sd, skill_id, USESKILL_FAIL_WEIGHTOVER, 0, 0); return 0; } @@ -14160,7 +15139,7 @@ int skill_check_condition_castend(struct map_session_data* sd, uint16 skill_id, || (skill_id==AM_CANNIBALIZE && c != i && battle_config.summon_flora&2) ) { //Fails when: exceed max limit. There are other plant types already out. - clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } } @@ -14180,7 +15159,7 @@ int skill_check_condition_castend(struct map_session_data* sd, uint16 skill_id, map->foreachinmap(skill->check_condition_mob_master_sub, sd->bl.m, BL_MOB, sd->bl.id, MOBID_SILVERSNIPER, skill_id, &c); } if( c >= maxcount ) { - clif->skill_fail(sd , skill_id, USESKILL_FAIL_SUMMON, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_SUMMON, 0, 0); return 0; } } @@ -14190,14 +15169,15 @@ int skill_check_condition_castend(struct map_session_data* sd, uint16 skill_id, int c = 0; i = map->foreachinmap(skill->check_condition_mob_master_sub, sd->bl.m, BL_MOB, sd->bl.id, MOBID_KO_KAGE, skill_id, &c); if( c >= skill->get_maxcount(skill_id,skill_lv) || c != i) { - clif->skill_fail(sd , skill_id, USESKILL_FAIL_LEVEL, 0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 0; } } break; default: - skill->check_condition_castend_unknown(sd, &skill_id, &skill_lv); - break; + if (!skill->check_condition_castend_unknown(sd, &skill_id, &skill_lv)) + break; + return 0; } st = &sd->battle_status; @@ -14205,35 +15185,33 @@ int skill_check_condition_castend(struct map_session_data* sd, uint16 skill_id, require = skill->get_requirement(sd,skill_id,skill_lv); if( require.hp > 0 && st->hp <= (unsigned int)require.hp) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_HP_INSUFFICIENT,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_HP_INSUFFICIENT, 0, 0); return 0; } if( require.weapon && !pc_check_weapontype(sd,require.weapon) ) { - clif->skill_fail(sd,skill_id,USESKILL_FAIL_THIS_WEAPON,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_THIS_WEAPON, 0, 0); return 0; } if( require.ammo ) { //Skill requires stuff equipped in the arrow slot. if((i=sd->equip_index[EQI_AMMO]) < 0 || !sd->inventory_data[i] ) { if( require.ammo&1<<8 ) - clif->skill_fail(sd,skill_id,USESKILL_FAIL_CANONBALL,0); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_CANONBALL, 0, 0); else clif->arrow_fail(sd,0); return 0; } else if( sd->status.inventory[i].amount < require.ammo_qty ) { char e_msg[100]; - sprintf(e_msg,"Skill Failed. [%s] requires %dx %s.", + sprintf(e_msg, msg_txt(884), // Skill Failed. [%s] requires %dx %s. skill->get_desc(skill_id), require.ammo_qty, itemdb_jname(sd->status.inventory[i].nameid)); clif->messagecolor_self(sd->fd, COLOR_RED, e_msg); return 0; } - if (!(require.ammo&1<<sd->inventory_data[i]->look)) { //Ammo type check. Send the "wrong weapon type" message - //which is the closest we have to wrong ammo type. [Skotlex] - clif->arrow_fail(sd,0); //Haplo suggested we just send the equip-arrows message instead. [Skotlex] - //clif->skill_fail(sd,skill_id,USESKILL_FAIL_THIS_WEAPON,0); + if ((require.ammo & (1 << sd->inventory_data[i]->subtype)) == 0 || !battle->check_arrows(sd)) { // Ammo type check. + clif->arrow_fail(sd, 0); // "Please equip the proper ammunition first." return 0; } } @@ -14257,7 +15235,7 @@ int skill_check_condition_castend(struct map_session_data* sd, uint16 skill_id, cause = USESKILL_FAIL_BLUEJAMSTONE; break; case ITEMID_HOLY_WATER: cause = USESKILL_FAIL_HOLYWATER; break; - case ITEMID_ANCILLA: + case ITEMID_ANSILA: cause = USESKILL_FAIL_ANCILLA; break; case ITEMID_ACCELERATOR: case ITEMID_HOVERING_BOOSTER: @@ -14272,11 +15250,11 @@ int skill_check_condition_castend(struct map_session_data* sd, uint16 skill_id, cause = USESKILL_FAIL_NEED_EQUIPMENT; /* Fall through */ default: - clif->skill_fail(sd, skill_id, cause, max(1,require.amount[i])|(require.itemid[i] << 16)); + clif->skill_fail(sd, skill_id, cause, max(1, require.amount[i]), require.itemid[i]); return 0; } } - clif->skill_fail(sd, skill_id, cause, 0); + clif->skill_fail(sd, skill_id, cause, 0, 0); return 0; } } @@ -14284,16 +15262,24 @@ int skill_check_condition_castend(struct map_session_data* sd, uint16 skill_id, return 1; } -void skill_check_condition_castend_unknown(struct map_session_data* sd, uint16 *skill_id, uint16 *skill_lv) { +static bool skill_check_condition_castend_unknown(struct map_session_data *sd, uint16 *skill_id, uint16 *skill_lv) +{ + return false; } // type&2: consume items (after skill was used) // type&1: consume the others (before skill was used) -int skill_consume_requirement( struct map_session_data *sd, uint16 skill_id, uint16 skill_lv, short type) { +static int skill_consume_requirement(struct map_session_data *sd, uint16 skill_id, uint16 skill_lv, short type) +{ struct skill_condition req; nullpo_ret(sd); + if ((!sd->autocast.itemskill_check_conditions && sd->autocast.type == AUTOCAST_ITEM) + || sd->autocast.type == AUTOCAST_IMPROVISE) { + return 1; + } + req = skill->get_requirement(sd,skill_id,skill_lv); if (type&1) { @@ -14302,9 +15288,15 @@ int skill_consume_requirement( struct map_session_data *sd, uint16 skill_id, uin case MC_IDENTIFY: req.sp = 0; break; + case WZ_EARTHSPIKE: + if (sd->sc.count > 0 && sd->sc.data[SC_EARTHSCROLL] != NULL) // If Earth Spike Scroll is used while SC_EARTHSCROLL is active, 10 SP are consumed. [Kenpachi] + req.sp = 10; + + break; default: - if( sd->state.autocast ) + if (sd->autocast.type != AUTOCAST_NONE) // Auto-cast skills don't consume SP. req.sp = 0; + break; } @@ -14370,7 +15362,8 @@ int skill_consume_requirement( struct map_session_data *sd, uint16 skill_id, uin return 1; } -struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 skill_id, uint16 skill_lv) { +static struct skill_condition skill_get_requirement(struct map_session_data *sd, uint16 skill_id, uint16 skill_lv) +{ struct skill_condition req; struct status_data *st; struct status_change *sc; @@ -14381,12 +15374,6 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 if( !sd ) return req; -#if 0 /* temporarily disabled, awaiting for kenpachi to detail this so we can make it work properly */ - if( sd->state.abra_flag ) -#else // not 0 - if( sd->skillitem == skill_id ) -#endif // 0 - return req; // Hocus-Pocus don't have requirements. sc = &sd->sc; if( !sc->count ) @@ -14412,7 +15399,8 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 case TK_READYTURN: case SG_FUSION: case KO_YAMIKUMO: - if( sc && sc->data[status->skill2sc(skill_id)] ) + case SU_HIDE: + if (sc && sc->data[status->skill2sc(skill_id)]) return req; /* Fall through */ default: @@ -14495,7 +15483,8 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 } for( i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++ ) { - if( (skill_id == AM_POTIONPITCHER || skill_id == CR_SLIMPITCHER || skill_id == CR_CULTIVATION) && i != skill_lv%11 - 1 ) + int item_idx = (skill_lv - 1) % MAX_SKILL_ITEM_REQUIRE; + if ((skill_id == AM_POTIONPITCHER || skill_id == CR_SLIMPITCHER || skill_id == CR_CULTIVATION) && i != item_idx) continue; switch( skill_id ) { @@ -14568,7 +15557,7 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 if ((item_index = pc->search_inventory(sd, req.itemid[i])) == INDEX_NOT_FOUND || sd->status.inventory[item_index].amount < req.amount[i] ) { - req.itemid[i] = ITEMID_TRAP_ALLOY; + req.itemid[i] = ITEMID_SPECIAL_ALLOY_TRAP; req.amount[i] = 1; } break; @@ -14595,14 +15584,14 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 switch(skill_lv) { case 1: case 2: - req.itemid[1] = ITEMID_REPAIR_A; + req.itemid[1] = ITEMID_REPAIRA; break; case 3: case 4: - req.itemid[1] = ITEMID_REPAIR_B; + req.itemid[1] = ITEMID_REPAIRB; break; case 5: - req.itemid[1] = ITEMID_REPAIR_C; + req.itemid[1] = ITEMID_REPAIRC; break; } req.amount[1] = 1; @@ -14698,24 +15687,25 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 return req; } -bool skill_get_requirement_off_unknown(struct status_change *sc, uint16 *skill_id) +static bool skill_get_requirement_off_unknown(struct status_change *sc, uint16 *skill_id) { - return false; + return false; } -bool skill_get_requirement_item_unknown(struct status_change *sc, struct map_session_data* sd, uint16 *skill_id, uint16 *skill_lv, uint16 *idx, int *i) +static bool skill_get_requirement_item_unknown(struct status_change *sc, struct map_session_data *sd, uint16 *skill_id, uint16 *skill_lv, uint16 *idx, int *i) { - return false; + return false; } -void skill_get_requirement_unknown(struct status_change *sc, struct map_session_data* sd, uint16 *skill_id, uint16 *skill_lv, struct skill_condition *req) +static void skill_get_requirement_unknown(struct status_change *sc, struct map_session_data *sd, uint16 *skill_id, uint16 *skill_lv, struct skill_condition *req) { } /*========================================== * Does cast-time reductions based on dex, item bonuses and config setting *------------------------------------------*/ -int skill_castfix (struct block_list *bl, uint16 skill_id, uint16 skill_lv) { +static int skill_castfix(struct block_list *bl, uint16 skill_id, uint16 skill_lv) +{ int time = skill->get_cast(skill_id, skill_lv); nullpo_ret(bl); @@ -14765,11 +15755,13 @@ int skill_castfix (struct block_list *bl, uint16 skill_id, uint16 skill_lv) { /*========================================== * Does cast-time reductions based on sc data. *------------------------------------------*/ -int skill_castfix_sc (struct block_list *bl, int time) { +static int skill_castfix_sc(struct block_list *bl, int time) +{ struct status_change *sc = status->get_sc(bl); if( time < 0 ) return 0; + nullpo_ret(bl); if( bl->type == BL_MOB ) // mobs casttime is fixed nothing to alter. return time; @@ -14790,6 +15782,8 @@ int skill_castfix_sc (struct block_list *bl, int time) { } if (sc->data[SC_POEMBRAGI]) time -= time * sc->data[SC_POEMBRAGI]->val2 / 100; + if (sc->data[SC_SKF_CAST] != NULL) + time -= time * sc->data[SC_SKF_CAST]->val1 / 100; if (sc->data[SC_IZAYOI]) time -= time * 50 / 100; } @@ -14798,7 +15792,9 @@ int skill_castfix_sc (struct block_list *bl, int time) { //ShowInfo("Castime castfix_sc = %d\n",time); return time; } -int skill_vfcastfix(struct block_list *bl, double time, uint16 skill_id, uint16 skill_lv) { + +static int skill_vfcastfix(struct block_list *bl, double time, uint16 skill_id, uint16 skill_lv) +{ #ifdef RENEWAL_CAST struct status_change *sc = status->get_sc(bl); struct map_session_data *sd = BL_CAST(BL_PC,bl); @@ -14806,6 +15802,7 @@ int skill_vfcastfix(struct block_list *bl, double time, uint16 skill_id, uint16 if( time < 0 ) return 0; + nullpo_ret(bl); if( bl->type == BL_MOB ) // mobs casttime is fixed nothing to alter. return (int)time; @@ -14877,6 +15874,7 @@ int skill_vfcastfix(struct block_list *bl, double time, uint16 skill_id, uint16 case WZ_FIREPILLAR: if(skill_lv < 5) break; + FALLTHROUGH case HW_GRAVITATION: case MG_SAFETYWALL: case MG_STONECURSE: @@ -14887,6 +15885,8 @@ int skill_vfcastfix(struct block_list *bl, double time, uint16 skill_id, uint16 } if (sc->data[SC_MYSTICSCROLL]) VARCAST_REDUCTION(sc->data[SC_MYSTICSCROLL]->val1); + if (sc->data[SC_SKF_CAST] != NULL) + VARCAST_REDUCTION(sc->data[SC_SKF_CAST]->val1); // Fixed cast reduction bonuses if( sc->data[SC__LAZINESS] ) @@ -14938,7 +15938,8 @@ int skill_vfcastfix(struct block_list *bl, double time, uint16 skill_id, uint16 /*========================================== * Does delay reductions based on dex/agi, sc data, item bonuses, ... *------------------------------------------*/ -int skill_delay_fix (struct block_list *bl, uint16 skill_id, uint16 skill_lv) { +static int skill_delay_fix(struct block_list *bl, uint16 skill_id, uint16 skill_lv) +{ int delaynodex = skill->get_delaynodex(skill_id, skill_lv); int time = skill->get_delay(skill_id, skill_lv); struct map_session_data *sd; @@ -15037,10 +16038,11 @@ struct square { int val2[5]; }; -void skill_brandishspear_first (struct square *tc, uint8 dir, int16 x, int16 y) { +static void skill_brandishspear_first(struct square *tc, enum unit_dir dir, int16 x, int16 y) +{ nullpo_retv(tc); - if(dir == 0){ + if (dir == UNIT_DIR_NORTH) { tc->val1[0]=x-2; tc->val1[1]=x-1; tc->val1[2]=x; @@ -15051,7 +16053,7 @@ void skill_brandishspear_first (struct square *tc, uint8 dir, int16 x, int16 y) tc->val2[2]= tc->val2[3]= tc->val2[4]=y-1; - } else if(dir==2){ + } else if (dir == UNIT_DIR_WEST) { tc->val1[0]= tc->val1[1]= tc->val1[2]= @@ -15062,7 +16064,7 @@ void skill_brandishspear_first (struct square *tc, uint8 dir, int16 x, int16 y) tc->val2[2]=y; tc->val2[3]=y-1; tc->val2[4]=y-2; - } else if(dir==4){ + } else if (dir == UNIT_DIR_SOUTH) { tc->val1[0]=x-2; tc->val1[1]=x-1; tc->val1[2]=x; @@ -15073,7 +16075,7 @@ void skill_brandishspear_first (struct square *tc, uint8 dir, int16 x, int16 y) tc->val2[2]= tc->val2[3]= tc->val2[4]=y+1; - } else if(dir==6){ + } else if (dir == UNIT_DIR_EAST) { tc->val1[0]= tc->val1[1]= tc->val1[2]= @@ -15084,7 +16086,7 @@ void skill_brandishspear_first (struct square *tc, uint8 dir, int16 x, int16 y) tc->val2[2]=y; tc->val2[3]=y-1; tc->val2[4]=y-2; - } else if(dir==1){ + } else if (dir == UNIT_DIR_NORTHWEST) { tc->val1[0]=x-1; tc->val1[1]=x; tc->val1[2]=x+1; @@ -15095,7 +16097,7 @@ void skill_brandishspear_first (struct square *tc, uint8 dir, int16 x, int16 y) tc->val2[2]=y-1; tc->val2[3]=y; tc->val2[4]=y+1; - } else if(dir==3){ + } else if (dir == UNIT_DIR_SOUTHWEST) { tc->val1[0]=x+3; tc->val1[1]=x+2; tc->val1[2]=x+1; @@ -15106,7 +16108,7 @@ void skill_brandishspear_first (struct square *tc, uint8 dir, int16 x, int16 y) tc->val2[2]=y+1; tc->val2[3]=y+2; tc->val2[4]=y+3; - } else if(dir==5){ + } else if (dir == UNIT_DIR_SOUTHEAST) { tc->val1[0]=x+1; tc->val1[1]=x; tc->val1[2]=x-1; @@ -15117,7 +16119,7 @@ void skill_brandishspear_first (struct square *tc, uint8 dir, int16 x, int16 y) tc->val2[2]=y+1; tc->val2[3]=y; tc->val2[4]=y-1; - } else if(dir==7){ + } else if (dir == UNIT_DIR_NORTHEAST) { tc->val1[0]=x-3; tc->val1[1]=x-2; tc->val1[2]=x-1; @@ -15132,29 +16134,27 @@ void skill_brandishspear_first (struct square *tc, uint8 dir, int16 x, int16 y) } -void skill_brandishspear_dir (struct square* tc, uint8 dir, int are) { - int c; +static void skill_brandishspear_dir(struct square *tc, enum unit_dir dir, int are) +{ nullpo_retv(tc); + Assert_retv(dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX); - for( c = 0; c < 5; c++ ) { - switch( dir ) { - case 0: tc->val2[c]+=are; break; - case 1: tc->val1[c]-=are; tc->val2[c]+=are; break; - case 2: tc->val1[c]-=are; break; - case 3: tc->val1[c]-=are; tc->val2[c]-=are; break; - case 4: tc->val2[c]-=are; break; - case 5: tc->val1[c]+=are; tc->val2[c]-=are; break; - case 6: tc->val1[c]+=are; break; - case 7: tc->val1[c]+=are; tc->val2[c]+=are; break; - } + for (int c = 0; c < 5; c++) { + tc->val1[c] += dirx[dir] * are; + tc->val2[c] += diry[dir] * are; } } -void skill_brandishspear(struct block_list* src, struct block_list* bl, uint16 skill_id, uint16 skill_lv, int64 tick, int flag) { +static void skill_brandishspear(struct block_list *src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int64 tick, int flag) +{ int c,n=4; - uint8 dir = map->calc_dir(src,bl->x,bl->y); struct square tc; - int x=bl->x,y=bl->y; + int x, y; + + nullpo_retv(bl); + x = bl->x; + y = bl->y; + enum unit_dir dir = map->calc_dir(src, x, y); skill->brandishspear_first(&tc,dir,x,y); skill->brandishspear_dir(&tc,dir,4); skill->area_temp[1] = bl->id; @@ -15199,7 +16199,8 @@ void skill_brandishspear(struct block_list* src, struct block_list* bl, uint16 s /*========================================== * Weapon Repair [Celest/DracoRPG] *------------------------------------------*/ -void skill_repairweapon (struct map_session_data *sd, int idx) { +static void skill_repairweapon(struct map_session_data *sd, int idx) +{ int material; int materials[4] = { ITEMID_IRON_ORE, @@ -15215,15 +16216,18 @@ void skill_repairweapon (struct map_session_data *sd, int idx) { if ( !( target_sd = map->id2sd(sd->menuskill_val) ) ) //Failed.... return; - if( idx == 0xFFFF ) // No item selected ('Cancel' clicked) + if (idx == 0xFFFF || idx == -1) // No item selected ('Cancel' clicked) return; - if( idx < 0 || idx >= MAX_INVENTORY ) + if (idx < 0 || idx >= sd->status.inventorySize) return; //Invalid index?? item = &target_sd->status.inventory[idx]; - if( item->nameid <= 0 || item->attribute == 0 ) + if( item->nameid <= 0 || (item->attribute & ATTR_BROKEN) == 0 ) return; //Again invalid item.... + if (item->card[0] == CARD0_PET) + return; + if( sd != target_sd && !battle->check_range(&sd->bl,&target_sd->bl, skill->get_range2(&sd->bl, sd->menuskill_id,sd->menuskill_val2) ) ){ clif->item_repaireffect(sd,idx,1); return; @@ -15234,15 +16238,16 @@ void skill_repairweapon (struct map_session_data *sd, int idx) { else material = materials[2]; // Armors consume 1 Steel if (pc->search_inventory(sd,material) == INDEX_NOT_FOUND) { - clif->skill_fail(sd,sd->menuskill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, sd->menuskill_id, USESKILL_FAIL_LEVEL, 0, 0); return; } clif->skill_nodamage(&sd->bl,&target_sd->bl,sd->menuskill_id,1,1); - item->attribute = 0;/* clear broken state */ + item->attribute |= ATTR_BROKEN; + item->attribute ^= ATTR_BROKEN; /* clear broken state */ - clif->equiplist(target_sd); + clif->equipList(target_sd); pc->delitem(sd, pc->search_inventory(sd, material), 1, 0, DELITEM_NORMAL, LOG_TYPE_CONSUME); // FIXME: is this the correct reason flag? @@ -15255,13 +16260,13 @@ void skill_repairweapon (struct map_session_data *sd, int idx) { /*========================================== * Item Appraisal *------------------------------------------*/ -void skill_identify (struct map_session_data *sd, int idx) +static void skill_identify(struct map_session_data *sd, int idx) { int flag=1; nullpo_retv(sd); sd->state.workinprogress = 0; - if(idx >= 0 && idx < MAX_INVENTORY) { + if (idx >= 0 && idx < sd->status.inventorySize) { if(sd->status.inventory[idx].nameid > 0 && sd->status.inventory[idx].identify == 0 ){ flag=0; sd->status.inventory[idx].identify=1; @@ -15273,11 +16278,11 @@ void skill_identify (struct map_session_data *sd, int idx) /*========================================== * Weapon Refine [Celest] *------------------------------------------*/ -void skill_weaponrefine (struct map_session_data *sd, int idx) +static void skill_weaponrefine(struct map_session_data *sd, int idx) { nullpo_retv(sd); - if (idx >= 0 && idx < MAX_INVENTORY) { + if (idx >= 0 && idx < sd->status.inventorySize) { struct item *item; struct item_data *ditem = sd->inventory_data[idx]; item = &sd->status.inventory[idx]; @@ -15293,7 +16298,7 @@ void skill_weaponrefine (struct map_session_data *sd, int idx) int i = 0, per; if( ditem->flag.no_refine ) { // if the item isn't refinable - clif->skill_fail(sd,sd->menuskill_id,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, sd->menuskill_id, USESKILL_FAIL_LEVEL, 0, 0); return; } if( item->refine >= sd->menuskill_val || item->refine >= 10 ){ @@ -15305,13 +16310,13 @@ void skill_weaponrefine (struct map_session_data *sd, int idx) return; } - per = status->get_refine_chance(ditem->wlv, (int)item->refine) * 10; + per = refine->get_refine_chance(ditem->wlv, (int)item->refine, REFINE_CHANCE_TYPE_NORMAL) * 10; // Aegis leaked formula. [malufett] - if( sd->status.class_ == JOB_MECHANIC_T ) + if (sd->status.class == JOB_MECHANIC_T) per += 100; else - per += 5 * ((signed int)sd->status.job_level - 50); + per += 5 * (sd->status.job_level - 50); pc->delitem(sd, i, 1, 0, DELITEM_NORMAL, LOG_TYPE_REFINE); // FIXME: is this the correct reason flag? if (per > rnd() % 1000) { @@ -15325,7 +16330,7 @@ void skill_weaponrefine (struct map_session_data *sd, int idx) } clif->delitem(sd, idx, 1, DELITEM_NORMAL); clif->upgrademessage(sd->fd, 0,item->nameid); - clif->inventorylist(sd); + clif->inventoryList(sd); clif->refine(sd->fd,0,idx,item->refine); if (ep) pc->equipitem(sd,idx,ep); @@ -15334,16 +16339,16 @@ void skill_weaponrefine (struct map_session_data *sd, int idx) item->card[0] == CARD0_FORGE && (int)MakeDWord(item->card[2],item->card[3]) == sd->status.char_id) { // Fame point system [DracoRPG] - switch(ditem->wlv){ - case 1: - pc->addfame(sd,1); // Success to refine to +10 a lv1 weapon you forged = +1 fame point - break; - case 2: - pc->addfame(sd,25); // Success to refine to +10 a lv2 weapon you forged = +25 fame point - break; - case 3: - pc->addfame(sd,1000); // Success to refine to +10 a lv3 weapon you forged = +1000 fame point - break; + switch (ditem->wlv) { + case 1: + pc->addfame(sd, RANKTYPE_BLACKSMITH, 1); // Success to refine to +10 a lv1 weapon you forged = +1 fame point + break; + case 2: + pc->addfame(sd, RANKTYPE_BLACKSMITH, 25); // Success to refine to +10 a lv2 weapon you forged = +25 fame point + break; + case 3: + pc->addfame(sd, RANKTYPE_BLACKSMITH, 1000); // Success to refine to +10 a lv3 weapon you forged = +1000 fame point + break; } } } else { @@ -15362,7 +16367,7 @@ void skill_weaponrefine (struct map_session_data *sd, int idx) /*========================================== * *------------------------------------------*/ -int skill_autospell (struct map_session_data *sd, uint16 skill_id) +static int skill_autospell(struct map_session_data *sd, uint16 skill_id) { uint16 skill_lv; int maxlv=1,lv; @@ -15405,7 +16410,7 @@ int skill_autospell (struct map_session_data *sd, uint16 skill_id) /*========================================== * Sitting skills functions. *------------------------------------------*/ -int skill_sit_count(struct block_list *bl, va_list ap) +static int skill_sit_count(struct block_list *bl, va_list ap) { int type = va_arg(ap, int); struct map_session_data *sd = NULL; @@ -15426,7 +16431,7 @@ int skill_sit_count(struct block_list *bl, va_list ap) return 0; } -int skill_sit_in(struct block_list *bl, va_list ap) +static int skill_sit_in(struct block_list *bl, va_list ap) { int type = va_arg(ap, int); struct map_session_data *sd = NULL; @@ -15450,7 +16455,7 @@ int skill_sit_in(struct block_list *bl, va_list ap) return 0; } -int skill_sit_out(struct block_list *bl, va_list ap) +static int skill_sit_out(struct block_list *bl, va_list ap) { int type = va_arg(ap, int); struct map_session_data *sd = NULL; @@ -15469,7 +16474,7 @@ int skill_sit_out(struct block_list *bl, va_list ap) return 0; } -int skill_sit (struct map_session_data *sd, int type) +static int skill_sit(struct map_session_data *sd, int type) { int flag = 0; int range = 0, lv; @@ -15489,9 +16494,9 @@ int skill_sit (struct map_session_data *sd, int type) } if( type ) { - clif->sc_load(&sd->bl,sd->bl.id,SELF,SI_SIT,0,0,0); + clif->sc_load(&sd->bl, sd->bl.id, SELF, status->get_sc_icon(SC_SIT), 0, 0, 0); } else { - 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)); } if (!flag) return 0; @@ -15509,7 +16514,7 @@ int skill_sit (struct map_session_data *sd, int type) /*========================================== * *------------------------------------------*/ -int skill_frostjoke_scream(struct block_list *bl, va_list ap) +static int skill_frostjoke_scream(struct block_list *bl, va_list ap) { struct block_list *src; uint16 skill_id,skill_lv; @@ -15542,10 +16547,12 @@ int skill_frostjoke_scream(struct block_list *bl, va_list ap) /*========================================== * *------------------------------------------*/ -void skill_unitsetmapcell (struct skill_unit *src, uint16 skill_id, uint16 skill_lv, cell_t cell, bool flag) { +static void skill_unitsetmapcell(struct skill_unit *src, uint16 skill_id, uint16 skill_lv, cell_t cell, bool flag) +{ int range = skill->get_unit_range(skill_id,skill_lv); int x,y; + nullpo_retv(src); for( y = src->bl.y - range; y <= src->bl.y + range; ++y ) for( x = src->bl.x - range; x <= src->bl.x + range; ++x ) map->list[src->bl.m].setcell(src->bl.m, x, y, cell, flag); @@ -15554,11 +16561,14 @@ void skill_unitsetmapcell (struct skill_unit *src, uint16 skill_id, uint16 skill /*========================================== * *------------------------------------------*/ -int skill_attack_area(struct block_list *bl, va_list ap) { +static int skill_attack_area(struct block_list *bl, va_list ap) +{ struct block_list *src,*dsrc; int atk_type,skill_id,skill_lv,flag,type; int64 tick; + nullpo_ret(bl); + if(status->isdead(bl)) return 0; @@ -15594,7 +16604,7 @@ int skill_attack_area(struct block_list *bl, va_list ap) { /*========================================== * *------------------------------------------*/ -int skill_clear_group (struct block_list *bl, int flag) +static int skill_clear_group(struct block_list *bl, int flag) { struct unit_data *ud = unit->bl2ud(bl); struct skill_unit_group *group[MAX_SKILLUNITGROUP]; @@ -15638,7 +16648,8 @@ int skill_clear_group (struct block_list *bl, int flag) /*========================================== * Returns the first element field found [Skotlex] *------------------------------------------*/ -struct skill_unit_group *skill_locate_element_field(struct block_list *bl) { +static struct skill_unit_group *skill_locate_element_field(struct block_list *bl) +{ struct unit_data *ud = unit->bl2ud(bl); int i; nullpo_ret(bl); @@ -15660,7 +16671,7 @@ struct skill_unit_group *skill_locate_element_field(struct block_list *bl) { } // for graffiti cleaner [Valaris] -int skill_graffitiremover(struct block_list *bl, va_list ap) +static int skill_graffitiremover(struct block_list *bl, va_list ap) { struct skill_unit *su = NULL; @@ -15676,7 +16687,7 @@ int skill_graffitiremover(struct block_list *bl, va_list ap) return 0; } -int skill_greed(struct block_list *bl, va_list ap) +static int skill_greed(struct block_list *bl, va_list ap) { struct block_list *src = va_arg(ap, struct block_list *); @@ -15693,7 +16704,7 @@ int skill_greed(struct block_list *bl, va_list ap) } //For Ranger's Detonator [Jobbie/3CeAM] -int skill_detonator(struct block_list *bl, va_list ap) +static int skill_detonator(struct block_list *bl, va_list ap) { struct skill_unit *su = NULL; struct block_list *src = va_arg(ap,struct block_list *); @@ -15727,10 +16738,10 @@ int skill_detonator(struct block_list *bl, va_list ap) case UNT_CLAYMORETRAP: case UNT_FIRINGTRAP: case UNT_ICEBOUNDTRAP: - map->foreachinrange(skill->trap_splash,bl,skill->get_splash(su->group->skill_id,su->group->skill_lv),su->group->bl_flag|BL_SKILL|~BCT_SELF,bl,su->group->tick); + skill->trap_do_splash(bl, su->group->skill_id, su->group->skill_lv, su->group->bl_flag | BL_SKILL | ~BCT_SELF, su->group->tick); break; default: - map->foreachinrange(skill->trap_splash,bl,skill->get_splash(su->group->skill_id,su->group->skill_lv),su->group->bl_flag,bl,su->group->tick); + skill->trap_do_splash(bl, su->group->skill_id, su->group->skill_lv, su->group->bl_flag, su->group->tick); } clif->changetraplook(bl, UNT_USED_TRAPS); su->group->limit = DIFF_TICK32(timer->gettick(),su->group->tick) + @@ -15744,7 +16755,7 @@ int skill_detonator(struct block_list *bl, va_list ap) /*========================================== * *------------------------------------------*/ -int skill_cell_overlap(struct block_list *bl, va_list ap) +static int skill_cell_overlap(struct block_list *bl, va_list ap) { uint16 skill_id; int *alive; @@ -15842,7 +16853,7 @@ int skill_cell_overlap(struct block_list *bl, va_list ap) /*========================================== * *------------------------------------------*/ -int skill_chastle_mob_changetarget(struct block_list *bl, va_list ap) +static int skill_chastle_mob_changetarget(struct block_list *bl, va_list ap) { struct unit_data *ud = unit->bl2ud(bl); struct block_list *from_bl = va_arg(ap, struct block_list *); @@ -15863,16 +16874,38 @@ int skill_chastle_mob_changetarget(struct block_list *bl, va_list ap) return 0; } +/** + * Does final adjustments (e.g. count enemies affected by splash) then runs trap splash function (skill_trap_splash). + * + * @param bl : trap skill unit's bl + * @param skill_id : Trap Skill ID + * @param skill_lv : Trap Skill Level + * @param bl_flag : Flag representing units affected by this trap + * @param tick : tick related to this trap + */ +static void skill_trap_do_splash(struct block_list *bl, uint16 skill_id, uint16 skill_lv, int bl_flag, int64 tick) +{ + int enemy_count = 0; + + if (skill->get_nk(skill_id) & NK_SPLASHSPLIT) { + enemy_count = map->foreachinrange(skill->area_sub, bl, skill->get_splash(skill_id, skill_lv), BL_CHAR, bl, skill_id, skill_lv, tick, BCT_ENEMY, skill->area_sub_count); + enemy_count = max(1, enemy_count); // Don't let enemy_count be 0 when spliting trap damage + } + + map->foreachinrange(skill->trap_splash, bl, skill->get_splash(skill_id, skill_lv), bl_flag, bl, tick, enemy_count); +} + /*========================================== * *------------------------------------------*/ -int skill_trap_splash(struct block_list *bl, va_list ap) +static int skill_trap_splash(struct block_list *bl, va_list ap) { struct block_list *src = va_arg(ap, struct block_list *); int64 tick = va_arg(ap, int64); struct skill_unit *src_su = NULL; struct skill_unit_group *sg; struct block_list *ss; + int enemy_count = va_arg(ap, int); nullpo_ret(bl); nullpo_ret(src); @@ -15967,7 +17000,7 @@ int skill_trap_splash(struct block_list *bl, va_list ap) } /* Fall through */ default: - skill->attack(skill->get_type(sg->skill_id),ss,src,bl,sg->skill_id,sg->skill_lv,tick,0); + skill->attack(skill->get_type(sg->skill_id), ss, src, bl, sg->skill_id, sg->skill_lv, tick, enemy_count); break; } return 1; @@ -15976,7 +17009,8 @@ int skill_trap_splash(struct block_list *bl, va_list ap) /*========================================== * *------------------------------------------*/ -int skill_enchant_elemental_end (struct block_list *bl, int type) { +static int skill_enchant_elemental_end(struct block_list *bl, int type) +{ struct status_change *sc; const enum sc_type scs[] = { SC_ENCHANTPOISON, SC_ASPERSIO, SC_PROPERTYFIRE, SC_PROPERTYWATER, SC_PROPERTYWIND, SC_PROPERTYGROUND, SC_PROPERTYDARK, SC_PROPERTYTELEKINESIS, SC_ENCHANTARMS }; int i; @@ -15992,16 +17026,17 @@ int skill_enchant_elemental_end (struct block_list *bl, int type) { return 0; } -bool skill_check_cloaking(struct block_list *bl, struct status_change_entry *sce) +static bool skill_check_cloaking(struct block_list *bl, struct status_change_entry *sce) { - static int dx[] = { 0, 1, 0, -1, -1, 1, 1, -1}; - static int dy[] = {-1, 0, 1, 0, -1, -1, 1, 1}; bool wall = true; + nullpo_retr(false, bl); if( (bl->type == BL_PC && battle_config.pc_cloak_check_type&1) || (bl->type != BL_PC && battle_config.monster_cloak_check_type&1) ) { //Check for walls. + static int dx[] = { 0, 1, 0, -1, -1, 1, 1, -1}; + static int dy[] = {-1, 0, 1, 0, -1, -1, 1, 1}; int i; ARR_FIND( 0, 8, i, map->getcell(bl->m, bl, bl->x+dx[i], bl->y+dy[i], CELL_CHKNOPASS) != 0 ); if( i == 8 ) @@ -16030,7 +17065,8 @@ bool skill_check_cloaking(struct block_list *bl, struct status_change_entry *sce /** * Verifies if an user can use SC_CLOAKING **/ -bool skill_can_cloak(struct map_session_data *sd) { +static bool skill_can_cloak(struct map_session_data *sd) +{ nullpo_retr(false, sd); //Avoid cloaking with no wall and low skill level. [Skotlex] @@ -16047,7 +17083,7 @@ bool skill_can_cloak(struct map_session_data *sd) { * Verifies if an user can still be cloaked (AS_CLOAKING) * Is called via map->foreachinrange when any kind of wall disapears **/ -int skill_check_cloaking_end(struct block_list *bl, va_list ap) +static int skill_check_cloaking_end(struct block_list *bl, va_list ap) { struct map_session_data *sd = BL_CAST(BL_PC, bl); @@ -16057,13 +17093,14 @@ int skill_check_cloaking_end(struct block_list *bl, va_list ap) return 0; } -bool skill_check_camouflage(struct block_list *bl, struct status_change_entry *sce) +static bool skill_check_camouflage(struct block_list *bl, struct status_change_entry *sce) { - static int dx[] = { 0, 1, 0, -1, -1, 1, 1, -1}; - static int dy[] = {-1, 0, 1, 0, -1, -1, 1, 1}; bool wall = true; + nullpo_retr(false, bl); if( bl->type == BL_PC ) { //Check for walls. + static int dx[] = { 0, 1, 0, -1, -1, 1, 1, -1}; + static int dy[] = {-1, 0, 1, 0, -1, -1, 1, 1}; int i; ARR_FIND( 0, 8, i, map->getcell(bl->m, bl, bl->x+dx[i], bl->y+dy[i], CELL_CHKNOPASS) != 0 ); if( i == 8 ) @@ -16084,7 +17121,7 @@ bool skill_check_camouflage(struct block_list *bl, struct status_change_entry *s return wall; } -bool skill_check_shadowform(struct block_list *bl, int64 damage, int hit) +static bool skill_check_shadowform(struct block_list *bl, int64 damage, int hit) { struct status_change *sc; @@ -16123,7 +17160,8 @@ bool skill_check_shadowform(struct block_list *bl, int64 damage, int hit) /*========================================== * *------------------------------------------*/ -struct skill_unit *skill_initunit (struct skill_unit_group *group, int idx, int x, int y, int val1, int val2) { +static struct skill_unit *skill_initunit(struct skill_unit_group *group, int idx, int x, int y, int val1, int val2) +{ struct skill_unit *su; nullpo_retr(NULL, group); @@ -16143,6 +17181,14 @@ struct skill_unit *skill_initunit (struct skill_unit_group *group, int idx, int su->val1=val1; su->val2 = val2; su->prev = 0; + su->visible = true; + + if (skill->get_inf2(group->skill_id) & INF2_HIDDEN_TRAP + && ((battle_config.trap_visibility == 1 && map_flag_vs(group->map)) // invisible in PvP/GvG + || battle_config.trap_visibility == 2 // always invisible + )) { + su->visible = false; + } idb_put(skill->unit_db, su->bl.id, su); map->addiddb(&su->bl); @@ -16175,7 +17221,8 @@ struct skill_unit *skill_initunit (struct skill_unit_group *group, int idx, int /*========================================== * *------------------------------------------*/ -int skill_delunit (struct skill_unit* su) { +static int skill_delunit(struct skill_unit *su) +{ struct skill_unit_group *group; nullpo_ret(su); @@ -16250,24 +17297,25 @@ int skill_delunit (struct skill_unit* su) { * *------------------------------------------*/ /// Returns the target skill_unit_group or NULL if not found. -struct skill_unit_group* skill_id2group(int group_id) +static struct skill_unit_group *skill_id2group(int group_id) { return (struct skill_unit_group*)idb_get(skill->group_db, group_id); } /// Returns a new group_id that isn't being used in skill->group_db. /// Fatal error if nothing is available. -int skill_get_new_group_id(void) +static int skill_get_new_group_id(void) { - if( skill->unit_group_newid >= MAX_SKILL_DB && skill->id2group(skill->unit_group_newid) == NULL ) + if (skill->unit_group_newid > MAX_SKILL_ID && skill->id2group(skill->unit_group_newid) == NULL) return skill->unit_group_newid++;// available - {// find next id + + { + // find next id int base_id = skill->unit_group_newid; - while( base_id != ++skill->unit_group_newid ) - { - if( skill->unit_group_newid < MAX_SKILL_DB ) - skill->unit_group_newid = MAX_SKILL_DB; - if( skill->id2group(skill->unit_group_newid) == NULL ) + while (base_id != ++skill->unit_group_newid) { + if (skill->unit_group_newid <= MAX_SKILL_ID) + skill->unit_group_newid = MAX_SKILL_ID + 1; + if (skill->id2group(skill->unit_group_newid) == NULL) return skill->unit_group_newid++;// available } // full loop, nothing available @@ -16276,7 +17324,7 @@ int skill_get_new_group_id(void) } } -struct skill_unit_group* skill_initunitgroup (struct block_list* src, int count, uint16 skill_id, uint16 skill_lv, int unit_id, int limit, int interval) +static struct skill_unit_group *skill_initunitgroup(struct block_list *src, int count, uint16 skill_id, uint16 skill_lv, int unit_id, int limit, int interval) { struct unit_data* ud = unit->bl2ud( src ); struct skill_unit_group* group; @@ -16310,6 +17358,7 @@ struct skill_unit_group* skill_initunitgroup (struct block_list* src, int count, group->party_id = status->get_party_id(src); group->guild_id = status->get_guild_id(src); group->bg_id = bg->team_get_id(src); + group->clan_id = clan->get_id(src); group->group_id = skill->get_new_group_id(); CREATE(group->unit.data, struct skill_unit, count); group->unit.count = count; @@ -16338,7 +17387,7 @@ struct skill_unit_group* skill_initunitgroup (struct block_list* src, int count, /*========================================== * *------------------------------------------*/ -int skill_delunitgroup(struct skill_unit_group *group, const char *file, int line, const char *func) +static int skill_delunitgroup(struct skill_unit_group *group, const char *file, int line, const char *func) { struct block_list* src; struct unit_data *ud; @@ -16474,7 +17523,7 @@ int skill_delunitgroup(struct skill_unit_group *group, const char *file, int lin /*========================================== * *------------------------------------------*/ -int skill_clear_unitgroup (struct block_list *src) +static int skill_clear_unitgroup(struct block_list *src) { struct unit_data *ud = unit->bl2ud(src); @@ -16489,13 +17538,14 @@ int skill_clear_unitgroup (struct block_list *src) /*========================================== * *------------------------------------------*/ -struct skill_unit_group_tickset *skill_unitgrouptickset_search(struct block_list *bl, struct skill_unit_group *group, int64 tick) +static struct skill_unit_group_tickset *skill_unitgrouptickset_search(struct block_list *bl, struct skill_unit_group *group, int64 tick) { int i,j=-1,s,id; struct unit_data *ud; struct skill_unit_group_tickset *set; nullpo_ret(bl); + nullpo_ret(group); if (group->interval==-1) return NULL; @@ -16530,10 +17580,16 @@ struct skill_unit_group_tickset *skill_unitgrouptickset_search(struct block_list /*========================================== * *------------------------------------------*/ -int skill_unit_timer_sub_onplace(struct block_list* bl, va_list ap) { - struct skill_unit* su = va_arg(ap,struct skill_unit *); - struct skill_unit_group* group = su->group; - int64 tick = va_arg(ap,int64); +static int skill_unit_timer_sub_onplace(struct block_list *bl, va_list ap) +{ + struct skill_unit* su; + struct skill_unit_group* group; + int64 tick; + + su = va_arg(ap,struct skill_unit *); + nullpo_ret(su); + group = su->group; + tick = va_arg(ap,int64); if( !su->alive || bl->prev == NULL ) return 0; @@ -16554,13 +17610,18 @@ int skill_unit_timer_sub_onplace(struct block_list* bl, va_list ap) { /** * @see DBApply */ -int skill_unit_timer_sub(union DBKey key, struct DBData *data, va_list ap) +static int skill_unit_timer_sub(union DBKey key, struct DBData *data, va_list ap) { - struct skill_unit* su = DB->data2ptr(data); - struct skill_unit_group* group = su->group; + struct skill_unit* su; + struct skill_unit_group* group; int64 tick = va_arg(ap,int64); bool dissonance; - struct block_list* bl = &su->bl; + struct block_list* bl; + + su = DB->data2ptr(data); + nullpo_ret(su); + group = su->group; + bl = &su->bl; if( !su->alive ) return 0; @@ -16593,6 +17654,7 @@ int skill_unit_timer_sub(union DBKey key, struct DBData *data, va_list ap) skill->delunit(su); break; } + FALLTHROUGH case UNT_SKIDTRAP: case UNT_LANDMINE: case UNT_SHOCKWAVE: @@ -16617,9 +17679,9 @@ int skill_unit_timer_sub(union DBKey key, struct DBData *data, va_list ap) // revert unit back into a trap struct item item_tmp; memset(&item_tmp,0,sizeof(item_tmp)); - item_tmp.nameid = group->item_id?group->item_id:ITEMID_TRAP; + item_tmp.nameid = group->item_id ? group->item_id : ITEMID_BOOBY_TRAP; item_tmp.identify = 1; - map->addflooritem(bl, &item_tmp, 1, bl->m, bl->x, bl->y, 0, 0, 0, 0); + map->addflooritem(bl, &item_tmp, 1, bl->m, bl->x, bl->y, 0, 0, 0, 0, false); } skill->delunit(su); } @@ -16661,7 +17723,7 @@ int skill_unit_timer_sub(union DBKey key, struct DBData *data, va_list ap) break; } clif->changetraplook(bl,UNT_USED_TRAPS); - map->foreachinrange(skill->trap_splash, bl, skill->get_splash(group->skill_id, group->skill_lv), group->bl_flag, bl, tick); + skill->trap_do_splash(bl, group->skill_id, group->skill_lv, group->bl_flag, tick); group->limit = DIFF_TICK32(tick,group->tick)+1500; su->limit = DIFF_TICK32(tick,group->tick)+1500; group->unit_id = UNT_USED_TRAPS; @@ -16670,7 +17732,7 @@ int skill_unit_timer_sub(union DBKey key, struct DBData *data, va_list ap) case UNT_FEINTBOMB: { struct block_list *src = map->id2bl(group->src_id); if( src ) { - map->foreachinrange(skill->area_sub, &su->bl, su->range, splash_target(src), src, SC_FEINTBOMB, group->skill_lv, tick, BCT_ENEMY|SD_ANIMATION|1, skill->castend_damage_id); + map->foreachinrange(skill->area_sub, &su->bl, su->range, skill->splash_target(src), src, SC_FEINTBOMB, group->skill_lv, tick, BCT_ENEMY|SD_ANIMATION|1, skill->castend_damage_id); status_change_end(src, SC__FEINTBOMB_MASTER, INVALID_TIMER); } skill->delunit(su); @@ -16768,7 +17830,8 @@ int skill_unit_timer_sub(union DBKey key, struct DBData *data, va_list ap) /*========================================== * Executes on all skill units every SKILLUNITTIMER_INTERVAL milliseconds. *------------------------------------------*/ -int skill_unit_timer(int tid, int64 tick, int id, intptr_t data) { +static int skill_unit_timer(int tid, int64 tick, int id, intptr_t data) +{ map->freeblock_lock(); skill->unit_db->foreach(skill->unit_db, skill->unit_timer_sub, tick); @@ -16781,7 +17844,7 @@ int skill_unit_timer(int tid, int64 tick, int id, intptr_t data) { /*========================================== * *------------------------------------------*/ -int skill_unit_move_sub(struct block_list* bl, va_list ap) +static int skill_unit_move_sub(struct block_list *bl, va_list ap) { struct skill_unit *su = NULL; struct skill_unit_group *group = NULL; @@ -16794,6 +17857,7 @@ int skill_unit_move_sub(struct block_list* bl, va_list ap) uint16 skill_id; int i; + nullpo_ret(target); nullpo_ret(bl); Assert_ret(bl->type == BL_SKILL); su = BL_UCAST(BL_SKILL, bl); @@ -16880,7 +17944,8 @@ int skill_unit_move_sub(struct block_list* bl, va_list ap) * units to figure out when they have left a group. * flag&4: Force a onleft event (triggered when the bl is killed, for example) *------------------------------------------*/ -int skill_unit_move(struct block_list *bl, int64 tick, int flag) { +static int skill_unit_move(struct block_list *bl, int64 tick, int flag) +{ nullpo_ret(bl); if( bl->prev == NULL ) @@ -16905,7 +17970,8 @@ int skill_unit_move(struct block_list *bl, int64 tick, int flag) { /*========================================== * *------------------------------------------*/ -int skill_unit_move_unit_group(struct skill_unit_group *group, int16 m, int16 dx, int16 dy) { +static int skill_unit_move_unit_group(struct skill_unit_group *group, int16 m, int16 dx, int16 dy) +{ int i,j; int64 tick = timer->gettick(); int *m_flag; @@ -16994,7 +18060,7 @@ int skill_unit_move_unit_group(struct skill_unit_group *group, int16 m, int16 dx /*========================================== * *------------------------------------------*/ -int skill_can_produce_mix (struct map_session_data *sd, int nameid, int trigger, int qty) +static int skill_can_produce_mix(struct map_session_data *sd, int nameid, int trigger, int qty) { int i,j; @@ -17043,8 +18109,8 @@ int skill_can_produce_mix (struct map_session_data *sd, int nameid, int trigger, if (pc->search_inventory(sd,id) == INDEX_NOT_FOUND) return 0; } else { - int x, y; - for(y=0,x=0;y<MAX_INVENTORY;y++) + int x = 0; + for (int y = 0; y < sd->status.inventorySize; y++) if( sd->status.inventory[y].nameid == id ) x+=sd->status.inventory[y].amount; if(x<qty*skill->dbs->produce_db[i].mat_amount[j]) @@ -17057,7 +18123,8 @@ int skill_can_produce_mix (struct map_session_data *sd, int nameid, int trigger, /*========================================== * *------------------------------------------*/ -int skill_produce_mix(struct map_session_data *sd, uint16 skill_id, int nameid, int slot1, int slot2, int slot3, int qty) { +static int skill_produce_mix(struct map_session_data *sd, uint16 skill_id, int nameid, int slot1, int slot2, int slot3, int qty) +{ int slot[3]; int i,sc,ele,idx,equip,wlv,make_per = 0,flag = 0,skill_lv = 0; int num = -1; // exclude the recipe @@ -17114,10 +18181,12 @@ int skill_produce_mix(struct map_session_data *sd, uint16 skill_id, int nameid, else temp_qty = 1; if (data->stack.inventory) { - for( i = 0; i < MAX_INVENTORY; i++ ) { + for (i = 0; i < sd->status.inventorySize; i++ ) { if( sd->status.inventory[i].nameid == nameid ) { if( sd->status.inventory[i].amount >= data->stack.amount ) { - clif->msgtable(sd, MSG_RUNE_STONE_MAX_AMOUNT); +#if PACKETVER >= 20090729 + clif->msgtable(sd, MSG_RUNESTONE_MAKEERROR_OVERCOUNT); +#endif return 0; } else { /** @@ -17252,12 +18321,15 @@ int skill_produce_mix(struct map_session_data *sd, uint16 skill_id, int nameid, case ITEMID_HAGALAZ: case ITEMID_OTHILA: D -= 500; //Rank C + FALLTHROUGH case ITEMID_ISA: case ITEMID_WYRD: D -= 500; //Rank B + FALLTHROUGH case ITEMID_NAUTHIZ: case ITEMID_URUZ: D -= 500; //Rank A + FALLTHROUGH case ITEMID_BERKANA: case ITEMID_LUX_ANIMA: D -= 500; //Rank S @@ -17339,7 +18411,7 @@ int skill_produce_mix(struct map_session_data *sd, uint16 skill_id, int nameid, int difficulty = 30 + rnd()%120; // Random number between (30 ~ 150) make_per = sd->status.job_level / 4 + st->luk / 2 + st->dex / 3; // (Caster?s Job Level / 4) + (Caster?s LUK / 2) + (Caster?s DEX / 3) - qty = ~(5 + rnd()%5) + 1; + qty = ~(5 + rnd()%5) + 1; // FIXME[Haru]: This smells, if anyone knows the intent, please rewrite the expression in a more clear form. switch(nameid){// difficulty factor case ITEMID_APPLE_BOMB: @@ -17375,7 +18447,7 @@ int skill_produce_mix(struct map_session_data *sd, uint16 skill_id, int nameid, qty = 5; if( qty < 0 || (skill_lv == 1 && make_per < difficulty)){ - qty = ~qty + 1; + qty = ~qty + 1; // FIXME[Haru]: This smells. If anyone knows the intent, please rewrite the expression in a more clear form. make_per = 0; }else make_per = 10000; @@ -17418,7 +18490,7 @@ int skill_produce_mix(struct map_session_data *sd, uint16 skill_id, int nameid, make_per = make_per * battle_config.wp_rate / 100; } - if (sd->class_&JOBL_BABY) //if it's a Baby Class + if ((sd->job & JOBL_BABY) != 0) //if it's a Baby Class make_per = (make_per * 50) / 100; //Baby penalty is 50% (bugreport:4847) if(make_per < 1) make_per = 1; @@ -17482,8 +18554,8 @@ int skill_produce_mix(struct map_session_data *sd, uint16 skill_id, int nameid, if(equip){ clif->produce_effect(sd,0,nameid); clif->misceffect(&sd->bl,3); - if(itemdb_wlv(nameid) >= 3 && ((ele? 1 : 0) + sc) >= 3) // Fame point system [DracoRPG] - pc->addfame(sd,10); // Success to forge a lv3 weapon with 3 additional ingredients = +10 fame point + if (itemdb_wlv(nameid) >= 3 && ((ele? 1 : 0) + sc) >= 3) // Fame point system [DracoRPG] + pc->addfame(sd, RANKTYPE_BLACKSMITH, 10); // Success to forge a lv3 weapon with 3 additional ingredients = +10 fame point } else { int fame = 0; tmp_item.amount = 0; @@ -17523,8 +18595,9 @@ int skill_produce_mix(struct map_session_data *sd, uint16 skill_id, int nameid, sd->potion_success_counter = 0; } - if (fame) - pc->addfame(sd,fame); + if (fame != 0 && (skill_id == AM_PHARMACY || skill_id == AM_TWILIGHT1 || skill_id == AM_TWILIGHT2 || skill_id == AM_TWILIGHT3)) { + pc->addfame(sd, RANKTYPE_ALCHEMIST, fame); + } //Visual effects and the like. switch (skill_id) { case AM_PHARMACY: @@ -17551,7 +18624,7 @@ int skill_produce_mix(struct map_session_data *sd, uint16 skill_id, int nameid, { //Cooking items. clif->specialeffect(&sd->bl, 608, AREA); if( sd->cook_mastery < 1999 ) - pc_setglobalreg(sd, script->add_str("COOK_MASTERY"),sd->cook_mastery + ( 1 << ( (skill->dbs->produce_db[idx].itemlv - 11) / 2 ) ) * 5); + pc_setglobalreg(sd, script->add_variable("COOK_MASTERY"),sd->cook_mastery + ( 1 << ( (skill->dbs->produce_db[idx].itemlv - 11) / 2 ) ) * 5); } break; } @@ -17565,24 +18638,28 @@ int skill_produce_mix(struct map_session_data *sd, uint16 skill_id, int nameid, tmp_item.amount = qty * skill->dbs->changematerial_db[i].qty[j]; if((flag = pc->additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE))) { clif->additem(sd,0,0,flag); - map->addflooritem(&sd->bl, &tmp_item, tmp_item.amount, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0); + map->addflooritem(&sd->bl, &tmp_item, tmp_item.amount, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0, false); } k++; } } break; } - if( k ){ + if (k) { +#if PACKETVER >= 20091013 clif->msgtable_skill(sd, skill_id, MSG_SKILL_SUCCESS); +#endif return 1; } } else if (tmp_item.amount) { //Success if((flag = pc->additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE))) { clif->additem(sd,0,0,flag); - map->addflooritem(&sd->bl, &tmp_item, tmp_item.amount, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0); + map->addflooritem(&sd->bl, &tmp_item, tmp_item.amount, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0, false); } - if( skill_id == GN_MIX_COOKING || skill_id == GN_MAKEBOMB || skill_id == GN_S_PHARMACY ) +#if PACKETVER >= 20091013 + if (skill_id == GN_MIX_COOKING || skill_id == GN_MAKEBOMB || skill_id == GN_S_PHARMACY) clif->msgtable_skill(sd, skill_id, MSG_SKILL_SUCCESS); +#endif return 1; } } @@ -17631,7 +18708,7 @@ int skill_produce_mix(struct map_session_data *sd, uint16 skill_id, int nameid, int rate = rnd()%500; memset(&tmp_item,0,sizeof(tmp_item)); if( rate < 50) i = 4; - else if( rate < 100) i = 2+rnd()%1; + else if( rate < 100) i = 2+rnd()%1; // FIXME[Haru]: This '%1' is certainly not intended. If anyone knows the purpose, please rewrite this code. else if( rate < 250 ) i = 1; else if( rate < 500 ) i = 0; tmp_item.nameid = compensation[i]; @@ -17639,29 +18716,33 @@ int skill_produce_mix(struct map_session_data *sd, uint16 skill_id, int nameid, tmp_item.identify = 1; if( pc->additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE) ) { clif->additem(sd,0,0,flag); - map->addflooritem(&sd->bl, &tmp_item, tmp_item.amount, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0); + map->addflooritem(&sd->bl, &tmp_item, tmp_item.amount, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0, false); } - clif->msgtable_skill(sd, skill_id, MSG_SKILL_FAILURE); +#if PACKETVER >= 20091013 + clif->msgtable_skill(sd, skill_id, MSG_SKILL_FAIL); +#endif } break; case GN_MAKEBOMB: case GN_S_PHARMACY: case GN_CHANGEMATERIAL: - clif->msgtable_skill(sd, skill_id, MSG_SKILL_FAILURE); +#if PACKETVER >= 20091013 + clif->msgtable_skill(sd, skill_id, MSG_SKILL_FAIL); +#endif break; default: if( skill->dbs->produce_db[idx].itemlv > 10 && skill->dbs->produce_db[idx].itemlv <= 20 ) { //Cooking items. clif->specialeffect(&sd->bl, 609, AREA); if( sd->cook_mastery > 0 ) - pc_setglobalreg(sd, script->add_str("COOK_MASTERY"), sd->cook_mastery - ( 1 << ((skill->dbs->produce_db[idx].itemlv - 11) / 2) ) - ( ( ( 1 << ((skill->dbs->produce_db[idx].itemlv - 11) / 2) ) >> 1 ) * 3 )); + pc_setglobalreg(sd, script->add_variable("COOK_MASTERY"), sd->cook_mastery - ( 1 << ((skill->dbs->produce_db[idx].itemlv - 11) / 2) ) - ( ( ( 1 << ((skill->dbs->produce_db[idx].itemlv - 11) / 2) ) >> 1 ) * 3 )); } } } return 0; } -int skill_arrow_create (struct map_session_data *sd, int nameid) +static int skill_arrow_create(struct map_session_data *sd, int nameid) { int i,j,flag,index=-1; struct item tmp_item; @@ -17696,18 +18777,20 @@ int skill_arrow_create (struct map_session_data *sd, int nameid) continue; if((flag = pc->additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE))) { clif->additem(sd,0,0,flag); - map->addflooritem(&sd->bl, &tmp_item, tmp_item.amount, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0); + map->addflooritem(&sd->bl, &tmp_item, tmp_item.amount, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0, false); } } return 0; } -int skill_poisoningweapon( struct map_session_data *sd, int nameid) { + +static int skill_poisoningweapon(struct map_session_data *sd, int nameid) +{ sc_type type; int chance, i; nullpo_ret(sd); if (nameid <= 0 || (i = pc->search_inventory(sd,nameid)) == INDEX_NOT_FOUND || pc->delitem(sd, i, 1, 0, DELITEM_NORMAL, LOG_TYPE_CONSUME)) { - clif->skill_fail(sd,GC_POISONINGWEAPON,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, GC_POISONINGWEAPON, USESKILL_FAIL_LEVEL, 0, 0); return 0; } switch( nameid ) @@ -17721,7 +18804,7 @@ int skill_poisoningweapon( struct map_session_data *sd, int nameid) { case ITEMID_POISON_LAUGHING: type = SC_MAGICMUSHROOM; break; case ITEMID_POISON_OBLIVION: type = SC_OBLIVIONCURSE; break; default: - clif->skill_fail(sd,GC_POISONINGWEAPON,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, GC_POISONINGWEAPON, USESKILL_FAIL_LEVEL, 0, 0); return 0; } @@ -17733,7 +18816,8 @@ int skill_poisoningweapon( struct map_session_data *sd, int nameid) { return 0; } -void skill_toggle_magicpower(struct block_list *bl, uint16 skill_id) { +static void skill_toggle_magicpower(struct block_list *bl, uint16 skill_id) +{ struct status_change *sc = status->get_sc(bl); // non-offensive and non-magic skills do not affect the status @@ -17757,7 +18841,8 @@ void skill_toggle_magicpower(struct block_list *bl, uint16 skill_id) { } } -int skill_magicdecoy(struct map_session_data *sd, int nameid) { +static int skill_magicdecoy(struct map_session_data *sd, int nameid) +{ int x, y, i, class_ = 0, skill_id; struct mob_data *md; nullpo_ret(sd); @@ -17767,7 +18852,7 @@ int skill_magicdecoy(struct map_session_data *sd, int nameid) { || (i = pc->search_inventory(sd, nameid)) == INDEX_NOT_FOUND || pc->delitem(sd, i, 1, 0, DELITEM_NORMAL, LOG_TYPE_CONSUME) != 0 ) { - clif->skill_fail(sd,NC_MAGICDECOY,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, NC_MAGICDECOY, USESKILL_FAIL_LEVEL, 0, 0); return 0; } @@ -17779,21 +18864,21 @@ int skill_magicdecoy(struct map_session_data *sd, int nameid) { sd->menuskill_val = 0; switch (nameid) { - case ITEMID_SCARLET_POINT: + case ITEMID_SCARLET_PTS: class_ = MOBID_MAGICDECOY_FIRE; break; - case ITEMID_INDIGO_POINT: + case ITEMID_INDIGO_PTS: class_ = MOBID_MAGICDECOY_WATER; break; - case ITEMID_LIME_GREEN_POINT: + case ITEMID_LIME_GREEN_PTS: class_ = MOBID_MAGICDECOY_WIND; break; - case ITEMID_YELLOW_WISH_POINT: + case ITEMID_YELLOW_WISH_PTS: class_ = MOBID_MAGICDECOY_EARTH; break; } - md = mob->once_spawn_sub(&sd->bl, sd->bl.m, x, y, sd->status.name, class_, "", SZ_SMALL, AI_NONE); + md = mob->once_spawn_sub(&sd->bl, sd->bl.m, x, y, sd->status.name, class_, "", SZ_SMALL, AI_NONE, 0); if( md ) { md->master_id = sd->bl.id; md->special_state.ai = AI_FLORA; @@ -17808,7 +18893,8 @@ int skill_magicdecoy(struct map_session_data *sd, int nameid) { } // Warlock Spellbooks. [LimitLine/3CeAM] -int skill_spellbook (struct map_session_data *sd, int nameid) { +static int skill_spellbook(struct map_session_data *sd, int nameid) +{ int i, max_preserve, skill_id, point; struct status_change *sc; @@ -17820,7 +18906,7 @@ int skill_spellbook (struct map_session_data *sd, int nameid) { for(i=SC_SPELLBOOK1; i <= SC_SPELLBOOK7; i++) if( sc && !sc->data[i] ) break; if( i > SC_SPELLBOOK7 ) { - clif->skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_READING, 0); + clif->skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_READING, 0, 0); return 0; } @@ -17830,7 +18916,7 @@ int skill_spellbook (struct map_session_data *sd, int nameid) { if( !pc->checkskill(sd, (skill_id = skill->dbs->spellbook_db[i].skill_id)) ) { // User don't know the skill sc_start(&sd->bl, &sd->bl, SC_SLEEP, 100, 1, skill->get_time(WL_READING_SB, pc->checkskill(sd,WL_READING_SB))); - clif->skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_DIFFICULT_SLEEP, 0); + clif->skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_DIFFICULT_SLEEP, 0, 0); return 0; } @@ -17839,7 +18925,7 @@ int skill_spellbook (struct map_session_data *sd, int nameid) { if( sc && sc->data[SC_READING_SB] ) { if( (sc->data[SC_READING_SB]->val2 + point) > max_preserve ) { - clif->skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_PRESERVATION_POINT, 0); + clif->skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_PRESERVATION_POINT, 0, 0); return 0; } for(i = SC_SPELLBOOK7; i >= SC_SPELLBOOK1; i--){ // This is how official saves spellbook. [malufett] @@ -17856,7 +18942,9 @@ int skill_spellbook (struct map_session_data *sd, int nameid) { return 1; } -int skill_select_menu(struct map_session_data *sd,uint16 skill_id) { + +static int skill_select_menu(struct map_session_data *sd, uint16 skill_id) +{ int id, lv, prob, aslvl = 0, idx = 0; nullpo_ret(sd); @@ -17869,7 +18957,7 @@ int skill_select_menu(struct map_session_data *sd,uint16 skill_id) { if( skill_id >= GS_GLITTERING || skill->get_type(skill_id) != BF_MAGIC || (id = sd->status.skill[idx].id) == 0 || sd->status.skill[idx].flag != SKILL_FLAG_PLAGIARIZED ) { - clif->skill_fail(sd,SC_AUTOSHADOWSPELL,0,0); + clif->skill_fail(sd, SC_AUTOSHADOWSPELL, 0, 0, 0); return 0; } @@ -17880,7 +18968,7 @@ int skill_select_menu(struct map_session_data *sd,uint16 skill_id) { return 0; } -int skill_elementalanalysis(struct map_session_data *sd, uint16 skill_lv, const struct itemlist *item_list) +static int skill_elementalanalysis(struct map_session_data *sd, uint16 skill_lv, const struct itemlist *item_list) { int i; @@ -17901,10 +18989,10 @@ int skill_elementalanalysis(struct map_session_data *sd, uint16 skill_lv, const del_amount -= (del_amount % 10); add_amount = (skill_lv == 1) ? del_amount * (5 + rnd()%5) : del_amount / 10 ; - if (idx < 0 || idx >= MAX_INVENTORY + if (idx < 0 || idx >= sd->status.inventorySize || (nameid = sd->status.inventory[idx].nameid) <= 0 || del_amount < 0 || del_amount > sd->status.inventory[idx].amount) { - clif->skill_fail(sd,SO_EL_ANALYSIS,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, SO_EL_ANALYSIS, USESKILL_FAIL_LEVEL, 0, 0); return 1; } @@ -17920,18 +19008,18 @@ int skill_elementalanalysis(struct map_session_data *sd, uint16 skill_lv, const case ITEMID_WIND_OF_VERDURE: product = ITEMID_ROUGH_WIND; break; case ITEMID_YELLOW_LIVE: product = ITEMID_GREAT_NATURE; break; default: - clif->skill_fail(sd,SO_EL_ANALYSIS,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, SO_EL_ANALYSIS, USESKILL_FAIL_LEVEL, 0, 0); return 1; } if( pc->delitem(sd, idx, del_amount, 0, DELITEM_SKILLUSE, LOG_TYPE_CONSUME) ) { - clif->skill_fail(sd,SO_EL_ANALYSIS,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, SO_EL_ANALYSIS, USESKILL_FAIL_LEVEL, 0, 0); return 1; } if( skill_lv == 2 && rnd()%100 < 25 ) { // At level 2 have a fail chance. You loose your items if it fails. - clif->skill_fail(sd,SO_EL_ANALYSIS,USESKILL_FAIL_LEVEL,0); + clif->skill_fail(sd, SO_EL_ANALYSIS, USESKILL_FAIL_LEVEL, 0, 0); return 1; } @@ -17944,7 +19032,7 @@ int skill_elementalanalysis(struct map_session_data *sd, uint16 skill_lv, const int flag = pc->additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_CONSUME); if (flag) { clif->additem(sd,0,0,flag); - map->addflooritem(&sd->bl, &tmp_item, tmp_item.amount, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0); + map->addflooritem(&sd->bl, &tmp_item, tmp_item.amount, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0, false); } } @@ -17953,7 +19041,7 @@ int skill_elementalanalysis(struct map_session_data *sd, uint16 skill_lv, const return 0; } -int skill_changematerial(struct map_session_data *sd, const struct itemlist *item_list) +static int skill_changematerial(struct map_session_data *sd, const struct itemlist *item_list) { int i, j, k, c, p = 0, nameid, amount; @@ -17972,11 +19060,13 @@ int skill_changematerial(struct map_session_data *sd, const struct itemlist *ite for (k = 0; k < VECTOR_LENGTH(*item_list); k++) { const struct itemlist_entry *entry = &VECTOR_INDEX(*item_list, k); int idx = entry->id; - Assert_ret(idx >= 0 && idx < MAX_INVENTORY); + Assert_ret(idx >= 0 && idx < sd->status.inventorySize); amount = entry->amount; nameid = sd->status.inventory[idx].nameid; if (nameid > 0 && sd->status.inventory[idx].identify == 0) { - clif->msgtable_skill(sd, GN_CHANGEMATERIAL, MSG_SKILL_ITEM_NEED_IDENTIFY); +#if PACKETVER >= 20091013 + clif->msgtable_skill(sd, GN_CHANGEMATERIAL, MSG_SKILL_FAIL_MATERIAL_IDENTITY); +#endif return 0; } if( nameid == skill->dbs->produce_db[i].mat_id[j] && (amount-p*skill->dbs->produce_db[i].mat_amount[j]) >= skill->dbs->produce_db[i].mat_amount[j] @@ -17996,16 +19086,17 @@ int skill_changematerial(struct map_session_data *sd, const struct itemlist *ite } } } - - if( p == 0) - clif->msgtable_skill(sd, GN_CHANGEMATERIAL, MSG_SKILL_ITEM_NOT_FOUND); - +#if PACKETVER >= 20091013 + if (p == 0) + clif->msgtable_skill(sd, GN_CHANGEMATERIAL, MSG_SKILL_RECIPE_NOTEXIST); +#endif return 0; } + /** * for Royal Guard's LG_TRAMPLE **/ -int skill_destroy_trap(struct block_list *bl, va_list ap) +static int skill_destroy_trap(struct block_list *bl, va_list ap) { struct skill_unit *su = NULL; struct skill_unit_group *sg; @@ -18020,7 +19111,7 @@ int skill_destroy_trap(struct block_list *bl, va_list ap) case UNT_CLAYMORETRAP: case UNT_FIRINGTRAP: case UNT_ICEBOUNDTRAP: - map->foreachinrange(skill->trap_splash,&su->bl, skill->get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag|BL_SKILL|~BCT_SELF, &su->bl,tick); + skill->trap_do_splash(&su->bl, sg->skill_id, sg->skill_lv, sg->bl_flag | BL_SKILL | ~BCT_SELF, tick); break; case UNT_LANDMINE: case UNT_BLASTMINE: @@ -18029,7 +19120,7 @@ int skill_destroy_trap(struct block_list *bl, va_list ap) case UNT_FLASHER: case UNT_FREEZINGTRAP: case UNT_CLUSTERBOMB: - map->foreachinrange(skill->trap_splash,&su->bl, skill->get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &su->bl,tick); + skill->trap_do_splash(&su->bl, sg->skill_id, sg->skill_lv, sg->bl_flag, tick); break; } // Traps aren't recovered. @@ -18037,14 +19128,16 @@ int skill_destroy_trap(struct block_list *bl, va_list ap) } return 0; } + /*========================================== * *------------------------------------------*/ -int skill_blockpc_end(int tid, int64 tick, int id, intptr_t data) { +static int skill_blockpc_end(int tid, int64 tick, int id, intptr_t data) +{ struct map_session_data *sd = map->id2sd(id); struct skill_cd * cd = NULL; - if (data <= 0 || data >= MAX_SKILL) + if (data <= 0 || data >= MAX_SKILL_DB) return 0; if (!sd || !sd->blockskill[data]) return 0; @@ -18092,7 +19185,8 @@ int skill_blockpc_end(int tid, int64 tick, int id, intptr_t data) { * @param tick the length of time the delay should last * @return 0 if successful, -1 otherwise */ -int skill_blockpc_start_(struct map_session_data *sd, uint16 skill_id, int tick) { +static int skill_blockpc_start_(struct map_session_data *sd, uint16 skill_id, int tick) +{ struct skill_cd* cd = NULL; uint16 idx = skill->get_index(skill_id); int64 now = timer->gettick(); @@ -18127,9 +19221,7 @@ int skill_blockpc_start_(struct map_session_data *sd, uint16 skill_id, int tick) if (DIFF_TICK32(cd->entry[i]->started + cd->entry[i]->duration, now) > tick) return 0; cd->entry[i]->duration = tick; -#if PACKETVER >= 20120604 cd->entry[i]->total = tick; -#endif cd->entry[i]->started = now; if( timer->settick(cd->entry[i]->timer,now+tick) != -1 ) return 0; @@ -18161,9 +19253,7 @@ int skill_blockpc_start_(struct map_session_data *sd, uint16 skill_id, int tick) cd->entry[cd->cursor] = ers_alloc(skill->cd_entry_ers,struct skill_cd_entry); cd->entry[cd->cursor]->duration = tick; -#if PACKETVER >= 20120604 cd->entry[cd->cursor]->total = tick; -#endif cd->entry[cd->cursor]->skidx = idx; cd->entry[cd->cursor]->skill_id = skill_id; cd->entry[cd->cursor]->started = now; @@ -18176,10 +19266,10 @@ int skill_blockpc_start_(struct map_session_data *sd, uint16 skill_id, int tick) } // [orn] -int skill_blockhomun_end(int tid, int64 tick, int id, intptr_t data) +static int skill_blockhomun_end(int tid, int64 tick, int id, intptr_t data) { struct homun_data *hd = map->id2hd(id); - if (data <= 0 || data >= MAX_SKILL) + if (data <= 0 || data >= MAX_SKILL_DB) return 0; if (hd != NULL) hd->blockskill[data] = 0; @@ -18187,7 +19277,9 @@ int skill_blockhomun_end(int tid, int64 tick, int id, intptr_t data) return 1; } -int skill_blockhomun_start(struct homun_data *hd, uint16 skill_id, int tick) { // [orn] +// [orn] +static int skill_blockhomun_start(struct homun_data *hd, uint16 skill_id, int tick) +{ uint16 idx = skill->get_index(skill_id); nullpo_retr (-1, hd); @@ -18203,10 +19295,10 @@ int skill_blockhomun_start(struct homun_data *hd, uint16 skill_id, int tick) { / } // [orn] -int skill_blockmerc_end(int tid, int64 tick, int id, intptr_t data) +static int skill_blockmerc_end(int tid, int64 tick, int id, intptr_t data) { struct mercenary_data *md = map->id2mc(id); - if (data <= 0 || data >= MAX_SKILL) + if (data <= 0 || data >= MAX_SKILL_DB) return 0; if (md != NULL) md->blockskill[data] = 0; @@ -18214,7 +19306,7 @@ int skill_blockmerc_end(int tid, int64 tick, int id, intptr_t data) return 1; } -int skill_blockmerc_start(struct mercenary_data *md, uint16 skill_id, int tick) +static int skill_blockmerc_start(struct mercenary_data *md, uint16 skill_id, int tick) { uint16 idx = skill->get_index(skill_id); nullpo_retr (-1, md); @@ -18229,12 +19321,15 @@ int skill_blockmerc_start(struct mercenary_data *md, uint16 skill_id, int tick) md->blockskill[idx] = 1; return timer->add(timer->gettick() + tick, skill->blockmerc_end, md->bl.id, idx); } + /** * Adds a new skill unit entry for this player to recast after map load **/ -void skill_usave_add(struct map_session_data * sd, uint16 skill_id, uint16 skill_lv) { +static void skill_usave_add(struct map_session_data *sd, uint16 skill_id, uint16 skill_lv) +{ struct skill_unit_save * sus = NULL; + nullpo_retv(sd); if( idb_exists(skill->usave_db,sd->status.char_id) ) { idb_remove(skill->usave_db,sd->status.char_id); } @@ -18247,9 +19342,12 @@ void skill_usave_add(struct map_session_data * sd, uint16 skill_id, uint16 skill return; } -void skill_usave_trigger(struct map_session_data *sd) { + +static void skill_usave_trigger(struct map_session_data *sd) +{ struct skill_unit_save * sus = NULL; + nullpo_retv(sd); if( ! (sus = idb_get(skill->usave_db,sd->status.char_id)) ) { return; } @@ -18263,10 +19361,12 @@ void skill_usave_trigger(struct map_session_data *sd) { /* * */ -int skill_split_atoi(char *str, int *val) +static int skill_split_atoi(char *str, int *val) { int i, j, step = 1; + nullpo_ret(val); + for (i=0; i<MAX_SKILL_LEVEL; i++) { if (!str) break; val[i] = atoi(str); @@ -18308,7 +19408,7 @@ int skill_split_atoi(char *str, int *val) /* * */ -void skill_init_unit_layout (void) +static void skill_init_unit_layout(void) { int i,j,pos = 0; @@ -18479,7 +19579,7 @@ void skill_init_unit_layout (void) } break; default: - ShowError("unknown unit layout at skill %d\n",i); + skill->init_unit_layout_unknown(i, pos); break; } if (!skill->dbs->unit_layout[pos].count) @@ -18580,7 +19680,14 @@ void skill_init_unit_layout (void) } -int skill_block_check(struct block_list *bl, sc_type type , uint16 skill_id) { +static void skill_init_unit_layout_unknown(int skill_idx, int pos) +{ + Assert_retv(skill_idx >= 0 && skill_idx < MAX_SKILL_DB); + ShowError("unknown unit layout at skill %d\n", skill->dbs->db[skill_idx].nameid); +} + +static int skill_block_check(struct block_list *bl, sc_type type, uint16 skill_id) +{ int inf = 0; struct status_change *sc = status->get_sc(bl); @@ -18727,7 +19834,8 @@ int skill_block_check(struct block_list *bl, sc_type type , uint16 skill_id) { return 0; } -int skill_get_elemental_type( uint16 skill_id , uint16 skill_lv ) { +static int skill_get_elemental_type(uint16 skill_id, uint16 skill_lv) +{ int type = 0; switch (skill_id) { @@ -18746,25 +19854,26 @@ int skill_get_elemental_type( uint16 skill_id , uint16 skill_lv ) { * update stored skill cooldowns for player logout * @param sd the affected player structure */ -void skill_cooldown_save(struct map_session_data * sd) { +static void skill_cooldown_save(struct map_session_data *sd) +{ int i; - struct skill_cd* cd = NULL; + struct skill_cd *cd = NULL; int64 now = 0; - // always check to make sure the session properly exists nullpo_retv(sd); - if( !(cd = idb_get(skill->cd_db, sd->status.char_id)) ) {// no skill cooldown is associated with this character + if ((cd = idb_get(skill->cd_db, sd->status.char_id)) == NULL) return; - } now = timer->gettick(); - // process each individual cooldown associated with the character - for( i = 0; i < cd->cursor; i++ ) { - cd->entry[i]->duration = DIFF_TICK32(cd->entry[i]->started+cd->entry[i]->duration,now); - if( cd->entry[i]->timer != INVALID_TIMER ) { - timer->delete(cd->entry[i]->timer,skill->blockpc_end); + for (i = 0; i < cd->cursor; i++) { + if (battle_config.guild_skill_relog_delay == 1 && cd->entry[i]->skill_id > GD_SKILLBASE && cd->entry[i]->skill_id < GD_MAX) + continue; + + cd->entry[i]->duration = DIFF_TICK32(cd->entry[i]->started + cd->entry[i]->duration, now); + if (cd->entry[i]->timer != INVALID_TIMER) { + timer->delete(cd->entry[i]->timer, skill->blockpc_end); cd->entry[i]->timer = INVALID_TIMER; } } @@ -18774,7 +19883,8 @@ void skill_cooldown_save(struct map_session_data * sd) { * reload stored skill cooldowns when a player logs in. * @param sd the affected player structure */ -void skill_cooldown_load(struct map_session_data * sd) { +static void skill_cooldown_load(struct map_session_data *sd) +{ int i; struct skill_cd* cd = NULL; int64 now = 0; @@ -18792,231 +19902,28 @@ void skill_cooldown_load(struct map_session_data * sd) { // process each individual cooldown associated with the character for( i = 0; i < cd->cursor; i++ ) { - cd->entry[i]->started = now; - cd->entry[i]->timer = timer->add(timer->gettick()+cd->entry[i]->duration,skill->blockpc_end,sd->bl.id,cd->entry[i]->skidx); - sd->blockskill[cd->entry[i]->skidx] = true; - } -} - -/*========================================== - * sub-function of DB reading. - * skill_db.txt - *------------------------------------------*/ -bool skill_parse_row_skilldb(char* split[], int columns, int current) { -// id,range,hit,inf,element,nk,splash,max,list_num,castcancel,cast_defence_rate,inf2,maxcount,skill_type,blow_count,name,description - uint16 skill_id = atoi(split[0]); - uint16 idx; - if( (skill_id >= GD_SKILLRANGEMIN && skill_id <= GD_SKILLRANGEMAX) - || (skill_id >= HM_SKILLRANGEMIN && skill_id <= HM_SKILLRANGEMAX) - || (skill_id >= MC_SKILLRANGEMIN && skill_id <= MC_SKILLRANGEMAX) - || (skill_id >= EL_SKILLRANGEMIN && skill_id <= EL_SKILLRANGEMAX) ) { - ShowWarning("skill_parse_row_skilldb: Skill id %d is forbidden (interferes with guild/homun/mercenary skill mapping)!\n", skill_id); - return false; - } + int64 remaining; - idx = skill->get_index(skill_id); - if( !idx ) // invalid skill id - return false; - - skill->dbs->db[idx].nameid = skill_id; - skill->split_atoi(split[1],skill->dbs->db[idx].range); - skill->dbs->db[idx].hit = atoi(split[2]); - skill->dbs->db[idx].inf = atoi(split[3]); - skill->split_atoi(split[4],skill->dbs->db[idx].element); - skill->dbs->db[idx].nk = (int)strtol(split[5], NULL, 0); - skill->split_atoi(split[6],skill->dbs->db[idx].splash); - skill->dbs->db[idx].max = atoi(split[7]); - skill->split_atoi(split[8],skill->dbs->db[idx].num); - - if( strcmpi(split[9],"yes") == 0 ) - skill->dbs->db[idx].castcancel = 1; - else - skill->dbs->db[idx].castcancel = 0; - skill->dbs->db[idx].cast_def_rate = atoi(split[10]); - skill->dbs->db[idx].inf2 = (int)strtol(split[11], NULL, 0); - skill->split_atoi(split[12],skill->dbs->db[idx].maxcount); - if( strcmpi(split[13],"weapon") == 0 ) - skill->dbs->db[idx].skill_type = BF_WEAPON; - else if( strcmpi(split[13],"magic") == 0 ) - skill->dbs->db[idx].skill_type = BF_MAGIC; - else if( strcmpi(split[13],"misc") == 0 ) - skill->dbs->db[idx].skill_type = BF_MISC; - else - skill->dbs->db[idx].skill_type = 0; - skill->split_atoi(split[14],skill->dbs->db[idx].blewcount); - safestrncpy(skill->dbs->db[idx].name, trim(split[15]), sizeof(skill->dbs->db[idx].name)); - safestrncpy(skill->dbs->db[idx].desc, trim(split[16]), sizeof(skill->dbs->db[idx].desc)); - strdb_iput(skill->name2id_db, skill->dbs->db[idx].name, skill_id); - script->set_constant2(skill->dbs->db[idx].name, (int)skill_id, false, false); - - return true; -} - -bool skill_parse_row_requiredb(char* split[], int columns, int current) { -// skill_id,HPCost,MaxHPTrigger,SPCost,HPRateCost,SPRateCost,ZenyCost,RequiredWeapons,RequiredAmmoTypes,RequiredAmmoAmount,RequiredState,SpiritSphereCost,RequiredItemID1,RequiredItemAmount1,RequiredItemID2,RequiredItemAmount2,RequiredItemID3,RequiredItemAmount3,RequiredItemID4,RequiredItemAmount4,RequiredItemID5,RequiredItemAmount5,RequiredItemID6,RequiredItemAmount6,RequiredItemID7,RequiredItemAmount7,RequiredItemID8,RequiredItemAmount8,RequiredItemID9,RequiredItemAmount9,RequiredItemID10,RequiredItemAmount10 - char* p; - int j; - - uint16 skill_id = atoi(split[0]); - uint16 idx = skill->get_index(skill_id); - if( !idx ) // invalid skill id - return false; - - skill->split_atoi(split[1],skill->dbs->db[idx].hp); - skill->split_atoi(split[2],skill->dbs->db[idx].mhp); - skill->split_atoi(split[3],skill->dbs->db[idx].sp); - skill->split_atoi(split[4],skill->dbs->db[idx].hp_rate); - skill->split_atoi(split[5],skill->dbs->db[idx].sp_rate); - skill->split_atoi(split[6],skill->dbs->db[idx].zeny); - - //Which weapon type are required, see doc/item_db for types - p = split[7]; - for( j = 0; j < 32; j++ ) { - int l = atoi(p); - if( l == 99 ) { // Any weapon - skill->dbs->db[idx].weapon = 0; - break; - } else - skill->dbs->db[idx].weapon |= 1<<l; - p = strchr(p,':'); - if(!p) - break; - p++; - } - - //FIXME: document this - p = split[8]; - for( j = 0; j < 32; j++ ) { - int l = atoi(p); - if( l == 99 ) { // Any ammo type - skill->dbs->db[idx].ammo = 0xFFFFFFFF; - break; - } else if( l ) // 0 stands for no requirement - skill->dbs->db[idx].ammo |= 1<<l; - p = strchr(p,':'); - if( !p ) - break; - p++; - } - skill->split_atoi(split[9],skill->dbs->db[idx].ammo_qty); - - if( strcmpi(split[10],"hiding") == 0 ) skill->dbs->db[idx].state = ST_HIDING; - else if( strcmpi(split[10],"cloaking") == 0 ) skill->dbs->db[idx].state = ST_CLOAKING; - else if( strcmpi(split[10],"hidden") == 0 ) skill->dbs->db[idx].state = ST_HIDDEN; - else if( strcmpi(split[10],"riding") == 0 ) skill->dbs->db[idx].state = ST_RIDING; - else if( strcmpi(split[10],"falcon") == 0 ) skill->dbs->db[idx].state = ST_FALCON; - else if( strcmpi(split[10],"cart") == 0 ) skill->dbs->db[idx].state = ST_CART; - else if( strcmpi(split[10],"shield") == 0 ) skill->dbs->db[idx].state = ST_SHIELD; - else if( strcmpi(split[10],"sight") == 0 ) skill->dbs->db[idx].state = ST_SIGHT; - else if( strcmpi(split[10],"explosionspirits") == 0 ) skill->dbs->db[idx].state = ST_EXPLOSIONSPIRITS; - else if( strcmpi(split[10],"cartboost") == 0 ) skill->dbs->db[idx].state = ST_CARTBOOST; - else if( strcmpi(split[10],"recover_weight_rate") == 0 ) skill->dbs->db[idx].state = ST_RECOV_WEIGHT_RATE; - else if( strcmpi(split[10],"move_enable") == 0 ) skill->dbs->db[idx].state = ST_MOVE_ENABLE; - else if( strcmpi(split[10],"water") == 0 ) skill->dbs->db[idx].state = ST_WATER; - else if( strcmpi(split[10],"dragon") == 0 ) skill->dbs->db[idx].state = ST_RIDINGDRAGON; - else if( strcmpi(split[10],"warg") == 0 ) skill->dbs->db[idx].state = ST_WUG; - else if( strcmpi(split[10],"ridingwarg") == 0 ) skill->dbs->db[idx].state = ST_RIDINGWUG; - else if( strcmpi(split[10],"mado") == 0 ) skill->dbs->db[idx].state = ST_MADO; - else if( strcmpi(split[10],"elementalspirit") == 0 ) skill->dbs->db[idx].state = ST_ELEMENTALSPIRIT; - else if( strcmpi(split[10],"poisonweapon") == 0 ) skill->dbs->db[idx].state = ST_POISONINGWEAPON; - else if( strcmpi(split[10],"rollingcutter") == 0 ) skill->dbs->db[idx].state = ST_ROLLINGCUTTER; - else if( strcmpi(split[10],"mh_fighting") == 0 ) skill->dbs->db[idx].state = ST_MH_FIGHTING; - else if( strcmpi(split[10],"mh_grappling") == 0 ) skill->dbs->db[idx].state = ST_MH_GRAPPLING; - else if( strcmpi(split[10],"peco") == 0 ) skill->dbs->db[idx].state = ST_PECO; - /** - * Unknown or no state - **/ - else skill->dbs->db[idx].state = ST_NONE; - - skill->split_atoi(split[11],skill->dbs->db[idx].spiritball); - for( j = 0; j < MAX_SKILL_ITEM_REQUIRE; j++ ) { - skill->dbs->db[idx].itemid[j] = atoi(split[12+ 2*j]); - skill->dbs->db[idx].amount[j] = atoi(split[13+ 2*j]); + if (battle_config.guild_skill_relog_delay == 2 && cd->entry[i]->skill_id >= GD_SKILLBASE && cd->entry[i]->skill_id < GD_MAX) { + remaining = cd->entry[i]->started + cd->entry[i]->total - now; + remaining = max(1, remaining); // expired cooldowns will be 1, so they'll expire in the normal way just after this. + } else { + cd->entry[i]->started = now; + remaining = cd->entry[i]->duration; + } + cd->entry[i]->timer = timer->add(timer->gettick() + remaining, skill->blockpc_end, sd->bl.id, cd->entry[i]->skidx); + sd->blockskill[cd->entry[i]->skidx] = true; } - - return true; -} - -bool skill_parse_row_castdb(char* split[], int columns, int current) { -// skill_id,CastingTime,AfterCastActDelay,AfterCastWalkDelay,Duration1,Duration2 - uint16 skill_id = atoi(split[0]); - uint16 idx = skill->get_index(skill_id); - if( !idx ) // invalid skill id - return false; - - skill->split_atoi(split[1],skill->dbs->db[idx].cast); - skill->split_atoi(split[2],skill->dbs->db[idx].delay); - skill->split_atoi(split[3],skill->dbs->db[idx].walkdelay); - skill->split_atoi(split[4],skill->dbs->db[idx].upkeep_time); - skill->split_atoi(split[5],skill->dbs->db[idx].upkeep_time2); - skill->split_atoi(split[6],skill->dbs->db[idx].cooldown); -#ifdef RENEWAL_CAST - skill->split_atoi(split[7],skill->dbs->db[idx].fixed_cast); -#endif - return true; -} - -bool skill_parse_row_castnodexdb(char* split[], int columns, int current) { -// Skill id,Cast,Delay (optional) - uint16 skill_id = atoi(split[0]); - uint16 idx = skill->get_index(skill_id); - if( !idx ) // invalid skill id - return false; - - skill->split_atoi(split[1],skill->dbs->db[idx].castnodex); - if( split[2] ) // optional column - skill->split_atoi(split[2],skill->dbs->db[idx].delaynodex); - - return true; -} - -bool skill_parse_row_unitdb(char* split[], int columns, int current) { -// ID,unit ID,unit ID 2,layout,range,interval,target,flag - uint16 skill_id = atoi(split[0]); - uint16 idx = skill->get_index(skill_id); - if( !idx ) // invalid skill id - return false; - - skill->dbs->db[idx].unit_id[0] = (int)strtol(split[1],NULL,16); - skill->dbs->db[idx].unit_id[1] = (int)strtol(split[2],NULL,16); - skill->split_atoi(split[3],skill->dbs->db[idx].unit_layout_type); - skill->split_atoi(split[4],skill->dbs->db[idx].unit_range); - skill->dbs->db[idx].unit_interval = atoi(split[5]); - - if( strcmpi(split[6],"noenemy")==0 ) skill->dbs->db[idx].unit_target = BCT_NOENEMY; - else if( strcmpi(split[6],"friend")==0 ) skill->dbs->db[idx].unit_target = BCT_NOENEMY; - else if( strcmpi(split[6],"party")==0 ) skill->dbs->db[idx].unit_target = BCT_PARTY; - else if( strcmpi(split[6],"ally")==0 ) skill->dbs->db[idx].unit_target = BCT_PARTY|BCT_GUILD; - else if( strcmpi(split[6],"guild")==0 ) skill->dbs->db[idx].unit_target = BCT_GUILD; - else if( strcmpi(split[6],"all")==0 ) skill->dbs->db[idx].unit_target = BCT_ALL; - else if( strcmpi(split[6],"enemy")==0 ) skill->dbs->db[idx].unit_target = BCT_ENEMY; - else if( strcmpi(split[6],"self")==0 ) skill->dbs->db[idx].unit_target = BCT_SELF; - else if( strcmpi(split[6],"sameguild")==0 ) skill->dbs->db[idx].unit_target = BCT_GUILD|BCT_SAMEGUILD; - else if( strcmpi(split[6],"noone")==0 ) skill->dbs->db[idx].unit_target = BCT_NOONE; - else skill->dbs->db[idx].unit_target = (int)strtol(split[6],NULL,16); - - skill->dbs->db[idx].unit_flag = (int)strtol(split[7],NULL,16); - - if (skill->dbs->db[idx].unit_flag&UF_DEFNOTENEMY && battle_config.defnotenemy) - skill->dbs->db[idx].unit_target = BCT_NOENEMY; - - //By default, target just characters. - skill->dbs->db[idx].unit_target |= BL_CHAR; - if (skill->dbs->db[idx].unit_flag&UF_NOPC) - skill->dbs->db[idx].unit_target &= ~BL_PC; - if (skill->dbs->db[idx].unit_flag&UF_NOMOB) - skill->dbs->db[idx].unit_target &= ~BL_MOB; - if (skill->dbs->db[idx].unit_flag&UF_SKILL) - skill->dbs->db[idx].unit_target |= BL_SKILL; - - return true; } -bool skill_parse_row_producedb(char* split[], int columns, int current) { +static bool skill_parse_row_producedb(char *split[], int columns, int current) +{ // ProduceItemID,ItemLV,RequireSkill,Requireskill_lv,MaterialID1,MaterialAmount1,...... int x,y; + int i; - int i = atoi(split[0]); + nullpo_retr(false, split); + i = atoi(split[0]); if( !i ) return false; @@ -19033,11 +19940,14 @@ bool skill_parse_row_producedb(char* split[], int columns, int current) { return true; } -bool skill_parse_row_createarrowdb(char* split[], int columns, int current) { +static bool skill_parse_row_createarrowdb(char *split[], int columns, int current) +{ // SourceID,MakeID1,MakeAmount1,...,MakeID5,MakeAmount5 int x,y; - int i = atoi(split[0]); + int i; + nullpo_retr(false, split); + i = atoi(split[0]); if( !i ) return false; @@ -19050,12 +19960,19 @@ bool skill_parse_row_createarrowdb(char* split[], int columns, int current) { return true; } -bool skill_parse_row_spellbookdb(char* split[], int columns, int current) { + +static bool skill_parse_row_spellbookdb(char *split[], int columns, int current) +{ // skill_id,PreservePoints - uint16 skill_id = atoi(split[0]); - int points = atoi(split[1]); - int nameid = atoi(split[2]); + uint16 skill_id; + int points; + int nameid; + + nullpo_retr(false, split); + skill_id = atoi(split[0]); + points = atoi(split[1]); + nameid = atoi(split[2]); if( !skill->get_index(skill_id) || !skill->get_max(skill_id) ) ShowError("spellbook_db: Invalid skill ID %d\n", skill_id); @@ -19073,10 +19990,16 @@ bool skill_parse_row_spellbookdb(char* split[], int columns, int current) { return false; } -bool skill_parse_row_improvisedb(char* split[], int columns, int current) { + +static bool skill_parse_row_improvisedb(char *split[], int columns, int current) +{ // SkillID,Rate - uint16 skill_id = atoi(split[0]); - short j = atoi(split[1]); + uint16 skill_id; + short j; + + nullpo_retr(false, split); + skill_id = atoi(split[0]); + j = atoi(split[1]); if( !skill->get_index(skill_id) || !skill->get_max(skill_id) ) { ShowError("skill_improvise_db: Invalid skill ID %d\n", skill_id); @@ -19099,10 +20022,14 @@ bool skill_parse_row_improvisedb(char* split[], int columns, int current) { return true; } -bool skill_parse_row_magicmushroomdb(char* split[], int column, int current) { + +static bool skill_parse_row_magicmushroomdb(char *split[], int column, int current) +{ // SkillID - uint16 skill_id = atoi(split[0]); + uint16 skill_id; + nullpo_retr(false, split); + skill_id = atoi(split[0]); if( !skill->get_index(skill_id) || !skill->get_max(skill_id) ) { ShowError("magicmushroom_db: Invalid skill ID %d\n", skill_id); return false; @@ -19117,20 +20044,12 @@ bool skill_parse_row_magicmushroomdb(char* split[], int column, int current) { return true; } -bool skill_parse_row_reproducedb(char* split[], int column, int current) { - uint16 skill_id = atoi(split[0]); - uint16 idx = skill->get_index(skill_id); - if( !idx ) - return false; - - skill->dbs->reproduce_db[idx] = true; - - return true; -} - -bool skill_parse_row_abradb(char* split[], int columns, int current) { +static bool skill_parse_row_abradb(char *split[], int columns, int current) +{ // skill_id,DummyName,RequiredHocusPocusLevel,Rate - uint16 skill_id = atoi(split[0]); + uint16 skill_id; + nullpo_retr(false, split); + skill_id = atoi(split[0]); if( !skill->get_index(skill_id) || !skill->get_max(skill_id) ) { ShowError("abra_db: Invalid skill ID %d\n", skill_id); return false; @@ -19147,12 +20066,16 @@ bool skill_parse_row_abradb(char* split[], int columns, int current) { return true; } -bool skill_parse_row_changematerialdb(char* split[], int columns, int current) { +static bool skill_parse_row_changematerialdb(char *split[], int columns, int current) +{ // ProductID,BaseRate,MakeAmount1,MakeAmountRate1...,MakeAmount5,MakeAmountRate5 - uint16 skill_id = atoi(split[0]); - short j = atoi(split[1]); + int skill_id; + short j; int x,y; + nullpo_retr(false, split); + skill_id = atoi(split[0]); + j = atoi(split[1]); for(x=0; x<MAX_SKILL_PRODUCE_DB; x++){ if( skill->dbs->produce_db[x].nameid == skill_id ) if( skill->dbs->produce_db[x].req_skill == GN_CHANGEMATERIAL ) @@ -19180,19 +20103,1348 @@ bool skill_parse_row_changematerialdb(char* split[], int columns, int current) { return true; } +#define skilldb_duplicate_warning(name, setting, skill) (ShowError("skill_read_skilldb: Duplicate entry '%s' in setting '%s' for Skill Id %d in '%s', skipping...\n", name, setting, skill, "db/"DBPATH"skill_db.conf")) +#define skilldb_invalid_error(name, setting, skill) (ShowError("skill_read_skilldb: Invalid entry '%s' in setting '%s' for Skill Id %d in '%s', skipping...\n", name, setting, skill, "db/"DBPATH"skill_db.conf")) + +/** + * Sets Level based configuration for skill groups from skill_db.conf [ Smokexyz/Hercules ] + * @param *conf pointer to config setting. + * @param *arr pointer to array to be set. + */ +static void skill_config_set_level(struct config_setting_t *conf, int *arr) +{ + int i=0; + + nullpo_retv(arr); + if (config_setting_is_group(conf)) { + for (i=0; i<MAX_SKILL_LEVEL; i++) { + char level[6]; // enough to contain "Lv100" in case of custom MAX_SKILL_LEVEL + sprintf(level, "Lv%d", i+1); + libconfig->setting_lookup_int(conf, level, &arr[i]); + } + } else if (config_setting_is_array(conf)) { + for (i=0; i<config_setting_length(conf) && i < MAX_SKILL_LEVEL; i++) { + arr[i] = libconfig->setting_get_int_elem(conf, i); + } + } else { + int val=libconfig->setting_get_int(conf); + for(i=0; i<MAX_SKILL_LEVEL; i++) { + arr[i] = val; + } + } +} + +/** + * Sets all values in a skill level array to a specified value [ Smokexyz/Hercules ] + * @param *arr pointer to array being parsed. + * @param value value being set for the array. + * @return (void) + */ +static void skill_level_set_value(int *arr, int value) +{ + int i=0; + + nullpo_retv(arr); + for(i=0; i<MAX_SKILL_LEVEL; i++) { + arr[i] = value; + } +} + +static void skill_validate_hittype(struct config_setting_t *conf, struct s_skill_db *sk) +{ + const char *type = NULL; + + nullpo_retv(sk); + if (libconfig->setting_lookup_string(conf, "Hit", &type)) { + if (strcmpi(type, "BDT_SKILL") == 0) { + sk->hit = BDT_SKILL; + } else if (strcmpi(type, "BDT_MULTIHIT") == 0) { + sk->hit = BDT_MULTIHIT; + } else if (strcmpi(type, "BDT_NORMAL") == 0) { + sk->hit = BDT_NORMAL; + } else { + skilldb_invalid_error(type, "Hit", sk->nameid); + return; + } + } +} + +/** + * Validates "SkillType" when reading skill_db.conf + * @param conf struct, pointer to skill configuration + * @param sk struct, pointer to s_skill_db + * @return (void) + */ +static void skill_validate_skilltype(struct config_setting_t *conf, struct s_skill_db *sk) +{ + struct config_setting_t *t = NULL, *tt = NULL; + + nullpo_retv(sk); + if((t=libconfig->setting_get_member(conf, "SkillType")) && config_setting_is_group(t)) { + int j=0; + while ((tt = libconfig->setting_get_elem(t, j++))) { + const char *type = config_setting_name(tt); + bool on = libconfig->setting_get_bool_real(tt); + + if (strcmpi(type, "Enemy") == 0) { + if (on) { + sk->inf |= INF_ATTACK_SKILL; + } else { + sk->inf &= ~INF_ATTACK_SKILL; + } + } else if (strcmpi(type, "Place") == 0) { + if (on) { + sk->inf |= INF_GROUND_SKILL; + } else { + sk->inf &= ~INF_GROUND_SKILL; + } + } else if (strcmpi(type, "Self") == 0) { + if (on) { + sk->inf |= INF_SELF_SKILL; + } else { + sk->inf &= ~INF_SELF_SKILL; + } + } else if (strcmpi(type, "Friend") == 0) { + if (on) { + sk->inf |= INF_SUPPORT_SKILL; + } else { + sk->inf &= ~INF_SUPPORT_SKILL; + } + } else if (strcmpi(type, "Trap") == 0) { + if (on) { + sk->inf |= INF_TARGET_TRAP; + } else { + sk->inf &= ~INF_TARGET_TRAP; + } + } else if (strcmpi(type, "Passive") != 0) { + skilldb_invalid_error(type, config_setting_name(t), sk->nameid); + } + } + } +} + +/** + * Validates "SkillInfo" when reading skill_db.conf + * @param conf struct, pointer to skill configuration + * @param sk struct, pointer to s_skill_db + * @return (void) + */ +static void skill_validate_skillinfo(struct config_setting_t *conf, struct s_skill_db *sk) +{ + struct config_setting_t *t = NULL, *tt = NULL; + + nullpo_retv(sk); + if ((t=libconfig->setting_get_member(conf, "SkillInfo")) && config_setting_is_group(t)) { + int j=0; + while ((tt = libconfig->setting_get_elem(t, j++))) { + const char *type = config_setting_name(tt); + bool on = libconfig->setting_get_bool_real(tt); + + if (strcmpi(type, "Quest") == 0) { + if (on) { + sk->inf2 |= INF2_QUEST_SKILL; + } else { + sk->inf2 &= ~INF2_QUEST_SKILL; + } + } else if (strcmpi(type, "NPC") == 0) { + if (on) { + sk->inf2 |= INF2_NPC_SKILL; + } else { + sk->inf2 &= ~INF2_NPC_SKILL; + } + } else if (strcmpi(type, "Wedding") == 0) { + if (on) { + sk->inf2 |= INF2_WEDDING_SKILL; + } else { + sk->inf2 &= ~INF2_WEDDING_SKILL; + } + } else if (strcmpi(type, "Spirit") == 0) { + if (on) { + sk->inf2 |= INF2_SPIRIT_SKILL; + } else { + sk->inf2 &= ~INF2_SPIRIT_SKILL; + } + } else if (strcmpi(type, "Guild") == 0) { + if (on) { + sk->inf2 |= INF2_GUILD_SKILL; + } else { + sk->inf2 &= ~INF2_GUILD_SKILL; + } + } else if (strcmpi(type, "Song") == 0) { + if (on) { + sk->inf2 |= INF2_SONG_DANCE; + } else { + sk->inf2 &= ~INF2_SONG_DANCE; + } + } else if (strcmpi(type, "Ensemble") == 0) { + if (on) { + sk->inf2 |= INF2_ENSEMBLE_SKILL; + } else { + sk->inf2 &= ~INF2_ENSEMBLE_SKILL; + } + } else if (strcmpi(type, "Trap") == 0) { + if (on) { + sk->inf2 |= INF2_TRAP; + } else { + sk->inf2 &= ~INF2_TRAP; + } + } else if (strcmpi(type, "TargetSelf") == 0) { + if (on) { + sk->inf2 |= INF2_TARGET_SELF; + } else { + sk->inf2 &= ~INF2_TARGET_SELF; + } + } else if (strcmpi(type, "NoCastSelf") == 0) { + if (on) { + sk->inf2 |= INF2_NO_TARGET_SELF; + } else { + sk->inf2 &= ~INF2_NO_TARGET_SELF; + } + } else if (strcmpi(type, "PartyOnly") == 0) { + if (on) { + sk->inf2 |= INF2_PARTY_ONLY; + } else { + sk->inf2 &= ~INF2_PARTY_ONLY; + } + } else if (strcmpi(type, "GuildOnly") == 0) { + if (on) { + sk->inf2 |= INF2_GUILD_ONLY; + } else { + sk->inf2 &= ~INF2_GUILD_ONLY; + } + } else if (strcmpi(type, "NoEnemy") == 0) { + if (on) { + sk->inf2 |= INF2_NO_ENEMY; + } else { + sk->inf2 &= ~INF2_NO_ENEMY; + } + } else if (strcmpi(type, "IgnoreLandProtector") == 0) { + if (on) { + sk->inf2 |= INF2_NOLP; + } else { + sk->inf2 &= ~INF2_NOLP; + } + } else if (strcmpi(type, "Chorus") == 0) { + if (on) { + sk->inf2 |= INF2_CHORUS_SKILL; + } else { + sk->inf2 &= ~INF2_CHORUS_SKILL; + } + } else if (strcmpi(type, "FreeCastNormal") == 0) { + if (on) { + sk->inf2 |= INF2_FREE_CAST_NORMAL; + } else { + sk->inf2 &= ~INF2_FREE_CAST_NORMAL; + } + } else if (strcmpi(type, "FreeCastReduced") == 0) { + if (on) { + sk->inf2 |= INF2_FREE_CAST_REDUCED; + } else { + sk->inf2 &= ~INF2_FREE_CAST_REDUCED; + } + } else if (strcmpi(type, "ShowSkillScale") == 0) { + if (on) { + sk->inf2 |= INF2_SHOW_SKILL_SCALE; + } else { + sk->inf2 &= ~INF2_SHOW_SKILL_SCALE; + } + } else if (strcmpi(type, "AllowReproduce") == 0) { + if (on) { + sk->inf2 |= INF2_ALLOW_REPRODUCE; + } else { + sk->inf2 &= ~INF2_ALLOW_REPRODUCE; + } + } else if (strcmpi(type, "HiddenTrap") == 0) { + if (on) { + sk->inf2 |= INF2_HIDDEN_TRAP; + } else { + sk->inf2 &= ~INF2_HIDDEN_TRAP; + } + } else if (strcmpi(type, "IsCombo") == 0) { + if (on) { + sk->inf2 |= INF2_IS_COMBO_SKILL; + } else { + sk->inf2 &= ~INF2_IS_COMBO_SKILL; + } + } else if (strcmpi(type, "None") != 0) { + skilldb_invalid_error(type, config_setting_name(t), sk->nameid); + } + } + } +} + +/** + * Validates "AttackType" when reading skill_db.conf + * @param conf struct, pointer to skill configuration + * @param sk struct, pointer to s_skill_db + * @return (void) + */ +static void skill_validate_attacktype(struct config_setting_t *conf, struct s_skill_db *sk) +{ + const char *type = NULL; + + nullpo_retv(sk); + if (libconfig->setting_lookup_string(conf, "AttackType", &type)) { + if (!strcmpi(type, "Weapon")) { + sk->skill_type = BF_WEAPON; + } else if (!strcmpi(type, "Magic")) { + sk->skill_type = BF_MAGIC; + } else if (!strcmpi(type, "Misc")) { + sk->skill_type = BF_MISC; + } else { + skilldb_invalid_error(type, "AttackType", sk->nameid); + return; + } + } +} + +/** + * Validates "Element" when reading skill_db.conf + * @param ele_t struct, pointer to skill configuration + * @param sk struct, pointer to s_skill_db + * @return (void) + */ +static void skill_validate_element(struct config_setting_t *conf, struct s_skill_db *sk) +{ + const char *type = NULL; + struct config_setting_t *t = NULL; + + nullpo_retv(sk); + if ((t=libconfig->setting_get_member(conf, "Element")) && config_setting_is_group(t)) { + int j = 0; + char lv[6]; // enough to contain "Lv100" in case of custom MAX_SKILL_LEVEL + + for (j=0; j < MAX_SKILL_LEVEL; j++) { + sprintf(lv, "Lv%d",j+1); + if (libconfig->setting_lookup_string(t, lv, &type)) { + if (strcmpi(type,"Ele_Weapon") == 0) + sk->element[j] = -1; + else if (strcmpi(type,"Ele_Endowed") == 0) + sk->element[j] = -2; + else if (strcmpi(type,"Ele_Random") == 0) + sk->element[j] = -3; + else if (!script->get_constant(type,&sk->element[j])) + skilldb_invalid_error(type, config_setting_name(conf), sk->nameid); + } + } + + } else if (libconfig->setting_lookup_string(conf, "Element", &type)) { + int ele = 0; + + if (strcmpi(type,"Ele_Weapon") == 0) + ele = -1; + else if (strcmpi(type,"Ele_Endowed") == 0) + ele = -2; + else if (strcmpi(type,"Ele_Random") == 0) + ele = -3; + else if (!script->get_constant(type, &ele)) { + skilldb_invalid_error(type, config_setting_name(conf), sk->nameid); + return; + } + + skill->level_set_value(sk->element, ele); + } +} + +/** + * Validates "DamageType" when reading skill_db.conf + * @param conf struct, pointer to skill configuration + * @param sk struct, pointer to s_skill_db + * @return (void) + */ +static void skill_validate_damagetype(struct config_setting_t *conf, struct s_skill_db *sk) +{ + struct config_setting_t *t = NULL, *tt = NULL; + + nullpo_retv(sk); + if ((t=libconfig->setting_get_member(conf, "DamageType")) && config_setting_is_group(t)) { + int j=0; + while ((tt = libconfig->setting_get_elem(t, j++))) { + const char *type = config_setting_name(tt); + bool on = libconfig->setting_get_bool_real(tt); + + if (strcmpi(type, "NoDamage") == 0) { + if (on) { + sk->nk |= NK_NO_DAMAGE; + } else { + sk->nk &= ~NK_NO_DAMAGE; + } + } else if (strcmpi(type, "SplashArea") == 0) { + if (on) { + sk->nk |= NK_SPLASH_ONLY; + } else { + sk->nk &= ~NK_SPLASH_ONLY; + } + } else if (strcmpi(type, "SplitDamage") == 0) { + if (on) { + sk->nk |= NK_SPLASHSPLIT; + } else { + sk->nk &= ~NK_SPLASHSPLIT; + } + } else if (strcmpi(type, "IgnoreCards") == 0) { + if (on) { + sk->nk |= NK_NO_CARDFIX_ATK; + } else { + sk->nk &= ~NK_NO_CARDFIX_ATK; + } + } else if (strcmpi(type, "IgnoreElement") == 0) { + if (on) { + sk->nk |= NK_NO_ELEFIX; + } else { + sk->nk &= ~NK_NO_ELEFIX; + } + } else if (strcmpi(type, "IgnoreDefense") == 0) { + if (on) { + sk->nk |= NK_IGNORE_DEF; + } else { + sk->nk &= ~NK_IGNORE_DEF; + } + } else if (strcmpi(type, "IgnoreFlee") == 0) { + if (on) { + sk->nk |= NK_IGNORE_FLEE; + } else { + sk->nk &= ~NK_IGNORE_FLEE; + } + } else if (strcmpi(type, "IgnoreDefCards") == 0) { + if (on) { + sk->nk |= NK_NO_CARDFIX_DEF; + } else { + sk->nk &= ~NK_NO_CARDFIX_DEF; + } + } else { + skilldb_invalid_error(type, config_setting_name(t), sk->nameid); + } + } + } +} + +/** + * Validates "SkillCast/DelayOptions" when reading skill_db.conf + * @param conf struct, pointer to skill configuration + * @param sk struct, pointer to s_skill_db + * @param delay boolean, switch for cast/delay setting + * @return (void) + */ +static void skill_validate_castnodex(struct config_setting_t *conf, struct s_skill_db *sk, bool delay) +{ + struct config_setting_t *t = NULL, *tt = NULL; + + nullpo_retv(sk); + if ((t=libconfig->setting_get_member(conf, delay?"SkillDelayOptions":"CastTimeOptions")) && config_setting_is_group(t)) { + int j = 0, tmpopt = 0; + while ((tt = libconfig->setting_get_elem(t, j++)) && j < 4) { + const char *type = config_setting_name(tt); + bool on = libconfig->setting_get_bool_real(tt); + + if (strcmpi(type, "IgnoreDex") == 0) { + if (on) { + tmpopt |= 1<<0; + } else { + tmpopt &= ~(1<<0); + } + } else if (strcmpi(type, "IgnoreStatusEffect") == 0) { + if (on) { + tmpopt |= 1<<1; + } else { + tmpopt &= ~(1<<1); + } + } else if (strcmpi(type, "IgnoreItemBonus") == 0) { + if (on) { + tmpopt |= 1<<2; + } else { + tmpopt &= ~(1<<2); + } + } else { + skilldb_invalid_error(type, config_setting_name(t), sk->nameid); + return; + } + + } + skill->level_set_value(delay?sk->delaynodex:sk->castnodex, tmpopt); + } +} + +/** + * Validates the "WeaponTypes" flag + * when parsing skill_db.conf + * @param *type const char, weapon type flag + * @param on boolean, switch for the flag + * @param *sk struct, pointer to s_skill_db + * @return void + */ +static int skill_validate_weapontype_sub(const char *type, bool on, struct s_skill_db *sk) +{ + nullpo_ret(sk); + if (strcmpi(type, "NoWeapon") == 0) { + if (on) { + sk->weapon |= 1<<W_FIST; + } else { + sk->weapon &= ~(1<<W_FIST); + } + } else if (strcmpi(type, "Daggers") == 0) { + if (on) { + sk->weapon |= 1<<W_DAGGER; + } else { + sk->weapon &= ~(1<<W_DAGGER); + } + } else if (strcmpi(type, "1HSwords") == 0) { + + if (on) { + sk->weapon |= 1<<W_1HSWORD; + } else { + sk->weapon &= ~(1<<W_1HSWORD); + } + } else if (strcmpi(type, "2HSwords") == 0) { + if (on) { + sk->weapon |= 1<<W_2HSWORD; + } else { + sk->weapon &= ~(1<<W_2HSWORD); + } + } else if (strcmpi(type, "1HSpears") == 0) { + if (on) { + sk->weapon |= 1<<W_1HSPEAR; + } else { + sk->weapon &= ~(1<<W_1HSPEAR); + } + } else if (strcmpi(type, "2HSpears") == 0) { + if (on) { + sk->weapon |= 1<<W_2HSPEAR; + } else { + sk->weapon &= ~(1<<W_2HSPEAR); + } + } else if (strcmpi(type, "1HAxes") == 0) { + if (on) { + sk->weapon |= 1<<W_1HAXE; + } else { + sk->weapon &= ~(1<<W_1HAXE); + } + } else if (strcmpi(type, "2HAxes") == 0) { + if (on) { + sk->weapon |= 1<<W_2HAXE; + } else { + sk->weapon &= ~(1<<W_2HAXE); + } + } else if (strcmpi(type, "Maces") == 0) { + if (on) { + sk->weapon |= 1<<W_MACE; + } else { + sk->weapon &= ~(1<<W_MACE); + } + } else if (strcmpi(type, "2HMaces") == 0) { + if (on) { + sk->weapon |= 1<<W_2HMACE; + } else { + sk->weapon &= ~(1<<W_2HMACE); + } + } else if (strcmpi(type, "Staves") == 0) { + if (on) { + sk->weapon |= 1<<W_STAFF; + } else { + sk->weapon &= ~(1<<W_STAFF); + } + } else if (strcmpi(type, "Bows") == 0) { + if (on) { + sk->weapon |= 1<<W_BOW; + } else { + sk->weapon &= ~(1<<W_BOW); + } + } else if (strcmpi(type, "Knuckles") == 0) { + if (on) { + sk->weapon |= 1<<W_KNUCKLE; + } else { + sk->weapon &= ~(1<<W_KNUCKLE); + } + } else if (strcmpi(type, "Instruments") == 0) { + if (on) { + sk->weapon |= 1<<W_MUSICAL; + } else { + sk->weapon &= ~(1<<W_MUSICAL); + } + } else if (strcmpi(type, "Whips") == 0) { + if (on) { + sk->weapon |= 1<<W_WHIP; + } else { + sk->weapon &= ~(1<<W_WHIP); + } + } else if (strcmpi(type, "Books") == 0) { + if (on) { + sk->weapon |= 1<<W_BOOK; + } else { + sk->weapon &= ~(1<<W_BOOK); + } + } else if (strcmpi(type, "Katars") == 0) { + if (on) { + sk->weapon |= 1<<W_KATAR; + } else { + sk->weapon &= ~(1<<W_KATAR); + } + } else if (strcmpi(type, "Revolvers") == 0) { + if (on) { + sk->weapon |= 1<<W_REVOLVER; + } else { + sk->weapon &= ~(1<<W_REVOLVER); + } + } else if (strcmpi(type, "Rifles") == 0) { + if (on) { + sk->weapon |= 1<<W_RIFLE; + } else { + sk->weapon &= ~(1<<W_RIFLE); + } + } else if (strcmpi(type, "GatlingGuns") == 0) { + if (on) { + sk->weapon |= 1<<W_GATLING; + } else { + sk->weapon &= ~(1<<W_GATLING); + } + } else if (strcmpi(type, "Shotguns") == 0) { + if (on) { + sk->weapon |= 1<<W_SHOTGUN; + } else { + sk->weapon &= ~(1<<W_SHOTGUN); + } + } else if (strcmpi(type, "GrenadeLaunchers") == 0) { + if (on) { + sk->weapon |= 1<<W_GRENADE; + } else { + sk->weapon &= ~(1<<W_GRENADE); + } + } else if (strcmpi(type, "FuumaShurikens") == 0) { + if (on) { + sk->weapon |= 1<<W_HUUMA; + } else { + sk->weapon &= ~(1<<W_HUUMA); + } + } else if (strcmpi(type, "2HStaves") == 0) { + if (on) { + sk->weapon |= 1<<W_2HSTAFF; + } else { + sk->weapon &= ~(1<<W_2HSTAFF); + } + } + /* MAX_SINGLE_WEAPON_TYPE excluded */ + else if (strcmpi(type, "DWDaggers") == 0) { + if (on) { + sk->weapon |= 1<<W_DOUBLE_DD; + } else { + sk->weapon &= ~(1<<W_DOUBLE_DD); + } + } else if (strcmpi(type, "DWSwords") == 0) { + if (on) { + sk->weapon |= 1<<W_DOUBLE_SS; + } else { + sk->weapon &= ~(1<<W_DOUBLE_SS); + } + } else if (strcmpi(type, "DWAxes") == 0) { + if (on) { + sk->weapon |= 1<<W_DOUBLE_AA; + } else { + sk->weapon &= ~(1<<W_DOUBLE_AA); + } + } else if (strcmpi(type, "DWDaggerSword") == 0) { + if (on) { + sk->weapon |= 1<<W_DOUBLE_DS; + } else { + sk->weapon &= ~(1<<W_DOUBLE_DS); + } + } else if (strcmpi(type, "DWDaggerAxe") == 0) { + if (on) { + sk->weapon |= 1<<W_DOUBLE_DA; + } else { + sk->weapon &= ~(1<<W_DOUBLE_DA); + } + } else if (strcmpi(type, "DWSwordAxe") == 0) { + if (on) { + sk->weapon |= 1<<W_DOUBLE_SA; + } else { + sk->weapon &= ~(1<<W_DOUBLE_SA); + } + } else if (strcmpi(type, "All") == 0) { + sk->weapon = 0; + } else { + ShowError("Item %d. Unknown weapon type %s\n", sk->nameid, type); + return 1; // invalid type + } + + return 0; +} + +/** + * Validates "WeaponTypes" + * when parsing skill_db.conf + * @param conf struct, pointer to the skill configuration + * @param sk struct, struct, pointer to s_skill_db + * @return (void) + */ +static void skill_validate_weapontype(struct config_setting_t *conf, struct s_skill_db *sk) +{ + struct config_setting_t *tt = NULL; + const char *type = NULL; + + nullpo_retv(sk); + if ((tt = libconfig->setting_get_member(conf, "WeaponTypes")) && config_setting_is_group(tt)) { + int j = 0; + struct config_setting_t *wpt = NULL; + while ((wpt = libconfig->setting_get_elem(tt, j++)) != NULL) { + if (skill->validate_weapontype_sub(config_setting_name(wpt), libconfig->setting_get_bool_real(wpt), sk)) + skilldb_invalid_error(config_setting_name(wpt), config_setting_name(tt), sk->nameid); + } + } else if (libconfig->setting_lookup_string(conf, "WeaponTypes", &type)) { + if (skill->validate_weapontype_sub(type, true, sk)) + skilldb_invalid_error(type, "WeaponTypes", sk->nameid); + } +} + +/** + * Validates the "AmmoTypes" flag + * when parsing skill_db.conf + * @param type string, ammo type flag + * @param on boolean, switch for the flag + * @param sk struct, pointer to s_skill_db + * @return void + */ +static int skill_validate_ammotype_sub(const char *type, bool on, struct s_skill_db *sk) +{ + nullpo_ret(sk); + if (strcmpi(type, "A_ARROW") == 0) { + if (on) { + sk->ammo |= 1<<A_ARROW; + } else { + sk->ammo &= ~(1<<A_ARROW); + } + } else if (strcmpi(type, "A_DAGGER") == 0) { + if (on) { + sk->ammo |= 1<<A_DAGGER; + } else { + sk->ammo &= ~(1<<A_DAGGER); + } + } else if (strcmpi(type, "A_BULLET") == 0) { + if (on) { + sk->ammo |= 1<<A_BULLET; + } else { + sk->ammo &= ~(1<<A_BULLET); + } + } else if (strcmpi(type, "A_SHELL") == 0) { + if (on) { + sk->ammo |= 1<<A_SHELL; + } else { + sk->ammo &= ~(1<<A_SHELL); + } + } else if (strcmpi(type, "A_GRENADE") == 0) { + if (on) { + sk->ammo |= 1<<A_GRENADE; + } else { + sk->ammo &= ~(1<<A_GRENADE); + } + } else if (strcmpi(type, "A_SHURIKEN") == 0) { + if (on) { + sk->ammo |= 1<<A_SHURIKEN; + } else { + sk->ammo &= ~(1<<A_SHURIKEN); + } + } else if (strcmpi(type, "A_KUNAI") == 0) { + if (on) { + sk->ammo |= 1<<A_KUNAI; + } else { + sk->ammo &= ~(1<<A_KUNAI); + } + } else if (strcmpi(type, "A_CANNONBALL") == 0) { + if (on) { + sk->ammo |= 1<<A_CANNONBALL; + } else { + sk->ammo &= ~(1<<A_CANNONBALL); + } + } else if (strcmpi(type, "A_THROWWEAPON") == 0) { + if (on) { + sk->ammo |= 1<<A_THROWWEAPON; + } else { + sk->ammo &= ~(1<<A_THROWWEAPON); + } + } else if (strcmpi(type, "All") == 0) { + if (on) { + sk->ammo = 0xFFFFFFFF; + } else { + sk->ammo = 0; + } + } else { + return 1; // Invalid Entry + } + + return 0; +} + +/** + * Validates the "AmmoTypes" flag + * when parsing skill_db.conf + * @param conf pointer to the skill configuration + * @param sk struct, pointer to s_skill_db + * @return void + */ +static void skill_validate_ammotype(struct config_setting_t *conf, struct s_skill_db *sk) +{ + struct config_setting_t *tt = NULL; + const char *tstr = NULL; + + nullpo_retv(sk); + if ((tt = libconfig->setting_get_member(conf, "AmmoTypes")) && config_setting_is_group(tt)) { + int j = 0; + struct config_setting_t *amt = { 0 }; + while ((amt = libconfig->setting_get_elem(tt, j++))) { + if (skill->validate_ammotype_sub(config_setting_name(amt), libconfig->setting_get_bool_real(amt), sk)) + skilldb_invalid_error(config_setting_name(amt), config_setting_name(tt), sk->nameid); + } + } else if( libconfig->setting_lookup_string(conf, "AmmoTypes", &tstr)) { + if (skill->validate_ammotype_sub(tstr, true, sk)) + skilldb_invalid_error(tstr, "AmmoTypes", sk->nameid); + } +} + +/** + * Validates the "State" flag + * when parsing skill_db.conf + * @param conf struct, pointer to the skill configuration + * @param sk struct, pointer to s_skill_db + * @return void + */ +static void skill_validate_state(struct config_setting_t *conf, struct s_skill_db *sk) +{ + const char *type = NULL; + + nullpo_retv(sk); + if (libconfig->setting_lookup_string(conf, "State", &type) && strcmpi(type,"None") != ST_NONE) { + if ( strcmpi(type,"Hiding") == 0 ) sk->state = ST_HIDING; + else if (strcmpi(type,"Cloaking") == 0 ) sk->state = ST_CLOAKING; + else if (strcmpi(type,"Hidden") == 0 ) sk->state = ST_HIDDEN; + else if (strcmpi(type,"Riding") == 0 ) sk->state = ST_RIDING; + else if (strcmpi(type,"Falcon") == 0 ) sk->state = ST_FALCON; + else if (strcmpi(type,"Cart") == 0 ) sk->state = ST_CART; + else if (strcmpi(type,"Shield") == 0 ) sk->state = ST_SHIELD; + else if (strcmpi(type,"Sight") == 0 ) sk->state = ST_SIGHT; + else if (strcmpi(type,"ExplosionSpirits") == 0 ) sk->state = ST_EXPLOSIONSPIRITS; + else if (strcmpi(type,"CartBoost") == 0 ) sk->state = ST_CARTBOOST; + else if (strcmpi(type,"NotOverWeight") == 0 ) sk->state = ST_RECOV_WEIGHT_RATE; + else if (strcmpi(type,"Moveable") == 0 ) sk->state = ST_MOVE_ENABLE; + else if (strcmpi(type,"InWater") == 0 ) sk->state = ST_WATER; + else if (strcmpi(type,"Dragon") == 0 ) sk->state = ST_RIDINGDRAGON; + else if (strcmpi(type,"Warg") == 0 ) sk->state = ST_WUG; + else if (strcmpi(type,"RidingWarg") == 0 ) sk->state = ST_RIDINGWUG; + else if (strcmpi(type,"MadoGear") == 0 ) sk->state = ST_MADO; + else if (strcmpi(type,"ElementalSpirit") == 0 ) sk->state = ST_ELEMENTALSPIRIT; + else if (strcmpi(type,"PoisonWeapon") == 0 ) sk->state = ST_POISONINGWEAPON; + else if (strcmpi(type,"RollingCutter") == 0 ) sk->state = ST_ROLLINGCUTTER; + else if (strcmpi(type,"MH_Fighting") == 0 ) sk->state = ST_MH_FIGHTING; + else if (strcmpi(type,"MH_Grappling") == 0 ) sk->state = ST_MH_GRAPPLING; + else if (strcmpi(type,"Peco") == 0 ) sk->state = ST_PECO; + else + skilldb_invalid_error(type, "State", sk->nameid); + } +} + +/** + * Validates the "Items" flag + * when parsing skill_db.conf + * @param conf struct, pointer to the skill configuration + * @param sk struct, pointer to s_skill_db + * @return void + */ +static void skill_validate_item_requirements(struct config_setting_t *conf, struct s_skill_db *sk) +{ + struct config_setting_t *tt = NULL; + + nullpo_retv(sk); + if ((tt=libconfig->setting_get_member(conf, "Items")) && config_setting_is_group(conf)) { + int itx=-1; + struct config_setting_t *it; + + while((it=libconfig->setting_get_elem(tt, ++itx)) && itx < MAX_SKILL_ITEM_REQUIRE) { + const char *type = config_setting_name(it); + + if( type[0] == 'I' && type[1] == 'D' && itemdb->exists(atoi(type+2)) ) + sk->itemid[itx] = atoi(type+2); + else if(!script->get_constant(type, &sk->itemid[itx])) { + ShowWarning("skill_read_skilldb: Invalid required Item '%s' given for skill Id %d in '%s', skipping...\n",type, sk->nameid, DBPATH"skill_db.conf"); + continue; + } + + if (config_setting_is_group(it)) { + // TODO: Per-level item requirements are not implemented yet! + // We just take the first level for the time being (old txt behavior) + sk->amount[itx] = libconfig->setting_get_int_elem(it, 0); + } else { + sk->amount[itx] = libconfig->setting_get_int(it); + } + } + } +} + +/** + * Validates the "Unit > Target" flag + * when parsing skill_db.conf + * @param conf struct, pointer to the skill configuration + * @param sk struct, pointer to s_skill_db + * @return void + */ +static void skill_validate_unit_target(struct config_setting_t *conf, struct s_skill_db *sk) +{ + const char *type = NULL; + + nullpo_retv(sk); + if(libconfig->setting_lookup_string(conf, "Target", &type)) { + + if(!strcmpi(type,"NotEnemy")) sk->unit_target = BCT_NOENEMY; + else if(!strcmpi(type,"NotParty")) sk->unit_target = BCT_NOPARTY; + else if (!strcmpi(type,"NotGuild")) sk->unit_target = BCT_NOGUILD; + else if(!strcmpi(type,"Friend")) sk->unit_target = BCT_NOENEMY; + else if(!strcmpi(type,"Party")) sk->unit_target = BCT_PARTY; + else if(!strcmpi(type,"Ally")) sk->unit_target = BCT_PARTY|BCT_GUILD; + else if(!strcmpi(type,"Guild")) sk->unit_target = BCT_GUILD; + else if(!strcmpi(type,"All")) sk->unit_target = BCT_ALL; + else if(!strcmpi(type,"Enemy")) sk->unit_target = BCT_ENEMY; + else if(!strcmpi(type,"Self")) sk->unit_target = BCT_SELF; + else if(!strcmpi(type,"SameGuild")) sk->unit_target = BCT_GUILD|BCT_SAMEGUILD; + } + + if (sk->unit_flag & UF_DEFNOTENEMY && battle_config.defnotenemy) + sk->unit_target = BCT_NOENEMY; + + //By default, target just characters. + sk->unit_target |= BL_CHAR; + + if (sk->unit_flag & UF_NOPC) + sk->unit_target &= ~BL_PC; + if (sk->unit_flag & UF_NOMOB) + sk->unit_target &= ~BL_MOB; + if (sk->unit_flag & UF_SKILL) + sk->unit_target |= BL_SKILL; +} + +/** + * Validates the "Unit > Flag" setting + * when parsing skill_db.conf + * @param type const char, name of the flag being parsed. + * @param on boolean, switch for flag setting + * @param sk struct, pointer to s_skill_db. + * @return (void) + */ +static int skill_validate_unit_flag_sub(const char *type, bool on, struct s_skill_db *sk) +{ + nullpo_ret(type); + nullpo_ret(sk); + if (strcmpi(type, "UF_DEFNOTENEMY") == 0) { + if (on) { + sk->unit_flag |= UF_DEFNOTENEMY; + } else { + sk->unit_flag &= ~UF_DEFNOTENEMY; + } + } else if (strcmpi(type, "UF_NOREITERATION") == 0) { + if (on) { + sk->unit_flag |= UF_NOREITERATION; + } else { + sk->unit_flag &= ~UF_NOREITERATION; + } + } else if (strcmpi(type, "UF_NOFOOTSET") == 0) { + if (on) { + sk->unit_flag |= UF_NOFOOTSET; + } else { + sk->unit_flag &= ~UF_NOFOOTSET; + } + } else if (strcmpi(type, "UF_NOOVERLAP") == 0) { + if (on) { + sk->unit_flag |= UF_NOOVERLAP; + } else { + sk->unit_flag &= ~UF_NOOVERLAP; + } + } else if (strcmpi(type, "UF_PATHCHECK") == 0) { + if (on) { + sk->unit_flag |= UF_PATHCHECK; + } else { + sk->unit_flag &= ~UF_PATHCHECK; + } + } else if (strcmpi(type, "UF_NOPC") == 0) { + if (on) { + sk->unit_flag |= UF_NOPC; + } else { + sk->unit_flag &= ~UF_NOPC; + } + } else if (strcmpi(type, "UF_NOMOB") == 0) { + if (on) { + sk->unit_flag |= UF_NOMOB; + } else { + sk->unit_flag &= ~UF_NOMOB; + } + } else if (strcmpi(type, "UF_SKILL") == 0) { + if (on) { + sk->unit_flag |= UF_SKILL; + } else { + sk->unit_flag &= ~UF_SKILL; + } + } else if (strcmpi(type, "UF_DANCE") == 0) { + if (on) { + sk->unit_flag |= UF_DANCE; + } else { + sk->unit_flag &= ~UF_DANCE; + } + } else if (strcmpi(type, "UF_ENSEMBLE") == 0) { + if (on) { + sk->unit_flag |= UF_ENSEMBLE; + } else { + sk->unit_flag &= ~UF_ENSEMBLE; + } + } else if (strcmpi(type, "UF_SONG") == 0) { + if (on) { + sk->unit_flag |= UF_SONG; + } else { + sk->unit_flag &= ~UF_SONG; + } + } else if (strcmpi(type, "UF_DUALMODE") == 0) { + if (on) { + sk->unit_flag |= UF_DUALMODE; + } else { + sk->unit_flag &= ~UF_DUALMODE; + } + } else if (strcmpi(type, "UF_RANGEDSINGLEUNIT") == 0) { + if (on) { + sk->unit_flag |= UF_RANGEDSINGLEUNIT; + } else { + sk->unit_flag &= ~UF_RANGEDSINGLEUNIT; + } + } else { + return 1; // Invalid Type + } + + return 0; +} + +/** + * Validate "Unit > Flag" setting + * when parsing skill_db.conf + * @param conf struct, pointer to the skill configuration + * @param sk struct, struct, pointer to s_skill_db + * @return (void) + */ +static void skill_validate_unit_flag(struct config_setting_t *conf, struct s_skill_db *sk) +{ + struct config_setting_t *t = NULL; + + nullpo_retv(sk); + if ((t=libconfig->setting_get_member(conf, "Flag")) && config_setting_is_group(t)) { + int j=0; + struct config_setting_t *tt = NULL; + while ((tt = libconfig->setting_get_elem(t, j++))) { + const char *name = config_setting_name(tt); + + if (skill->validate_unit_flag_sub(name, libconfig->setting_get_bool_real(tt), sk)) + skilldb_invalid_error(name, config_setting_name(t), sk->nameid); + } + } +} +/** + * Validate additional field settings via plugins + * when parsing skill_db.conf + * @param conf struct, pointer to the skill configuration + * @param sk struct, struct, pointer to s_skill_db + * @return (void) + */ +static void skill_validate_additional_fields(struct config_setting_t *conf, struct s_skill_db *sk) +{ + // Does nothing like a boss. *cough* plugins *cough* +} + +/** + * Validates a skill entry and adds it to the database. [ Smokexyz/Hercules ] + * @param sk contains skill data to be checked. + * @param *source filepath constant. + * @return boolean true on success. + */ +static bool skill_validate_skilldb(struct s_skill_db *sk, const char *source) +{ + int idx; + + nullpo_retr(false, sk); + idx = skill->get_index(sk->nameid); + if (idx == 0) { + ShowWarning("skill_validate_skilldb: Invalid skill Id %d provided in '%s'! ... skipping\n", sk->nameid, source); + ShowInfo("It is possible that the skill Id is 0 or unavailable (interferes with guild/homun/mercenary skill mapping).\n"); + return false; + } else if (sk->max <= 0) { + ShowError("skill_validate_skilldb: Invalid Max Level %d specified for skill Id %d in '%s', skipping...\n", sk->max, sk->nameid, source); + return false; + } + + /* Direct assignment of temporary skill storage to skill db */ + skill->dbs->db[idx] = *sk; + /* Put skill name in name2id DB */ + strdb_iput(skill->name2id_db, skill->dbs->db[idx].name, skill->dbs->db[idx].nameid); + /* Set Name to Id script constants */ + script->set_constant2(skill->dbs->db[idx].name, (int)skill->dbs->db[idx].nameid, false, false); + + return true; +} + +/** + * Reads skill_db.conf from relative filepath and processes [ Smokexyz/Hercules ] + * entries into the skill database. + * @param filename contains the file path and name. + * @return boolean true on success + */ +static bool skill_read_skilldb(const char *filename) +{ + struct config_t skilldb; + struct config_setting_t *sk, *conf; + char filepath[256]; + int count=0, index=0; + bool duplicate[MAX_SKILL_DB] = {0}; + + nullpo_retr(false, filename); + + libconfig->format_db_path(filename, filepath, sizeof(filepath)); + + if (!libconfig->load_file(&skilldb, filepath)) { + return false; // Libconfig error report. + } + + // Possible Syntax error. + if ((sk=libconfig->setting_get_member(skilldb.root, "skill_db")) == NULL) { + ShowError("skill_read_skilldb: Skill DB could not be loaded, please check '%s'.\n", filepath); + libconfig->destroy(&skilldb); + return false; + } + + while ((conf = libconfig->setting_get_elem(sk,index++))) { + int idx=0, skill_id=0, temp=0; + struct config_setting_t *t = NULL, *tt = NULL; + struct s_skill_db tmp_db = { 0 }; + + /* Skill ID */ + if (!libconfig->setting_lookup_int(conf, "Id", &skill_id)) { + ShowError("skill_read_skilldb: Skill Id not specified for entry %d in '%s', skipping...\n", index, filepath ); + continue; + } + + tmp_db.nameid = skill_id; + + if((idx = skill->get_index(skill_id)) == 0) { + ShowError("skill_read_skilldb: Skill Id %d is out of range, or within a reserved range (for guild, homunculus, mercenary or elemental skills). skipping...\n", idx); + continue; + } + + if (duplicate[idx]) { + ShowWarning("skill_read_skilldb: Duplicate Skill Id %d in entry %d in '%s', skipping...\n", skill_id, index, filepath); + continue; + } + + /* Skill Name Constant */ + if (!libconfig->setting_lookup_mutable_string(conf, "Name", tmp_db.name, sizeof(tmp_db.name))) { + ShowError("skill_read_skilldb: Name not specified for skill Id %d in '%s', skipping...\n", skill_id, filepath); + continue; + } + + /* Skill Description */ + libconfig->setting_lookup_mutable_string(conf, "Description", tmp_db.desc, sizeof(tmp_db.desc)); + + /* Max Level */ + if (!libconfig->setting_lookup_int(conf, "MaxLevel", &temp)) { + ShowError("skill_read_skilldb: MaxLevel not specified for skill Id %d in '%s', skipping...\n", skill_id, filepath); + continue; + } else { + tmp_db.max = temp; + } + + /* Range */ + if ((t=libconfig->setting_get_member(conf, "Range"))) + skill->config_set_level(t, tmp_db.range); + + /* Hit Type */ + skill->validate_hittype(conf, &tmp_db); + + /* Skill Type */ + skill->validate_skilltype(conf, &tmp_db); + + /* Skill Info */ + skill->validate_skillinfo(conf, &tmp_db); + + /* Skill Attack Type */ + skill->validate_attacktype(conf, &tmp_db); + + /* Skill Element */ + skill->validate_element(conf, &tmp_db); + + /* Damage Type */ + skill->validate_damagetype(conf, &tmp_db); + + /* Splash Range */ + if ((t = libconfig->setting_get_member(conf, "SplashRange"))) + skill->config_set_level(t, tmp_db.splash); + + /* Number of Hits */ + if ((t = libconfig->setting_get_member(conf, "NumberOfHits")) && config_setting_is_group(t)) + skill->config_set_level(t, tmp_db.num); + else if ((libconfig->setting_lookup_int(conf, "NumberOfHits", &temp))) + skill->level_set_value(tmp_db.num, temp); + else + skill->level_set_value(tmp_db.num, 1); // Default 1 + + /* Interrupt Cast */ + if (libconfig->setting_lookup_bool(conf, "InterruptCast", &tmp_db.castcancel) == CONFIG_FALSE) + tmp_db.castcancel = 0; + + /* Cast Defense Rate */ + libconfig->setting_lookup_int(conf, "CastDefRate", &tmp_db.cast_def_rate); + + /* Skill Instances */ + if ((t = libconfig->setting_get_member(conf, "SkillInstances"))) + skill->config_set_level(t, tmp_db.maxcount); + + /* Knock-Back Tiles */ + if ((t = libconfig->setting_get_member(conf, "KnockBackTiles"))) + skill->config_set_level(t, tmp_db.blewcount); + /** + * Skill Cast / Delay data handling + */ + /* Cast Time */ + if ((t=libconfig->setting_get_member(conf, "CastTime"))) + skill->config_set_level(t, tmp_db.cast); + + /* After Cast Act Delay */ + if ((t=libconfig->setting_get_member(conf, "AfterCastActDelay"))) + skill->config_set_level(t, tmp_db.delay); + + /* After Cast Walk Delay */ + if ((t=libconfig->setting_get_member(conf, "AfterCastWalkDelay"))) + skill->config_set_level(t, tmp_db.walkdelay); + + /* Skill Data/Duration */ + if ((t=libconfig->setting_get_member(conf, "SkillData1"))) + skill->config_set_level(t, tmp_db.upkeep_time); + + /* Skill Data/Duration 2 */ + if ((t=libconfig->setting_get_member(conf, "SkillData2"))) + skill->config_set_level(t, tmp_db.upkeep_time2); + + /* Skill Cool Down */ + if ((t=libconfig->setting_get_member(conf, "CoolDown"))) + skill->config_set_level(t, tmp_db.cooldown); + +#ifdef RENEWAL_CAST + /* Fixed Casting Time */ + if ((t=libconfig->setting_get_member(conf, "FixedCastTime"))) + skill->config_set_level(t, tmp_db.fixed_cast); +#endif + /* Cast Time Options */ + skill->validate_castnodex(conf, &tmp_db, false); + skill->validate_castnodex(conf, &tmp_db, true); + + /** + * Skill Requirements data handling + */ + if ((t=libconfig->setting_get_member(conf, "Requirements")) && config_setting_is_group(t)) { + + /* HP Costs */ + if ((tt = libconfig->setting_get_member(t, "HPCost"))) + skill->config_set_level(tt, tmp_db.hp); + + /* Max HP Trigger */ + if ((tt = libconfig->setting_get_member(t, "MaxHPTrigger"))) + skill->config_set_level(tt, tmp_db.mhp); + + /* SP Cost */ + if ((tt = libconfig->setting_get_member(t, "SPCost"))) + skill->config_set_level(tt, tmp_db.sp); + + /* HP Rate */ + if ((tt = libconfig->setting_get_member(t, "HPRateCost"))) + skill->config_set_level(tt, tmp_db.hp_rate); + + /* SP Rate */ + if ((tt = libconfig->setting_get_member(t, "SPRateCost"))) + skill->config_set_level(tt, tmp_db.sp_rate); + + /* Zeny Cost */ + if ((tt = libconfig->setting_get_member(t, "ZenyCost"))) + skill->config_set_level(tt, tmp_db.zeny); + + /* Spirit Sphere Cost */ + if ((tt = libconfig->setting_get_member(t, "SpiritSphereCost"))) + skill->config_set_level(tt, tmp_db.spiritball); + + /* Weapon Types */ + skill->validate_weapontype(t, &tmp_db); + + /* Ammunition Types */ + skill->validate_ammotype(t, &tmp_db); + + /* Ammunition Amount */ + if ((tt = libconfig->setting_get_member(t, "AmmoAmount"))) + skill->config_set_level(tt, tmp_db.ammo_qty); + + /* State */ + skill->validate_state(t, &tmp_db); + + /* Spirit Sphere Cost */ + if ((tt = libconfig->setting_get_member(t, "SpiritSphereCost"))) + skill->config_set_level(tt, tmp_db.spiritball); + + /* Item Requirements and Amounts */ + skill->validate_item_requirements(t, &tmp_db); + } + + /** + * Skill Unit data handling + */ + if ((t=libconfig->setting_get_member(conf, "Unit")) && config_setting_is_group(t)) { + + /* Unit IDs [1,2] */ + if ((tt=libconfig->setting_get_member(t, "Id")) && config_setting_is_array(tt)) { + tmp_db.unit_id[0] = libconfig->setting_get_int_elem(tt, 0); + tmp_db.unit_id[1] = libconfig->setting_get_int_elem(tt, 1); + } else { + libconfig->setting_lookup_int(t, "Id", &tmp_db.unit_id[0]); + } + + /* Layout */ + if((tt=libconfig->setting_get_member(t, "Layout"))) + skill->config_set_level(tt, tmp_db.unit_layout_type); + + /* Range */ + if((tt=libconfig->setting_get_member(t, "Range"))) + skill->config_set_level(tt, tmp_db.unit_range); + + /* Interval */ + if(libconfig->setting_lookup_int(t, "Interval", &temp)) + tmp_db.unit_interval = temp; + + /* Flag */ + skill->validate_unit_flag(t, &tmp_db); + + /* Target */ + skill->validate_unit_target(t, &tmp_db); + } + + /* Additional Fields for Plugins */ + skill->validate_additional_fields(conf, &tmp_db); + + // Validate the skill entry, add it to the duplicate array and increment count on success. + if ((duplicate[idx] = skill->validate_skilldb(&tmp_db, filepath))) + count++; + } + + libconfig->destroy(&skilldb); + + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filepath); + + return true; +} + +#undef skilldb_duplicate_warning +#undef skilldb_invalid_error + /*=============================== * DB reading. - * skill_db.txt - * skill_require_db.txt - * skill_cast_db.txt - * skill_castnodex_db.txt - * skill_nocast_db.txt - * skill_unit_db.txt * produce_db.txt * create_arrow_db.txt * abra_db.txt *------------------------------*/ -void skill_readdb(bool minimal) { +static void skill_readdb(bool minimal) +{ // init skill db structures db_clear(skill->name2id_db); @@ -19205,10 +21457,12 @@ void skill_readdb(bool minimal) { safestrncpy(skill->dbs->db[0].name, "UNKNOWN_SKILL", sizeof(skill->dbs->db[0].name)); safestrncpy(skill->dbs->db[0].desc, "Unknown Skill", sizeof(skill->dbs->db[0].desc)); + itemdb->name_constants(); // refresh ItemDB constants before loading of skills + #ifdef ENABLE_CASE_CHECK - script->parser_current_file = DBPATH"skill_db.txt"; + script->parser_current_file = DBPATH"skill_db.conf"; #endif // ENABLE_CASE_CHECK - sv->readdb(map->db_path, DBPATH"skill_db.txt", ',', 17, 17, MAX_SKILL_DB, skill->parse_row_skilldb); + skill->read_skilldb(DBPATH"skill_db.conf"); #ifdef ENABLE_CASE_CHECK script->parser_current_file = NULL; #endif // ENABLE_CASE_CHECK @@ -19216,15 +21470,6 @@ void skill_readdb(bool minimal) { if (minimal) return; - sv->readdb(map->db_path, DBPATH"skill_require_db.txt", ',', 32, 32, MAX_SKILL_DB, skill->parse_row_requiredb); -#ifdef RENEWAL_CAST - sv->readdb(map->db_path, "re/skill_cast_db.txt", ',', 8, 8, MAX_SKILL_DB, skill->parse_row_castdb); -#else - sv->readdb(map->db_path, "pre-re/skill_cast_db.txt", ',', 7, 7, MAX_SKILL_DB, skill->parse_row_castdb); -#endif - sv->readdb(map->db_path, DBPATH"skill_castnodex_db.txt", ',', 2, 3, MAX_SKILL_DB, skill->parse_row_castnodexdb); - sv->readdb(map->db_path, DBPATH"skill_unit_db.txt", ',', 8, 8, MAX_SKILL_DB, skill->parse_row_unitdb); - skill->init_unit_layout(); sv->readdb(map->db_path, "produce_db.txt", ',', 4, 4+2*MAX_PRODUCE_RESOURCE, MAX_SKILL_PRODUCE_DB, skill->parse_row_producedb); sv->readdb(map->db_path, "create_arrow_db.txt", ',', 1+2, 1+2*MAX_ARROW_RESOURCE, MAX_SKILL_ARROW_DB, skill->parse_row_createarrowdb); @@ -19233,12 +21478,11 @@ void skill_readdb(bool minimal) { sv->readdb(map->db_path, "spellbook_db.txt", ',', 3, 3, MAX_SKILL_SPELLBOOK_DB, skill->parse_row_spellbookdb); //Guillotine Cross sv->readdb(map->db_path, "magicmushroom_db.txt", ',', 1, 1, MAX_SKILL_MAGICMUSHROOM_DB, skill->parse_row_magicmushroomdb); - sv->readdb(map->db_path, "skill_reproduce_db.txt", ',', 1, 1, MAX_SKILL_DB, skill->parse_row_reproducedb); sv->readdb(map->db_path, "skill_improvise_db.txt", ',', 2, 2, MAX_SKILL_IMPROVISE_DB, skill->parse_row_improvisedb); sv->readdb(map->db_path, "skill_changematerial_db.txt", ',', 4, 4+2*5, MAX_SKILL_PRODUCE_DB, skill->parse_row_changematerialdb); } -void skill_reload(void) +static void skill_reload(void) { struct s_mapiterator *iter; struct map_session_data *sd; @@ -19271,7 +21515,8 @@ void skill_reload(void) /*========================================== * *------------------------------------------*/ -int do_init_skill(bool minimal) { +static int do_init_skill(bool minimal) +{ skill->name2id_db = strdb_alloc(DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA, MAX_SKILL_NAME_LENGTH); skill->read_db(minimal); @@ -19304,7 +21549,8 @@ int do_init_skill(bool minimal) { return 0; } -int do_final_skill(void) { +static int do_final_skill(void) +{ db_destroy(skill->name2id_db); db_destroy(skill->group_db); db_destroy(skill->unit_db); @@ -19317,8 +21563,10 @@ int do_final_skill(void) { ers_destroy(skill->cd_entry_ers); return 0; } + /* initialize the interface */ -void skill_defaults(void) { +void skill_defaults(void) +{ const int skill_enchant_eff[5] = { 10, 14, 17, 19, 20 }; const int skill_deluge_eff[5] = { 5, 9, 12, 14, 15 }; @@ -19368,8 +21616,12 @@ void skill_defaults(void) { skill->get_hp = skill_get_hp; skill->get_mhp = skill_get_mhp; skill->get_sp = skill_get_sp; + skill->get_hp_rate = skill_get_hp_rate; + skill->get_sp_rate = skill_get_sp_rate; skill->get_state = skill_get_state; skill->get_spiritball = skill_get_spiritball; + skill->get_itemid = skill_get_itemid; + skill->get_itemqty = skill_get_itemqty; skill->get_zeny = skill_get_zeny; skill->get_num = skill_get_num; skill->get_cast = skill_get_cast; @@ -19398,12 +21650,12 @@ void skill_defaults(void) { skill->tree_get_max = skill_tree_get_max; skill->get_name = skill_get_name; skill->get_desc = skill_get_desc; - skill->chk = skill_chk; skill->get_casttype = skill_get_casttype; skill->get_casttype2 = skill_get_casttype2; skill->is_combo = skill_is_combo; skill->name2id = skill_name2id; skill->isammotype = skill_isammotype; + skill->castend_type = skill_castend_type; skill->castend_id = skill_castend_id; skill->castend_pos = skill_castend_pos; skill->castend_map = skill_castend_map; @@ -19449,8 +21701,11 @@ void skill_defaults(void) { skill->can_cloak = skill_can_cloak; skill->enchant_elemental_end = skill_enchant_elemental_end; skill->not_ok = skillnotok; + skill->not_ok_unknown = skill_notok_unknown; skill->not_ok_hom = skillnotok_hom; + skill->not_ok_hom_unknown = skillnotok_hom_unknown; skill->not_ok_mercenary = skillnotok_mercenary; + skill->validate_autocast_data = skill_validate_autocast_data; skill->chastle_mob_changetarget = skill_chastle_mob_changetarget; skill->can_produce_mix = skill_can_produce_mix; skill->produce_mix = skill_produce_mix; @@ -19474,6 +21729,7 @@ void skill_defaults(void) { skill->onskillusage = skill_onskillusage; skill->cell_overlap = skill_cell_overlap; skill->timerskill = skill_timerskill; + skill->trap_do_splash = skill_trap_do_splash; skill->trap_splash = skill_trap_splash; skill->check_condition_mercenary = skill_check_condition_mercenary; skill->locate_element_field = skill_locate_element_field; @@ -19497,6 +21753,7 @@ void skill_defaults(void) { skill->sit_out = skill_sit_out; skill->unitsetmapcell = skill_unitsetmapcell; skill->unit_onplace_timer = skill_unit_onplace_timer; + skill->unit_onplace_timer_unknown = skill_unit_onplace_timer_unknown; skill->unit_effect = skill_unit_effect; skill->unit_timer_sub_onplace = skill_unit_timer_sub_onplace; skill->unit_move_sub = skill_unit_move_sub; @@ -19507,17 +21764,35 @@ void skill_defaults(void) { skill->unit_timer = skill_unit_timer; skill->unit_timer_sub = skill_unit_timer_sub; skill->init_unit_layout = skill_init_unit_layout; - skill->parse_row_skilldb = skill_parse_row_skilldb; - skill->parse_row_requiredb = skill_parse_row_requiredb; - skill->parse_row_castdb = skill_parse_row_castdb; - skill->parse_row_castnodexdb = skill_parse_row_castnodexdb; - skill->parse_row_unitdb = skill_parse_row_unitdb; + skill->init_unit_layout_unknown = skill_init_unit_layout_unknown; + /* Skill DB Libconfig */ + skill->validate_hittype = skill_validate_hittype; + skill->validate_attacktype = skill_validate_attacktype; + skill->validate_element = skill_validate_element; + skill->validate_skilltype = skill_validate_skilltype; + skill->validate_skillinfo = skill_validate_skillinfo; + skill->validate_damagetype = skill_validate_damagetype; + skill->validate_castnodex = skill_validate_castnodex; + skill->validate_weapontype = skill_validate_weapontype; + skill->validate_ammotype = skill_validate_ammotype; + skill->validate_state = skill_validate_state; + skill->validate_item_requirements = skill_validate_item_requirements; + skill->validate_unit_target = skill_validate_unit_target; + skill->validate_unit_flag = skill_validate_unit_flag; + skill->validate_additional_fields = skill_validate_additional_fields; + skill->validate_skilldb = skill_validate_skilldb; + skill->validate_weapontype_sub = skill_validate_weapontype_sub; + skill->validate_ammotype_sub = skill_validate_ammotype_sub; + skill->validate_unit_flag_sub = skill_validate_unit_flag_sub; + skill->read_skilldb = skill_read_skilldb; + skill->config_set_level = skill_config_set_level; + skill->level_set_value = skill_level_set_value; + /* */ skill->parse_row_producedb = skill_parse_row_producedb; skill->parse_row_createarrowdb = skill_parse_row_createarrowdb; skill->parse_row_abradb = skill_parse_row_abradb; skill->parse_row_spellbookdb = skill_parse_row_spellbookdb; skill->parse_row_magicmushroomdb = skill_parse_row_magicmushroomdb; - skill->parse_row_reproducedb = skill_parse_row_reproducedb; skill->parse_row_improvisedb = skill_parse_row_improvisedb; skill->parse_row_changematerialdb = skill_parse_row_changematerialdb; skill->usave_add = skill_usave_add; @@ -19568,4 +21843,8 @@ void skill_defaults(void) { skill->get_requirement_off_unknown = skill_get_requirement_off_unknown; skill->get_requirement_item_unknown = skill_get_requirement_item_unknown; skill->get_requirement_unknown = skill_get_requirement_unknown; + skill->splash_target = skill_splash_target; + skill->check_npc_chaospanic = skill_check_npc_chaospanic; + skill->count_wos = skill_count_wos; + skill->get_linked_song_dance_id = skill_get_linked_song_dance_id; } |