瀏覽代碼

Fixed bugreport:5788 WL_READING_SB should now work like official behavior(save preserved spell on relog, proper skill fail message, maximum number of spell that a char can hold)
Fixed bugreport:5657 WL_EARTHSTRAIN should now work like official behavior(total number of range [15x4+lv], layout when casted, interval)
Updated WL_IMPRISON duration, rate and proper skill fail message.
Special thanks to Yommy and his amazing tool...

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

rud0lp20 13 年之前
父節點
當前提交
2e4022e97a
共有 9 個文件被更改,包括 150 次插入140 次删除
  1. 6 6
      db/pre-re/skill_cast_db.txt
  2. 1 1
      db/pre-re/skill_unit_db.txt
  3. 6 6
      db/re/skill_cast_db.txt
  4. 1 1
      db/re/skill_unit_db.txt
  5. 3 1
      src/map/clif.c
  6. 0 10
      src/map/pc.h
  7. 101 110
      src/map/skill.c
  8. 20 5
      src/map/status.c
  9. 12 0
      src/map/status.h

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

@@ -1184,8 +1184,8 @@
 //==========================================
 
 //===== Warlock ============================
-//-- WL_WHITEIMPRISON //CHECK Is reuse delay hard coded to work only if successful? Duration 1 may be incorrect. Is duration 2 for if casted on self?
-2201,0,0,0,10000:12000:14000:16000:18000,5000,0
+//-- WL_WHITEIMPRISON 
+2201,0,0,0,6000:8000:10000:12000:14000,15000,0,0
 //-- WL_SOULEXPANSION
 2202,2000,500,0,0,0,0
 //-- WL_FROSTMISTY
@@ -1212,8 +1212,8 @@
 //-- WL_CHAINLIGHTNING //CHECK Whats duration 1 used for?
 2214,4500:5000:5500:6000:6500,3000,0,1000,0,0
 
-//-- WL_EARTHSTRAIN //CHECK What is duration 2 used for?
-2216,3000:4000:5000:6000:7000,1000,0,500,60000,10000
+//-- WL_EARTHSTRAIN 
+2216,2000:3000:4000:5000:6000,1000,0,150,0,10000,2000
 //-- WL_TETRAVORTEX //CHECK Duration 1 might be correct?
 2217,6000:7000:8000:9000:10000,2000,0,20000,0,15000
 
@@ -1227,8 +1227,8 @@
 //-- WL_SUMMONSTONE
 2229,2000,0,0,30000:40000:50000:60000:70000,0,0
 
-//-- WL_READING_SB //CHECK Is duration 1 how long the spell will be stored for?
-2231,4000,500,0,30000,0,5000
+//-- WL_READING_SB
+2231,4000,500,0,0,0,5000
 //==========================================
 
 //===== Ranger =============================

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

@@ -101,7 +101,7 @@
 2032,0xe1,    ,  2, 0,1000,enemy, 0x018	//GC_POISONSMOKE
 
 2214,0x86,    ,  0, 5, 100,enemy, 0x080	//WL_CHAINLIGHTNING
-2216,0xcb,    , -1, 2,2000,enemy, 0x018	//WL_EARTHSTRAIN
+2216,0xcb,    , -1, 0, 150,enemy, 0x018	//WL_EARTHSTRAIN
 
 2238,0xd8,    ,  0, 1,1000,enemy, 0x006	//RA_ELECTRICSHOCKER
 2239,0xd9,    ,  0, 1,1000,enemy, 0x006	//RA_CLUSTERBOMB

+ 6 - 6
db/re/skill_cast_db.txt

@@ -1185,8 +1185,8 @@
 //==========================================
 
 //===== Warlock ============================
-//-- WL_WHITEIMPRISON //CHECK Is reuse delay hard coded to work only if successful? Duration 1 may be incorrect. Is duration 2 for if casted on self?
-2201,0,0,0,10000:12000:14000:16000:18000,5000,0,0
+//-- WL_WHITEIMPRISON 
+2201,0,0,0,6000:8000:10000:12000:14000,15000,0,0
 //-- WL_SOULEXPANSION
 2202,2000,500,0,0,0,0,0
 //-- WL_FROSTMISTY
