Land Protector, Ground Skill Splash, Storm Gust Knock-back

- Land Protector now behaves like on official servers (bugreport:5237)
  * Land Protector now protects from units being placed on it, no matter if they have splash range or not
  * Land Protector no longer protects from damage from units not outside Land Protector that splash inside
  * Meteor Storm no longer shows meteors falling if they would land on Land Protector
  * Pneuma can no longer be placed next to Land Protector
  * Also cleaned up the code a bit, so the checks are done where they should be done
- Ground skill splash ranges updated to their official values (bugreport:5237)
  * Lord of Vermilion places units in a 11x11 area with 3x3 splash range each
  * Storm Gust places units in a 9x9 area with 3x3 splash range each
  * Heaven's Drive places units in a 5x5 area with no splash range
  * Venom Dust now has a splash range of 3x3 and is consequently larger than before
- Storm Gust's knock-back behavior updated to official (bugreport:5237)
  * Each of Storm Gust's units will knock back "Away from center"
  * As units in the south-west are processed first, the knock-back direction will usually be north-east
  * At the edges the knock-back direction will be "to the outside"
  * Land Protector and Ganbantein will influence the knock-back behavior strongly, e.g. if Storm Gust has a hole in the middle, it will have a "suck in" effect
  * Added a config option for those who want the old "random direction" behavior from eAthena
This commit is contained in:
Playtester 2014-11-15 16:32:26 +01:00
parent ea6ba2dd3f
commit e7e8b5454b
7 changed files with 32 additions and 27 deletions

View File

@ -323,3 +323,10 @@ cart_revo_knockback: yes
// On official servers, Arrow Shower blow direction always rely on skill placed location to target instead of caster to target // On official servers, Arrow Shower blow direction always rely on skill placed location to target instead of caster to target
arrow_shower_knockback: yes arrow_shower_knockback: yes
// On official servers, Storm Gust consists of 81 units that all deal 3x3 splash damage "away from center". Due to
// south-western cells being processed first, this usually leads to a knockback to the northeast. Knockback at the
// edges will be away from SG. Knockback direction can also be influenced by Ganbantein and Land Protector. If you
// punch a hole into SG it will for example create a "suck in" effect.
// If you disable this setting, the knockback direction will be completely random (eAthena style).
stormgust_knockback: yes

View File

