Browse Source

* Reduced memory used for the skill_tree DB by 30+mb
* Added script commands isday and isnight
* Updated Sharp Shooting, Ankle Snare, Magnum Break, Tiger Fist, Devotion, Soul Burn

git-svn-id: https://svn.code.sf.net/p/rathena/svn/branches/stable@1072 54d463be-8e91-2dee-dedb-b68131a5f0ec

(no author) 20 years ago
parent
commit
d6688a9f90
11 changed files with 229 additions and 32 deletions
  1. 18 0
      Changelog.txt
  2. 4 0
      db/Changelog.txt
  3. 1 1
      db/skill_db.txt
  4. 2 2
      db/skill_tree.txt
  5. 14 0
      src/map/clif.c
  6. 89 0
      src/map/map.c
  7. 1 0
      src/map/map.h
  8. 7 2
      src/map/pc.c
  9. 8 5
      src/map/pc.h
  10. 16 0
      src/map/script.c
  11. 69 22
      src/map/skill.c

+ 18 - 0
Changelog.txt

@@ -1,5 +1,23 @@
 Date	Added
 Date	Added
 
 
+02/11
+        * EXPERIMENTAL: Reduced memory used for the skill_tree DB by 30+mb [celest]
+        * Added script commands isday and isnight - checks whether its night or 
+          daytime. Example: if(isnight()) ... [celest]
+        * Skill Updates [celest]
+          - Sharp Shooting: Dropped jA's and kA's code and wrote a new
+            map_foreachinpath function in map.c -- about 14 times faster, but still
+            uncomplete
+          - Ankle Snare: Added matthias' suggestion to let agility reduce more trap
+            time, but no less than 3 seconds.
+          - Magnum Break: simplified code a bit, and changed to non-targetting /
+            automatically damages an area around the caster
+          - Tiger Fist: enable it to be used by its own besides as a part of
+            the combo skills
+          - Devotion: Fixed maximum level difference not reading its setting from
+            battle_athena.conf, thanks to leinsirk
+          - Soul Burn: Added some safety checks
+
 02/10
 02/10
 	* Login/Login SQL: fixed the client version check function [Sirius]
 	* Login/Login SQL: fixed the client version check function [Sirius]
 
 

+ 4 - 0
db/Changelog.txt

@@ -5,6 +5,10 @@
 	Ayathoya items == Added but no effect ( all are "ect" itens)
 	Ayathoya items == Added but no effect ( all are "ect" itens)
 	Skill databases == celest working on them i believe.
 	Skill databases == celest working on them i believe.
 
 