@@ -1213,8 +1213,8 @@
 //-- WL_CHAINLIGHTNING //CHECK Whats duration 1 used for?
 2214,4500:5000:5500:6000:6500,3000,0,1000,0,0,0
 
-//-- WL_EARTHSTRAIN //CHECK What is duration 2 used for?
-2216,3000:4000:5000:6000:7000,1000,0,500,60000,10000,0
+//-- WL_EARTHSTRAIN 
+2216,2000:3000:4000:5000:6000,1000,0,150,0,10000,2000
 //-- WL_TETRAVORTEX //CHECK Duration 1 might be correct?
 2217,6000:7000:8000:9000:10000,2000,0,20000,0,15000,0
 
@@ -1228,8 +1228,8 @@
 //-- WL_SUMMONSTONE
 2229,2000,0,0,30000:40000:50000:60000:70000,0,0,0
 
-//-- WL_READING_SB //CHECK Is duration 1 how long the spell will be stored for?
-2231,4000,500,0,30000,0,5000,0
+//-- WL_READING_SB 
+2231,4000,500,0,0,0,5000,0
 //==========================================
 
 //===== Ranger =============================

+ 1 - 1
db/re/skill_unit_db.txt

@@ -101,7 +101,7 @@
 2032,0xe1,    ,  2, 0,1000,enemy, 0x018	//GC_POISONSMOKE
 
 2214,0x86,    ,  0, 5, 100,enemy, 0x080	//WL_CHAINLIGHTNING
-2216,0xcb,    , -1, 2,2000,enemy, 0x018	//WL_EARTHSTRAIN
+2216,0xcb,    , -1, 0, 150,enemy, 0x018	//WL_EARTHSTRAIN
 
 2238,0xd8,    ,  0, 1,1000,enemy, 0x006	//RA_ELECTRICSHOCKER
 2239,0xd9,    ,  0, 1,1000,enemy, 0x006	//RA_CLUSTERBOMB

+ 3 - 1
src/map/clif.c

@@ -15859,8 +15859,10 @@ int clif_spellbook_list(struct map_session_data *sd)
 		sd->menuskill_id = WL_READING_SB;
 		sd->menuskill_val = c;
 	}
-	else
+	else{
 		status_change_end(&sd->bl,SC_STOP,INVALID_TIMER);
+		clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK, 0);
+	}
 
 	return 1;
 }

+ 0 - 10
src/map/pc.h

@@ -23,9 +23,6 @@
 #define MAX_PC_SKILL_REQUIRE 5
 #define MAX_PC_FEELHATE 3
 
-//For Warlock
-#define MAX_SPELLBOOK 10
-
 struct weapon_data {
 	int atkmods[3];
 	// all the variables except atkmods get zero'ed in each call of status_calc_pc
@@ -422,13 +419,6 @@ struct map_session_data {
 		bool changed; // if true, should sync with charserver on next mailbox request
 	} mail;
 
-	// Reading SpellBook
-	struct {
-		unsigned short skillid;
-		unsigned char level;
-		unsigned char points;
-	} rsb[MAX_SPELLBOOK];
-
 	//Quest log system [Kevin] [Inkfish]
 	int num_quests;
 	int avail_quests;

+ 101 - 110
src/map/skill.c

@@ -97,7 +97,7 @@ bool skill_reproduce_db[MAX_SKILL_DB];
 struct s_skill_spellbook_db {
 	int nameid;
 	int skillid;
-	int points;
+	int point;
 };
 
 struct s_skill_spellbook_db skill_spellbook_db[MAX_SKILL_SPELLBOOK_DB];
@@ -482,8 +482,10 @@ int skillnotok (int skillid, struct map_session_data *sd)
 		return 1;
 	}
 