@ -7,7 +7,7 @@
// target = friend (party +guildmates +neutral players) / party / guild // target = friend (party +guildmates +neutral players) / party / guild
// ally (party +guildmates) / all / sameguild (guild but no allies) / enemy // ally (party +guildmates) / all / sameguild (guild but no allies) / enemy
// flag 0x00001(UF_DEFNOTENEMY) If 'defunit_not_enemy' is set, the target is changed to 'friend' // flag 0x00001(UF_DEFNOTENEMY) If 'defunit_not_enemy' is set, the target is changed to 'friend'
// 0x00002(UF_NOREITERRATION) Spell cannot be stacked // 0x00002(UF_NOREITERATION) Spell cannot be stacked
// 0x00004(UF_NOFOOTSET) Spell cannot be cast near/on targets // 0x00004(UF_NOFOOTSET) Spell cannot be cast near/on targets
// 0x00008(UF_NOOVERLAP) Spell effects do not overlap // 0x00008(UF_NOOVERLAP) Spell effects do not overlap
// 0x00010(UF_PATHCHECK) Only cells with a shootable path will be placed // 0x00010(UF_PATHCHECK) Only cells with a shootable path will be placed
@ -41,10 +41,10 @@
79,0x84, , -1, 1,3000,enemy, 0x8018 //PR_MAGNUS 79,0x84, , -1, 1,3000,enemy, 0x8018 //PR_MAGNUS
80,0x87,0x88, 0, 1,2000,enemy, 0x4006 //WZ_FIREPILLAR 80,0x87,0x88, 0, 1,2000,enemy, 0x4006 //WZ_FIREPILLAR
83,0x86, , 0, 3,1000,enemy, 0x010 //WZ_METEOR 83,0x86, , 0, 3,1000,enemy, 0x010 //WZ_METEOR
85,0x86, , 0, 6:6:6:6:6:6:6:6:6:6:8,1250,enemy,0x018 //WZ_VERMILION 85,0x86, , 5, 1:1:1:1:1:1:1:1:1:1:3,1250,enemy,0x018 //WZ_VERMILION
87,0x8d, , -1, 0, -1,all, 0x9010 //WZ_ICEWALL 87,0x8d, , -1, 0, -1,all, 0x9010 //WZ_ICEWALL
89,0x86, , 0, 5, 450,enemy, 0x018 //WZ_STORMGUST 89,0x86, , 4, 1, 450,enemy, 0x018 //WZ_STORMGUST
91,0x86, , 0, 2,1000,enemy, 0x010 //WZ_HEAVENDRIVE 91,0x86, , 2, 0,1000,enemy, 0x010 //WZ_HEAVENDRIVE
92,0x8e, , 2, 0, -1,enemy, 0x8010 //WZ_QUAGMIRE 92,0x8e, , 2, 0, -1,enemy, 0x8010 //WZ_QUAGMIRE
115,0x90, , 0, 1,1000,enemy, 0x8006 //HT_SKIDTRAP 115,0x90, , 0, 1,1000,enemy, 0x8006 //HT_SKIDTRAP
116,0x93, , 0, 1,1000,enemy, 0x8006 //HT_LANDMINE 116,0x93, , 0, 1,1000,enemy, 0x8006 //HT_LANDMINE
@ -56,7 +56,7 @@
122,0x8f, , 0, 1,1000,enemy, 0x8006 //HT_BLASTMINE 122,0x8f, , 0, 1,1000,enemy, 0x8006 //HT_BLASTMINE
123,0x98, , 0, 1,1000,enemy, 0x8006 //HT_CLAYMORETRAP 123,0x98, , 0, 1,1000,enemy, 0x8006 //HT_CLAYMORETRAP
125,0x99, , 0, 1,1000,all, 0x8000 //HT_TALKIEBOX 125,0x99, , 0, 1,1000,all, 0x8000 //HT_TALKIEBOX
140,0x92, , -1, 0,1000,enemy, 0x8000 //AS_VENOMDUST 140,0x92, , -1, 1,1000,enemy, 0x8000 //AS_VENOMDUST
220,0xb0, , 0, 0, -1,all, 0x8002 //RG_GRAFFITI 220,0xb0, , 0, 0, -1,all, 0x8002 //RG_GRAFFITI
229,0xb1, , 0, 1,1000,enemy, 0x006 //AM_DEMONSTRATION 229,0xb1, , 0, 1,1000,enemy, 0x006 //AM_DEMONSTRATION
254,0x86, , -1, 0, 300,enemy, 0x010 //CR_GRANDCROSS 254,0x86, , -1, 0, 300,enemy, 0x010 //CR_GRANDCROSS

View File