+02/11
+        * Updated Chain Crush to require level 2 Tiger Fist, thanks to matthias for
+          pointing it out [celest]
+
 02/05
 02/05
         * item_db.txt Added prices to all Magic Scrolls and to Horse Crest,
         * item_db.txt Added prices to all Magic Scrolls and to Horse Crest,
 	  added +100-1000 Zeny effect to Gold Coin (it's used in st.Patric event)
 	  added +100-1000 Zeny effect to Gold Coin (it's used in st.Patric event)

+ 1 - 1
db/skill_db.txt

@@ -6,7 +6,7 @@
 4,0,0,0,0,0,10,0,no,0,0,0,none,0	//SM_RECOVERY#HP回復力向上#
 4,0,0,0,0,0,10,0,no,0,0,0,none,0	//SM_RECOVERY#HP回復力向上#
 5,-1,6,1,0,0,10,1,no,0,0,0,weapon,0	//SM_BASH#バッシュ#
 5,-1,6,1,0,0,10,1,no,0,0,0,weapon,0	//SM_BASH#バッシュ#
 6,8,6,1,0,1,10,1,no,0,0,0,none,0	//SM_PROVOKE#プロ?ック#
 6,8,6,1,0,1,10,1,no,0,0,0,none,0	//SM_PROVOKE#プロ?ック#
-7,-1,6,1,3,2,10,5,no,0,0,0,weapon,2	//SM_MAGNUM#?グナ?ブレイク#
+7,0,6,4,3,2,10,5,no,0,0,0,weapon,2	//SM_MAGNUM#?グナ?ブレイク#
 8,0,6,4,0,1,10,1,no,0,0,0,weapon,0	//SM_ENDURE#インデュア#
 8,0,6,4,0,1,10,1,no,0,0,0,weapon,0	//SM_ENDURE#インデュア#
 9,0,0,0,0,0,10,0,no,0,0,0,none,0	//MG_SRECOVERY#SP回復力向上#
 9,0,0,0,0,0,10,0,no,0,0,0,none,0	//MG_SRECOVERY#SP回復力向上#
 10,8,6,4,3,1,1,1,yes,0,0,0,magic,0	//MG_SIGHT#サイト#
 10,8,6,4,3,1,1,1,yes,0,0,0,magic,0	//MG_SIGHT#サイト#

+ 2 - 2
db/skill_tree.txt

@@ -988,7 +988,7 @@
 4016,264,1,271,3,260,2,268,3,0,0,0,0//MO_BODYRELOCATION	#残影#
 4016,264,1,271,3,260,2,268,3,0,0,0,0//MO_BODYRELOCATION	#残影#
 4016,265,10,259,5,261,5,0,0,0,0,0,0//MO_DODGE		#見切り#
 4016,265,10,259,5,261,5,0,0,0,0,0,0//MO_DODGE		#見切り#
 4016,266,5,261,5,0,0,0,0,0,0,0,0//MO_FINGEROFFENSIVE	#指弾#
 4016,266,5,261,5,0,0,0,0,0,0,0,0//MO_FINGEROFFENSIVE	#指弾#
-4016,267,5,266,3,0,0,0,0,0,0,0,0/MO_INVESTIGATE		#発勁#
+4016,267,5,266,3,0,0,0,0,0,0,0,0//MO_INVESTIGATE		#発勁#
 4016,268,5,273,3,0,0,0,0,0,0,0,0//MO_STEELBODY		#金剛#
 4016,268,5,273,3,0,0,0,0,0,0,0,0//MO_STEELBODY		#金剛#
 4016,269,5,265,5,0,0,0,0,0,0,0,0//MO_BLADESTOP		#白刃取り#
 4016,269,5,265,5,0,0,0,0,0,0,0,0//MO_BLADESTOP		#白刃取り#
 4016,270,5,262,1,0,0,0,0,0,0,0,0//MO_EXPLOSIONSPIRITS	#爆裂波動#
 4016,270,5,262,1,0,0,0,0,0,0,0,0//MO_EXPLOSIONSPIRITS	#爆裂波動#
@@ -997,7 +997,7 @@
 4016,273,5,272,3,0,0,0,0,0,0,0,0//MO_COMBOFINISH		#猛龍拳#
 4016,273,5,272,3,0,0,0,0,0,0,0,0//MO_COMBOFINISH		#猛龍拳#
 4016,370,5,259,7,261,5,0,0,0,0,0,0//CH_PALMSTRIKE		#基本スキル#
 4016,370,5,259,7,261,5,0,0,0,0,0,0//CH_PALMSTRIKE		#基本スキル#
 4016,371,5,259,5,263,5,261,5,273,3,0,0//CH_TIGERFIST		#基本スキル#
 4016,371,5,259,5,263,5,261,5,273,3,0,0//CH_TIGERFIST		#基本スキル#
-4016,372,10,259,5,261,5,0,0,0,0,0,0//CH_CHAINCRUSH		#基本スキル#
+4016,372,10,259,5,261,5,371,2,0,0,0,0//CH_CHAINCRUSH		#基本スキル#
 4016,401,1,261,5,262,1,270,5,0,0,0,0//CH_SOULCOLLECT
 4016,401,1,261,5,262,1,270,5,0,0,0,0//CH_SOULCOLLECT
 //Professor
 //Professor
 4017,1,9,0,0,0,0,0,0,0,0,0,0//NV_BASIC	#基本スキル#
 4017,1,9,0,0,0,0,0,0,0,0,0,0//NV_BASIC	#基本スキル#

+ 14 - 0
src/map/clif.c

@@ -9016,6 +9016,20 @@ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd) {
 					return;
 					return;
 				}
 				}
 			}
 			}
