Meteor Storm, Catnip Meteor (fixes #983)

* Official implementation of Meteor Storm behavior
-- It deploys all units directly at castend
-- Each unit has a different expiration time
-- On the last interval of a unit the meteor will appear
-- When the unit expires, it will deal damage
-- If the unit gets removed at any time, no meteor will appear and no damage will be dealt
* It is now easily possible to configure Meteor Storm behavior via database files, no more extra coding required
* Fixed Meteor Storm sometimes dealing damage even though no meteor appeared
* Fixed Meteor Storm being able to hit through walls when the caster has moved in the meantime
* Catnip Meteor will now create 3-7 meteors depending on level
* Catnip Meteor now has an interval of 500ms instead of 1000ms
* Catnip Meteor now has a curse duration of 20s instead of 5s
This commit is contained in:
Playtester
2016-03-28 16:37:46 +02:00
parent 894d6f2fe6
commit 1365d9418e
4 changed files with 50 additions and 43 deletions

View File

@@ -159,7 +159,7 @@
//-- WZ_SIGHTRASHER
81,500,2000,0,500,0,0
//-- WZ_METEOR
83,15000,2000:3000:3000:4000:4000:5000:5000:6000:6000:7000,0,500,5000,0
83,15000,2000:3000:3000:4000:4000:5000:5000:6000:6000:7000,0,2000:3000:3000:4000:4000:5000:5000:6000:6000:7000,5000,0
//-- WZ_JUPITEL
84,2500:3000:3500:4000:4500:5000:5500:6000:6500:7000,0,0,0,0,0
//-- WZ_VERMILION

View File

@@ -160,7 +160,7 @@
//-- WZ_SIGHTRASHER
81,320,2000,0,500,0,0,80
//-- WZ_METEOR
83,9600,2000:3000:3000:4000:4000:5000:5000:6000:6000:7000,0,500,5000,0,2400
83,9600,2000:3000:3000:4000:4000:5000:5000:6000:6000:7000,0,2000:3000:3000:4000:4000:5000:5000:6000:6000:7000,5000,0,2400
//-- WZ_JUPITEL
84,1600:1920:2240:2560:2880:3200:3520:3840:4160:4480,0,0,0,0,0,400:480:560:640:720:800:880:960:1040:1120
//-- WZ_VERMILION
@@ -1778,7 +1778,7 @@
//-- SU_CN_POWDERING
5027,1500,1000,0,3000:4000:5000:6000:7000,0,0,0
//-- SU_CN_METEOR
5028,6000,1000,0,500,5000,2000:3000:4000:5000:6000,0
5028,6000,1000,0,1500:2000:2500:3000:3500,20000,2000:3000:4000:5000:6000,0
//-- SU_SV_ROOTTWIST
5029,0,1000,0,7000:9000:11000:13000:15000,0,3000,0
//-- SU_SV_ROOTTWIST_ATK

View File

@@ -178,7 +178,7 @@
5013,0x102, , 3, 0, -1,all, 0x2002 //LG_KINGS_GRACE
5027,0x106, , 1:1:2:2:3, 0, -1,enemy, 0x2010 // SU_CN_POWDERING
5028,0x86, , 1, 0,1000,enemy, 0x10 // SU_CN_METEOR
5028,0x86, , 0, 1, 500,enemy, 0x10 // SU_CN_METEOR
5029,0x107, , 0, 0,1000,enemy, 0x10 // SU_SV_ROOTTWIST
8020,0xf5, , 3, 0,2300:2100:1900:1700:1500,enemy, 0x018 //MH_POISON_MIST

View File

@@ -4110,20 +4110,6 @@ static int skill_timerskill(int tid, unsigned int tick, int id, intptr_t data)
break;
switch( skl->skill_id )
{
case WZ_METEOR:
case SU_CN_METEOR:
if( skl->type >= 0 )
{
int x = skl->type>>16, y = skl->type&0xFFFF;
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);
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);
}
else if( path_search_long(NULL, src->m, src->x, src->y, skl->x, skl->y, CELL_CHKWALL) )
skill_unitsetting(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,skl->flag);
break;
case GN_CRAZYWEED_ATK:
{
int dummy = 1, i = skill_get_unit_range(skl->skill_id,skl->skill_lv);
@@ -11683,35 +11669,20 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui
case WZ_METEOR:
case SU_CN_METEOR: {
int area = skill_get_splash(skill_id, skill_lv);
short tmpx = 0, tmpy = 0, x1 = 0, y1 = 0;
short tmpx = 0, tmpy = 0;
if (sd && skill_id == SU_CN_METEOR) {
short item_idx = pc_search_inventory(sd, ITEMID_CATNIP_FRUIT);
if (item_idx >= 0) {
pc_delitem(sd, item_idx, 1, 0, 1, LOG_TYPE_CONSUME);
skill_area_temp[3] = 1;
} else
skill_area_temp[3] = 0;
flag |= 1;
}
}
for( i = 0; i < 2 + (skill_lv>>1); i++ ) {
for (i = 1; i <= skill_get_time(skill_id, skill_lv)/skill_get_unit_interval(skill_id); i++) {
// Creates a random Cell in the Splash Area
tmpx = x - 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)
&& !map_getcell(src->m, tmpx, tmpy, CELL_CHKLANDPROTECTOR))
clif_skill_poseffect(src,skill_id,skill_lv,tmpx,tmpy,tick);
if( i > 0 )
skill_addtimerskill(src,tick+i*1000,0,tmpx,tmpy,skill_id,skill_lv,(x1<<16)|y1,0);
x1 = tmpx;
y1 = tmpy;
skill_unitsetting(src, skill_id, skill_lv, tmpx, tmpy, flag+i*skill_get_unit_interval(skill_id));
}
skill_addtimerskill(src,tick+i*1000,0,tmpx,tmpy,skill_id,skill_lv,-1,0);
}
break;
@@ -12496,6 +12467,7 @@ static bool skill_dance_switch(struct skill_unit* unit, int flag)
* @param x Position x
* @param y Position y
* @param flag &1: Used to determine when the skill 'morphs' (Warp portal becomes active, or Fire Pillar becomes active)
* xx_METEOR: flag &1 contains if the unit can cause curse, flag is also the duration of the unit in milliseconds
* @return skill_unit_group
*/
struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 skill_id, uint16 skill_lv, int16 x, int16 y, int flag)
@@ -12566,7 +12538,11 @@ struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 skill_
case NPC_EVILLAND:
val1=skill_lv+3;
break;
case WZ_METEOR:
case SU_CN_METEOR:
limit = flag - (flag&1);
val1 = (flag&1);
break;
case WZ_FIREPILLAR:
if( map_getcell(src->m, x, y, CELL_CHKLANDPROTECTOR) )
return NULL;
@@ -13430,28 +13406,36 @@ int skill_unit_onplace_timer(struct skill_unit *unit, struct block_list *bl, uns
} while(sg->interval > 0 && x == bl->x && y == bl->y &&
++count < SKILLUNITTIMER_INTERVAL/sg->interval && !status_isdead(bl) );
}
break;
break;
case WZ_HEAVENDRIVE:
status_change_end(bl, SC_SV_ROOTTWIST, INVALID_TIMER);
break;
break;
#ifndef RENEWAL // The storm gust counter was dropped in renewal
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,&unit->bl,bl,sg->skill_id,sg->skill_lv,tick,0) <= 0 && tsc)
tsc->sg_counter=0; //Attack absorbed.
break;
break;
#endif
case GS_DESPERADO:
if (rnd()%100 < unit->val1)
skill_attack(BF_WEAPON,ss,&unit->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
break;
break;
case SU_CN_METEOR:
if (sg->val1)
skill_area_temp[3] = 1;
else
skill_area_temp[3] = 0;
skill_attack(skill_get_type(sg->skill_id),ss,&unit->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
break;
case GN_CRAZYWEED_ATK:
if( bl->type == BL_SKILL ) {
struct skill_unit *su = (struct skill_unit *)bl;
if( su && !(skill_get_inf2(su->group->skill_id)&INF2_TRAP) )
break;
}
//Fall through
default:
skill_attack(skill_get_type(sg->skill_id),ss,&unit->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
}
@@ -18165,7 +18149,12 @@ static int skill_unit_timer_sub(DBKey key, DBData *data, va_list ap)
break;
default:
if (group->val2 == 1 && (group->skill_id == WZ_METEOR || group->skill_id == SU_CN_METEOR)) {
// Deal damage before expiration
break;
}
skill_delunit(unit);
break;
}
} else {// skill unit is still active
switch( group->unit_id ) {
@@ -18220,6 +18209,20 @@ static int skill_unit_timer_sub(DBKey key, DBData *data, va_list ap)
skill_delunitgroup(group);
}
break;
default:
if (group->skill_id == WZ_METEOR || group->skill_id == SU_CN_METEOR) {
if (group->val2 == 0 && (DIFF_TICK(tick, group->tick) >= group->limit - group->interval || DIFF_TICK(tick, group->tick) >= unit->limit - group->interval)) {
// Unit will expire the next interval, start dropping Meteor
struct block_list* src;
if ((src = map_id2bl(group->src_id)) != NULL) {
clif_skill_poseffect(src, group->skill_id, group->skill_lv, bl->x, bl->y, tick);
group->val2 = 1;
}
}
// No damage until expiration
return 0;
}
break;
}
}
@@ -18246,6 +18249,10 @@ static int skill_unit_timer_sub(DBKey key, DBData *data, va_list ap)
group->bl_flag= BL_NUL;
}
}
else if (group->skill_id == WZ_METEOR || group->skill_id == SU_CN_METEOR) {
skill_delunit(unit);
return 0;
}
}
if( dissonance )