From 8aee60e91807930e4d43965a20c8991b416d7f29 Mon Sep 17 00:00:00 2001
From: shennetsind <ind@henn.et>
Date: Sat, 14 Mar 2015 23:18:53 -0300
Subject: Multiple SC fixes

First and foremost an ancient status_change_timer issue which'd cause a timer deletion error when an status within status_change_timer kills the unit.
This commit also fixes some pointer/map-free-block logic which showed up while debugging status_change_timer.
Special Thanks to Haruna.

Signed-off-by: shennetsind <ind@henn.et>
---
 src/map/status.c | 60 +++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 44 insertions(+), 16 deletions(-)

diff --git a/src/map/status.c b/src/map/status.c
index b278324b0..c2b634665 100644
--- a/src/map/status.c
+++ b/src/map/status.c
@@ -9634,7 +9634,8 @@ int status_change_start(struct block_list *src, struct block_list *bl, enum sc_t
 			}
 			break;
 		case SC_CASH_BOSS_ALARM:
-			clif->bossmapinfo(sd->fd, map->id2boss(sce->val1), 0); // First Message
+			if( sd )
+				clif->bossmapinfo(sd->fd, map->id2boss(sce->val1), 0); // First Message
 			break;
 		case SC_MER_HP:
 			status_percent_heal(bl, 100, 0); // Recover Full HP
@@ -9778,7 +9779,6 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const
 	nullpo_ret(bl);
 
 	sc = status->get_sc(bl);
-	st = status->get_status_data(bl);
 
 	if(type < 0 || type >= SC_MAX || !sc || !(sce = sc->data[type]))
 		return 0;
@@ -9788,6 +9788,8 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const
 	if (sce->timer != tid && tid != INVALID_TIMER)
 		return 0;
 
+	st = status->get_status_data(bl);
+	
 	if( sd && sce->timer == INVALID_TIMER && !sd->state.loggingout )
 		chrif->del_scdata_single(sd->status.account_id,sd->status.char_id,type);
 
@@ -9969,7 +9971,9 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const
 					}
 
 					sce->val2 = 0;
-					skill->del_unitgroup(group,ALC_MARK);
+					
+					if( group )
+						skill->del_unitgroup(group,ALC_MARK);
 				}
 
 				if ((sce->val1&0xFFFF) == CG_MOONLIT)
@@ -10072,7 +10076,8 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const
 			if (sce->val3) { //Clear the group.
 				struct skill_unit_group* group = skill->id2group(sce->val3);
 				sce->val3 = 0;
-				skill->del_unitgroup(group,ALC_MARK);
+				if( group )
+					skill->del_unitgroup(group,ALC_MARK);
 			}
 			break;
 		case SC_HERMODE:
@@ -10118,10 +10123,12 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const
 				break;
 			// Note: vending/buying is closed by unit_remove_map, no
 			// need to do it here.
-			map->quit(sd);
-			// Because map->quit calls status_change_end with tid -1
-			// from here it's not neccesary to continue
-			return 1;
+			if( sd ) {
+				map->quit(sd);
+				// Because map->quit calls status_change_end with tid -1
+				// from here it's not neccesary to continue
+				return 1;
+			}
 			break;
 		case SC_STOP:
 			if( sce->val2 ) {
@@ -10227,7 +10234,8 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const
 				struct block_list *src = map->id2bl(sce->val2);
 				if(src) {
 					struct status_change *ssc = status->get_sc(src);
-					ssc->bs_counter--;
+					if( ssc )
+						ssc->bs_counter--;
 				}
 			}
 			break;
@@ -10568,6 +10576,8 @@ int status_change_timer(int tid, int64 tick, int id, intptr_t data) {
 		ShowError("status_change_timer: Mismatch for type %d: %d != %d (bl id %d)\n",type,tid,sce->timer, bl->id);
 		return 0;
 	}
+	
+	sce->timer = INVALID_TIMER;
 
 	sd = BL_CAST(BL_PC, bl);
 
@@ -10598,7 +10608,6 @@ int status_change_timer(int tid, int64 tick, int id, intptr_t data) {
 			}
 			sc_timer_next(sce->val2+tick, status->change_timer, bl->id, data);
 			return 0;