+		} else if (skillnum == CH_TIGERFIST) {
+			if (sd->sc_data[SC_COMBO].timer == -1 || sd->sc_data[SC_COMBO].val1 != MO_COMBOFINISH) {
+				if (!sd->state.skill_flag ) {
+					sd->state.skill_flag = 1;
+					if (!sd->attacktarget) {
+						clif_skillinfo(sd, CH_TIGERFIST, 1, -2);
+						return;
+					} else
+						target_id = sd->attacktarget;
+				} else if (sd->bl.id == target_id) {
+					clif_skillinfo(sd, CH_TIGERFIST, 1, -2);
+					return;
+				}
+			}
 		}
 		}
 		if ((lv = pc_checkskill(sd, skillnum)) > 0) {
 		if ((lv = pc_checkskill(sd, skillnum)) > 0) {
 			if (skilllv > lv)
 			if (skilllv > lv)

+ 89 - 0
src/map/map.c

@@ -652,6 +652,95 @@ void map_foreachincell(int (*func)(struct block_list*,va_list),int m,int x,int y
 	bl_list_count = blockcount;
 	bl_list_count = blockcount;
 }
 }
 
 
+/*============================================================
+* For checking a path between two points (x0, y0) and (x1, y1)
+*------------------------------------------------------------
+ */
+void map_foreachinpath(int (*func)(struct block_list*,va_list),int m,int x0,int y0,int x1,int y1,int range,int length,int type,...) {
+	va_list ap;
+	int bx,by;
+	struct block_list *bl=NULL;
+	int blockcount=bl_list_count,i,c;
+	double s;
+	int in;	// slope, interception
+
+	if(m < 0)
+		return;
+	va_start(ap,type);
+	if (x0 < 0) x0 = 0;
+	if (y0 < 0) y0 = 0;
+	if (x1 >= map[m].xs) x1 = map[m].xs-1;
+	if (y1 >= map[m].ys) y1 = map[m].ys-1;
+
+//	y = ax + c	// ugh, algebra! xp
+//	x = (y - c) / a
+	if (x0 == x1) {
+		s = 999; in = 0;
+	} else if (y0 == y1) {
+		s = 0; in = y0;
+	} else {
+		s = (double)(y1 - y0)/(double)(x1 - x0);
+		in = y0 - s * x0;
+	}
+	//printf ("%lf %d\n", s, in);
+
+	if (type == 0 || type != BL_MOB)
+		for (by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++) {
+			for(bx=x0/BLOCK_SIZE;bx<=x1/BLOCK_SIZE;bx++){
+				bl = map[m].block[bx+by*map[m].bxs];
+				c = map[m].block_count[bx+by*map[m].bxs];
+				for(i=0;i<c && bl;i++,bl=bl->next){
+					if(bl && type && bl->type!=type)
+						continue;
+					if(bl) {
+						printf ("%lf %lf\n", s * bl->x + in - bl->y, (in - bl->y)/s - bl->x);
+
+						if (((s == 999 && bl->x == x0) ||
+							(s == 0 && in == y0 && bl->y == y0) ||
+							abs(s * bl->x + in - bl->y) <= range ||
+							abs((bl->y - in)/s - bl->x) <= range) &&
+							bl_list_count<BL_LIST_MAX)
+							bl_list[bl_list_count++]=bl;
+					}
+				}
+			}
+		}
+	if(type==0 || type==BL_MOB)
+		for(by=y0/BLOCK_SIZE;by<=y1/BLOCK_SIZE;by++){
+			for(bx=x0/BLOCK_SIZE;bx<=x1/BLOCK_SIZE;bx++){
+				bl = map[m].block_mob[bx+by*map[m].bxs];
+				c = map[m].block_mob_count[bx+by*map[m].bxs];
+				for(i=0;i<c && bl;i++,bl=bl->next){
+					if(bl) {
+						printf ("%lf %lf\n", s * bl->x + in - bl->y, (bl->y - in)/s - bl->x);
+						if (((s == 999 && bl->x == x0) ||
+							(s == 0 && in == y0 && bl->y == y0) ||
+							abs(s * bl->x + in - bl->y) <= range ||
+							abs((bl->y - in)/s - bl->x) <= range) &&
+							bl_list_count<BL_LIST_MAX)
+							bl_list[bl_list_count++]=bl;
+					}
+				}
+			}
+		}
+
+	if(bl_list_count>=BL_LIST_MAX) {
+		if(battle_config.error_log)
+			printf("map_foreachinarea: *WARNING* block count too many!\n");
+	}
+
+	map_freeblock_lock();	// メモリからの解放を禁止する
+
+	for(i=blockcount;i<bl_list_count;i++)
+		if(bl_list[i]->prev)	// 有?かどうかチェック
+			func(bl_list[i],ap);
+
+	map_freeblock_unlock();	// 解放を許可する
+
+	va_end(ap);
+	bl_list_count = blockcount;
+}
+
 /*==========================================
 /*==========================================
  * 床アイテムやエフェクト用の一時obj割り?て
  * 床アイテムやエフェクト用の一時obj割り?て
  * object[]への保存とid_db登?まで
  * object[]への保存とid_db登?まで

+ 1 - 0
src/map/map.h

@@ -689,6 +689,7 @@ void map_foreachinarea(int (*)(struct block_list*,va_list),int,int,int,int,int,i
 // -- moonsoul (added map_foreachincell)
 // -- moonsoul (added map_foreachincell)
 void map_foreachincell(int (*)(struct block_list*,va_list),int,int,int,int,...);
 void map_foreachincell(int (*)(struct block_list*,va_list),int,int,int,int,...);
 void map_foreachinmovearea(int (*)(struct block_list*,va_list),int,int,int,int,int,int,int,int,...);
 void map_foreachinmovearea(int (*)(struct block_list*,va_list),int,int,int,int,int,int,int,int,...);
+void map_foreachinpath(int (*)(struct block_list*,va_list),int,int,int,int,int,int,int,int,...); // Celest
 int map_countnearpc(int,int,int);
 int map_countnearpc(int,int,int);
 //blockŠÖ˜A‚ɒljÁ
 //blockŠÖ˜A‚ɒljÁ
 int map_count_oncell(int m,int x,int y);
 int map_count_oncell(int m,int x,int y);

+ 7 - 2
src/map/pc.c

@@ -48,7 +48,7 @@ static int exp_table[14][MAX_LEVEL];
 static char statp[255][7];
 static char statp[255][7];
 
 
 // h-files are for declarations, not for implementations... [Shinomori]
 // h-files are for declarations, not for implementations... [Shinomori]
-struct skill_tree_entry skill_tree[3][MAX_PC_CLASS][100];
+struct skill_tree_entry skill_tree[3][25][MAX_SKILL_TREE];
 // timer for night.day implementation
 // timer for night.day implementation
 int day_timer_tid;
 int day_timer_tid;
 int night_timer_tid;
 int night_timer_tid;
@@ -6843,7 +6843,12 @@ int pc_readdb(void)
 		s_class = pc_calc_base_job(atoi(split[0]));
 		s_class = pc_calc_base_job(atoi(split[0]));
 		i = s_class.job;
 		i = s_class.job;
 		u = s_class.upper;
 		u = s_class.upper;
-		for(j=0;skill_tree[u][i][j].id;j++);
+		// check for bounds [celest]
+		if (i > 25 || u > 3)
+			continue;
+		for(j = 0; skill_tree[u][i][j].id && j < MAX_SKILL_TREE; j++);
+		if (j == MAX_SKILL_TREE)
+			continue;
 		skill_tree[u][i][j].id=atoi(split[1]);
 		skill_tree[u][i][j].id=atoi(split[1]);
 		skill_tree[u][i][j].max=atoi(split[2]);
 		skill_tree[u][i][j].max=atoi(split[2]);
 
 

+ 8 - 5
src/map/pc.h

@@ -9,6 +9,8 @@
 #define CART_MASK 0x788
 #define CART_MASK 0x788
 #define STATE_BLIND 0x10
 #define STATE_BLIND 0x10
 
 
+#define MAX_SKILL_TREE 51
+
 #define pc_setdead(sd) ((sd)->state.dead_sit = 1)
 #define pc_setdead(sd) ((sd)->state.dead_sit = 1)
 #define pc_setsit(sd) ((sd)->state.dead_sit = 2)
 #define pc_setsit(sd) ((sd)->state.dead_sit = 2)
 //#define pc_setstand(sd) ((sd)->state.dead_sit = 0)
 //#define pc_setstand(sd) ((sd)->state.dead_sit = 0)
@@ -175,13 +177,14 @@ int pc_calc_base_job2(int b_class);	// Celest
 int pc_calc_upper(int b_class);
 int pc_calc_upper(int b_class);
 
 
 struct skill_tree_entry {
 struct skill_tree_entry {
-	int id;
-	int max;
+	short id;
+	unsigned char max;
 	struct {
 	struct {
-		short id,lv;
-	} need[6];
+		short id;
+		unsigned char lv;
+	} need[5];
 }; // Celest
 }; // Celest
-extern struct skill_tree_entry skill_tree[3][MAX_PC_CLASS][100];
+extern struct skill_tree_entry skill_tree[3][25][MAX_SKILL_TREE];
 
 
 int pc_read_gm_account(int fd);
 int pc_read_gm_account(int fd);
 int pc_setinvincibletimer(struct map_session_data *sd,int);
 int pc_setinvincibletimer(struct map_session_data *sd,int);

+ 16 - 0
src/map/script.c

@@ -298,6 +298,8 @@ int buildin_skilluseid(struct script_state *st); // originally by Qamera [celest
 int buildin_skillusepos(struct script_state *st); // originally by Qamera [celest]
 int buildin_skillusepos(struct script_state *st); // originally by Qamera [celest]
 int buildin_logmes(struct script_state *st); // [Lupus]
 int buildin_logmes(struct script_state *st); // [Lupus]
 int buildin_summon(struct script_state *st); // [celest]
 int buildin_summon(struct script_state *st); // [celest]
+int buildin_isnight(struct script_state *st); // [celest]
+int buildin_isday(struct script_state *st); // [celest]
 
 
 void push_val(struct script_stack *stack,int type,int val);
 void push_val(struct script_stack *stack,int type,int val);
 int run_func(struct script_state *st);
 int run_func(struct script_state *st);
@@ -522,6 +524,8 @@ struct {
 	{buildin_skillusepos,"skillusepos","iiii"}, // [Celest]
 	{buildin_skillusepos,"skillusepos","iiii"}, // [Celest]
 	{buildin_logmes,"logmes","s"}, //this command actls as MES but prints info into LOG file either SQL/TXT [Lupus]
 	{buildin_logmes,"logmes","s"}, //this command actls as MES but prints info into LOG file either SQL/TXT [Lupus]
 	{buildin_summon,"summon","si*"}, // summons a slave monster [Celest]
 	{buildin_summon,"summon","si*"}, // summons a slave monster [Celest]
+	{buildin_isnight,"isnight",""}, // check whether it is night time [Celest]
+	{buildin_isday,"isday",""}, // check whether it is day time [Celest]
 	{NULL,NULL,NULL},
 	{NULL,NULL,NULL},
 };
 };
 int buildin_message(struct script_state *st); // [MouseJstr]
 int buildin_message(struct script_state *st); // [MouseJstr]
@@ -6515,6 +6519,18 @@ int buildin_summon(struct script_state *st)
 	return 0;
 	return 0;
 }
 }
 
 
+int buildin_isnight(struct script_state *st)
+{
+	push_val(st->stack,C_INT, (night_flag == 1));
+	return 0;
+}
+
+int buildin_isday(struct script_state *st)
+{
+	push_val(st->stack,C_INT, (night_flag == 0));
+	return 0;
+}
+
 //
 //
 // ŽÀ�s•”main
 // ŽÀ�s•”main
 //
 //

+ 69 - 22
src/map/skill.c

@@ -1,4 +1,4 @@
-// $Id: skill.c,v 1.8 2004/12/16 6:46:08 PM Celestia $
+// $Id: skill.c,v 1.8 2004/01/07 10:46:38 PM Celestia $
 /* スキル?係 */
 /* スキル?係 */
 
 
 #include <stdio.h>
 #include <stdio.h>
@@ -2297,10 +2297,12 @@ int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int s
 
 
 	case SN_SHARPSHOOTING:			/* シャ?プシュ?ティング */
 	case SN_SHARPSHOOTING:			/* シャ?プシュ?ティング */
 		{
 		{
-			int dx, dy, wx = 0, wy = 0;
+		#if 0	// temporarily keeping this block for future reference [celest]
+			/*int dx, dy, wx = 0, wy = 0;
 			int weight, num = 0;
 			int weight, num = 0;
 			int x1 = src->x, y1 = src->y;
 			int x1 = src->x, y1 = src->y;
 			int x0 = bl->x, y0 = bl->y;
 			int x0 = bl->x, y0 = bl->y;
+			int *xs, *ys;
 
 
 			dx = (x1 - x0);
 			dx = (x1 - x0);
 			if (dx < 0) {
 			if (dx < 0) {
@@ -2310,6 +2312,8 @@ int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int s
 			}
 			}
 			dy = (y1 - y0);
 			dy = (y1 - y0);
 			weight = dx > abs(dy) ? dx : abs(y1 - y0);
 			weight = dx > abs(dy) ? dx : abs(y1 - y0);
+			xs = (int *)aCallocA(weight, sizeof(int));
+			ys = (int *)aCallocA(weight, sizeof(int));
 			while ((x0 != x1 || y0 != y1) && num < skill_get_range(skillid,skilllv)) { // fixed [Shinomori]
 			while ((x0 != x1 || y0 != y1) && num < skill_get_range(skillid,skilllv)) { // fixed [Shinomori]
 				wx += dx;
 				wx += dx;
 				wy += dy;
 				wy += dy;
@@ -2325,11 +2329,28 @@ int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int s
 					if (dy > 0) { y0++; }
 					if (dy > 0) { y0++; }
 					else { y0--; }
 					else { y0--; }
 				}
 				}
-				map_foreachinarea (skill_attack_area,src->m,x0,y0,x0,y0,0,
-						BF_WEAPON,src,src,skillid,skilllv,tick,flag,BCT_ENEMY);
+				//xs[number] = x0;
+				//ys[number] = y0
+				printf ("%d - %d %d\n", weight, x0, y0);
+				//map_foreachinarea (skill_attack_area,src->m,x0,y0,x0,y0,0,
+						//BF_WEAPON,src,src,skillid,skilllv,tick,flag,BCT_ENEMY);
 				num++;	// make sure it doesn't run infinitely
 				num++;	// make sure it doesn't run infinitely
 			}
 			}
+			//for num = 0; num < weight; num++
+			//map_foreach skill attack area
+			//if last of xs || ys != x y, manually skill attack
 			clif_skill_nodamage(src,bl,skillid,skilllv,1);
 			clif_skill_nodamage(src,bl,skillid,skilllv,1);
+			aFree (xs);
+			aFree (ys);*/
+		#endif
+
+		#if 0	// change 0 to 1 to switch to the this system [celest]
+			skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag);
+		#else
+			map_foreachinpath (skill_attack_area,src->m,src->x,src->y,bl->x,bl->y,
+				2,skill_get_range(skillid,skilllv),0,
+				BF_WEAPON,src,src,skillid,skilllv,tick,flag,BCT_ENEMY);
+		#endif
 		}
 		}
 		break;
 		break;
 
 