-	if (sd->blockskill[i] > 0)
+	if (sd->blockskill[i] > 0){
+		clif_skill_fail(sd, skillid, USESKILL_FAIL_SKILLINTERVAL, 0);
 		return 1;
+	}
 	/**
 	 * It has been confirmed on a official server (thanks to Yommy) that item-cast skills bypass all the restrictions above
 	 * Also, without this check, an exploit where an item casting + healing (or any other kind buff) isn't deleted after used on a restricted map
@@ -4104,50 +4106,47 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
 		{
 			int i;
 			// Priority is to release SpellBook
-			ARR_FIND(0,MAX_SPELLBOOK,i,sd->rsb[i].skillid != 0);
-			if( i < MAX_SPELLBOOK )
+			if( sc && sc->data[SC_READING_SB] )
 			{ // SpellBook
-				int rsb_skillid, rsb_skilllv;
-
-				if( skilllv > 1 )
-				{
-					ARR_FIND(0,MAX_SPELLBOOK,i,sd->rsb[i].skillid == 0);
-					i--; // At skilllvl 2, Release uses the last learned skill in spellbook
-				}
-
-				rsb_skillid = sd->rsb[i].skillid;
-				rsb_skilllv = sd->rsb[i].level;
-
-				if( sc && sc->data[SC_READING_SB] && sc->data[SC_READING_SB]->val2 > 0 )
-					sc->data[SC_READING_SB]->val2 -= sd->rsb[i].points;
-
-				if( skilllv > 1 )
-					sd->rsb[i].skillid = 0; // Last position - only remove it from list
-				else
-					memmove(&sd->rsb[0],&sd->rsb[1],sizeof(sd->rsb) - sizeof(sd->rsb[0]));
+				int skill_id, skill_lv, point, s = 0;
+				int spell[SC_MAXSPELLBOOK-SC_SPELLBOOK1 + 1];
+
+				for(i = SC_MAXSPELLBOOK; i >= SC_SPELLBOOK1; i--) // List all available spell to be released
+					if( sc->data[i] ) spell[s++] = i;
+
+				i = spell[s==1?0:rand()%s];// Random select of spell to be released.
+				if( s && sc->data[i] ){// Now extract the data from the preserved spell
+					skill_id = sc->data[i]->val1; 
+					skill_lv = sc->data[i]->val2;
+					point = sc->data[i]->val3;
+					status_change_end(src, (sc_type)i, INVALID_TIMER);
+				}else //something went wrong :(
+					break;
 
-				if( sd->rsb[0].skillid == 0 )
+				if( sc->data[SC_READING_SB]->val2 > point )
+					sc->data[SC_READING_SB]->val2 -= point;
+				else // Last spell to be released 
 					status_change_end(src, SC_READING_SB, INVALID_TIMER);
 
-				clif_skill_nodamage(src,bl,skillid,skilllv,1);
-				if( !skill_check_condition_castbegin(sd,rsb_skillid,rsb_skilllv) )
+				clif_skill_nodamage(src, bl, skillid, skilllv, 1);
+				if( !skill_check_condition_castbegin(sd, skill_id, skill_lv) )
 					break;
 
-				switch( skill_get_casttype(rsb_skillid) )
+				switch( skill_get_casttype(skill_id) )
 				{
 					case CAST_GROUND:
-						skill_castend_pos2(src,bl->x,bl->y,rsb_skillid,rsb_skilllv,tick,0);
+						skill_castend_pos2(src, bl->x, bl->y, skill_id, skill_lv, tick, 0);
 						break;
 					case CAST_NODAMAGE:
-						skill_castend_nodamage_id(src,bl,rsb_skillid,rsb_skilllv,tick,0);
+						skill_castend_nodamage_id(src, bl, skill_id, skill_lv, tick, 0);
 						break;
 					case CAST_DAMAGE:
-						skill_castend_damage_id(src,bl,rsb_skillid,rsb_skilllv,tick,0);
+						skill_castend_damage_id(src, bl, skill_id, skill_lv, tick, 0);
 						break;
 				}
 
-				sd->ud.canact_tick = tick + skill_delayfix(src, rsb_skillid, rsb_skilllv);
-				clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, rsb_skillid, rsb_skilllv), 0, 0, 0);
+				sd->ud.canact_tick = tick + skill_delayfix(src, skill_id, skill_lv);
+				clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, skill_id, skill_lv), 0, 0, 0);
 			}
 			else
 			{ // Summon Balls
@@ -4166,7 +4165,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
 
 				if( j == 0 )
 				{ // No Spheres
-					clif_skill_fail(sd,skillid,USESKILL_FAIL_LEVEL,0);
+					clif_skill_fail(sd,skillid,USESKILL_FAIL_SUMMON_NONE,0);
 					break;
 				}
 				
@@ -7600,16 +7599,25 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 		break;
 
 	case WL_WHITEIMPRISON:
-		if( !(tsc && tsc->data[type]) && (src == bl || battle_check_target(src, bl, BCT_ENEMY)) && !is_boss(bl) )// Should not work with bosses.
+		if( (src == bl || battle_check_target(src, bl, BCT_ENEMY)) && !is_boss(bl) )// Should not work with bosses.
 		{
-			int rate = 50 + 3 * skilllv + ( sd? sd->status.job_level : 50 ) / 4;
-			i = sc_start2(bl,type,rate,skilllv,src->id,(src == bl)?skill_get_time2(skillid,skilllv):skill_get_time(skillid, skilllv));
-			clif_skill_nodamage(src,bl,skillid,skilllv,i);
+			int rate = ( sd? sd->status.job_level : 50 ) / 4;
+		
+			if(src == bl ) rate = 100; // Success Chance: On self, 100%
+			else if(bl->type == BL_PC) rate += 20 + 10 * skilllv; // On Players, (20 + 10 * Skill Level) %
+			else rate += 40 + 10 * skilllv; // On Monsters, (40 + 10 * Skill Level) %
+
+			if( !(tsc && tsc->data[type]) ){
+				i = sc_start2(bl,type,rate,skilllv,src->id,(src == bl)?5000:(bl->type == BL_PC)?skill_get_time(skillid,skilllv):skill_get_time2(skillid, skilllv));
+				clif_skill_nodamage(src,bl,skillid,skilllv,i);
+			}
+
 			if( sd && i )
 				skill_blockpc_start(sd,skillid,4000); // Reuse Delay only activated on success
-		}
-		else if( sd )
-			clif_skill_fail(sd,skillid,USESKILL_FAIL_LEVEL,0);
+			else if(sd)
+				clif_skill_fail(sd,skillid,USESKILL_FAIL_LEVEL,0);
+		}else if( sd )
+			clif_skill_fail(sd,skillid,USESKILL_FAIL_TOTARGET,0);
 		break;
 
 	case WL_FROSTMISTY:
@@ -7711,25 +7719,19 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 	case WL_READING_SB:
 		if( sd )
 		{
-			int i, preserved = 0, max_preserve = 4 * pc_checkskill(sd,WL_FREEZE_SP) + sstatus->int_ / 10 + sd->status.base_level / 10;
-			ARR_FIND(0, MAX_SPELLBOOK, i, sd->rsb[i].skillid == 0); // Search for a Free Slot
-			if( i == MAX_SPELLBOOK )
-			{
-				clif_skill_fail(sd,skillid,USESKILL_FAIL_SKILLINTERVAL,0);
-				break;
-			}
-			for( i = 0; i < MAX_SPELLBOOK && sd->rsb[i].skillid; i++ )
-				preserved += sd->rsb[i].points;
+			struct status_change *sc = status_get_sc(bl);
+			int i, max_preserve = 4 * pc_checkskill(sd, WL_FREEZE_SP) + sstatus->int_ / 10 + sd->status.base_level / 10;
 
-			if( preserved >= max_preserve )
-			{
-				clif_skill_fail(sd,skillid,USESKILL_FAIL_SKILLINTERVAL,0);
+			for(i=SC_SPELLBOOK1; i <= SC_MAXSPELLBOOK; i++) if( sc && !sc->data[i] ) break;
+			if( i == SC_MAXSPELLBOOK ) 
+			{ 
+				clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_READING, 0);
 				break;
 			}
 
-			sc_start(bl,SC_STOP,100,skilllv,-1); //Can't move while selecting a spellbook.
+			sc_start(bl, SC_STOP, 100, skilllv, INVALID_TIMER); //Can't move while selecting a spellbook.
 			clif_spellbook_list(sd);
-			clif_skill_nodamage(src,bl,skillid,skilllv,1);
+			clif_skill_nodamage(src, bl, skillid, skilllv, 1);
 		}
 		break;
 	/**
@@ -9590,20 +9592,19 @@ int skill_castend_pos2(struct block_list* src, int x, int y, int skillid, int sk
 	case WL_EARTHSTRAIN:
 		{
 			int i, wave = skilllv + 4, dir = map_calc_dir(src,x,y);
-			int sx = x, sy = y;
+			int sx = x = src->x, sy = y = src->y; // Store first caster's location to avoid glitch on unit setting
 
-			for( i = 0; i < wave; i++ )
+			for( i = 1; i <= wave; i++ )
 			{
-				switch( dir )
-				{
-				case 0: case 1: case 7: sy = src->y + i; break;
-				case 3: case 4: case 5: sy = src->y - i; break;
-				case 2: sx = src->x - i; break;
-				case 6: sx = src->x + i; break;
+				switch( dir ){
+					case 0: case 1: case 7: sy = y + i; break;
+					case 3: case 4: case 5: sy = y - i; break;
+					case 2: sx = x - i; break;
+					case 6: sx = x + i; break;
 				}
-				skill_addtimerskill(src,gettick() + (200 * i),0,sx,sy,skillid,skilllv,dir,flag&2); // Temp code until animation is replaced. [Rytech]
-				//skill_addtimerskill(src,gettick() + (150 * i),0,sx,sy,skillid,skilllv,dir,flag&2); // Official steping timer, but disabled due to too much noise.
+				skill_addtimerskill(src,gettick() + (150 * i),0,sx,sy,skillid,skilllv,dir,flag&2);
 			}
+			if(sd) skill_blockpc_start(sd, skillid, skill_get_cooldown(skillid, skilllv));
 		}
 		break;
 	/**
@@ -15738,57 +15739,52 @@ int skill_magicdecoy(struct map_session_data *sd, int nameid) {
 
 // Warlock Spellbooks. [LimitLine/3CeAM]
 int skill_spellbook (struct map_session_data *sd, int nameid) {
-	int i, j, points, skillid, preserved = 0, max_preserve;
-	nullpo_ret(sd);
+	int i, max_preserve, skill_id, point;
+	struct status_change *sc;
 	
-	if( sd->sc.data[SC_STOP] ) status_change_end(&sd->bl,SC_STOP,INVALID_TIMER);
-	if( nameid <= 0 ) return 0;
+	nullpo_ret(sd);
 
-	if( pc_search_inventory(sd,nameid) < 0 )
-	{ // User with no item on inventory
-		clif_skill_fail(sd,WL_READING_SB,USESKILL_FAIL_SKILLINTERVAL,0);
-		return 0;
-	}
+	sc = status_get_sc(&sd->bl);
+	status_change_end(&sd->bl, SC_STOP, INVALID_TIMER);
 
-	ARR_FIND(0,MAX_SPELLBOOK,j,sd->rsb[j].skillid == 0); // Search for a free slot
-	if( j == MAX_SPELLBOOK )
-	{ // No more free slots
-		clif_skill_fail(sd,WL_READING_SB,USESKILL_FAIL_SPELLBOOK_PRESERVATION_POINT,0);
+	for(i=SC_SPELLBOOK1; i <= SC_MAXSPELLBOOK; i++) if( sc && !sc->data[i] ) break;
+	if( i > SC_MAXSPELLBOOK ) 
+	{ 
+		clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_READING, 0);
 		return 0;
 	}
 
 	ARR_FIND(0,MAX_SKILL_SPELLBOOK_DB,i,skill_spellbook_db[i].nameid == nameid); // Search for information of this item
-	if( i == MAX_SKILL_SPELLBOOK_DB )
-	{ // Fake nameid
-		clif_skill_fail(sd,WL_READING_SB,USESKILL_FAIL_SKILLINTERVAL,0);
-		return 0;
-	}
+	if( i == MAX_SKILL_SPELLBOOK_DB ) return 0; 
 
-	skillid = skill_spellbook_db[i].skillid;
-	points = skill_spellbook_db[i].points;
-
-	if( !pc_checkskill(sd,skillid) )
+	if( !pc_checkskill(sd, (skill_id = skill_spellbook_db[i].skillid)) )
 	{ // User don't know the skill
-		sc_start(&sd->bl,SC_SLEEP,100,1,skill_get_time(WL_READING_SB,pc_checkskill(sd,WL_READING_SB)));
-		clif_skill_fail(sd,WL_READING_SB,USESKILL_FAIL_SPELLBOOK_DIFFICULT_SLEEP,0);
+		sc_start(&sd->bl, SC_SLEEP, 100, 1, skill_get_time(WL_READING_SB, pc_checkskill(sd,WL_READING_SB)));
+		clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_DIFFICULT_SLEEP, 0);
 		return 0;
 	}
 
-	max_preserve = 4 * pc_checkskill(sd,WL_FREEZE_SP) + status_get_int(&sd->bl) / 10 + sd->status.base_level / 10;
-	for( i = 0; i < MAX_SPELLBOOK && sd->rsb[i].skillid; i++ )
-		preserved += sd->rsb[i].points;
+	max_preserve = 4 * pc_checkskill(sd, WL_FREEZE_SP) + status_get_int(&sd->bl) / 10 + sd->status.base_level / 10;
+	point = skill_spellbook_db[i].point;
 
-	if( preserved + points >= max_preserve )
-	{ // No more free points
-		clif_skill_fail(sd,WL_READING_SB,USESKILL_FAIL_SKILLINTERVAL,0);
-		return 0;
+	if( sc && sc->data[SC_READING_SB] ){
+		if( (sc->data[SC_READING_SB]->val2 + point) > max_preserve )
+		{ 
+			clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_PRESERVATION_POINT, 0);
+			return 0;
+		}
+		for(i = SC_MAXSPELLBOOK; i >= SC_SPELLBOOK1; i--){ // This is how official saves spellbook. [malufett]
+			if( !sc->data[i] ){
+				sc->data[SC_READING_SB]->val2 += point; // increase points
+				sc_start4(&sd->bl, (sc_type)i, 100, skill_id, pc_checkskill(sd,skill_id), point, 0, INVALID_TIMER);
+				break;
+			}
+		}
+	}else{
+		sc_start2(&sd->bl, SC_READING_SB, 100, 0, point, INVALID_TIMER);
+		sc_start4(&sd->bl, SC_MAXSPELLBOOK, 100, skill_id, pc_checkskill(sd,skill_id), point, 0, INVALID_TIMER);
 	}
 
-	sd->rsb[j].skillid = skillid;
-	sd->rsb[j].level = pc_checkskill(sd,skillid);
-	sd->rsb[j].points = points;
-	sc_start2(&sd->bl,SC_READING_SB,100,0,preserved+points,-1);
-
 	return 1;
 }
 int skill_select_menu(struct map_session_data *sd,int flag,int skill_id) {
@@ -16435,16 +16431,13 @@ void skill_init_unit_layout (void)
 	earthstrain_unit_pos = pos;
 	for( i = 0; i < 8; i++ )
 	{ // For each Direction
-		skill_unit_layout[pos].count = 3; // Temp code being used as the official method makes too much noise in game. [Rytech]
-		//skill_unit_layout[pos].count = 15; // This line is here to replace the above one once gravity changes the animation.
+		skill_unit_layout[pos].count = 15;
 		switch( i )
 		{
 		case 0: case 1: case 3: case 4: case 5: case 7:
 			{
-				int dx[] = {-5, 0, 5};
-				int dy[] = { 0, 0, 0};
-				//int dx[] = {-7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7}; // Leave this here for future use.
-				//int dy[] = { 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0};
+				int dx[] = {-7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7};
+				int dy[] = { 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0};
 				memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
 				memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
 			}
@@ -16452,10 +16445,8 @@ void skill_init_unit_layout (void)
 		case 2:
 		case 6:
 			{
-				int dx[] = { 0, 0, 0};
-				int dy[] = {-5, 0, 5};
-				//int dx[] = { 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0}; // Leave this here for future use.
-				//int dy[] = {-7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7};
+				int dx[] = { 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0};
+				int dy[] = {-7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7};
 				memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
 				memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
 			}
@@ -16847,7 +16838,7 @@ static bool skill_parse_row_spellbookdb(char* split[], int columns, int current)
 	else
 	{
 		skill_spellbook_db[current].skillid = skillid;
-		skill_spellbook_db[current].points = points;
+		skill_spellbook_db[current].point = points;
 		skill_spellbook_db[current].nameid = nameid;
 
 		return true;

+ 20 - 5
src/map/status.c

@@ -785,6 +785,14 @@ void initChangeTables(void) {
 	StatusIconChangeTable[SC_SPHERE_3] = SI_SPHERE_3;
 	StatusIconChangeTable[SC_SPHERE_4] = SI_SPHERE_4;
 	StatusIconChangeTable[SC_SPHERE_5] = SI_SPHERE_5;
+	// Warlock Preserved spells
+	StatusIconChangeTable[SC_SPELLBOOK1] = SI_SPELLBOOK1;
+	StatusIconChangeTable[SC_SPELLBOOK2] = SI_SPELLBOOK2;
+	StatusIconChangeTable[SC_SPELLBOOK3] = SI_SPELLBOOK3;
+	StatusIconChangeTable[SC_SPELLBOOK4] = SI_SPELLBOOK4;
+	StatusIconChangeTable[SC_SPELLBOOK5] = SI_SPELLBOOK5;
+	StatusIconChangeTable[SC_SPELLBOOK6] = SI_SPELLBOOK6;
+	StatusIconChangeTable[SC_MAXSPELLBOOK] = SI_SPELLBOOK7;
 
 	StatusIconChangeTable[SC_NEUTRALBARRIER_MASTER] = SI_NEUTRALBARRIER_MASTER;
 	StatusIconChangeTable[SC_STEALTHFIELD_MASTER] = SI_STEALTHFIELD_MASTER;
@@ -5831,9 +5839,12 @@ int status_get_sc_def(struct block_list *bl, enum sc_type type, int rate, int ti
 		if (sd) //Duration greatly reduced for players.
 			tick /= 15;
 		//No defense against it (buff).
-	case SC_WHITEIMPRISON:
 		rate -= (status_get_lv(bl) / 5 + status->vit / 4 + status->agi / 10)*100; // Lineal Reduction of Rate
-		//tick_def = (int)floor(log10(status_get_lv(bl)) * 10.);
+		break;
+	case SC_WHITEIMPRISON:
+		rate -= (status_get_lv(bl) / 5 + status->vit / 4 + status->agi / 10)*100; 
+		if( tick != 5000) // not applied on caster
+			tick -= (status->vit + status->luk) / 20 * 1000; 
 		break;
 	case SC_BURNING:
 		// From iROwiki : http://forums.irowiki.org/showpost.php?p=577240&postcount=583
@@ -7600,7 +7611,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val
 			break;
 		case SC_READING_SB:
 			// val2 = sp reduction per second
-			tick_time = 1000; // [GodLesZ] tick time
+			tick_time = 5000; // [GodLesZ] tick time
 			break;
 		case SC_SPHERE_1:
 		case SC_SPHERE_2:
@@ -9644,9 +9655,13 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data)
 		break;
 
 	case SC_READING_SB:
-		if( !status_charge(bl, 0, sce->val2) )
+		if( !status_charge(bl, 0, sce->val2) ){
+			int i;
+			for(i = SC_SPELLBOOK1; i <= SC_MAXSPELLBOOK; i++) // Also remove stored spell as well.
+				status_change_end(bl, (sc_type)i, INVALID_TIMER);
 			break;
-		sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
+		}
+		sc_timer_next(5000 + tick, status_change_timer, bl->id, data);
 		return 0;
 
 	case SC_ELECTRICSHOCKER:

+ 12 - 0
src/map/status.h

@@ -600,6 +600,18 @@ typedef enum sc_type {
 	SC_EARTH_INSIGNIA,
 	/* new pushcart */
 	SC_PUSH_CART,
+	/* Warlock Spell books */
+	SC_SPELLBOOK1,
+	SC_SPELLBOOK2,
+	SC_SPELLBOOK3,
+	SC_SPELLBOOK4,
+	SC_SPELLBOOK5,
+	SC_SPELLBOOK6,
+/**
+ * In official server there are only 7 maximum number of spell books that can be memorized
+ * To increase the maximum value just add another status type before SC_MAXSPELLBOOK (ex. SC_SPELLBOOK7, SC_SPELLBOOK8 and so on)
+ **/
+	SC_MAXSPELLBOOK,
 	
 	SC_MAX, //Automatically updated max, used in for's to check we are within bounds.
 } sc_type;