@ -7,7 +7,7 @@
// target = friend (party +guildmates +neutral players) / party / guild // target = friend (party +guildmates +neutral players) / party / guild
// ally (party +guildmates) / all / sameguild (guild but no allies) / enemy // ally (party +guildmates) / all / sameguild (guild but no allies) / enemy
// flag 0x00001(UF_DEFNOTENEMY) If 'defunit_not_enemy' is set, the target is changed to 'friend' // flag 0x00001(UF_DEFNOTENEMY) If 'defunit_not_enemy' is set, the target is changed to 'friend'
// 0x00002(UF_NOREITERRATION) Spell cannot be stacked // 0x00002(UF_NOREITERATION) Spell cannot be stacked
// 0x00004(UF_NOFOOTSET) Spell cannot be cast near/on targets // 0x00004(UF_NOFOOTSET) Spell cannot be cast near/on targets
// 0x00008(UF_NOOVERLAP) Spell effects do not overlap // 0x00008(UF_NOOVERLAP) Spell effects do not overlap
// 0x00010(UF_PATHCHECK) Only cells with a shootable path will be placed // 0x00010(UF_PATHCHECK) Only cells with a shootable path will be placed
@ -41,10 +41,10 @@
79,0x84, , -1, 1,3000,enemy, 0x8018 //PR_MAGNUS 79,0x84, , -1, 1,3000,enemy, 0x8018 //PR_MAGNUS
80,0x87,0x88, 0, 1,2000,enemy, 0x4006 //WZ_FIREPILLAR 80,0x87,0x88, 0, 1,2000,enemy, 0x4006 //WZ_FIREPILLAR
83,0x86, , 0, 3,1000,enemy, 0x010 //WZ_METEOR 83,0x86, , 0, 3,1000,enemy, 0x010 //WZ_METEOR
85,0x86, , 0, 6:6:6:6:6:6:6:6:6:6:8,1250,enemy,0x018 //WZ_VERMILION 85,0x86, , 5, 1:1:1:1:1:1:1:1:1:1:3,1250,enemy,0x018 //WZ_VERMILION
87,0x8d, , -1, 0, -1,all, 0x9010 //WZ_ICEWALL 87,0x8d, , -1, 0, -1,all, 0x9010 //WZ_ICEWALL
89,0x86, , 0, 5, 450,enemy, 0x018 //WZ_STORMGUST 89,0x86, , 4, 1, 450,enemy, 0x018 //WZ_STORMGUST
91,0x86, , 0, 2,1000,enemy, 0x010 //WZ_HEAVENDRIVE 91,0x86, , 2, 0,1000,enemy, 0x010 //WZ_HEAVENDRIVE
92,0x8e, , 2, 0, -1,enemy, 0x8010 //WZ_QUAGMIRE 92,0x8e, , 2, 0, -1,enemy, 0x8010 //WZ_QUAGMIRE
115,0x90, , 0, 1,1000,enemy, 0x8006 //HT_SKIDTRAP 115,0x90, , 0, 1,1000,enemy, 0x8006 //HT_SKIDTRAP
116,0x93, , 0, 1,1000,enemy, 0x8006 //HT_LANDMINE 116,0x93, , 0, 1,1000,enemy, 0x8006 //HT_LANDMINE
@ -56,7 +56,7 @@
122,0x8f, , 0, 1,1000,enemy, 0x8006 //HT_BLASTMINE 122,0x8f, , 0, 1,1000,enemy, 0x8006 //HT_BLASTMINE
123,0x98, , 0, 1,1000,enemy, 0x8006 //HT_CLAYMORETRAP 123,0x98, , 0, 1,1000,enemy, 0x8006 //HT_CLAYMORETRAP
125,0x99, , 0, 1,1000,all, 0x8000 //HT_TALKIEBOX 125,0x99, , 0, 1,1000,all, 0x8000 //HT_TALKIEBOX
140,0x92, , -1, 0,1000,enemy, 0x8000 //AS_VENOMDUST 140,0x92, , -1, 1,1000,enemy, 0x8000 //AS_VENOMDUST
220,0xb0, , 0, 0, -1,all, 0x8002 //RG_GRAFFITI 220,0xb0, , 0, 0, -1,all, 0x8002 //RG_GRAFFITI
229,0xb1, , 0, 1,1000,enemy, 0x006 //AM_DEMONSTRATION 229,0xb1, , 0, 1,1000,enemy, 0x006 //AM_DEMONSTRATION
254,0x86, , -1, 0, 300,enemy, 0x010 //CR_GRANDCROSS 254,0x86, , -1, 0, 300,enemy, 0x010 //CR_GRANDCROSS

View File

@ -7921,6 +7921,7 @@ static const struct _battle_data {
{ "mob_icewall_walk_block", &battle_config.mob_icewall_walk_block, 75, 0, 255, }, { "mob_icewall_walk_block", &battle_config.mob_icewall_walk_block, 75, 0, 255, },
{ "boss_icewall_walk_block", &battle_config.boss_icewall_walk_block, 0, 0, 255, }, { "boss_icewall_walk_block", &battle_config.boss_icewall_walk_block, 0, 0, 255, },
{ "snap_dodge", &battle_config.snap_dodge, 0, 0, 1, }, { "snap_dodge", &battle_config.snap_dodge, 0, 0, 1, },
{ "stormgust_knockback", &battle_config.stormgust_knockback, 1, 0, 1, },
}; };
#ifndef STATS_OPT_OUT #ifndef STATS_OPT_OUT

View File