@@ -2486,7 +2507,7 @@ int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int s
 		break;
 		break;
 	/* 武器系範?攻?スキル */
 	/* 武器系範?攻?スキル */
 	case AC_SHOWER:			/* アロ?シャワ? */
 	case AC_SHOWER:			/* アロ?シャワ? */
-	case SM_MAGNUM:			/* マグナムブレイク */
+//	case SM_MAGNUM:			/* マグナムブレイク */
 	case AS_GRIMTOOTH:		/* グリムトゥ?ス */
 	case AS_GRIMTOOTH:		/* グリムトゥ?ス */
 	case MC_CARTREVOLUTION:	/* カ?トレヴォリュ?ション */
 	case MC_CARTREVOLUTION:	/* カ?トレヴォリュ?ション */
 	case NPC_SPLASHATTACK:	/* スプラッシュアタック */
 	case NPC_SPLASHATTACK:	/* スプラッシュアタック */
@@ -2496,11 +2517,11 @@ int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int s
 			/* 個別にダメ?ジを?える */
 			/* 個別にダメ?ジを?える */
 			if(bl->id!=skill_area_temp[1]){
 			if(bl->id!=skill_area_temp[1]){
 				int dist=0;
 				int dist=0;
-				if(skillid==SM_MAGNUM){	/* マグナムブレイクなら中心からの距離を計算 */
-					int dx=abs( bl->x - skill_area_temp[2] );
-					int dy=abs( bl->y - skill_area_temp[3] );
-					dist=((dx>dy)?dx:dy);
-				}
+				//if(skillid==SM_MAGNUM){	/* マグナムブレイクなら中心からの距離を計算 */
+				//	int dx=abs( bl->x - skill_area_temp[2] );
+				//	int dy=abs( bl->y - skill_area_temp[3] );
+				//	dist=((dx>dy)?dx:dy);
+				//}
 				skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,
 				skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,
 					0x0500|dist  );
 					0x0500|dist  );
 				if (bl->type == BL_MOB && skillid == AS_GRIMTOOTH) {
 				if (bl->type == BL_MOB && skillid == AS_GRIMTOOTH) {
@@ -2512,10 +2533,11 @@ int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int s
 		}else{
 		}else{
 			int ar=1;
 			int ar=1;
 			int x=bl->x,y=bl->y;
 			int x=bl->x,y=bl->y;
-			if( skillid==SM_MAGNUM){
+			/*if( skillid==SM_MAGNUM){
 				x=src->x;
 				x=src->x;
 				y=src->y;
 				y=src->y;
-			}else if(skillid==AC_SHOWER || skillid==ASC_METEORASSAULT)	/* アロ?シャワ?、メテオアサルト範?5*5 */
+			}else*/
+			if(skillid==AC_SHOWER || skillid==ASC_METEORASSAULT)	/* アロ?シャワ?、メテオアサルト範?5*5 */
 				ar=2;
 				ar=2;
 			else if(skillid==AS_SPLASHER)	/* ベナムスプラッシャ?範?3*3 */
 			else if(skillid==AS_SPLASHER)	/* ベナムスプラッシャ?範?3*3 */
 				ar=1;
 				ar=1;
@@ -2535,8 +2557,19 @@ int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int s
 				bl->m,x-ar,y-ar,x+ar,y+ar,0,
 				bl->m,x-ar,y-ar,x+ar,y+ar,0,
 				src,skillid,skilllv,tick, flag|BCT_ENEMY|1,
 				src,skillid,skilllv,tick, flag|BCT_ENEMY|1,
 				skill_castend_damage_id);
 				skill_castend_damage_id);