-			break;
 
 		case SC_SKA:
 			if(--(sce->val2)>0) {
@@ -10927,6 +10936,8 @@ int status_change_timer(int tid, int64 tick, int id, intptr_t data) {
 				}
 
 				if( !flag ) { // Random Skill Cast
+					map->freeblock_lock();
+
 					if (sd && !pc_issit(sd)) { //can't cast if sit
 						int mushroom_skill_id = 0;
 						unit->stop_attack(bl);
@@ -10950,7 +10961,10 @@ int status_change_timer(int tid, int64 tick, int id, intptr_t data) {
 					}
 
 					clif->emotion(bl,E_HEH);
-					sc_timer_next(4000+tick,status->change_timer,bl->id,data);
+					if( sc->data[type] )
+						sc_timer_next(4000+tick,status->change_timer,bl->id,data);
+
+					map->freeblock_unlock();
 				}
 				return 0;
 			}
@@ -10998,8 +11012,15 @@ int status_change_timer(int tid, int64 tick, int id, intptr_t data) {
 				int heal = st->max_hp * 3 / 100;
 				if (sc->count && sc->data[SC_AKAITSUKI] && heal)
 					heal = ~heal + 1;
+				
+				map->freeblock_lock();
+				
 				status->heal(bl, heal, 0, 2);
-				sc_timer_next(5000 + tick, status->change_timer, bl->id, data);
+				if( sc->data[type] ) {
+					sc_timer_next(5000 + tick, status->change_timer, bl->id, data);
+				}
+				map->freeblock_unlock();
+				
 				return 0;
 			}
 			break;
@@ -11117,8 +11138,8 @@ int status_change_timer(int tid, int64 tick, int id, intptr_t data) {
 				if ( sc->data[type] ) {
 					sc_timer_next(1000 + tick, status->change_timer, bl->id, data);
 				}
-				map->freeblock_unlock();
 				status->heal(src, damage*(5 + 5 * sce->val1)/100, 0, 0); // 5 + 5% per level
+				map->freeblock_unlock();
 				return 0;
 			}
 			break;
@@ -11225,6 +11246,7 @@ int status_change_timer(int tid, int64 tick, int id, intptr_t data) {
 					sc_timer_next(1000 + tick, status->change_timer, bl->id, data);
 				}
 				map->freeblock_unlock();
+				return 0;
 			}
 			break;
 
@@ -11305,12 +11327,13 @@ int status_change_timer(int tid, int64 tick, int id, intptr_t data) {
 		case SC_WATER_DROP:
 		case SC_WIND_CURTAIN:
 		case SC_STONE_SHIELD:
-			if(status->charge(bl, 0, sce->val2) && (sce->val4==-1 || (sce->val4-=sce->val3)>=0))
+			if(status->charge(bl, 0, sce->val2) && (sce->val4==-1 || (sce->val4-=sce->val3)>=0)) {
 				sc_timer_next(sce->val3 + tick, status->change_timer, bl->id, data);
-			else
+				return 0;
+			} else
 				if (bl->type == BL_ELEM)
 					elemental->change_mode(BL_CAST(BL_ELEM,bl),MAX_ELESKILLTREE);
-			return 0;
+			break;
 
 		case SC_STOMACHACHE:
 			if( --(sce->val4) > 0 ) {
@@ -11696,6 +11719,8 @@ int status_change_clear_buffs (struct block_list* bl, int type) {
 	if (!sc || !sc->count)
 		return 0;
 
+	map->freeblock_lock();
+	
 	if (type&6) //Debuffs
 		for (i = SC_COMMON_MIN; i <= SC_COMMON_MAX; i++)
 			status_change_end(bl, (sc_type)i, INVALID_TIMER);
@@ -11742,6 +11767,9 @@ int status_change_clear_buffs (struct block_list* bl, int type) {
 		}
 		status_change_end(bl, (sc_type)i, INVALID_TIMER);
 	}
+	
+	map->freeblock_unlock();
+	
 	return 0;
 }
 
-- 
cgit v1.2.3-70-g09d2