@ -577,6 +577,7 @@ extern struct Battle_Config
int mob_icewall_walk_block; //How a normal monster should be trapped in icewall [Playtester] int mob_icewall_walk_block; //How a normal monster should be trapped in icewall [Playtester]
int boss_icewall_walk_block; //How a boss monster should be trapped in icewall [Playtester] int boss_icewall_walk_block; //How a boss monster should be trapped in icewall [Playtester]
int snap_dodge; // Enable or disable dodging damage snapping away [csnv] int snap_dodge; // Enable or disable dodging damage snapping away [csnv]
int stormgust_knockback;
} battle_config; } battle_config;
void do_init_battle(void); void do_init_battle(void);

View File

@ -2760,6 +2760,7 @@ void skill_attack_blow(struct block_list *src, struct block_list *dsrc, struct b
break; break;
// This ensures the storm randomly pushes instead of exactly a cell backwards per official mechanics. // This ensures the storm randomly pushes instead of exactly a cell backwards per official mechanics.
case WZ_STORMGUST: case WZ_STORMGUST:
if(!battle_config.stormgust_knockback)
dir = rand()%8; dir = rand()%8;
break; break;
case WL_CRIMSONROCK: case WL_CRIMSONROCK:
@ -3349,9 +3350,12 @@ static int skill_check_unit_range_sub(struct block_list *bl, va_list ap)
g_skill_id = unit->group->skill_id; g_skill_id = unit->group->skill_id;
switch (skill_id) { switch (skill_id) {
case AL_PNEUMA: //Pneuma doesn't work even if just one cell overlaps with Land Protector
if(g_skill_id == SA_LANDPROTECTOR)
break;
//Fall through
case MH_STEINWAND: case MH_STEINWAND:
case MG_SAFETYWALL: case MG_SAFETYWALL:
case AL_PNEUMA:
case SC_MAELSTROM: case SC_MAELSTROM:
case SO_ELEMENTAL_SHIELD: case SO_ELEMENTAL_SHIELD:
if(g_skill_id != MH_STEINWAND && g_skill_id != MG_SAFETYWALL && g_skill_id != AL_PNEUMA && g_skill_id != SC_MAELSTROM && g_skill_id != SO_ELEMENTAL_SHIELD) if(g_skill_id != MH_STEINWAND && g_skill_id != MG_SAFETYWALL && g_skill_id != AL_PNEUMA && g_skill_id != SC_MAELSTROM && g_skill_id != SO_ELEMENTAL_SHIELD)
@ -3873,7 +3877,8 @@ static int skill_timerskill(int tid, unsigned int tick, int id, intptr_t data)
int x = skl->type>>16, y = skl->type&0xFFFF; int x = skl->type>>16, y = skl->type&0xFFFF;
if( path_search_long(NULL, src->m, src->x, src->y, x, y, CELL_CHKWALL) ) if( path_search_long(NULL, src->m, src->x, src->y, x, y, CELL_CHKWALL) )
skill_unitsetting(src,skl->skill_id,skl->skill_lv,x,y,skl->flag); skill_unitsetting(src,skl->skill_id,skl->skill_lv,x,y,skl->flag);
if( path_search_long(NULL, src->m, src->x, src->y, skl->x, skl->y, CELL_CHKWALL) ) if( path_search_long(NULL, src->m, src->x, src->y, skl->x, skl->y, CELL_CHKWALL)
&& !map_getcell(src->m, skl->x, skl->y, CELL_CHKLANDPROTECTOR) )
clif_skill_poseffect(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,tick); clif_skill_poseffect(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,tick);
} }
else if( path_search_long(NULL, src->m, src->x, src->y, skl->x, skl->y, CELL_CHKWALL) ) else if( path_search_long(NULL, src->m, src->x, src->y, skl->x, skl->y, CELL_CHKWALL) )
@ -11098,7 +11103,8 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui
tmpx = x - area + rnd()%(area * 2 + 1); tmpx = x - area + rnd()%(area * 2 + 1);
tmpy = y - area + rnd()%(area * 2 + 1); tmpy = y - area + rnd()%(area * 2 + 1);
if( i == 0 && path_search_long(NULL, src->m, src->x, src->y, tmpx, tmpy, CELL_CHKWALL) ) if( i == 0 && path_search_long(NULL, src->m, src->x, src->y, tmpx, tmpy, CELL_CHKWALL)
&& !map_getcell(src->m, tmpx, tmpy, CELL_CHKLANDPROTECTOR))
clif_skill_poseffect(src,skill_id,skill_lv,tmpx,tmpy,tick); clif_skill_poseffect(src,skill_id,skill_lv,tmpx,tmpy,tick);
if( i > 0 ) if( i > 0 )
@ -12321,7 +12327,6 @@ struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 skill_
map_foreachincell(skill_maelstrom_suction,src->m,ux,uy,BL_SKILL,skill_id,skill_lv); map_foreachincell(skill_maelstrom_suction,src->m,ux,uy,BL_SKILL,skill_id,skill_lv);
// Check active cell to failing or remove current unit // Check active cell to failing or remove current unit
if( range <= 0 )
map_foreachincell(skill_cell_overlap,src->m,ux,uy,BL_SKILL,skill_id, &alive, src); map_foreachincell(skill_cell_overlap,src->m,ux,uy,BL_SKILL,skill_id, &alive, src);
if( !alive ) if( !alive )
continue; continue;
@ -12391,7 +12396,7 @@ static int skill_unit_onplace(struct skill_unit *unit, struct block_list *bl, un
nullpo_ret(sg = unit->group); nullpo_ret(sg = unit->group);
nullpo_ret(ss = map_id2bl(sg->src_id)); nullpo_ret(ss = map_id2bl(sg->src_id));
if( (skill_get_type(sg->skill_id) == BF_MAGIC && map_getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR) && sg->skill_id != SA_LANDPROTECTOR) || if( (skill_get_type(sg->skill_id) == BF_MAGIC && map_getcell(unit->bl.m, unit->bl.x, unit->bl.y, CELL_CHKLANDPROTECTOR) && sg->skill_id != SA_LANDPROTECTOR) ||
map_getcell(bl->m, bl->x, bl->y, CELL_CHKMAELSTROM) ) map_getcell(bl->m, bl->x, bl->y, CELL_CHKMAELSTROM) )
return 0; //AoE skills are ineffective. [Skotlex] return 0; //AoE skills are ineffective. [Skotlex]
@ -17305,7 +17310,7 @@ int skill_unit_timer_sub_onplace(struct block_list* bl, va_list ap)
nullpo_ret(group = unit->group); nullpo_ret(group = unit->group);
if( !(skill_get_inf2(group->skill_id)&(INF2_SONG_DANCE|INF2_TRAP)) && !(skill_get_inf3(group->skill_id)&(INF3_NOLP)) && group->skill_id != NC_NEUTRALBARRIER && map_getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR) ) if( !(skill_get_inf2(group->skill_id)&(INF2_SONG_DANCE|INF2_TRAP)) && !(skill_get_inf3(group->skill_id)&(INF3_NOLP)) && group->skill_id != NC_NEUTRALBARRIER && map_getcell(unit->bl.m, unit->bl.x, unit->bl.y, CELL_CHKLANDPROTECTOR) )
return 0; //AoE skills are ineffective. [Skotlex] return 0; //AoE skills are ineffective. [Skotlex]
if( battle_check_target(&unit->bl,bl,group->target_flag) <= 0 ) if( battle_check_target(&unit->bl,bl,group->target_flag) <= 0 )

View File

@ -1947,15 +1947,6 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui
if( sd ) { if( sd ) {
if( skill_isNotOk(skill_id, sd) || !skill_check_condition_castbegin(sd, skill_id, skill_lv) ) if( skill_isNotOk(skill_id, sd) || !skill_check_condition_castbegin(sd, skill_id, skill_lv) )
return 0; return 0;
/**
* Pneuma cannot be cancelled past this point, the client displays the animation even,
* if we cancel it from nodamage_id, so it has to be here for it to not display the animation.
*/
if( skill_id == AL_PNEUMA && map_getcell(src->m, skill_x, skill_y, CELL_CHKLANDPROTECTOR) ) {
clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
return 0;
}
} }
if( (skill_id >= SC_MANHOLE && skill_id <= SC_FEINTBOMB) && map_getcell(src->m, skill_x, skill_y, CELL_CHKMAELSTROM) ) { if( (skill_id >= SC_MANHOLE && skill_id <= SC_FEINTBOMB) && map_getcell(src->m, skill_x, skill_y, CELL_CHKMAELSTROM) ) {