-			if (skillid == SM_MAGNUM)	// fire element for 10 seconds
-				status_change_start(src,SC_FLAMELAUNCHER,0,0,0,0,10000,0);
+		}
+		break;
+
+	case SM_MAGNUM:			/* マグナムブレイク [celest] */
+		{
+			int dist = 0;
+			int dx = abs( bl->x - skill_area_temp[2] );
+			int dy = abs( bl->y - skill_area_temp[3] );
+			dist = ((dx>dy)?dx:dy);
+			map_foreachinarea (skill_attack_area,src->m,src->x-1,src->y-1,src->x+1,src->y+1,0,
+				BF_WEAPON,src,src,skillid,skilllv,tick,0x0500|dist,BCT_ENEMY);
+			status_change_start (src,SC_FLAMELAUNCHER,0,0,0,0,10000,0);
+			clif_skill_nodamage (src,src,skillid,skilllv,1);
 		}
 		}
 		break;
 		break;
 
 
@@ -2773,15 +2806,20 @@ int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int s
 				if (skilllv == 5)
 				if (skilllv == 5)
 					skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,0 );
 					skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,0 );
 				if (bl->type == BL_PC) {
 				if (bl->type == BL_PC) {
-					((struct map_session_data *)bl)->status.sp = 0;
-					clif_updatestatus((struct map_session_data *)bl,SP_SP);
+					struct map_session_data *tsd = (struct map_session_data *)bl;
+					if (tsd) {
+						tsd->status.sp = 0;
+						clif_updatestatus((struct map_session_data *)bl,SP_SP);
+					}
 				}
 				}
 			} else {
 			} else {
 				clif_skill_nodamage(src,src,skillid,skilllv,1);
 				clif_skill_nodamage(src,src,skillid,skilllv,1);
 				if (skilllv == 5)
 				if (skilllv == 5)
 					skill_attack(BF_MAGIC,src,src,src,skillid,skilllv,tick,0 );
 					skill_attack(BF_MAGIC,src,src,src,skillid,skilllv,tick,0 );
-				sd->status.sp = 0;
-				clif_updatestatus(sd,SP_SP);
+				if (sd) {
+					sd->status.sp = 0;
+					clif_updatestatus(sd,SP_SP);
+				}				
 			}
 			}
 			status_change_start(src,SC_BLOCKSKILL,skilllv,0,skillid,0, (skilllv < 5 ? 10000: 15000),0 );
 			status_change_start(src,SC_BLOCKSKILL,skilllv,0,skillid,0, (skilllv < 5 ? 10000: 15000),0 );
 		}
 		}
