Przeglądaj źródła

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

Playtester 9 lat temu
rodzic
commit
1365d9418e
4 zmienionych plików z 50 dodań i 43 usunięć
  1. 1 1
      db/pre-re/skill_cast_db.txt
  2. 2 2
      db/re/skill_cast_db.txt
  3. 1 1
      db/re/skill_unit_db.txt
  4. 46 39
      src/map/skill.c

+ 1 - 1
db/pre-re/skill_cast_db.txt

@@ -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

+ 2 - 2
db/re/skill_cast_db.txt

@@ -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

+ 1 - 1
db/re/skill_unit_db.txt

@@ -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

+ 46 - 39
src/map/skill.c

@@ -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 )