From 70b8b8bc38a45335a0e1ee4fd27cb06fea056ec8 Mon Sep 17 00:00:00 2001 From: Cydh Ramdh Date: Fri, 22 Aug 2014 23:28:48 +0700 Subject: [PATCH 1/5] Bug Fixes: - Fixed map-server crashed caused by Wall of Thorn when atatcked by Fire element (with wide range skill unit, bugreport9102). Also reverted some changes in c046668 - Added config conf/battle/skill.conf 'arrow_shower_knockback', as follow up 453b6d0. Direction of Arrow Shower knockback depends on Arrow Shower location Signed-off-by: Cydh Ramdh --- conf/battle/skill.conf | 3 + db/pre-re/skill_unit_db.txt | 4 +- db/re/skill_unit_db.txt | 4 +- src/map/battle.c | 9 +- src/map/battle.h | 3 +- src/map/skill.c | 519 +++++++++++++++++++----------------- src/map/skill.h | 7 +- src/map/status.c | 2 +- src/map/unit.c | 2 +- 9 files changed, 298 insertions(+), 255 deletions(-) diff --git a/conf/battle/skill.conf b/conf/battle/skill.conf index 4e66d299bd..297c47acf9 100644 --- a/conf/battle/skill.conf +++ b/conf/battle/skill.conf @@ -306,3 +306,6 @@ teleport_on_portal: no // Is the knockback direction for Cart Revolution always West? (Note 1) // On official servers it will knock the target always to the West. If disabled it will knock the target backwards. cart_revo_knockback: yes + +// On official servers, Arrow Shower blow direction always rely on skill placed location to target instead of caster to target +arrow_shower_knockback: yes diff --git a/db/pre-re/skill_unit_db.txt b/db/pre-re/skill_unit_db.txt index b003cb9666..998cbb83f8 100644 --- a/db/pre-re/skill_unit_db.txt +++ b/db/pre-re/skill_unit_db.txt @@ -1,7 +1,7 @@ // Skill Unit Database // // Structure of Database: -// ID,unit ID,unit ID 2,layout,range,interval,target,flag +// Skill ID,Unit ID,Unit ID 2,Layout,Range,Interval,Target,Flag // // layout = -1:special, 0:1*1, 1:3*3, 2:5*5, up to 5:11*11 // target = friend (party +guildmates +neutral players) / party / guild @@ -151,7 +151,7 @@ 2468,0xf4, , 0, 1,1000,all, 0x010 //SO_EARTH_INSIGNIA 2479,0xe5, , 0, 1,1000,enemy, 0xC006 //GN_THORNS_TRAP -2482,0xe6,0x7f, 0, 1, 100,all, 0xD000 //GN_WALLOFTHORN +2482,0xe6,0x7f, -1, 1, 300,enemy, 0xD000 //GN_WALLOFTHORN 2484,0x86, , 0, 1, 100,enemy, 0x080 //GN_CRAZYWEED_ATK 2485,0xe7, , 0, 2,2000,enemy, 0x8098 //GN_DEMONIC_FIRE 2487,0xe8, , 2, 0, -1,enemy, 0x2000 //GN_FIRE_EXPANSION_SMOKE_POWDER diff --git a/db/re/skill_unit_db.txt b/db/re/skill_unit_db.txt index 45a4c91fa4..54c114bc23 100644 --- a/db/re/skill_unit_db.txt +++ b/db/re/skill_unit_db.txt @@ -1,7 +1,7 @@ // Skill Unit Database // // Structure of Database: -// ID,unit ID,unit ID 2,layout,range,interval,target,flag +// Skill ID,Unit ID,Unit ID 2,Layout,Range,Interval,Target,Flag // // layout = -1:special, 0:1*1, 1:3*3, 2:5*5, up to 5:11*11 // target = friend (party +guildmates +neutral players) / party / guild @@ -153,7 +153,7 @@ 2468,0xf4, , 0, 1,1000,all, 0x010 //SO_EARTH_INSIGNIA 2479,0xe5, , 0, 1,1000,enemy, 0xC006 //GN_THORNS_TRAP -2482,0xe6,0x7f, 0, 1, 100,all, 0xD000 //GN_WALLOFTHORN +2482,0xe6,0x7f, -1, 1, 300,enemy, 0xD000 //GN_WALLOFTHORN 2484,0x86, , 0, 1, 100,enemy, 0x080 //GN_CRAZYWEED_ATK 2485,0xe7, , 0, 2,2000,enemy, 0x8098 //GN_DEMONIC_FIRE 2487,0xe8, , 2, 0, -1,enemy, 0x2000 //GN_FIRE_EXPANSION_SMOKE_POWDER diff --git a/src/map/battle.c b/src/map/battle.c index b99f8c799b..7859f9530d 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -6277,14 +6277,14 @@ struct Damage battle_calc_misc_attack(struct block_list *src,struct block_list * * Initial refactoring by Baalberith * Refined and optimized by helvetica */ -struct Damage battle_calc_attack(int attack_type,struct block_list *bl,struct block_list *target,uint16 skill_id,uint16 skill_lv,int count) +struct Damage battle_calc_attack(int attack_type,struct block_list *bl,struct block_list *target,uint16 skill_id,uint16 skill_lv,int flag) { struct Damage d; switch(attack_type) { - case BF_WEAPON: d = battle_calc_weapon_attack(bl,target,skill_id,skill_lv,count); break; - case BF_MAGIC: d = battle_calc_magic_attack(bl,target,skill_id,skill_lv,count); break; - case BF_MISC: d = battle_calc_misc_attack(bl,target,skill_id,skill_lv,count); break; + case BF_WEAPON: d = battle_calc_weapon_attack(bl,target,skill_id,skill_lv,flag); break; + case BF_MAGIC: d = battle_calc_magic_attack(bl,target,skill_id,skill_lv,flag); break; + case BF_MISC: d = battle_calc_misc_attack(bl,target,skill_id,skill_lv,flag); break; default: ShowError("battle_calc_attack: unknown attack type! %d (skill_id=%d, skill_lv=%d)\n", attack_type, skill_id, skill_lv); memset(&d,0,sizeof(d)); @@ -7798,6 +7798,7 @@ static const struct _battle_data { { "at_monsterignore", &battle_config.autotrade_monsterignore, 0, 0, 1, }, { "idletime_option", &battle_config.idletime_option, 0x25, 1, INT_MAX, }, { "spawn_direction", &battle_config.spawn_direction, 0, 0, 1, }, + { "arrow_shower_knockback", &battle_config.arrow_shower_knockback, 1, 0, 1, }, }; #ifndef STATS_OPT_OUT /** diff --git a/src/map/battle.h b/src/map/battle.h index 9b77624a99..5cdaebb376 100644 --- a/src/map/battle.h +++ b/src/map/battle.h @@ -79,7 +79,7 @@ struct block_list; // Damage Calculation -struct Damage battle_calc_attack(int attack_type,struct block_list *bl,struct block_list *target,uint16 skill_id,uint16 skill_lv,int count); +struct Damage battle_calc_attack(int attack_type,struct block_list *bl,struct block_list *target,uint16 skill_id,uint16 skill_lv,int flag); int64 battle_calc_return_damage(struct block_list *bl, struct block_list *src, int64 *, int flag, uint16 skill_id, bool status_reflect); @@ -567,6 +567,7 @@ extern struct Battle_Config int autotrade_monsterignore; int idletime_option; int spawn_direction; + int arrow_shower_knockback; } battle_config; void do_init_battle(void); diff --git a/src/map/skill.c b/src/map/skill.c index b724b75629..84e915b7f8 100755 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -112,8 +112,9 @@ int firewall_unit_pos; int icewall_unit_pos; int earthstrain_unit_pos; int firerain_unit_pos; +int wallofthorn_unit_pos; -struct s_skill_nounit_layout skill_nounit_layout[MAX_SKILL_UNIT_LAYOUT]; +struct s_skill_nounit_layout skill_nounit_layout[MAX_SKILL_UNIT_LAYOUT2]; int overbrand_nounit_pos; int overbrand_brandish_nounit_pos; @@ -771,6 +772,8 @@ struct s_skill_unit_layout* skill_get_unit_layout (uint16 skill_id, uint16 skill return &skill_unit_layout [earthstrain_unit_pos + dir]; else if( skill_id == RL_FIRE_RAIN ) return &skill_unit_layout[firerain_unit_pos + dir]; + else if( skill_id == GN_WALLOFTHORN ) + return &skill_unit_layout[wallofthorn_unit_pos + dir]; ShowError("skill_get_unit_layout: unknown unit layout for skill %d (level %d)\n", skill_id, skill_lv); return &skill_unit_layout[0]; // default 1x1 layout @@ -2723,19 +2726,110 @@ static void skill_do_copy(struct block_list* src,struct block_list *bl, uint16 s clif_addskill(tsd,skill_id); } } + +/** +* Knockback the target on skill_attack +* @param src is the master behind the attack +* @param dsrc is the actual originator of the damage, can be the same as src, or a BL_SKILL +* @param target is the target to be attacked. +* @param blewcount +* @param skill_id +* @param skill_lv +* @param damage +* @param tick +* @param flag can hold a bunch of information: +*/ +void skill_attack_blow(struct block_list *src, struct block_list *dsrc, struct block_list *target, uint8 blewcount, uint16 skill_id, uint16 skill_lv, int64 damage, unsigned int tick, int flag) { + int8 dir = -1; // Default direction + //Only knockback if it's still alive, otherwise a "ghost" is left behind. [Skotlex] + //Reflected spells do not bounce back (src == dsrc since it only happens for direct skills) + if (!blewcount || target == dsrc || status_isdead(target)) + return; + + // Skill spesific direction + switch (skill_id) { + case MG_FIREWALL: + case PR_SANCTUARY: + case SC_TRIANGLESHOT: + case GN_WALLOFTHORN: + case EL_FIRE_MANTLE: + dir = unit_getdir(target); // Backwards + break; + // This ensures the storm randomly pushes instead of exactly a cell backwards per official mechanics. + case WZ_STORMGUST: + dir = rand()%8; + break; + case WL_CRIMSONROCK: + dir = map_calc_dir(target,skill_area_temp[4],skill_area_temp[5]); + break; + case MC_CARTREVOLUTION: + if (battle_config.cart_revo_knockback) + dir = 6; // Official servers push target to the West + break; + case AC_SHOWER: + if (!battle_config.arrow_shower_knockback) + dir = map_calc_dir(target, src->x, src->y); + break; + } + + // Blown-specific handling + switch( skill_id ) { + case LG_OVERBRAND_BRANDISH: + // Give knockback damage bonus only hits the wall. (bugreport:9096) + if (skill_blown(dsrc,target,blewcount,dir,0x04|0x08|0x10|0x20) < blewcount) + skill_addtimerskill(src, tick + status_get_amotion(src), target->id, 0, 0, LG_OVERBRAND_PLUSATK, skill_lv, BF_WEAPON, flag|SD_ANIMATION); + break; + case SR_KNUCKLEARROW: + { + short x = target->x, y = target->y; + + // Ignore knockback damage bonus if in WOE (player cannot be knocked in WOE) + // Boss & Immune Knockback (mode or from bonus bNoKnockBack) target still remains the damage bonus + // (bugreport:9096) + if (skill_blown(dsrc, target, blewcount, dir_ka, 0x04) < blewcount) + skill_addtimerskill(src, tick + 300 * ((flag&2) ? 1 : 2), target->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag|4); + + dir_ka = -1; + + // Move attacker to the target position after knocked back + if ((target->x != x || target->y != y) && unit_movepos(src,target->x,target->y,1,1)) { + clif_slide(src, target->x, target->y); + clif_fixpos(src); + } + } + break; + case RL_R_TRIP: + if (skill_blown(dsrc,target,blewcount,dir,0) < blewcount) + skill_addtimerskill(src, tick + status_get_amotion(src), target->id, 0, 0, RL_R_TRIP_PLUSATK, skill_lv, BF_WEAPON, flag|SD_ANIMATION); + break; + default: + skill_blown(dsrc,target,blewcount,dir, 0x0 ); + if (!blewcount && target->type == BL_SKILL && damage > 0) { + TBL_SKILL *su = (TBL_SKILL*)target; + if (su->group && su->group->skill_id == HT_BLASTMINE) + skill_blown(src, target, 3, -1, 0); + } + break; + } + clif_fixpos(target); +} + /* * ========================================================================= * Does a skill attack with the given properties. - * src is the master behind the attack (player/mob/pet) - * dsrc is the actual originator of the damage, can be the same as src, or a BL_SKILL - * bl is the target to be attacked. - * flag can hold a bunch of information: - * flag&0xFFF is passed to the underlying battle_calc_attack for processing - * (usually holds number of targets, or just 1 for simple splash attacks) - * flag&0x1000 is used to tag that this is a splash-attack (so the damage - * packet shouldn't display a skill animation) - * flag&0x2000 is used to signal that the skill_lv should be passed as -1 to the - * client (causes player characters to not scream skill name) + * @param src is the master behind the attack (player/mob/pet) + * @param dsrc is the actual originator of the damage, can be the same as src, or a BL_SKILL + * @param bl is the target to be attacked. + * @param flag can hold a bunch of information: + * flag&1 + * flag&2 - Disable re-triggered by double casting + * flag&4 - Skip to blow target (because already knocked back before skill_attack somewhere) + * + * flag&0xFFF is passed to the underlying battle_calc_attack for processing + * (usually holds number of targets, or just 1 for simple splash attacks) + * + * Values from enum e_skill_display * + * Values from enum e_battle_check_target *-------------------------------------------------------------------------*/ int64 skill_attack (int attack_type, struct block_list* src, struct block_list *dsrc, struct block_list *bl, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag) { @@ -2744,7 +2838,7 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list * struct status_change *tsc; struct map_session_data *sd, *tsd; int64 damage; - int8 rmdamage=0;//magic reflected + int8 rmdamage = 0;//magic reflected int type; bool shadow_flag = false; bool additional_effects = true; @@ -3076,8 +3170,8 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list * shadow_flag = skill_check_shadowform(bl, damage, dmg.div_); + // Instant damage if( !dmg.amotion ) { - //Instant damage if( (!tsc || (!tsc->data[SC_DEVOTION] && skill_id != CR_REFLECTSHIELD)) && !shadow_flag ) status_fix_damage(src,bl,damage,dmg.dmotion); //Deal damage before knockback to allow stuff like firewall+storm gust combo. if( !status_isdead(bl) && additional_effects ) @@ -3086,79 +3180,12 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list * skill_counter_additional_effect(src,bl,skill_id,skill_lv,dmg.flag,tick); } - //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 direction - // Skill spesific direction - switch (skill_id) { - case MG_FIREWALL: - case PR_SANCTUARY: - case SC_TRIANGLESHOT: - case GN_WALLOFTHORN: - case EL_FIRE_MANTLE: - dir = unit_getdir(bl); // Backwards - break; - // This ensures the storm randomly pushes instead of exactly a cell backwards per official mechanics. - case WZ_STORMGUST: - dir = rand()%8; - break; - case WL_CRIMSONROCK: - dir = map_calc_dir(bl,skill_area_temp[4],skill_area_temp[5]); - break; - case MC_CARTREVOLUTION: - if (battle_config.cart_revo_knockback) - dir = 6; // Official servers push target to the West - break; - case AC_SHOWER: - // Direction between target to actual attacker location instead of the unit location (bugreport:1709) - if (dsrc != src) - dir = map_calc_dir(bl, src->x, src->y); - break; - } - // Blown-specific handling - switch( skill_id ) { - case LG_OVERBRAND_BRANDISH: - // Give knockback damage bonus only hits the wall. (bugreport:9096) - if( skill_blown(dsrc,bl,dmg.blewcount,dir,0x04|0x08|0x10|0x20) < dmg.blewcount ) - skill_addtimerskill(src, tick + status_get_amotion(src), bl->id, 0, 0, LG_OVERBRAND_PLUSATK, skill_lv, BF_WEAPON, flag|SD_ANIMATION); - break; - case SR_KNUCKLEARROW: - if (!(flag&4)) { - short x = bl->x, y = bl->y; + // Blow! + if (!(flag&4)) + skill_attack_blow(src, dsrc, bl, (uint8)dmg.blewcount, skill_id, skill_lv, damage, tick, flag); - // Ignore knockback damage bonus if in WOE (player cannot be knocked in WOE) - // Boss & Immune Knockback (mode or from bonus bNoKnockBack) target still remains the damage bonus - // (bugreport:9096) - if (skill_blown(dsrc, bl, dmg.blewcount, dir_ka, 0x04) < dmg.blewcount) - skill_addtimerskill(src, tick + 300 * ((flag&2) ? 1 : 2), bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag|4); - - dir_ka = -1; - - // Move attacker to the target position after knocked back - if ((bl->x != x || bl->y != y) && unit_movepos(src,bl->x,bl->y,1,1)) { - clif_slide(src, bl->x, bl->y); - clif_fixpos(src); - } - } - break; - case RL_R_TRIP: - if( skill_blown(dsrc,bl,dmg.blewcount,dir,0) < dmg.blewcount ) - skill_addtimerskill(src, tick + status_get_amotion(src), bl->id, 0, 0, RL_R_TRIP_PLUSATK, skill_lv, BF_WEAPON, flag|SD_ANIMATION); - break; - default: - skill_blown(dsrc,bl,dmg.blewcount,dir, 0x0 ); - if ( !dmg.blewcount && bl->type == BL_SKILL && damage > 0 ){ - TBL_SKILL *su = (TBL_SKILL*)bl; - if( su->group && su->group->skill_id == HT_BLASTMINE) - skill_blown(src, bl, 3, -1, 0); - } - break; - } - } - - //Delayed damage must be dealt after the knockback (it needs to know actual position of target) - if (dmg.amotion) { + // Delayed damage must be dealt after the knockback (it needs to know actual position of target) + if( dmg.amotion ) { if( shadow_flag ) { if( !status_isdead(bl) && additional_effects ) skill_additional_effect(src, bl, skill_id, skill_lv, dmg.flag, dmg.dmg_lv, tick); @@ -10855,6 +10882,8 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui skill_unitsetting(src,skill_id,skill_lv,x,y,0); break; } + + // Skill Unit Setting case MG_SAFETYWALL: case MG_FIREWALL: case MG_THUNDERSTORM: @@ -10964,6 +10993,7 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui sd->skill_id_old = skill_id; flag|=1;//Set flag to 1 to prevent deleting ammo (it will be deleted on group-delete). case GS_GROUNDDRIFT: //Ammo should be deleted right away. + case GN_WALLOFTHORN: skill_unitsetting(src,skill_id,skill_lv,x,y,0); break; case RG_GRAFFITI: /* Graffiti [Valaris] */ @@ -11390,19 +11420,6 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui skill_unitsetting(src,skill_id,skill_lv,x,y,0); break; - case GN_WALLOFTHORN: { - static const int dx[] = {-1,-2,-2,-2,-2,-2,-1, 0, 1, 2, 2, 2, 2, 2, 1, 0}; - static const int dy[] = { 2, 2, 1, 0,-1,-2,-2,-2,-2,-2,-1, 0, 1, 2, 2, 2}; - struct unit_data *ud = unit_bl2ud(src); - - for (i = 0; i < 16; i++) { - x = ud->skillx + dx[i]; - y = ud->skilly + dy[i]; - skill_unitsetting(src, skill_id, skill_lv, x, y, 0); - } - flag |= 1; - } - break; case GN_CRAZYWEED: { int area = skill_get_splash(GN_CRAZYWEED_ATK, skill_lv); for( i = 0; i < 3 + (skill_lv/2); i++ ) { @@ -12123,6 +12140,7 @@ struct skill_unit_group* skill_unitsetting (struct block_list *src, uint16 skill skill_clear_group(src, 8); break; case GN_WALLOFTHORN: + // Turns to Firewall if( flag&1 ) limit = 3000; val3 = (x<<16)|y; @@ -12188,8 +12206,8 @@ struct skill_unit_group* skill_unitsetting (struct block_list *src, uint16 skill struct skill_unit *unit; int ux = x + layout->dx[i]; int uy = y + layout->dy[i]; - int val1_2 = skill_lv; - int val2_2 = 0; + int unit_val1 = skill_lv; + int unit_val2 = 0; int alive = 1; // are the coordinates out of range? @@ -12205,11 +12223,11 @@ struct skill_unit_group* skill_unitsetting (struct block_list *src, uint16 skill switch( skill_id ) { case MG_FIREWALL: case NJ_KAENSIN: - val2_2 = group->val2; + unit_val2 = group->val2; break; case WZ_ICEWALL: - val1_2 = (skill_lv <= 1) ? 500 : 200 + 200*skill_lv; - val2_2 = map_getcell(src->m, ux, uy, CELL_GETTYPE); + unit_val1 = (skill_lv <= 1) ? 500 : 200 + 200*skill_lv; + unit_val2 = map_getcell(src->m, ux, uy, CELL_GETTYPE); break; case HT_LANDMINE: case MA_LANDMINE: @@ -12234,35 +12252,37 @@ struct skill_unit_group* skill_unitsetting (struct block_list *src, uint16 skill case RA_FIRINGTRAP: case RA_ICEBOUNDTRAP: case RL_B_TRAP: - val1_2 = 3500; + unit_val1 = 3500; break; case GS_DESPERADO: - val1_2 = abs(layout->dx[i]); - val2_2 = abs(layout->dy[i]); - if (val1_2 < 2 || val2_2 < 2) { //Nearby cross, linear decrease with no diagonals - if (val2_2 > val1_2) val1_2 = val2_2; - if (val1_2) val1_2--; - val1_2 = 36 -12*val1_2; + unit_val1 = abs(layout->dx[i]); + unit_val2 = abs(layout->dy[i]); + if (unit_val1 < 2 || unit_val2 < 2) { //Nearby cross, linear decrease with no diagonals + if (unit_val2 > unit_val1) unit_val1 = unit_val2; + if (unit_val1) unit_val1--; + unit_val1 = 36 -12*unit_val1; } else //Diagonal edges - val1_2 = 28 -4*val1_2 -4*val2_2; - if (val1_2 < 1) val1_2 = 1; - val2_2 = 0; + unit_val1 = 28 -4*unit_val1 -4*unit_val2; + if (unit_val1 < 1) unit_val1 = 1; + unit_val2 = 0; break; case WM_REVERBERATION: case WM_POEMOFNETHERWORLD: - val1_2 = 1 + skill_lv; + unit_val1 = 1 + skill_lv; break; case GN_WALLOFTHORN: - val1_2 = 2000 + 2000 * skill_lv; - val2_2 = 20; + if (flag&1) // Turned become Firewall + break; + unit_val1 = 2000 + 2000 * skill_lv; // HP + unit_val2 = 20; // Max hits break; default: if (group->state.song_dance&0x1) - val2_2 = unit_flag&(UF_DANCE|UF_SONG); //Store whether this is a song/dance + unit_val2 = unit_flag&(UF_DANCE|UF_SONG); //Store whether this is a song/dance break; } if (unit_flag&UF_RANGEDSINGLEUNIT && i == (layout->count / 2)) - val2_2 |= UF_RANGEDSINGLEUNIT; // center. + unit_val2 |= UF_RANGEDSINGLEUNIT; // center. if( sd && map_getcell(src->m, ux, uy, CELL_CHKMAELSTROM) ) //Does not recover SP from monster skills map_foreachincell(skill_maelstrom_suction,src->m,ux,uy,BL_SKILL,skill_id,skill_lv); @@ -12273,7 +12293,7 @@ struct skill_unit_group* skill_unitsetting (struct block_list *src, uint16 skill if( !alive ) continue; - nullpo_retr(NULL, (unit = skill_initunit(group,i,ux,uy,val1_2,val2_2))); + nullpo_retr(NULL, (unit = skill_initunit(group,i,ux,uy,unit_val1,unit_val2))); unit->limit = limit; unit->range = range; @@ -12558,11 +12578,11 @@ static int skill_unit_onplace (struct skill_unit *unit, struct block_list *bl, u /*========================================== * Process skill unit each interval (sg->interval, see interval field of skill_unit_db.txt) - * @param src Skill unit + * @param unit Skill unit * @param bl Valid 'target' above the unit, that has been check in skill_unit_timer_sub_onplace * @param tick *------------------------------------------*/ -int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, unsigned int tick) +int skill_unit_onplace_timer (struct skill_unit *unit, struct block_list *bl, unsigned int tick) { struct skill_unit_group *sg; struct block_list *ss; @@ -12574,13 +12594,13 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns uint16 skill_id; int diff = 0; - nullpo_ret(src); + nullpo_ret(unit); nullpo_ret(bl); - if (bl->prev == NULL || !src->alive || status_isdead(bl)) + if (bl->prev == NULL || !unit->alive || status_isdead(bl)) return 0; - nullpo_ret(sg = src->group); + nullpo_ret(sg = unit->group); nullpo_ret(ss = map_id2bl(sg->src_id)); tsd = BL_CAST(BL_PC, bl); @@ -12616,31 +12636,42 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns ts->tick += sg->interval*(map_count_oncell(bl->m,bl->x,bl->y,BL_CHAR)-1); } + // Wall of Thorn damaged by Fire element unit [Cydh] + //! TODO: This check doesn't matter the location, as long as one of units touched, this check will be executed. + if (bl->type == BL_SKILL && skill_get_ele(sg->skill_id, sg->skill_lv) == ELE_FIRE) { + struct skill_unit *su = (struct skill_unit *)bl; + if (su && su->group && su->group->unit_id == UNT_WALLOFTHORN) { + skill_unitsetting(map_id2bl(su->group->src_id), su->group->skill_id, su->group->skill_lv, su->group->val3>>16, su->group->val3&0xffff, 1); + su->group->limit = sg->limit = 0; + su->group->unit_id = sg->unit_id = UNT_USED_TRAPS; + return skill_id; + } + } + switch (sg->unit_id) { case UNT_FIREWALL: case UNT_KAEN: { int count = 0; - const int x = bl->x, y = bl->y; - if (skill_id == GN_WALLOFTHORN && battle_check_target(ss, bl, BCT_ENEMY) <= 0) + if (skill_id == GN_WALLOFTHORN && battle_check_target(ss, bl, sg->target_flag) <= 0) break; //Take into account these hit more times than the timer interval can handle. do - skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick+count*sg->interval,0); - while(--src->val2 && x == bl->x && y == bl->y && + skill_attack(BF_MAGIC,ss,&unit->bl,bl,sg->skill_id,sg->skill_lv,tick+count*sg->interval,0); + while(--unit->val2 && x == bl->x && y == bl->y && ++count < SKILLUNITTIMER_INTERVAL/sg->interval && !status_isdead(bl)); - if (src->val2 <= 0) - skill_delunit(src); + if (unit->val2 <= 0) + skill_delunit(unit); } break; case UNT_SANCTUARY: if( battle_check_undead(tstatus->race, tstatus->def_ele) || tstatus->race==RC_DEMON ) { //Only damage enemies with offensive Sanctuary. [Skotlex] - if( battle_check_target(&src->bl,bl,BCT_ENEMY) > 0 && skill_attack(BF_MAGIC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0) ) + if( battle_check_target(&unit->bl,bl,BCT_ENEMY) > 0 && skill_attack(BF_MAGIC, ss, &unit->bl, bl, sg->skill_id, sg->skill_lv, tick, 0) ) sg->val1 -= 2; // reduce healing count if this was meant for damaging [hekate] } else { int heal = skill_calc_heal(ss,bl,sg->skill_id,sg->skill_lv,true); @@ -12655,7 +12686,7 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns break; if( status_isimmune(bl) ) heal = 0; - clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1); + clif_skill_nodamage(&unit->bl, bl, AL_HEAL, heal, 1); if( tsc && tsc->data[SC_AKAITSUKI] && heal ) heal = ~heal + 1; status_heal(bl, heal, 0, 0); @@ -12670,8 +12701,8 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns //Will heal demon and undead element monsters, but not players. if ((bl->type == BL_PC) || (!battle_check_undead(tstatus->race, tstatus->def_ele) && tstatus->race!=RC_DEMON)) { //Damage enemies - if(battle_check_target(&src->bl,bl,BCT_ENEMY)>0) - skill_attack(BF_MISC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); + if(battle_check_target(&unit->bl,bl,BCT_ENEMY)>0) + skill_attack(BF_MISC, ss, &unit->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); } else { int heal = skill_calc_heal(ss,bl,sg->skill_id,sg->skill_lv,true); @@ -12679,7 +12710,7 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns break; if (status_isimmune(bl)) heal = 0; - clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1); + clif_skill_nodamage(&unit->bl, bl, AL_HEAL, heal, 1); status_heal(bl, heal, 0, 0); } break; @@ -12687,7 +12718,7 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns case UNT_MAGNUS: if (!battle_check_undead(tstatus->race,tstatus->def_ele) && tstatus->race!=RC_DEMON) break; - skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + skill_attack(BF_MAGIC,ss,&unit->bl,bl,sg->skill_id,sg->skill_lv,tick,0); break; case UNT_DUMMYSKILL: @@ -12708,7 +12739,7 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns else // mobs if( status_charge(ss, 0, 2) ) // costs 2 SP per hit { - if( !skill_attack(BF_WEAPON,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick+count*sg->interval,0) ) + if( !skill_attack(BF_WEAPON,ss,&unit->bl,bl,sg->skill_id,sg->skill_lv,tick+count*sg->interval,0) ) status_charge(ss, 0, 8); //costs additional 8 SP if miss } else @@ -12732,8 +12763,8 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns 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); + if (rnd()%100 < unit->val1) + skill_attack(BF_WEAPON,ss,&unit->bl,bl,sg->skill_id,sg->skill_lv,tick,0); break; case GN_CRAZYWEED_ATK: if( bl->type == BL_SKILL ){ @@ -12742,19 +12773,19 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns break; } default: - skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + skill_attack(skill_get_type(sg->skill_id),ss,&unit->bl,bl,sg->skill_id,sg->skill_lv,tick,0); } break; case UNT_FIREPILLAR_WAITING: - skill_unitsetting(ss,sg->skill_id,sg->skill_lv,src->bl.x,src->bl.y,1); - skill_delunit(src); + skill_unitsetting(ss,sg->skill_id,sg->skill_lv,unit->bl.x,unit->bl.y,1); + skill_delunit(unit); break; case UNT_SKIDTRAP: { - skill_blown(&src->bl,bl,skill_get_blewcount(sg->skill_id,sg->skill_lv),unit_getdir(bl),0); + skill_blown(&unit->bl,bl,skill_get_blewcount(sg->skill_id,sg->skill_lv),unit_getdir(bl),0); sg->unit_id = UNT_USED_TRAPS; - clif_changetraplook(&src->bl, UNT_USED_TRAPS); + clif_changetraplook(&unit->bl, UNT_USED_TRAPS); sg->limit=DIFF_TICK(tick,sg->tick)+1500; } break; @@ -12769,25 +12800,25 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns if( td ) sec = DIFF_TICK(td->tick, tick); - if( sg->unit_id == UNT_MANHOLE || battle_config.skill_trap_type || !map_flag_gvg(src->bl.m) ) { - unit_movepos(bl, src->bl.x, src->bl.y, 0, 0); + if( sg->unit_id == UNT_MANHOLE || battle_config.skill_trap_type || !map_flag_gvg(unit->bl.m) ) { + unit_movepos(bl, unit->bl.x, unit->bl.y, 0, 0); clif_fixpos(bl); } sg->val2 = bl->id; } else sec = 3000; //Couldn't trap it? if (sg->unit_id == UNT_ANKLESNARE) { - clif_skillunit_update(&src->bl); + clif_skillunit_update(&unit->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") * bugreport:3961 **/ - clif_changetraplook(&src->bl, UNT_ANKLESNARE); + clif_changetraplook(&unit->bl, UNT_ANKLESNARE); } sg->limit = DIFF_TICK(tick,sg->tick)+sec; sg->interval = -1; - src->range = 0; + unit->range = 0; } break; @@ -12796,10 +12827,10 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns if( status_get_mode(bl)&MD_BOSS ) break; if( status_change_start(ss, bl,type,10000,sg->skill_lv,sg->group_id,0,0,skill_get_time2(sg->skill_id, sg->skill_lv), 8) ) { - map_moveblock(bl, src->bl.x, src->bl.y, tick); + map_moveblock(bl, unit->bl.x, unit->bl.y, tick); clif_fixpos(bl); } - map_foreachinrange(skill_trap_splash, &src->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &src->bl, tick); + map_foreachinrange(skill_trap_splash, &unit->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &unit->bl, tick); sg->unit_id = UNT_USED_TRAPS; //Changed ID so it does not invoke a for each in area again. } break; @@ -12831,11 +12862,11 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns if (tsc && tsc->data[SC__MANHOLE]) break; 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); + map_foreachinrange(skill_trap_splash, &unit->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag|BL_SKILL|~BCT_SELF, &unit->bl, tick); else - map_foreachinrange(skill_trap_splash, &src->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &src->bl, tick); + map_foreachinrange(skill_trap_splash, &unit->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &unit->bl, tick); if (sg->unit_id != UNT_FIREPILLAR_ACTIVE) - clif_changetraplook(&src->bl,(sg->unit_id == UNT_LANDMINE ? UNT_FIREPILLAR_ACTIVE : UNT_USED_TRAPS)); + clif_changetraplook(&unit->bl,(sg->unit_id == UNT_LANDMINE ? UNT_FIREPILLAR_ACTIVE : UNT_USED_TRAPS)); sg->unit_id = UNT_USED_TRAPS; //Changed ID so it does not invoke a for each in area again. sg->limit = DIFF_TICK(tick, sg->tick) + 1500 + (sg->unit_id == UNT_CLUSTERBOMB || sg->unit_id == UNT_ICEBOUNDTRAP ? 1000 : 0); // Cluster Bomb/Icebound has 1s to disappear once activated. @@ -12845,9 +12876,9 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns if (sg->src_id == bl->id) break; if (sg->val2 == 0) { - clif_talkiebox(&src->bl, sg->valstr); + clif_talkiebox(&unit->bl, sg->valstr); sg->unit_id = UNT_USED_TRAPS; - clif_changetraplook(&src->bl, UNT_USED_TRAPS); + clif_changetraplook(&unit->bl, UNT_USED_TRAPS); sg->limit = DIFF_TICK(tick, sg->tick) + 5000; sg->val2 = -1; } @@ -12865,7 +12896,7 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns break; case UNT_DISSONANCE: - skill_attack(BF_MISC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); + skill_attack(BF_MISC, ss, &unit->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); break; case UNT_APPLEIDUN: { //Apple of Idun [Skotlex] @@ -12881,14 +12912,14 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns heal = skill_calc_heal(ss,bl,sg->skill_id, sg->skill_lv, true); if (tsc->data[SC_AKAITSUKI] && heal) heal = ~heal + 1; - clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1); + clif_skill_nodamage(&unit->bl, bl, AL_HEAL, heal, 1); status_heal(bl, heal, 0, 0); } break; case UNT_TATAMIGAESHI: case UNT_DEMONSTRATION: - skill_attack(BF_WEAPON,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + skill_attack(BF_WEAPON,ss,&unit->bl,bl,sg->skill_id,sg->skill_lv,tick,0); break; case UNT_GOSPEL: @@ -12955,14 +12986,14 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns break; } } - else if (battle_check_target(&src->bl,bl,BCT_ENEMY)>0) + else if (battle_check_target(&unit->bl,bl,BCT_ENEMY)>0) { // Offensive Effect int i = rnd()%9; // Negative buff count int time = skill_get_time2(sg->skill_id, sg->skill_lv); switch (i) { case 0: // Deal 1~9999 damage - skill_attack(BF_MISC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + skill_attack(BF_MISC,ss,&unit->bl,bl,sg->skill_id,sg->skill_lv,tick,0); break; case 1: // Curse sc_start(ss, bl,SC_CURSE,100,1,time); @@ -12994,10 +13025,10 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns case UNT_BASILICA: { - int i = battle_check_target(&src->bl, bl, BCT_ENEMY); + int i = battle_check_target(&unit->bl, bl, BCT_ENEMY); if( i > 0 && !(status_get_mode(bl)&MD_BOSS) ) { // knock-back any enemy except Boss - skill_blown(&src->bl, bl, skill_get_blewcount(skill_id, sg->skill_lv), unit_getdir(bl), 0); + skill_blown(&unit->bl, bl, skill_get_blewcount(skill_id, sg->skill_lv), unit_getdir(bl), 0); clif_fixpos(bl); break; } @@ -13013,7 +13044,7 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns case UNT_PSYCHIC_WAVE: case UNT_MAGMA_ERUPTION: case UNT_MAKIBISHI: - skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + skill_attack(skill_get_type(sg->skill_id),ss,&unit->bl,bl,sg->skill_id,sg->skill_lv,tick,0); break; case UNT_GROUNDDRIFT_WIND: @@ -13021,9 +13052,9 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns case UNT_GROUNDDRIFT_POISON: case UNT_GROUNDDRIFT_WATER: case UNT_GROUNDDRIFT_FIRE: - map_foreachinrange(skill_trap_splash,&src->bl, + map_foreachinrange(skill_trap_splash,&unit->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, - &src->bl,tick); + &unit->bl,tick); sg->unit_id = UNT_USED_TRAPS; //clif_changetraplook(&src->bl, UNT_FIREPILLAR_ACTIVE); sg->limit=DIFF_TICK(tick,sg->tick)+1500; @@ -13047,9 +13078,9 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns hp = tstatus->max_hp * hp / 100; sp = tstatus->max_sp * sp / 100; if (tstatus->hp < tstatus->max_hp) - clif_skill_nodamage(&src->bl, bl, AL_HEAL, hp, 1); + clif_skill_nodamage(&unit->bl, bl, AL_HEAL, hp, 1); if (tstatus->sp < tstatus->max_sp) - clif_skill_nodamage(&src->bl, bl, MG_SRECOVERY, sp, 1); + clif_skill_nodamage(&unit->bl, bl, MG_SRECOVERY, sp, 1); if (tsc && tsc->data[SC_AKAITSUKI] && hp) hp = ~hp + 1; status_heal(bl, hp, sp, 3); @@ -13084,18 +13115,18 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns break; 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); + clif_changetraplook(&unit->bl,UNT_USED_TRAPS); + map_foreachinrange(skill_trap_splash,&unit->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &unit->bl,tick); sg->limit = DIFF_TICK(tick,sg->tick)+1000; sg->unit_id = UNT_USED_TRAPS; break; case UNT_SEVERE_RAINSTORM: - if( battle_check_target(&src->bl, bl, BCT_ENEMY) > 0 ) - skill_attack(BF_WEAPON,ss,&src->bl,bl,WM_SEVERE_RAINSTORM_MELEE,sg->skill_lv,tick,0); + if( battle_check_target(&unit->bl, bl, BCT_ENEMY) > 0 ) + skill_attack(BF_WEAPON,ss,&unit->bl,bl,WM_SEVERE_RAINSTORM_MELEE,sg->skill_lv,tick,0); break; case UNT_NETHERWORLD: - if (!(status_get_mode(bl)&MD_BOSS) || (!map_flag_gvg2(ss->m) && battle_check_target(&src->bl,bl,BCT_PARTY) < 0)) { + if (!(status_get_mode(bl)&MD_BOSS) || (!map_flag_gvg2(ss->m) && battle_check_target(&unit->bl,bl,BCT_PARTY) < 0)) { if (!(tsc && tsc->data[type])) { sc_start(ss, bl, type, 100, sg->skill_lv, skill_get_time2(sg->skill_id,sg->skill_lv)); sg->limit = DIFF_TICK(tick,sg->tick); @@ -13123,14 +13154,13 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns break; case UNT_WALLOFTHORN: + if (unit->val2-- <= 0) // Max hit reached + break; if (status_get_mode(bl)&MD_BOSS) break; // This skill doesn't affect to Boss monsters. [iRO Wiki] - if (battle_check_target(ss, bl, BCT_ENEMY) <= 0) { - unit_stop_walking(bl, 1); - skill_blown(&src->bl, bl, skill_get_blewcount(sg->skill_id, sg->skill_lv), unit_getdir(bl), 0x2); - clif_fixpos(bl); - } else - skill_attack(skill_get_type(sg->skill_id), ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); + skill_blown(&unit->bl, bl, skill_get_blewcount(sg->skill_id, sg->skill_lv), -1, 0); + clif_fixpos(bl); + skill_addtimerskill(ss, tick + 100, bl->id, unit->bl.x, unit->bl.y, sg->skill_id, sg->skill_lv, skill_get_type(sg->skill_id), 4|SD_LEVEL); break; case UNT_DEMONIC_FIRE: @@ -13138,14 +13168,14 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns case 1: default: sc_start(ss, bl, SC_BURNING, 4 + 4 * sg->skill_lv, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv)); - skill_attack(skill_get_type(skill_id), ss, &src->bl, bl, sg->skill_id, sg->skill_lv + 10 * sg->val2, tick, 0); + skill_attack(skill_get_type(skill_id), ss, &unit->bl, bl, sg->skill_id, sg->skill_lv + 10 * sg->val2, tick, 0); break; } break; case UNT_HELLS_PLANT: - if( battle_check_target(&src->bl,bl,BCT_ENEMY) > 0 ) - skill_attack(skill_get_type(GN_HELLS_PLANT_ATK), ss, &src->bl, bl, GN_HELLS_PLANT_ATK, sg->skill_lv, tick, 0); + if( battle_check_target(&unit->bl,bl,BCT_ENEMY) > 0 ) + skill_attack(skill_get_type(GN_HELLS_PLANT_ATK), ss, &unit->bl, bl, GN_HELLS_PLANT_ATK, sg->skill_lv, tick, 0); if( ss != bl) // The caster is the only one who can step on the Plants without destroying them sg->limit = DIFF_TICK(tick, sg->tick) + 100; break; @@ -13153,7 +13183,7 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns case UNT_CLOUD_KILL: if(tsc && !tsc->data[type]) status_change_start(ss, bl,type,10000,sg->skill_lv,sg->group_id,0,0,skill_get_time2(sg->skill_id,sg->skill_lv),8); - skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + skill_attack(skill_get_type(sg->skill_id),ss,&unit->bl,bl,sg->skill_id,sg->skill_lv,tick,0); break; case UNT_WARMER: @@ -13166,7 +13196,7 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns else hp = tstatus->max_hp * sg->skill_lv / 100; if( tstatus->hp != tstatus->max_hp ) - clif_skill_nodamage(&src->bl, bl, AL_HEAL, hp, 0); + clif_skill_nodamage(&unit->bl, bl, AL_HEAL, hp, 0); if( tsc && tsc->data[SC_AKAITSUKI] && hp ) hp = ~hp + 1; status_heal(bl, hp, 0, 0); @@ -13213,20 +13243,20 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns break; case UNT_BANDING: - if( battle_check_target(&src->bl, bl, BCT_ENEMY) > 0 && !(status_get_mode(bl)&MD_BOSS) && !(tsc && tsc->data[SC_BANDING_DEFENCE]) ) - sc_start(ss, bl, SC_BANDING_DEFENCE, (status_get_lv(&src->bl) / 5) + (sg->skill_lv * 5) - (status_get_agi(bl) / 10), 90, skill_get_time2(sg->skill_id, sg->skill_lv)); + if( battle_check_target(&unit->bl, bl, BCT_ENEMY) > 0 && !(status_get_mode(bl)&MD_BOSS) && !(tsc && tsc->data[SC_BANDING_DEFENCE]) ) + sc_start(ss, bl, SC_BANDING_DEFENCE, (status_get_lv(&unit->bl) / 5) + (sg->skill_lv * 5) - (status_get_agi(bl) / 10), 90, skill_get_time2(sg->skill_id, sg->skill_lv)); break; case UNT_FIRE_MANTLE: - if( battle_check_target(&src->bl, bl, BCT_ENEMY) > 0 ) - skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + if( battle_check_target(&unit->bl, bl, BCT_ENEMY) > 0 ) + skill_attack(BF_MAGIC,ss,&unit->bl,bl,sg->skill_id,sg->skill_lv,tick,0); break; case UNT_ZENKAI_WATER: case UNT_ZENKAI_LAND: case UNT_ZENKAI_FIRE: case UNT_ZENKAI_WIND: - if( battle_check_target(&src->bl,bl,BCT_ENEMY) > 0 ) { + if( battle_check_target(&unit->bl,bl,BCT_ENEMY) > 0 ) { switch( sg->unit_id ) { case UNT_ZENKAI_WATER: switch (rnd()%2 + 1) { @@ -13270,24 +13300,24 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns break; case UNT_LAVA_SLIDE: - skill_attack(BF_WEAPON, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); + skill_attack(BF_WEAPON, ss, &unit->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); if(++sg->val1 > 4) //after 5 stop hit and destroy me sg->limit = DIFF_TICK(tick, sg->tick); break; case UNT_POISON_MIST: - skill_attack(BF_MAGIC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); + skill_attack(BF_MAGIC, ss, &unit->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); status_change_start(ss, bl, SC_BLIND, (10 + 10 * sg->skill_lv)*100, sg->skill_lv, sg->skill_id, 0, 0, skill_get_time2(sg->skill_id, sg->skill_lv), 2|8); break; case UNT_B_TRAP: sc_start(ss,bl,SC_B_TRAP,100,sg->skill_lv,max(status_get_str(bl) * 150,5000)); //(custom) sg->unit_id = UNT_USED_TRAPS; - clif_changetraplook(&src->bl, UNT_USED_TRAPS); + clif_changetraplook(&unit->bl, UNT_USED_TRAPS); sg->limit = DIFF_TICK(tick,sg->tick)+1500; break; case UNT_FIRE_RAIN: clif_skill_damage(ss,bl,tick,status_get_amotion(ss),0, - skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,SD_ANIMATION|SD_SPLASH), + skill_attack(skill_get_type(sg->skill_id),ss,&unit->bl,bl,sg->skill_id,sg->skill_lv,tick,SD_ANIMATION|SD_SPLASH), 1,sg->skill_id,sg->skill_lv,6); break; } @@ -13563,11 +13593,10 @@ static int skill_unit_effect (struct block_list* bl, va_list ap) /*========================================== * Check skill unit while receiving damage * @param unit Skill unit - * @param bl Attacker * @param damage Received damage * @return Damage *------------------------------------------*/ -int64 skill_unit_ondamaged (struct skill_unit *unit, struct block_list *bl, int64 damage) +int64 skill_unit_ondamaged (struct skill_unit *unit, int64 damage) { struct skill_unit_group *sg; @@ -16963,17 +16992,16 @@ int skill_delunitgroup_(struct skill_unit_group *group, const char* file, int li { struct block_list* src; struct unit_data *ud; - int i,j; + short i, j; - if( group == NULL ) - { + if( group == NULL ) { ShowDebug("skill_delunitgroup: group is NULL (source=%s:%d, %s)! Please report this! (#3504)\n", file, line, func); return 0; } src=map_id2bl(group->src_id); ud = unit_bl2ud(src); - if(!src || !ud) { + if (!src || !ud) { ShowError("skill_delunitgroup: Group's source not found! (src_id: %d skill_id: %d)\n", group->src_id, group->skill_id); return 0; } @@ -16995,31 +17023,25 @@ int skill_delunitgroup_(struct skill_unit_group *group, const char* file, int li } } - if (skill_get_unit_flag(group->skill_id)&(UF_DANCE|UF_SONG|UF_ENSEMBLE)) - { + if (skill_get_unit_flag(group->skill_id)&(UF_DANCE|UF_SONG|UF_ENSEMBLE)) { struct status_change* sc = status_get_sc(src); - if (sc && sc->data[SC_DANCING]) - { + if (sc && sc->data[SC_DANCING]) { sc->data[SC_DANCING]->val2 = 0 ; //This prevents status_change_end attempting to redelete the group. [Skotlex] status_change_end(src, SC_DANCING, INVALID_TIMER); } } - // end Gospel's status change on 'src' - // (needs to be done when the group is deleted by other means than skill deactivation) - if (group->unit_id == UNT_GOSPEL) { - struct status_change *sc = status_get_sc(src); - if(sc && sc->data[SC_GOSPEL]) { - sc->data[SC_GOSPEL]->val3 = 0; //Remove reference to this group. [Skotlex] - status_change_end(src, SC_GOSPEL, INVALID_TIMER); - } + // End SC from the master when the skill group is deleted + i = SC_NONE; + switch (group->unit_id) { + case UNT_GOSPEL: i = SC_GOSPEL; break; + case UNT_BASILICA: i = SC_BASILICA; break; } - - if (group->unit_id == UNT_BASILICA) { + if (i != SC_NONE) { struct status_change *sc = status_get_sc(src); - if(sc && sc->data[SC_BASILICA]) { - sc->data[SC_BASILICA]->val3 = 0; - status_change_end(src, SC_BASILICA, INVALID_TIMER); + if (sc && sc->data[i]) { + sc->data[i]->val3 = 0; //Remove reference to this group. [Skotlex] + status_change_end(src, (sc_type)i, INVALID_TIMER); } } @@ -17371,10 +17393,16 @@ static int skill_unit_timer_sub(DBKey key, DBData *data, va_list ap) } break; case UNT_WALLOFTHORN: - if( unit->val1 <= 0 ) { + if (unit->val1 <= 0 || unit->val2 <= 0 || group->val3 < 0) { group->unit_id = UNT_USED_TRAPS; group->limit = DIFF_TICK(tick, group->tick); } + /*if (group->val3 < 0) { // Remove if attacked by fire element, turned to Fire Wall + skill_delunitgroup(group); + break; + } + if (unit->val1 <= 0 || unit->val2 <= 0) // Remove unit only if no HP or hit limit reached + skill_delunit(unit);*/ break; } } @@ -17550,10 +17578,13 @@ int skill_unit_move (struct block_list *bl, unsigned int tick, int flag) { } /*========================================== - * + * Moves skill unit group to map m with coordinates x & y (example when knocked back) + * @param group Skill Group + * @param m Map + * @param dx + * @param dy *------------------------------------------*/ -int skill_unit_move_unit_group (struct skill_unit_group *group, int16 m, int16 dx, int16 dy) -{ +void skill_unit_move_unit_group(struct skill_unit_group *group, int16 m, int16 dx, int16 dy) { int i,j; unsigned int tick = gettick(); int *m_flag; @@ -17561,14 +17592,14 @@ int skill_unit_move_unit_group (struct skill_unit_group *group, int16 m, int16 d struct skill_unit *unit2; if (group == NULL) - return 0; - if (group->unit_count<=0) - return 0; - if (group->unit==NULL) - return 0; + return; + if (group->unit_count <= 0) + return; + if (group->unit == NULL) + return; if (skill_get_unit_flag(group->skill_id)&UF_ENSEMBLE) - return 0; //Ensembles may not be moved around. + return; //Ensembles may not be moved around. m_flag = (int *) aCalloc(group->unit_count, sizeof(int)); // m_flag @@ -17576,25 +17607,25 @@ int skill_unit_move_unit_group (struct skill_unit_group *group, int16 m, int16 d // 1: Unit will move to a slot that had another unit of the same group (skill_unit_onplace not needed) // 2: Another unit from same group will end up positioned on this unit (skill_unit_onout not needed) // 3: Both 1+2. - for(i=0;iunit_count;i++){ - unit1=&group->unit[i]; - if (!unit1->alive || unit1->bl.m!=m) + for(i = 0; i < group->unit_count; i++){ + unit1 =& group->unit[i]; + if (!unit1->alive || unit1->bl.m != m) continue; - for(j=0;junit_count;j++){ - unit2=&group->unit[j]; + for(j = 0; j < group->unit_count; j++){ + unit2 =& group->unit[j]; if (!unit2->alive) continue; - if (unit1->bl.x+dx==unit2->bl.x && unit1->bl.y+dy==unit2->bl.y){ + if (unit1->bl.x+dx == unit2->bl.x && unit1->bl.y+dy == unit2->bl.y){ m_flag[i] |= 0x1; } - if (unit1->bl.x-dx==unit2->bl.x && unit1->bl.y-dy==unit2->bl.y){ + if (unit1->bl.x-dx == unit2->bl.x && unit1->bl.y-dy == unit2->bl.y){ m_flag[i] |= 0x2; } } } j = 0; - for (i=0;iunit_count;i++) { - unit1=&group->unit[i]; + for (i = 0; i < group->unit_count; i++) { + unit1 =& group->unit[i]; if (!unit1->alive) continue; if (!(m_flag[i]&0x2)) { @@ -17603,8 +17634,7 @@ int skill_unit_move_unit_group (struct skill_unit_group *group, int16 m, int16 d map_foreachincell(skill_unit_effect,unit1->bl.m,unit1->bl.x,unit1->bl.y,group->bl_flag,&unit1->bl,tick,4); } //Move Cell using "smart" criteria (avoid useless moving around) - switch(m_flag[i]) - { + switch(m_flag[i]) { case 0: //Cell moves independently, safely move it. map_moveblock(&unit1->bl, unit1->bl.x+dx, unit1->bl.y+dy, tick); @@ -17612,8 +17642,7 @@ int skill_unit_move_unit_group (struct skill_unit_group *group, int16 m, int16 d case 1: //Cell moves unto another cell, look for a replacement cell that won't collide //and has no cell moving into it (flag == 2) - for(;junit_count;j++) - { + for(;junit_count;j++) { if(m_flag[j]!=2 || !group->unit[j].alive) continue; //Move to where this cell would had moved. @@ -17635,7 +17664,7 @@ int skill_unit_move_unit_group (struct skill_unit_group *group, int16 m, int16 d } } aFree(m_flag); - return 0; + return; } /*========================================== @@ -19063,6 +19092,14 @@ void skill_init_unit_layout (void) { pos++; } break; + case GN_WALLOFTHORN: { + static const int dx[] = {-1,-2,-2,-2,-2,-2,-1, 0, 1, 2, 2, 2, 2, 2, 1, 0}; + static const int dy[] = { 2, 2, 1, 0,-1,-2,-2,-2,-2,-2,-1, 0, 1, 2, 2, 2}; + skill_unit_layout[pos].count = 16; + memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); + memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); + } + break; default: ShowError("unknown unit layout at skill %d\n",i); break; @@ -19359,7 +19396,7 @@ void skill_init_nounit_layout (void) { pos++; } - if( pos >= MAX_SKILL_UNIT_LAYOUT ) + if( pos >= MAX_SKILL_UNIT_LAYOUT2 ) ShowError("skill_init_nounit_layout: The skill_nounit_layout has met the limit or overflowed (pos=%d)\n", pos); } diff --git a/src/map/skill.h b/src/map/skill.h index 6408f7a898..de136b97f6 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -184,7 +184,8 @@ struct s_skill_db { }; extern struct s_skill_db skill_db[MAX_SKILL_DB]; -#define MAX_SKILL_UNIT_LAYOUT 55 +#define MAX_SKILL_UNIT_LAYOUT 52 +#define MAX_SKILL_UNIT_LAYOUT2 17 #define MAX_SQUARE_LAYOUT 5 // 11*11 Placement of a maximum unit #define MAX_SKILL_UNIT_COUNT ((MAX_SQUARE_LAYOUT*2+1)*(MAX_SQUARE_LAYOUT*2+1)) struct s_skill_unit_layout { @@ -388,7 +389,7 @@ int skill_delunitgroup_(struct skill_unit_group *group, const char* file, int li void skill_clear_unitgroup(struct block_list *src); int skill_clear_group(struct block_list *bl, int flag); void ext_skill_unit_onplace(struct skill_unit *unit, struct block_list *bl, unsigned int tick); -int64 skill_unit_ondamaged(struct skill_unit *unit,struct block_list *bl,int64 damage); +int64 skill_unit_ondamaged(struct skill_unit *unit,int64 damage); int skill_castfix( struct block_list *bl, uint16 skill_id, uint16 skill_lv); int skill_castfix_sc( struct block_list *bl, int time); @@ -410,7 +411,7 @@ int skill_check_pc_partner(struct map_session_data *sd, uint16 skill_id, uint16 int skill_check_unit_cell(uint16 skill_id,int16 m,int16 x,int16 y,int unit_id); int skill_unit_out_all( struct block_list *bl,unsigned int tick,int range); int skill_unit_move(struct block_list *bl,unsigned int tick,int flag); -int skill_unit_move_unit_group( struct skill_unit_group *group, int16 m,int16 dx,int16 dy); +void skill_unit_move_unit_group( struct skill_unit_group *group, int16 m,int16 dx,int16 dy); struct skill_unit_group *skill_check_dancing( struct block_list *src ); diff --git a/src/map/status.c b/src/map/status.c index bcc88a717b..03881f3ee8 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -1352,7 +1352,7 @@ int status_damage(struct block_list *src,struct block_list *target,int64 dhp, in } if (target->type == BL_SKILL) - return (int)skill_unit_ondamaged((struct skill_unit *)target, src, hp); + return (int)skill_unit_ondamaged((struct skill_unit *)target, hp); status = status_get_status_data(target); if(!status || status == &dummy_status ) diff --git a/src/map/unit.c b/src/map/unit.c index cccc9bbf54..18ae5fef74 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -898,7 +898,7 @@ uint8 unit_getdir(struct block_list *bl) * @param dx: Destination cell X * @param dy: Destination cell Y * @param count: How many cells to push bl - * @param flag: Whether or not to send position packet updates + * @param flag: &1 Whether or not to send position packet updates * @return count (can be modified due to map cell restrictions) */ int unit_blown(struct block_list* bl, int dx, int dy, int count, int flag) From 4fd7e46345b657ae818f583e3f5b63c8e0c3bebd Mon Sep 17 00:00:00 2001 From: Cydh Ramdh Date: Fri, 22 Aug 2014 23:42:13 +0700 Subject: [PATCH 2/5] Follow up 70b8b8b, an error for PRE-RE Signed-off-by: Cydh Ramdh --- src/map/skill.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map/skill.c b/src/map/skill.c index 84e915b7f8..ded25e3fd5 100755 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -12758,7 +12758,7 @@ int skill_unit_onplace_timer (struct skill_unit *unit, struct block_list *bl, un case WZ_STORMGUST: //SG counter does not reset per stormgust. IE: One hit from a SG and two hits from another will freeze you. if (tsc) 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) + if (skill_attack(skill_get_type(sg->skill_id),ss,&unit->bl,bl,sg->skill_id,sg->skill_lv,tick,0) <= 0 && tsc) tsc->sg_counter=0; //Attack absorbed. break; #endif From 416f6d7ba893691da675020b161d7c05764d9504 Mon Sep 17 00:00:00 2001 From: Cydh Ramdh Date: Sat, 23 Aug 2014 01:47:30 +0700 Subject: [PATCH 3/5] - Added error messages for some script commands - Halt script if no item equipped for 'delequip' just like 'delitem' does Signed-off-by: Cydh Ramdh --- src/map/script.c | 175 +++++++++++++++++++++++++++-------------------- 1 file changed, 101 insertions(+), 74 deletions(-) diff --git a/src/map/script.c b/src/map/script.c index fdf4ef9bbd..e1bfb26253 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -8052,29 +8052,31 @@ BUILDIN_FUNC(getequippercentrefinery) /*========================================== * Refine +1 item at pos and log and display refine *------------------------------------------*/ -BUILDIN_FUNC(successrefitem) -{ - int i = -1, num, up = 1; +BUILDIN_FUNC(successrefitem) { + short i = -1, up = 1; + int pos; TBL_PC *sd; - num = script_getnum(st,2); + pos = script_getnum(st,2); sd = script_rid2sd(st); - if( sd == NULL ) + if (sd == NULL) return 0; - if( script_hasdata(st, 3) ) + if (script_hasdata(st, 3)) up = script_getnum(st, 3); - if (num > 0 && num <= ARRAYLENGTH(equip)) - i = pc_checkequip(sd,equip[num-1]); - if(i >= 0) { + if (pos > 0 && pos <= ARRAYLENGTH(equip)) + i = pc_checkequip(sd,equip[pos-1]); + if (i >= 0) { unsigned int ep = sd->status.inventory[i].equip; //Logs items, got from (N)PC scripts [Lupus] log_pick_pc(sd, LOG_TYPE_SCRIPT, -1, &sd->status.inventory[i]); - if (sd->status.inventory[i].refine >= MAX_REFINE) + if (sd->status.inventory[i].refine >= MAX_REFINE) { + script_pushint(st, MAX_REFINE); return SCRIPT_CMD_SUCCESS; + } sd->status.inventory[i].refine += up; sd->status.inventory[i].refine = cap_value( sd->status.inventory[i].refine, 0, MAX_REFINE); @@ -8089,10 +8091,10 @@ BUILDIN_FUNC(successrefitem) clif_additem(sd,i,1,0); pc_equipitem(sd,i,ep); clif_misceffect(&sd->bl,3); - if(sd->status.inventory[i].refine == MAX_REFINE && + if (sd->status.inventory[i].refine == MAX_REFINE && sd->status.inventory[i].card[0] == CARD0_FORGE && - sd->status.char_id == (int)MakeDWord(sd->status.inventory[i].card[2],sd->status.inventory[i].card[3]) - ){ // Fame point system [DracoRPG] + sd->status.char_id == (int)MakeDWord(sd->status.inventory[i].card[2],sd->status.inventory[i].card[3])) + { // Fame point system [DracoRPG] switch (sd->inventory_data[i]->wlv){ case 1: pc_addfame(sd, battle_config.fame_refine_lv1); // Success to refine to +10 a lv1 weapon you forged = +1 fame point @@ -8105,57 +8107,63 @@ BUILDIN_FUNC(successrefitem) break; } } + script_pushint(st, sd->status.inventory[i].refine); + return SCRIPT_CMD_SUCCESS; } - return SCRIPT_CMD_SUCCESS; + ShowError("buildin_successrefitem: No item equipped at pos %d (CID=%d/AID=%d).\n", pos, sd->status.char_id, sd->status.account_id); + script_pushint(st, -1); + return SCRIPT_CMD_FAILURE; } /*========================================== * Show a failed Refine +1 attempt *------------------------------------------*/ -BUILDIN_FUNC(failedrefitem) -{ - int i=-1,num; +BUILDIN_FUNC(failedrefitem) { + short i = -1; + int pos; TBL_PC *sd; - num = script_getnum(st,2); + pos = script_getnum(st,2); sd = script_rid2sd(st); - if( sd == NULL ) + if (sd == NULL) return 0; - if (num > 0 && num <= ARRAYLENGTH(equip)) - i=pc_checkequip(sd,equip[num-1]); - if(i >= 0) { + if (pos > 0 && pos <= ARRAYLENGTH(equip)) + i = pc_checkequip(sd,equip[pos-1]); + if (i >= 0) { sd->status.inventory[i].refine = 0; pc_unequipitem(sd,i,3); //recalculate bonus clif_refine(sd->fd,1,i,sd->status.inventory[i].refine); //notify client of failure - pc_delitem(sd,i,1,0,2,LOG_TYPE_SCRIPT); - clif_misceffect(&sd->bl,2); // display failure effect + script_pushint(st, 1); + return SCRIPT_CMD_SUCCESS; } - return SCRIPT_CMD_SUCCESS; + ShowError("buildin_failedrefitem: No item equipped at pos %d (CID=%d/AID=%d).\n", pos, sd->status.char_id, sd->status.account_id); + script_pushint(st, 0); + return SCRIPT_CMD_FAILURE; } /*========================================== * Downgrades an Equipment Part by -1 . [Masao] *------------------------------------------*/ -BUILDIN_FUNC(downrefitem) -{ - int i = -1, num, down = 1; +BUILDIN_FUNC(downrefitem) { + short i = -1, down = 1; + int pos; TBL_PC *sd; sd = script_rid2sd(st); if( sd == NULL ) return 0; - num = script_getnum(st,2); - if( script_hasdata(st, 3) ) + pos = script_getnum(st,2); + if (script_hasdata(st, 3)) down = script_getnum(st, 3); - if (num > 0 && num <= ARRAYLENGTH(equip)) - i = pc_checkequip(sd,equip[num-1]); - if(i >= 0) { + if (pos > 0 && pos <= ARRAYLENGTH(equip)) + i = pc_checkequip(sd,equip[pos-1]); + if (i >= 0) { unsigned int ep = sd->status.inventory[i].equip; //Logs items, got from (N)PC scripts [Lupus] @@ -8174,30 +8182,40 @@ BUILDIN_FUNC(downrefitem) clif_additem(sd,i,1,0); pc_equipitem(sd,i,ep); clif_misceffect(&sd->bl,2); + script_pushint(st, sd->status.inventory[i].refine); + return SCRIPT_CMD_SUCCESS; } - return SCRIPT_CMD_SUCCESS; + ShowError("buildin_downrefitem: No item equipped at pos %d (CID=%d/AID=%d).\n", pos, sd->status.char_id, sd->status.account_id); + script_pushint(st, -1); + return SCRIPT_CMD_FAILURE; } /*========================================== * Delete the item equipped at pos. *------------------------------------------*/ -BUILDIN_FUNC(delequip) -{ - int i=-1,num,ret=0; +BUILDIN_FUNC(delequip) { + short i = -1; + int pos; + int8 ret; TBL_PC *sd; - num = script_getnum(st,2); + pos = script_getnum(st,2); sd = script_rid2sd(st); - if( sd == NULL ) + if (sd == NULL) return 0; - if (num > 0 && num <= ARRAYLENGTH(equip)) - i=pc_checkequip(sd,equip[num-1]); - if(i >= 0) { + if (pos > 0 && pos <= ARRAYLENGTH(equip)) + i = pc_checkequip(sd,equip[pos-1]); + if (i >= 0) { pc_unequipitem(sd,i,3); //recalculate bonus ret = !(pc_delitem(sd,i,1,0,2,LOG_TYPE_SCRIPT)); } + else { + ShowError("buildin_delequip: No item equipped at pos %d (CID=%d/AID=%d).\n", pos, sd->status.char_id, sd->status.account_id); + st->state = END; + return SCRIPT_CMD_FAILURE; + } script_pushint(st,ret); return SCRIPT_CMD_SUCCESS; @@ -8206,27 +8224,29 @@ BUILDIN_FUNC(delequip) /*========================================== * Break the item equipped at pos. *------------------------------------------*/ -BUILDIN_FUNC(breakequip) -{ - int i=-1,num; +BUILDIN_FUNC(breakequip) { + short i = -1; + int pos; TBL_PC *sd; - num = script_getnum(st,2); + pos = script_getnum(st,2); sd = script_rid2sd(st); - if( sd == NULL ) + if (sd == NULL) return 0; - if (num > 0 && num <= ARRAYLENGTH(equip)) - i = pc_checkequip(sd,equip[num-1]); + if (pos > 0 && pos <= ARRAYLENGTH(equip)) + i = pc_checkequip(sd,equip[pos-1]); if (i >= 0) { sd->status.inventory[i].attribute = 1; pc_unequipitem(sd,i,3); clif_equiplist(sd); script_pushint(st,1); - } else - script_pushint(st,0); + return SCRIPT_CMD_SUCCESS; + } - return SCRIPT_CMD_SUCCESS; + ShowError("buildin_breakequip: No item equipped at pos %d (CID=%d/AID=%d).\n", pos, sd->status.char_id, sd->status.account_id); + script_pushint(st,0); + return SCRIPT_CMD_FAILURE; } /*========================================== @@ -14214,42 +14234,49 @@ BUILDIN_FUNC(day) //======================================================= // Unequip [Spectre] //------------------------------------------------------- -BUILDIN_FUNC(unequip) -{ - size_t num; +BUILDIN_FUNC(unequip) { + int pos; TBL_PC *sd; - num = script_getnum(st,2); - sd = script_rid2sd(st); - if( sd != NULL && num >= 1 && num <= ARRAYLENGTH(equip) ) - { - short i = pc_checkequip(sd,equip[num-1]); - if (i >= 0) + if (!(sd = script_rid2sd(st))) + return SCRIPT_CMD_SUCCESS; + + pos = script_getnum(st,2); + if (pos >= 1 && pos <= ARRAYLENGTH(equip)) { + short i = pc_checkequip(sd,equip[pos-1]); + if (i >= 0) { pc_unequipitem(sd,i,1|2); + script_pushint(st, 1); + return SCRIPT_CMD_SUCCESS; + } } - return SCRIPT_CMD_SUCCESS; + ShowError("buildin_unequip: No item equipped at pos %d (CID=%d/AID=%d).\n", pos, sd->status.char_id, sd->status.account_id); + script_pushint(st, 0); + return SCRIPT_CMD_FAILURE; } -BUILDIN_FUNC(equip) -{ +BUILDIN_FUNC(equip) { unsigned short nameid = 0; int i; TBL_PC *sd; struct item_data *item_data; - sd = script_rid2sd(st); + if (!(sd = script_rid2sd(st))) + return SCRIPT_CMD_SUCCESS; - nameid=script_getnum(st,2); - if((item_data = itemdb_exists(nameid)) == NULL) - { - ShowError("wrong item ID : equipitem(%hu)\n",nameid); - return 1; + nameid = script_getnum(st,2); + if ((item_data = itemdb_exists(nameid))) { + ARR_FIND( 0, MAX_INVENTORY, i, sd->status.inventory[i].nameid == nameid ); + if (i < MAX_INVENTORY) { + pc_equipitem(sd,i,item_data->equip); + script_pushint(st,1); + return SCRIPT_CMD_SUCCESS; + } } - ARR_FIND( 0, MAX_INVENTORY, i, sd->status.inventory[i].nameid == nameid ); - if( i < MAX_INVENTORY ) - pc_equipitem(sd,i,item_data->equip); - return SCRIPT_CMD_SUCCESS; + ShowError("buildin_equip: Item %hu cannot be equipped\n",nameid); + script_pushint(st,0); + return SCRIPT_CMD_FAILURE; } BUILDIN_FUNC(autoequip) From 14fddd3790401b7623d48cbeb927cd68636b6499 Mon Sep 17 00:00:00 2001 From: Euphy Date: Fri, 22 Aug 2014 15:36:23 -0400 Subject: [PATCH 4/5] Added script checks against swapping items using third-party hacks. - Follow-up to 416f6d7, as further protection against item exploits. Contains a few other fixes. Signed-off-by: Euphy --- npc/cities/geffen.txt | 4 +-- npc/custom/etc/marriage.txt | 10 +++--- npc/events/RWC_2012.txt | 12 ++++++- npc/events/valentinesday_2012.txt | 2 +- npc/jobs/2-2/crusader.txt | 6 ++-- npc/merchants/refine.txt | 10 ++---- npc/other/Global_Functions.txt | 44 ++++++++++++++++++++++++- npc/quests/cooking_quest.txt | 2 +- npc/re/instances/WolfchevLaboratory.txt | 26 ++++++--------- npc/re/merchants/card_separation.txt | 5 +++ npc/re/merchants/enchan_mal.txt | 15 ++++++++- npc/re/merchants/enchan_mora.txt | 14 ++++++++ npc/re/merchants/enchan_upg.txt | 15 ++++++++- npc/re/merchants/refine.txt | 10 ++---- npc/re/quests/quests_malaya.txt | 9 +++-- 15 files changed, 135 insertions(+), 49 deletions(-) diff --git a/npc/cities/geffen.txt b/npc/cities/geffen.txt index 705c689a52..4f5b15b47b 100644 --- a/npc/cities/geffen.txt +++ b/npc/cities/geffen.txt @@ -125,7 +125,7 @@ geffen,147,26,0 script Ralphie 97,{ } geffen,111,48,0 script Stacey 101,{ - if (getequipid(1) == 2299) { + if (getequipid(EQI_HEAD_TOP) == 2299) { mes "[Stacey]"; mes "Oh...!"; mes "Is that an Orc Helm you're wearing?! That's so cool! Wow..."; @@ -141,7 +141,7 @@ geffen,111,48,0 script Stacey 101,{ } close; } - else if (getequipid(1) == 5094) { + else if (getequipid(EQI_HEAD_TOP) == 5094) { mes "[Stacey]"; mes "Oh..."; mes "Wow..."; diff --git a/npc/custom/etc/marriage.txt b/npc/custom/etc/marriage.txt index 440beed4f3..b7604c5fcf 100644 --- a/npc/custom/etc/marriage.txt +++ b/npc/custom/etc/marriage.txt @@ -394,19 +394,19 @@ OnTimer55000: //Subfunction: Checks that the groom/bride is still wearing their stuff. function SF_equip_check { - if (Sex && getequipid(2) != 7170) { + if (Sex && getequipid(EQI_ARMOR) != 7170) { mes "["+@name$+"]"; mes "Child, what did you do with your "+getitemname(7170)+"?"; emotion e_dots; return 0; } - if (Sex == 0 && getequipid(2) != 2338) { + if (Sex == 0 && getequipid(EQI_ARMOR) != 2338) { mes "["+@name$+"]"; mes "Child, you are supposed to wear a "+getitemname(2338)+" at all times during the ceremony..."; emotion e_dots; return 0; } - if (Sex == 0 && $@wed_veil && getequipid(1) != 2206) { + if (Sex == 0 && $@wed_veil && getequipid(EQI_HEAD_TOP) != 2206) { mes "["+@name$+"]"; mes "Child, you can't take off your "+getitemname(2206)+" yet...."; emotion e_dots; @@ -692,12 +692,12 @@ function SF_TryRegister { else set @item, 2338; - if (getequipid(2) != @item) { + if (getequipid(EQI_ARMOR) != @item) { mes "["+@name$+"]"; mes "You should be wearing a "+getitemname(@item)+" if you want to get married."; close; } - if (Sex == 0 && $@wed_veil && getequipid(1) != 2206) { + if (Sex == 0 && $@wed_veil && getequipid(EQI_HEAD_TOP) != 2206) { mes "["+@name$+"]"; mes "Where is your "+getitemname(2206)+"? It's a necessary complement to your dress."; close; diff --git a/npc/events/RWC_2012.txt b/npc/events/RWC_2012.txt index 7c1bc5807c..d27e84dec3 100644 --- a/npc/events/RWC_2012.txt +++ b/npc/events/RWC_2012.txt @@ -67,6 +67,11 @@ prontera,147,61,3 script Driller#pron 87,{ mes "This item has already been enchanted. I can't work on this as it is against the rules."; close; } + + // anti-hack + if (callfunc("F_IsEquipIDHack", .@part, .@equip_id)) + close; + delequip .@part; if (rand(1,10) > 5) { getitem .@slotted,1; @@ -113,7 +118,6 @@ prontera,147,59,3 script Goldberg#pron 878,{ mes "Alright, then, see you next time..."; close; } - set .@equip_refine, getequiprefinerycnt(.@part); setarray .@equip_card[0], getequipcardid(.@part,0),getequipcardid(.@part,1),getequipcardid(.@part,2),getequipcardid(.@part,3); if (.@select == 1) { switch(getequipid(.@part)) { @@ -257,6 +261,11 @@ prontera,147,59,3 script Goldberg#pron 878,{ else if (.@i < 301) set .@enchant, .@enc[2]; else set .@enchant,9; + // anti-hack + if (callfunc("F_IsEquipIDHack", .@part, .@equip_id) || + callfunc("F_IsEquipCardHack", .@part, .@equip_card[0], .@equip_card[1], .@equip_card[2], .@equip_card[3])) + close; + set .@equip_card[.@slot], .@enchant; if (.@slot == 2 && .@enchant == 0) { set .@equip_card[3],0; @@ -269,6 +278,7 @@ prontera,147,59,3 script Goldberg#pron 878,{ set .@equip_card[3],0; } + set .@equip_refine, getequiprefinerycnt(.@part); delequip .@part; if (.@enchant == 9) { mes "[Goldberg]"; diff --git a/npc/events/valentinesday_2012.txt b/npc/events/valentinesday_2012.txt index 8325641efa..864bb6cd56 100644 --- a/npc/events/valentinesday_2012.txt +++ b/npc/events/valentinesday_2012.txt @@ -118,7 +118,7 @@ mosk_in,21,244,3 script Baker Extraordinaire 410,{ close; } else if(#V_QUE12==1) { mes .@n$; - if (getequipid(1) == 5024 && getequiprefinerycnt(1) >= 8) { + if (getequipid(EQI_HEAD_TOP) == 5024 && getequiprefinerycnt(EQI_HEAD_TOP) >= 8) { mes "Ah, so Pinkamenia told you"; mes "to bring me the +8 Cake"; mes "Hat. Now I'm going"; diff --git a/npc/jobs/2-2/crusader.txt b/npc/jobs/2-2/crusader.txt index 4fc9bce26d..b21077584f 100644 --- a/npc/jobs/2-2/crusader.txt +++ b/npc/jobs/2-2/crusader.txt @@ -360,7 +360,7 @@ prt_castle,164,32,1 script Man in Anguish 733,{ mes "Let me take"; mes "a look at your face."; next; - if(getequipid(7) != 2608 && getequipid(8) != 2608) { + if(getequipid(EQI_ACC_L) != 2608 && getequipid(EQI_ACC_R) != 2608) { mes "[Murnak Mijoul]"; mes "Hmm. You wish to become a Crusader, but do not wear a Rosary? I have no business with you if you cannot uphold our customs."; close; @@ -401,7 +401,7 @@ prt_castle,164,32,1 script Man in Anguish 733,{ mes "I'll let you retake the test as much as you like. But if you continuously fail, there's an inherent problem with your"; mes "state of mind."; next; - if(getequipid(7) != 2608 && getequipid(8) != 2608) { + if(getequipid(EQI_ACC_L) != 2608 && getequipid(EQI_ACC_R) != 2608) { mes "[Murnak Mijoul]"; mes "Wait..."; mes "Where have you left your Rosary? You can't let that lie around just anywhere. We are supposed to be warriors of holiness."; @@ -888,7 +888,7 @@ prt_castle,35,151,5 script Patron Knight 751,{ } next; if (select("I would like to begin.:Give me some time to prepare.") == 1) { - if(getequipid(7) != 2608 && getequipid(8) != 2608) { + if(getequipid(EQI_ACC_L) != 2608 && getequipid(EQI_ACC_R) != 2608) { mes "[Bliant Piyord]"; mes "Just a second, you do not have a Rosary equipped. As a Crusader, you must always have a Rosary on your person."; next; diff --git a/npc/merchants/refine.txt b/npc/merchants/refine.txt index 87e87d3ea4..ce02236793 100644 --- a/npc/merchants/refine.txt +++ b/npc/merchants/refine.txt @@ -708,13 +708,9 @@ function script refinemain { set Zeny, Zeny-.@price; delitem .@material,1; - //custom checks - if(getequipisequiped(.@part) == 0) { // hacker has removed the item (not changed, why?) - mes "[" + getarg(0) + "]"; - mes "Look here... you don't have any Items on..."; - close; - } - if(getequiprefinerycnt(.@part) != .@refinerycnt || getequipid(.@part) != .@refineitemid) { // hacker has changed the item + // anti-hack + if (callfunc("F_IsEquipIDHack", .@part, .@refineitemid) || + callfunc("F_IsEquipRefineHack", .@part, .@refinerycnt)) { mes "[" + getarg(0) + "]"; emotion e_an; mes "Wait a second..."; diff --git a/npc/other/Global_Functions.txt b/npc/other/Global_Functions.txt index f990bbe141..f19475a9df 100644 --- a/npc/other/Global_Functions.txt +++ b/npc/other/Global_Functions.txt @@ -3,7 +3,7 @@ //===== By: ================================================== //= Lupus, kobra_k88 //===== Current Version: ===================================== -//= 2.23 +//= 2.24 //===== Compatible With: ===================================== //= rAthena Project //===== Description: ========================================= @@ -46,6 +46,7 @@ //= 2.21 Added format string to "F_InsertPlural" and more checks to "F_GetPlural". [Euphy] //= 2.22 Further improvements to "F_GetPlural". [Euphy] //= 2.23 Completed article function and added "F_GetArticle". [Euphy] +//= 2.24 Added functions to check for equipment swap hacks. [Euphy] //============================================================ ////////////////////////////////////////////////////////////////////////////////// @@ -362,6 +363,47 @@ function script Time2Str { } +////////////////////////////////////////////////////////////////////////////////// +// Checks if equipment has been swapped (i.e. via hacks). +// The function checks the current equipment at a position against the supplied +// values, and logs any mismatches. +// Returns 0 if match, 1 if mismatch. +// -- callfunc "F_IsEquipIDHack",, +// -- callfunc "F_IsEquipRefineHack",, +// -- callfunc "F_IsEquipCardHack",,,,, +////////////////////////////////////////////////////////////////////////////////// +function script F_IsEquipIDHack { + set .@id_chk, getequipid(getarg(0)); + set .@id, getarg(1); + if (.@id != .@id_chk) { + logmes "Hack: Tried to swap equip " + getitemname(.@id) + " for " + getitemname(.@id_chk) + "."; + return 1; + } + return 0; +} +function script F_IsEquipRefineHack { + set .@refine_chk, getequiprefinerycnt(getarg(0)); + set .@refine, getarg(1); + if (.@refine != .@refine_chk) { + logmes "Hack: Tried to swap equip with refine +" + .@refine + " for +" + .@refine_chk + "."; + return 1; + } + return 0; +} +function script F_IsEquipCardHack { + set .@pos, getarg(0); + for (set .@i,0; .@i < 4; set .@i, .@i+1) { + set .@card, getarg(.@i + 1); + set .@card_chk, getequipcardid(.@pos, .@i); + if (.@card != .@card_chk) { + logmes "Hack: Tried to swap card " + getitemname(.@card) + " for " + getitemname(.@card_chk) + "."; + return 1; + } + } + return 0; +} + + ////////////////////////////////////////////////////////////////////////////////// // Functions for text manipulation [Euphy] ////////////////////////////////////////////////////////////////////////////////// diff --git a/npc/quests/cooking_quest.txt b/npc/quests/cooking_quest.txt index c5768e21da..be34fd34b8 100644 --- a/npc/quests/cooking_quest.txt +++ b/npc/quests/cooking_quest.txt @@ -79,7 +79,7 @@ prt_castle,43,30,3 script Charles Orleans#cook 878,{ mes "the stairs for me, alright?"; goto L_End; } - else if (getequipid(1) != 5026) { + else if (getequipid(EQI_HEAD_TOP) != 5026) { if (Sex) { cutin "orleans_5",0; emotion e_an; diff --git a/npc/re/instances/WolfchevLaboratory.txt b/npc/re/instances/WolfchevLaboratory.txt index 7000e983d5..194804310e 100644 --- a/npc/re/instances/WolfchevLaboratory.txt +++ b/npc/re/instances/WolfchevLaboratory.txt @@ -924,20 +924,20 @@ lhz_cube,233,24,4 script Sorcerer#Bio4Reward 4_M_UMDANCEKID,{ .@refine_count = getequiprefinerycnt(.@part); .@equip_item = getequipid(.@part); .@lhz_max_num = 4000; - if (.@equip_item == Agent_Katar || .@equip_item == Guillotine_Katar || .@equip_item == Ygnus_Stale || - .@equip_item == End_Sektura || .@equip_item = Cannon_Spear || .@equip_item == Giant_Lance || - .@equip_item == Aztoe_Nail || .@equip_item == Scarletto_Nail || .@equip_item == Bloody_Cross) { + if (.@equip_item == 13069 || .@equip_item == 1291 || .@equip_item == 1392 || + .@equip_item == 1393 || .@equip_item = 1435 || .@equip_item == 1490 || + .@equip_item == 13069 || .@equip_item == 13070 || .@equip_item == 16017) { .@type = 1; - if (.@equip_item == Giant_Lance) + if (.@equip_item == 1490) .@lhz_max_num = 4200; - } else if (.@equip_item == Catapult || .@equip_item == Big_CrossBow || .@equip_item == Creeper_Bow) { + } else if (.@equip_item == 18109 || .@equip_item == 18110 || .@equip_item == 18111) { .@type = 2; - } else if (.@equip_item == Chilly_Spell_Book || .@equip_item == Recovery_Light) { + } else if (.@equip_item == 1584 || .@equip_item == 1659) { .@type = 3; - } else if (.@equip_item== Giant_Shield || .@equip_item == Geffenia_Book_Water || .@equip_item == Bible_Of_Promise2 || - .@equip_item == Salvage_Cape || .@equip_item == Assassin_Handcuffs || .@equip_item == Green_Operation_Coat || - .@equip_item == Ancient_Gold_Deco) { - if (.@equip_item == Salvage_Cape || .@equip_item == Ancient_Gold_Deco) + } else if (.@equip_item == 2160 || .@equip_item == 2161 || .@equip_item == 2162 || + .@equip_item == 2582 || .@equip_item == 2892 || .@equip_item == 15044 || + .@equip_item == 18570) { + if (.@equip_item == 2582 || .@equip_item == 18570) .@lhz_max_num = 4200; } else { mes "[Pudding]"; @@ -1258,12 +1258,6 @@ lhz_cube,233,24,4 script Sorcerer#Bio4Reward 4_M_UMDANCEKID,{ } else { specialeffect2 EF_FIREHIT; getitem2 .@equip_item, 1, 1, .@refine_count, 0, .@equip_card[0], .@equip_card[1], .@equip_card[2], .@equip_card[3]; - //dispbottom "1:"+ .@equip_item; - //dispbottom "1:"+ .@refine_count; - //dispbottom "1:"+ .@equip_card[0]; - //dispbottom "1:"+ .@equip_card[1]; - //dispbottom "1:"+ .@equip_card[2]; - //dispbottom "1:"+ .@equip_card[3]; emotion e_ho; mes "[Pudding]"; mes "Fortunately the power of "+((.@socket_type==3)?"'^952420Thirst for Blood^000000'":"'^F2766EWill of Warrior^000000'")+" has been dwelt well in your equipment..."; diff --git a/npc/re/merchants/card_separation.txt b/npc/re/merchants/card_separation.txt index 5dbde69ddd..8118dd6644 100644 --- a/npc/re/merchants/card_separation.txt +++ b/npc/re/merchants/card_separation.txt @@ -285,6 +285,11 @@ set .@equip_id, getequipid(.@equip_num); set .@equip_refine, getequiprefinerycnt(.@equip_num); + + // anti-hack + if (callfunc("F_IsEquipCardHack", .@equip_num, .@equip_card[0], .@equip_card[1], .@equip_card[2], .@equip_card[3])) + close; + delequip .@equip_num; // Chance of retaining refine level. diff --git a/npc/re/merchants/enchan_mal.txt b/npc/re/merchants/enchan_mal.txt index 51c9c4d95a..8f94660eb6 100644 --- a/npc/re/merchants/enchan_mal.txt +++ b/npc/re/merchants/enchan_mal.txt @@ -318,7 +318,6 @@ L_Socket: set .@select, @mal_enchant_select; set .@equip_id, @mal_equip_id; set .@equip_name$, getitemname(.@equip_id)+((getitemslots(.@equip_id))?"["+getitemslots(.@equip_id)+"]":""); - set .@equip_refine, getequiprefinerycnt(EQI_HAND_R); setarray .@equip_card[0], getequipcardid(EQI_HAND_R,0),getequipcardid(EQI_HAND_R,1),getequipcardid(EQI_HAND_R,2),getequipcardid(EQI_HAND_R,3); set @mal_equip_id,0; set @mal_enchant_select,0; @@ -570,6 +569,12 @@ L_Socket: mes "There is something wrong. Please try again."; close; } + + // anti-hack + if (callfunc("F_IsEquipIDHack", EQI_HAND_R, .@equip_id) || + callfunc("F_IsEquipCardHack", EQI_HAND_R, .@equip_card[0], .@equip_card[1], .@equip_card[2], .@equip_card[3])) + close; + if (.@equip_card[3] == 0 && getarg(1) < 4) set .@equip_card[3],.@enchant; else if (.@equip_card[2] == 0 && getarg(1) < 3) set .@equip_card[2],.@enchant; else if (.@equip_card[1] == 0 && getarg(1) < 2) set .@equip_card[1],.@enchant; @@ -595,6 +600,7 @@ L_Socket: mes "[Mayomayo]"; mes "I have enchanted ^990000slot "+.@socket+"^000000 of this equipment."; delitem .@coin[.@coin_select],.@total[.@coin_select]; + set .@equip_refine, getequiprefinerycnt(EQI_HAND_R); delequip EQI_HAND_R; // GetNonSlotItemSock2 .@equip_refine .@equip_id .@equip_card[0] .@equip_card[1] .@equip_card[2] .@equip_card[3] @@ -629,6 +635,13 @@ L_Socket: mes "[Mayomayo]"; mes "Initialize the enchant effect from the equipment."; delitem 6417,1; //Silvervine + + // anti-hack + if (callfunc("F_IsEquipIDHack", EQI_HAND_R, .@equip_id) || + callfunc("F_IsEquipCardHack", EQI_HAND_R, .@equip_card[0], .@equip_card[1], .@equip_card[2], .@equip_card[3])) + close; + + set .@equip_refine, getequiprefinerycnt(EQI_HAND_R); delequip EQI_HAND_R; // GetNonSlotItemSock2 .@equip_refine .@equip_id .@equip_card[0] .@equip_card[1] .@equip_card[2] .@equip_card[3] diff --git a/npc/re/merchants/enchan_mora.txt b/npc/re/merchants/enchan_mora.txt index 486f7ed485..6439a25bf8 100644 --- a/npc/re/merchants/enchan_mora.txt +++ b/npc/re/merchants/enchan_mora.txt @@ -1078,6 +1078,13 @@ L_Socket: mes "Well I guess I was wrong..."; close; } + + // anti-hack + if (callfunc("F_IsEquipIDHack", .@part, .@equip_id) || + callfunc("F_IsEquipRefineHack", .@part, .@equip_refine) || + callfunc("F_IsEquipCardHack", .@part, .@equip_card[0], .@equip_card[1], .@equip_card[2], .@equip_card[3])) + close; + if (.@equip_card[3] == 0 && getarg(2) < 4) { set .@equip_card[3],.@enchant; } else if (.@equip_card[2] == 0 && getarg(2) < 3) { @@ -1103,6 +1110,13 @@ L_Socket: } delitem 6380,1; //Mora_Coin set Zeny, Zeny-100000; + + // anti-hack + if (callfunc("F_IsEquipIDHack", .@part, .@equip_id) || + callfunc("F_IsEquipRefineHack", .@part, .@equip_refine) || + callfunc("F_IsEquipCardHack", .@part, .@equip_card[0], .@equip_card[1], .@equip_card[2], .@equip_card[3])) + close; + delequip .@part; if (.@enchant == 9) { specialeffect2 EF_SUI_EXPLOSION; diff --git a/npc/re/merchants/enchan_upg.txt b/npc/re/merchants/enchan_upg.txt index d678db00d2..77337be4d5 100644 --- a/npc/re/merchants/enchan_upg.txt +++ b/npc/re/merchants/enchan_upg.txt @@ -89,7 +89,6 @@ prt_in,28,73,3 script Devil Enchant Master#prq 63,{ mes "I don't want to touch your equipment now!"; close; } - set .@equip_refine, getequiprefinerycnt(.@part); if (.@select == 1) { if (!countitem(6484)) { @@ -218,6 +217,7 @@ prt_in,28,73,3 script Devil Enchant Master#prq 63,{ mes "Are you listening to me? I will only do for you if you bring the Enchant Book!"; close; } + set .@equip_refine, getequiprefinerycnt(.@part); if (.@enchant == 0) { specialeffect EF_SHIELDCHARGE; mes "Oh! Unbelievable!! It failed!! Please come again!"; @@ -228,6 +228,12 @@ prt_in,28,73,3 script Devil Enchant Master#prq 63,{ mes "The slot ^9900004^000000 has been enchanted!"; } delitem 6484,1; //Enchant_Book + + // anti-hack + if (callfunc("F_IsEquipIDHack", .@part, .@equip_id) || + callfunc("F_IsEquipCardHack", .@part, .@equip_card[0], .@equip_card[1], .@equip_card[2], .@equip_card[3])) + close; + delequip .@part; // GetNonSlotItemSock2 .@equip_refine .@equip_id .@equip_card[0] .@equip_card[1] .@equip_card[2] .@enchant @@ -255,6 +261,13 @@ prt_in,28,73,3 script Devil Enchant Master#prq 63,{ specialeffect EF_REPAIRWEAPON; mes "I initialized the enchant effects."; set Zeny, Zeny - 100000; + + // anti-hack + if (callfunc("F_IsEquipIDHack", .@part, .@equip_id) || + callfunc("F_IsEquipCardHack", .@part, .@equip_card[0], .@equip_card[1], .@equip_card[2], .@equip_card[3])) + close; + + set .@equip_refine, getequiprefinerycnt(.@part); delequip .@part; // GetNonSlotItemSock2 .@equip_refine .@equip_id .@equip_card[0] .@equip_card[1] .@equip_card[2] 0 diff --git a/npc/re/merchants/refine.txt b/npc/re/merchants/refine.txt index cf0e9648ff..97d049d31b 100644 --- a/npc/re/merchants/refine.txt +++ b/npc/re/merchants/refine.txt @@ -157,13 +157,9 @@ function script refinenew { set Zeny,Zeny - .@price; delitem .@material,1; - //custom checks - if (getequipisequiped(.@part) == 0) { // hacker has removed the item (not changed, why?) - mes "[" + getarg(0) + "]"; - mes "Look here... you don't have any items on..."; - close; - } - if (getequiprefinerycnt(.@part) != .@refinerycnt || getequipid(.@part) != .@refineitemid) { // hacker has changed the item + // anti-hack + if (callfunc("F_IsEquipIDHack", .@part, .@refineitemid) || + callfunc("F_IsEquipRefineHack", .@part, .@refinerycnt)) { mes "[" + getarg(0) + "]"; emotion e_an; mes "Wait a second..."; diff --git a/npc/re/quests/quests_malaya.txt b/npc/re/quests/quests_malaya.txt index 445c433174..0dc91c37ee 100644 --- a/npc/re/quests/quests_malaya.txt +++ b/npc/re/quests/quests_malaya.txt @@ -3773,7 +3773,7 @@ ma_fild01,158,243,6 script Tribe Blacksmith#malaya 582,{ break; } - if (!(.@item)) { + if (!getequipisequiped(.@part)) { mes "[Bayani]"; mes "You should wear the equipment to upgrade and not come without it on you."; close; @@ -3819,11 +3819,14 @@ ma_fild01,158,243,6 script Tribe Blacksmith#malaya 582,{ mes "[Bayani]"; mes "BAM!!"; next; - delitem 6499,20; //Ancient_Grudge + + // anti-hack + if (callfunc("F_IsEquipIDHack", .@part, .@item)) + close; + delequip .@part; getitem .@newItem, 1; - mes "[Bayani]"; mes "Ha ha. Perfect."; mes "Congratulations. Your armor is better than ever."; From 3b2762ca3c9e0eba1e8aa255d4a69a8747e7ebdc Mon Sep 17 00:00:00 2001 From: Euphy Date: Fri, 22 Aug 2014 16:06:32 -0400 Subject: [PATCH 5/5] Enable NPC logging through 'logmes' by default. - Commented some unnecessary log messages. Bug fixes: - M_DOPPELGANGER had too much HP. (Hercules 9cd3ec0) - Huuma_Swirling_Petal should not have an element. (bugreport:9073) http://rathena.org/board/tracker/issue-9073-huuma-swirling-petal/ Signed-off-by: Euphy --- conf/log_athena.conf | 2 +- db/mercenary_db.txt | 2 +- db/re/item_db.txt | 2 +- npc/custom/item_signer.txt | 2 +- npc/events/event_skill_reset.txt | 2 +- npc/kafras/functions_kafras.txt | 2 +- npc/other/Global_Functions.txt | 2 +- sql-files/item_db_re.sql | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/conf/log_athena.conf b/conf/log_athena.conf index 6136188a70..8d18f2b568 100644 --- a/conf/log_athena.conf +++ b/conf/log_athena.conf @@ -97,7 +97,7 @@ log_mvpdrop: no log_commands: yes // Log NPC 'logmes' commands (Note 1) -log_npc: no +log_npc: yes // Log CHAT (Global, Whisper, Party, Guild, Main chat) (Note 3) // LOGGING FILTERS diff --git a/db/mercenary_db.txt b/db/mercenary_db.txt index db84c6889f..2bd47edb10 100644 --- a/db/mercenary_db.txt +++ b/db/mercenary_db.txt @@ -8,7 +8,7 @@ 1506,DISGUISE,Disguise,55,7543,180,2,279,546,18,29,0,72,45,35,48,65,10,12,1,6,82,147,516,768,384 1275,ALICE,Alice,62,10000,221,2,550,700,5,5,64,64,42,85,100,130,10,12,1,7,60,200,502,2304,480 1965,M_WILD_ROSE,Wild Rose,38,2980,130,2,315,360,0,15,65,85,15,35,65,80,10,12,0,2,24,100,964,864,288 -1966,M_DOPPELGANGER,Doppelganger,72,249000,200,2,1340,1590,60,35,88,90,30,35,125,65,10,12,1,6,67,100,480,480,288 +1966,M_DOPPELGANGER,Doppelganger,72,7800,200,2,1340,1590,60,35,88,90,30,35,125,65,10,12,1,6,67,100,480,480,288 1967,M_YGNIZEM,Egnigem Cenia,58,11200,320,2,823,1212,35,8,60,35,52,18,79,20,10,12,1,7,43,145,576,432,288 2000,M_GAMEMASTER,[GM] Game Master,50,7000,250,2,100,50,6,17,1,109,1,60,215,111,10,0,0,7,20,150,450,432,300 2001,F_GAMEMASTER,[GM] Game Master,50,7000,250,2,100,50,6,17,1,109,1,60,215,111,10,0,0,7,20,150,450,432,300 diff --git a/db/re/item_db.txt b/db/re/item_db.txt index 1907de5e9b..df784def1d 100644 --- a/db/re/item_db.txt +++ b/db/re/item_db.txt @@ -6990,7 +6990,7 @@ 13310,P_Huuma_Suriken1,P.Huuma Suriken I,5,0,,0,170,,1,0,0x02000000,63,2,34,3,60,0,22,{ bonus bMatk,50; },{},{} 13311,Huuma_Shadow,Sword Huuma Shuriken,5,5000,,1500,170,,1,0,0x02000000,63,2,34,3,99,1,22,{ bonus bStr,3; },{},{} 13312,Huuma_Job_Test,Prototype Huuma Shuriken,5,0,,3000,0,,1,0,0x02000000,63,2,34,4,99,1,22,{},{},{} -13313,Huuma_Swirling_Petal,Flower Huuma Shuriken,5,100000,,1500,150,,1,2,0x02000000,63,2,34,3,110,1,22,{ bonus bMatk,50; bonus bAtkEle,Ele_Fire; bonus2 bSkillAtk,"KO_HUUMARANKA",20; },{},{} +13313,Huuma_Swirling_Petal,Flower Huuma Shuriken,5,100000,,1500,150,,1,2,0x02000000,63,2,34,3,110,1,22,{ bonus bMatk,50; bonus2 bSkillAtk,"KO_HUUMARANKA",20; },{},{} 13314,Huuma_Fluttering_Snow,Wave Huuma Shuriken,5,100000,,1500,200,,1,0,0x02000000,63,2,34,4,110,1,22,{ bonus bMatk,50; bonus bAtkEle,Ele_Water; bonus3 bAutoSpell,"NJ_HYOUSYOURAKU",(getskilllv("NJ_HYOUSYOURAKU")?getskilllv("NJ_HYOUSYOURAKU"):1),30; },{},{} 13315,Huuma_Thunderstorm,Thunderstorm Huuma Shuriken,5,100000,,1500,200,,1,0,0x02000000,63,2,34,4,110,1,22,{ bonus bMatk,50; bonus bAtkEle,Ele_Wind; bonus3 bAutoSpell,"NJ_RAIGEKISAI",(getskilllv("NJ_RAIGEKISAI")?getskilllv("NJ_RAIGEKISAI"):1),30; },{},{} 13316,Upg_Huuma_Shuriken,Upg Huuma Shuriken,5,20,,1500,55,,1,1,0x02000000,63,2,34,3,1,1,22,{ bonus bBaseAtk,(getrefine()*10); bonus bMatk,(getrefine()*5); bonus bLongAtkRate,(getrefine()); if(BaseLevel>70) bonus bBaseAtk,(((BaseLevel-70)/10)*5); },{},{} diff --git a/npc/custom/item_signer.txt b/npc/custom/item_signer.txt index b1f8825c96..75fc891f0a 100644 --- a/npc/custom/item_signer.txt +++ b/npc/custom/item_signer.txt @@ -119,7 +119,7 @@ prt_in,24,61,7 script Perchik 47,{ if (!countitem2(.@id,1,.@ref,0,.@slot[0],.@slot[1],.@slot[2],.@slot[3])) { mes "Where is "+getitemname(@id)+"...?"; npctalk "You're a snoozy cheater!"; - logmes "CHEATER: Tried to sign an item not having it: "+getitemname(@id); + logmes "Hack: Tried to sign an item not having it: "+getitemname(@id); emotion e_wah; close; } diff --git a/npc/events/event_skill_reset.txt b/npc/events/event_skill_reset.txt index 44176d845b..3d7585cf33 100644 --- a/npc/events/event_skill_reset.txt +++ b/npc/events/event_skill_reset.txt @@ -104,6 +104,6 @@ L_Reset: mes @npcname$; mes "Thank you."; emotion e_thx; - logmes "SKILL RESET EVENT"; +// logmes "SKILL RESET EVENT"; close; } diff --git a/npc/kafras/functions_kafras.txt b/npc/kafras/functions_kafras.txt index db4dbb969c..1bf8ef220b 100644 --- a/npc/kafras/functions_kafras.txt +++ b/npc/kafras/functions_kafras.txt @@ -578,7 +578,7 @@ function script F_EntKafCode { set @kafcode_try,@kafcode_try+1; if(@kafcode_try>10) { set @kafcode_try,0; - logmes "Hack: Tried to fit storage password."; + logmes "Tried to fit storage password."; } if(input(@code_) == 1) { mes "You can't use such big password."; diff --git a/npc/other/Global_Functions.txt b/npc/other/Global_Functions.txt index f19475a9df..2eb18c7934 100644 --- a/npc/other/Global_Functions.txt +++ b/npc/other/Global_Functions.txt @@ -140,7 +140,7 @@ function script F_ClearGarbage { ////////////////////////////////////////////////////////////////////////////////// function script Job_Change { jobchange getarg(0),Upper; - logmes "CLASS CHANGE: " +strcharinfo(0)+ " become a "+jobname(Class); +// logmes "CLASS CHANGE: " +strcharinfo(0)+ " become a "+jobname(Class); return; } diff --git a/sql-files/item_db_re.sql b/sql-files/item_db_re.sql index 659399c472..6c28592d04 100644 --- a/sql-files/item_db_re.sql +++ b/sql-files/item_db_re.sql @@ -7021,7 +7021,7 @@ REPLACE INTO `item_db_re` VALUES (13309,'Huuma_Giant_Wheel_C','Huuma Giant Wheel REPLACE INTO `item_db_re` VALUES (13310,'P_Huuma_Suriken1','P.Huuma Suriken I',5,0,NULL,0,'170',NULL,1,0,0x02000000,63,2,34,3,'60',0,22,'bonus bMatk,50;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (13311,'Huuma_Shadow','Sword Huuma Shuriken',5,5000,NULL,1500,'170',NULL,1,0,0x02000000,63,2,34,3,'99',1,22,'bonus bStr,3;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (13312,'Huuma_Job_Test','Prototype Huuma Shuriken',5,0,NULL,3000,'0',NULL,1,0,0x02000000,63,2,34,4,'99',1,22,NULL,NULL,NULL); -REPLACE INTO `item_db_re` VALUES (13313,'Huuma_Swirling_Petal','Flower Huuma Shuriken',5,100000,NULL,1500,'150',NULL,1,2,0x02000000,63,2,34,3,'110',1,22,'bonus bMatk,50; bonus bAtkEle,Ele_Fire; bonus2 bSkillAtk,"KO_HUUMARANKA",20;',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (13313,'Huuma_Swirling_Petal','Flower Huuma Shuriken',5,100000,NULL,1500,'150',NULL,1,2,0x02000000,63,2,34,3,'110',1,22,'bonus bMatk,50; bonus2 bSkillAtk,"KO_HUUMARANKA",20;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (13314,'Huuma_Fluttering_Snow','Wave Huuma Shuriken',5,100000,NULL,1500,'200',NULL,1,0,0x02000000,63,2,34,4,'110',1,22,'bonus bMatk,50; bonus bAtkEle,Ele_Water; bonus3 bAutoSpell,"NJ_HYOUSYOURAKU",(getskilllv("NJ_HYOUSYOURAKU")?getskilllv("NJ_HYOUSYOURAKU"):1),30;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (13315,'Huuma_Thunderstorm','Thunderstorm Huuma Shuriken',5,100000,NULL,1500,'200',NULL,1,0,0x02000000,63,2,34,4,'110',1,22,'bonus bMatk,50; bonus bAtkEle,Ele_Wind; bonus3 bAutoSpell,"NJ_RAIGEKISAI",(getskilllv("NJ_RAIGEKISAI")?getskilllv("NJ_RAIGEKISAI"):1),30;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (13316,'Upg_Huuma_Shuriken','Upg Huuma Shuriken',5,20,NULL,1500,'55',NULL,1,1,0x02000000,63,2,34,3,'1',1,22,'bonus bBaseAtk,(getrefine()*10); bonus bMatk,(getrefine()*5); bonus bLongAtkRate,(getrefine()); if(BaseLevel>70) bonus bBaseAtk,(((BaseLevel-70)/10)*5);',NULL,NULL);