@@ -3363,7 +3401,7 @@ int skill_castend_nodamage_id( struct block_list *src, struct block_list *bl,int
 			lv = (lv<0)?-lv:lv;
 			lv = (lv<0)?-lv:lv;
 			if((dstsd->bl.type!=BL_PC)	// 相手はPCじゃないとだめ
 			if((dstsd->bl.type!=BL_PC)	// 相手はPCじゃないとだめ
 			 ||(sd->bl.id == dstsd->bl.id)	// 相手が自分はだめ
 			 ||(sd->bl.id == dstsd->bl.id)	// 相手が自分はだめ
-			 ||(lv > 10)			// レベル差±10まで
+			 ||(lv > battle_config.devotion_level_difference)			// レベル差±10まで
 			 ||(!sd->status.party_id && !sd->status.guild_id)	// PTにもギルドにも所?無しはだめ
 			 ||(!sd->status.party_id && !sd->status.guild_id)	// PTにもギルドにも所?無しはだめ
 			 ||((sd->status.party_id != dstsd->status.party_id)	// 同じパ?ティ?か、
 			 ||((sd->status.party_id != dstsd->status.party_id)	// 同じパ?ティ?か、
 			 &&(sd->status.guild_id != dstsd->status.guild_id))	// 同じギルドじゃないとだめ
 			 &&(sd->status.guild_id != dstsd->status.guild_id))	// 同じギルドじゃないとだめ
@@ -5989,9 +6027,11 @@ int skill_unit_onplace(struct skill_unit *src,struct block_list *bl,unsigned int
 			struct status_change *sc_data=status_get_sc_data(bl);
 			struct status_change *sc_data=status_get_sc_data(bl);
 			if(sg->val2==0 && sc_data && sc_data[SC_ANKLE].timer==-1){
 			if(sg->val2==0 && sc_data && sc_data[SC_ANKLE].timer==-1){
 				int moveblock = ( bl->x/BLOCK_SIZE != src->bl.x/BLOCK_SIZE || bl->y/BLOCK_SIZE != src->bl.y/BLOCK_SIZE);
 				int moveblock = ( bl->x/BLOCK_SIZE != src->bl.x/BLOCK_SIZE || bl->y/BLOCK_SIZE != src->bl.y/BLOCK_SIZE);
-				int sec = skill_get_time2(sg->skill_id,sg->skill_lv) - status_get_agi(bl)/10;
+				int sec = skill_get_time2(sg->skill_id,sg->skill_lv) - status_get_agi(bl)*100;
 				if(status_get_mode(bl)&0x20)
 				if(status_get_mode(bl)&0x20)
 					sec = sec/5;
 					sec = sec/5;
+				if (sec < 3000)	// minimum time of 3 seconds [celest]
+					sec = 3000;
 				battle_stopwalking(bl,1);
 				battle_stopwalking(bl,1);
 				status_change_start(bl,SC_ANKLE,sg->skill_lv,0,0,0,sec,0);
 				status_change_start(bl,SC_ANKLE,sg->skill_lv,0,0,0,sec,0);
 
 
@@ -7034,7 +7074,7 @@ int skill_check_condition(struct map_session_data *sd,int type)
 			return 0;
 			return 0;
 		break;
 		break;
 	case CH_TIGERFIST:						//伏虎拳
 	case CH_TIGERFIST:						//伏虎拳
-		if(sd->sc_data[SC_COMBO].timer == -1 || sd->sc_data[SC_COMBO].val1 != MO_COMBOFINISH)
+		if((sd->sc_data[SC_COMBO].timer == -1 || sd->sc_data[SC_COMBO].val1 != MO_COMBOFINISH) && !sd->state.skill_flag)
 			return 0;
 			return 0;
 		break;
 		break;
 	case CH_CHAINCRUSH:						//連柱崩?
 	case CH_CHAINCRUSH:						//連柱崩?
@@ -7619,6 +7659,7 @@ int skill_use_id( struct map_session_data *sd, int target_id,
 	    skill_num != MO_EXTREMITYFIST &&
 	    skill_num != MO_EXTREMITYFIST &&
 	    skill_num != CH_TIGERFIST &&
 	    skill_num != CH_TIGERFIST &&
 	    skill_num != CH_CHAINCRUSH) ||
 	    skill_num != CH_CHAINCRUSH) ||
+		(skill_num == CH_CHAINCRUSH && sd->state.skill_flag) ||
 		(skill_num == MO_EXTREMITYFIST && sd->state.skill_flag) )
 		(skill_num == MO_EXTREMITYFIST && sd->state.skill_flag) )
 		pc_stopattack(sd);
 		pc_stopattack(sd);
 
 
@@ -7652,11 +7693,17 @@ int skill_use_id( struct map_session_data *sd, int target_id,
 		}
 		}
 		break;
 		break;
 	case MO_COMBOFINISH:		/*猛龍拳*/
 	case MO_COMBOFINISH:		/*猛龍拳*/
-	case CH_TIGERFIST:		/* 伏虎拳 */
+//	case CH_TIGERFIST:		/* 伏虎拳 */
 	case CH_CHAINCRUSH:		/* 連柱崩? */
 	case CH_CHAINCRUSH:		/* 連柱崩? */
 		target_id = sd->attacktarget;
 		target_id = sd->attacktarget;
 		break;
 		break;
 
 
+	case CH_TIGERFIST:		/* 伏虎拳 */
+		if(sc_data && sc_data[SC_COMBO].timer != -1 && sc_data[SC_COMBO].val1 == MO_COMBOFINISH)
+			target_id = sd->attacktarget;
+		break;
+
+
 // -- moonsoul	(altered to allow proper usage of extremity from new champion combos)
 // -- moonsoul	(altered to allow proper usage of extremity from new champion combos)
 //
 //
 	case MO_EXTREMITYFIST:	/*阿修羅覇鳳拳*/
 	case MO_EXTREMITYFIST:	/*阿修羅覇鳳拳*/