Przeglądaj źródła

-fix bugreport:7458 (deleting associate timer before cleanup status)
-merge clif_guild_skillup and clif_skillup (old TODO)
-add implementation for packet : 0x8c7, 0x99f and 0x1c9, variant of ZC_SKILL_ENTRY (0x11f)
-replace some hardcoded skill_flag by enum values

-little cleanup in skill_attack and regroup some skill with same effect (blewcount, animation).
-rewritte combo handling to regroup info and not duplicate info.
-change eleanor skill to use SC_COMBO as memo instead SC_STYLE_CHANGE
-separate SC_TINDER_BREAKER from SC_CLOSE_CONFINE since they don't act same way
-change MH_SONIC_CRAW to one attack with multiple hit instead x attack.

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

glighta 12 lat temu
rodzic
commit
7bc683e21e
15 zmienionych plików z 750 dodań i 759 usunięć
  1. 2 2
      db/re/skill_db.txt
  2. 1 0
      src/common/mmo.h
  3. 2 2
      src/map/atcommand.c
  4. 15 10
      src/map/battle.c
  5. 120 120
      src/map/chrif.c
  6. 66 68
      src/map/clif.c
  7. 1 2
      src/map/clif.h
  8. 55 54
      src/map/guild.c
  9. 2 0
      src/map/map.c
  10. 8 3
      src/map/mob.c
  11. 14 10
      src/map/pc.c
  12. 312 327
      src/map/skill.c
  13. 3 0
      src/map/skill.h
  14. 124 148
      src/map/status.c
  15. 25 13
      src/map/unit.c

+ 2 - 2
db/re/skill_db.txt

@@ -1128,7 +1128,7 @@
 8025,7,6,2,4:0:4:0:4,0,0,5,1,no,0,0,0,magic,0,	MH_XENO_SLASHER,Xeno Slasher
 8026,5:5:7:7:9,6,16,0,0x1,0,5,1,no,0,0,0,magic,0,	MH_SILENT_BREEZE,Silent Breeze
 8027,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0,		MH_STYLE_CHANGE,Style Change
-8028,1,8,1,0,0,0,5,1,no,0,0,0,weapon,0,		MH_SONIC_CRAW,Sonic Claw
+8028,1,8,1,0,0,0,5,1,no,0,0x0200,0,weapon,0,		MH_SONIC_CRAW,Sonic Claw
 8029,1,6,4,0,0,0,5,1,no,0,0x200,0,weapon,0,	MH_SILVERVEIN_RUSH,Silver Bain Rush
 8030,1,6,4,0,0,0,5,1,no,0,0x200,0,weapon,0,	MH_MIDNIGHT_FRENZY,Midnight Frenzy
 8031,5:6:7:8:9,6,1,0,0,0,5,1,no,0,0,0,weapon,3,	MH_STAHL_HORN,Steel Horn
@@ -1136,7 +1136,7 @@
 8033,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0,		MH_STEINWAND,Stone Wall
 8034,9,6,1,6,0x2,1:1:1:1:2,5,1,no,0,0,0,magic,0,	MH_HEILIGE_STANGE,Holy Pole
 8035,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0,		MH_ANGRIFFS_MODUS,Attack Mode
-8036,3:4:5:6:7,6,1,0,0,0,5,1,no,0,0,0,weapon,0,	MH_TINDER_BREAKER,Tinder Breaker
+8036,3:4:5:6:7,6,1,0,0,0,5,1,no,0,0x0200,0,weapon,0,	MH_TINDER_BREAKER,Tinder Breaker
 8037,1,6,4,0,0,0,5,1,no,0,0x200,0,weapon,0,	MH_CBC,Continual Break Combo
 8038,1,6,4,0,0,0,5,1,no,0,0x200,0,weapon,0,	MH_EQC,Eternal Quick Combo
 8039,0,6,4,3,0x2,1:1:1:2:2,5,1,no,0,0,0,weapon,0,	MH_MAGMA_FLOW,Magma Flow

+ 1 - 0
src/common/mmo.h

@@ -217,6 +217,7 @@ enum e_skill_flag
 	SKILL_FLAG_PLAGIARIZED,
 	SKILL_FLAG_REPLACED_LV_0, // temporary skill overshadowing permanent skill of level 'N - SKILL_FLAG_REPLACED_LV_0',
 	SKILL_FLAG_PERM_GRANTED, // permanent, granted through someway e.g. script
+	SKILL_FLAG_TMP_COMBO, //@FIXME for homon combo atm
 	//...
 };
 

+ 2 - 2
src/map/atcommand.c

@@ -3159,7 +3159,7 @@ ACMD_FUNC(lostskill)
 	}
 
 	sd->status.skill[skill_id].lv = 0;
-	sd->status.skill[skill_id].flag = 0;
+	sd->status.skill[skill_id].flag = SKILL_FLAG_PERMANENT;
 	clif_deleteskill(sd,skill_id);
 	clif_displaymessage(fd, msg_txt(71)); // You have forgotten the skill.
 
@@ -8707,7 +8707,7 @@ ACMD_FUNC(cart) {
 #define MC_CART_MDFY(x) \
 	sd->status.skill[MC_PUSHCART].id = x?MC_PUSHCART:0; \
 	sd->status.skill[MC_PUSHCART].lv = x?1:0; \
-	sd->status.skill[MC_PUSHCART].flag = x?1:0;
+	sd->status.skill[MC_PUSHCART].flag = x?SKILL_FLAG_TEMPORARY:SKILL_FLAG_PERMANENT;
 
 	int val = atoi(message);
 	bool need_skill = pc_checkskill(sd, MC_PUSHCART) ? false : true;

+ 15 - 10
src/map/battle.c

@@ -1131,9 +1131,9 @@ int battle_calc_damage(struct block_list *src,struct block_list *bl,struct Damag
 		if( sd && (sce = sc->data[SC_FORCEOFVANGUARD]) && flag&BF_WEAPON && rnd()%100 < sce->val2 )
 			pc_addspiritball(sd,skill_get_time(LG_FORCEOFVANGUARD,sce->val1),sce->val3);
 		if (sc->data[SC_STYLE_CHANGE]) {
-                    TBL_HOM *hd = BL_CAST(BL_HOM,bl); //when being hit
-                    if (hd && (rnd()%100<(status_get_lv(bl)/2)) ) hom_addspiritball(hd, 10); //add a sphere
-                }
+			TBL_HOM *hd = BL_CAST(BL_HOM,bl); //when being hit
+			if (hd && (rnd()%100<(status_get_lv(bl)/2)) ) hom_addspiritball(hd, 10); //add a sphere
+		}
 
 		if( sc->data[SC__DEADLYINFECT] && damage > 0 && rnd()%100 < 65 + 5 * sc->data[SC__DEADLYINFECT]->val1 )
 			status_change_spread(bl, src); // Deadly infect attacked side
@@ -1193,10 +1193,10 @@ int battle_calc_damage(struct block_list *src,struct block_list *bl,struct Damag
 			sc_start(src,bl,sc->data[SC_POISONINGWEAPON]->val2,100,sc->data[SC_POISONINGWEAPON]->val1,skill_get_time2(GC_POISONINGWEAPON, 1));
 		if( sc->data[SC__DEADLYINFECT] && damage > 0 && rnd()%100 < 65 + 5 * sc->data[SC__DEADLYINFECT]->val1 )
 			status_change_spread(src, bl);
-                if (sc->data[SC_STYLE_CHANGE]) {
-                    TBL_HOM *hd = BL_CAST(BL_HOM,src); //when attacking
-                    if (hd && (rnd()%100<(20+status_get_lv(bl)/5)) ) hom_addspiritball(hd, 10);
-                }
+		if (sc->data[SC_STYLE_CHANGE]) {
+			TBL_HOM *hd = BL_CAST(BL_HOM,src); //when attacking
+			if (hd && (rnd()%100<(20+status_get_lv(bl)/5)) ) hom_addspiritball(hd, 10);
+		}
 	}
 
 	if (battle_config.pk_mode && sd && bl->type == BL_PC && damage && map[bl->m].flag.pvp)
@@ -1742,6 +1742,11 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
 		wd.flag |= battle_range_type(src, target, skill_id, skill_lv);
 		switch(skill_id)
 		{
+			case MH_SONIC_CRAW:{
+				TBL_HOM *hd = BL_CAST(BL_HOM,src);
+				wd.div_ = hd->homunculus.spiritball;
+			}
+				break;
 			case MO_FINGEROFFENSIVE:
 				if(sd) {
 					if (battle_config.finger_offensive_type)
@@ -3124,9 +3129,9 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
 				}
 			}
 			if(sc->data[SC_STYLE_CHANGE]){
-                                TBL_HOM *hd = BL_CAST(BL_HOM,src);
-                                if (hd) ATK_ADD(hd->homunculus.spiritball * 3);
-                        }
+				TBL_HOM *hd = BL_CAST(BL_HOM,src);
+				if (hd) ATK_ADD(hd->homunculus.spiritball * 3);
+			}
 		}
 
 		switch (skill_id) {

+ 120 - 120
src/map/chrif.c

@@ -139,28 +139,28 @@ struct auth_node* chrif_search(int account_id) {
 
 struct auth_node* chrif_auth_check(int account_id, int char_id, enum sd_state state) {
 	struct auth_node *node = chrif_search(account_id);
-	
+
 	return ( node && node->char_id == char_id && node->state == state ) ? node : NULL;
 }
 
 bool chrif_auth_delete(int account_id, int char_id, enum sd_state state) {
 	struct auth_node *node;
-	
+
 	if ( (node = chrif_auth_check(account_id, char_id, state) ) ) {
 		int fd = node->sd ? node->sd->fd : node->fd;
-		
+
 		if ( session[fd] && session[fd]->session_data == node->sd )
 			session[fd]->session_data = NULL;
-		
+
 		if ( node->char_dat )
 			aFree(node->char_dat);
-		
+
 		if ( node->sd )
 			aFree(node->sd);
-		
+
 		ers_free(auth_db_ers, node);
 		idb_remove(auth_db,account_id);
-		
+
 		return true;
 	}
 	return false;
@@ -169,14 +169,14 @@ bool chrif_auth_delete(int account_id, int char_id, enum sd_state state) {
 //Moves the sd character to the auth_db structure.
 static bool chrif_sd_to_auth(TBL_PC* sd, enum sd_state state) {
 	struct auth_node *node;
-	
+
 	if ( chrif_search(sd->status.account_id) )
 		return false; //Already exists?
 
 	node = ers_alloc(auth_db_ers, struct auth_node);
-	
+
 	memset(node, 0, sizeof(struct auth_node));
-	
+
 	node->account_id = sd->status.account_id;
 	node->char_id = sd->status.char_id;
 	node->login_id1 = sd->login_id1;
@@ -188,33 +188,33 @@ static bool chrif_sd_to_auth(TBL_PC* sd, enum sd_state state) {
 	node->state = state;
 
 	sd->state.active = 0;
-	
+
 	idb_put(auth_db, node->account_id, node);
-	
+
 	return true;
 }
 
 static bool chrif_auth_logout(TBL_PC* sd, enum sd_state state) {
-	
+
 	if(sd->fd && state == ST_LOGOUT) { //Disassociate player, and free it after saving ack returns. [Skotlex]
 		//fd info must not be lost for ST_MAPCHANGE as a final packet needs to be sent to the player.
 		if ( session[sd->fd] )
 			session[sd->fd]->session_data = NULL;
 		sd->fd = 0;
 	}
-	
+
 	return chrif_sd_to_auth(sd, state);
 }
 
 bool chrif_auth_finished(TBL_PC* sd) {
 	struct auth_node *node= chrif_search(sd->status.account_id);
-	
+
 	if ( node && node->sd == sd && node->state == ST_LOGIN ) {
 		node->sd = NULL;
-		
+
 		return chrif_auth_delete(node->account_id, node->char_id, ST_LOGIN);
 	}
-	
+
 	return false;
 }
 // sets char-server's user id
@@ -239,17 +239,17 @@ void chrif_checkdefaultlogin(void) {
 // sets char-server's ip address
 int chrif_setip(const char* ip) {
 	char ip_str[16];
-	
+
 	if ( !( char_ip = host2ip(ip) ) ) {
 		ShowWarning("Failed to Resolve Char Server Address! (%s)\n", ip);
-		
+
 		return 0;
 	}
-	
+
 	safestrncpy(char_ip_str, ip, sizeof(char_ip_str));
-	
+
 	ShowInfo("Char Server IP Address : '"CL_WHITE"%s"CL_RESET"' -> '"CL_WHITE"%s"CL_RESET"'.\n", ip, ip2str(char_ip, ip_str));
-	
+
 	return 1;
 }
 
@@ -290,7 +290,7 @@ int chrif_save(struct map_session_data *sd, int flag) {
 	if (flag)
 		sd->state.storage_flag = 0; //Force close it.
 
-	//Saving of registry values. 
+	//Saving of registry values.
 	if (sd->state.reg_dirty&4)
 		intif_saveregistry(sd, 3); //Save char regs
 	if (sd->state.reg_dirty&2)
@@ -314,7 +314,7 @@ int chrif_save(struct map_session_data *sd, int flag) {
 	if( sd->md && mercenary_get_lifetime(sd->md) > 0 )
 		mercenary_save(sd->md);
 	if( sd->ed && elemental_get_lifetime(sd->ed) > 0 )
-		elemental_save(sd->ed);	
+		elemental_save(sd->ed);
 	if( sd->save_quest )
 		intif_quest_save(sd);
 
@@ -339,9 +339,9 @@ int chrif_connect(int fd) {
 // sends maps to char-server
 int chrif_sendmap(int fd) {
 	int i;
-	
+
 	ShowStatus("Sending maps to char server...\n");
-	
+
 	// Sending normal maps, not instances
 	WFIFOHEAD(fd, 4 + instance_start * 4);
 	WFIFOW(fd,0) = 0x2afa;
@@ -362,12 +362,12 @@ int chrif_recvmap(int fd) {
 	for(i = 10, j = 0; i < RFIFOW(fd,2); i += 4, j++) {
 		map_setipport(RFIFOW(fd,i), ip, port);
 	}
-	
+
 	if (battle_config.etc_log)
 		ShowStatus("Received maps from %d.%d.%d.%d:%d (%d maps)\n", CONVIP(ip), port, j);
 
 	other_mapserver_count++;
-	
+
 	return 0;
 }
 
@@ -381,10 +381,10 @@ int chrif_removemap(int fd) {
 		map_eraseipport(RFIFOW(fd, i), ip, port);
 
 	other_mapserver_count--;
-	
+
 	if(battle_config.etc_log)
 		ShowStatus("remove map of server %d.%d.%d.%d:%d (%d maps)\n", CONVIP(ip), port, j);
-	
+
 	return 0;
 }
 
@@ -420,7 +420,7 @@ int chrif_changemapserver(struct map_session_data* sd, uint32 ip, uint16 port) {
 	WFIFOL(char_fd,31) = htonl(session[sd->fd]->client_addr);
 	WFIFOL(char_fd,35) = sd->group_id;
 	WFIFOSET(char_fd,39);
-	
+
 	return 0;
 }
 
@@ -428,7 +428,7 @@ int chrif_changemapserver(struct map_session_data* sd, uint32 ip, uint16 port) {
 /// R 2b06 <account_id>.L <login_id1>.L <login_id2>.L <char_id>.L <map_index>.W <x>.W <y>.W <ip>.L <port>.W
 int chrif_changemapserverack(int account_id, int login_id1, int login_id2, int char_id, short map_index, short x, short y, uint32 ip, uint16 port) {
 	struct auth_node *node;
-	
+
 	if ( !( node = chrif_auth_check(account_id, char_id, ST_MAPCHANGE) ) )
 		return -1;
 
@@ -476,7 +476,7 @@ int chrif_connectack(int fd) {
  */
 static int chrif_reconnect(DBKey key, DBData *data, va_list ap) {
 	struct auth_node *node = db_data2ptr(data);
-	
+
 	switch (node->state) {
 		case ST_LOGIN:
 			if ( node->sd && node->char_dat == NULL ) {//Since there is no way to request the char auth, make it fail.
@@ -493,16 +493,16 @@ static int chrif_reconnect(DBKey key, DBData *data, va_list ap) {
 			struct map_session_data *sd = node->sd;
 			uint32 ip;
 			uint16 port;
-			
+
 			if( map_mapname2ipport(sd->mapindex,&ip,&port) == 0 )
 				chrif_changemapserver(sd, ip, port);
 			else //too much lag/timeout is the closest explanation for this error.
 				clif_authfail_fd(sd->fd, 3);
-			
+
 			break;
 			}
 	}
-	
+
 	return 0;
 }
 
@@ -510,9 +510,9 @@ static int chrif_reconnect(DBKey key, DBData *data, va_list ap) {
 /// Called when all the connection steps are completed.
 void chrif_on_ready(void) {
 	ShowStatus("Map Server is now online.\n");
-	
+
 	chrif_state = 2;
-	
+
 	chrif_check_shutdown();
 
 	//If there are players online, send them to the char-server. [Skotlex]
@@ -533,16 +533,16 @@ void chrif_on_ready(void) {
  *
  *------------------------------------------*/
 int chrif_sendmapack(int fd) {
-	
+
 	if (RFIFOB(fd,2)) {
 		ShowFatalError("chrif : send map list to char server failed %d\n", RFIFOB(fd,2));
 		exit(EXIT_FAILURE);
 	}
 
 	memcpy(wisp_server_name, RFIFOP(fd,3), NAME_LENGTH);
-	
+
 	chrif_on_ready();
-	
+
 	return 0;
 }
 
@@ -550,7 +550,7 @@ int chrif_sendmapack(int fd) {
  * Request sc_data from charserver [Skotlex]
  *------------------------------------------*/
 int chrif_scdata_request(int account_id, int char_id) {
-	
+
 #ifdef ENABLE_SC_SAVING
 	chrif_check(-1);
 
@@ -560,7 +560,7 @@ int chrif_scdata_request(int account_id, int char_id) {
 	WFIFOL(char_fd,6) = char_id;
 	WFIFOSET(char_fd,10);
 #endif
-	
+
 	return 0;
 }
 
@@ -617,7 +617,7 @@ void chrif_authok(int fd) {
 	//Causes problems if the currently connected player tries to quit or this data belongs to an already connected player which is trying to re-auth.
 	if ( ( sd = map_id2sd(account_id) ) != NULL )
 		return;
-	
+
 	if ( ( node = chrif_search(account_id) ) == NULL )
 		return; // should not happen
 
@@ -646,7 +646,7 @@ void chrif_authok(int fd) {
 	} else { //Auth Failed
 		pc_authfail(sd);
 	}
-	
+
 	chrif_char_offline(sd); //Set him offline, the char server likely has it set as online already.
 	chrif_auth_delete(account_id, char_id, ST_LOGIN);
 }
@@ -664,7 +664,7 @@ void chrif_authfail(int fd) {/* HELLO WORLD. ip in RFIFOL 15 is not being used (
 	sex        = RFIFOB(fd,14);
 
 	node = chrif_search(account_id);
-	
+
 	if( node != NULL &&
 		node->account_id == account_id &&
 		node->char_id == char_id &&
@@ -685,7 +685,7 @@ void chrif_authfail(int fd) {/* HELLO WORLD. ip in RFIFOL 15 is not being used (
 int auth_db_cleanup_sub(DBKey key, DBData *data, va_list ap) {
 	struct auth_node *node = db_data2ptr(data);
 	const char* states[] = { "Login", "Logout", "Map change" };
-	
+
 	if(DIFF_TICK(gettick(),node->node_created)>60000) {
 		switch (node->state) {
 			case ST_LOGOUT:
@@ -719,7 +719,7 @@ int chrif_charselectreq(struct map_session_data* sd, uint32 s_ip) {
 
 	if( !sd || !sd->bl.id || !sd->login_id1 )
 		return -1;
-	
+
 	chrif_check(-1);
 
 	WFIFOHEAD(char_fd,18);
@@ -737,10 +737,10 @@ int chrif_charselectreq(struct map_session_data* sd, uint32 s_ip) {
  * Search Char trough id on char serv
  *------------------------------------------*/
 int chrif_searchcharid(int char_id) {
-	
+
 	if( !char_id )
 		return -1;
-	
+
 	chrif_check(-1);
 
 	WFIFOHEAD(char_fd,6);
@@ -755,7 +755,7 @@ int chrif_searchcharid(int char_id) {
  * Change Email
  *------------------------------------------*/
 int chrif_changeemail(int id, const char *actual_email, const char *new_email) {
-	
+
 	if (battle_config.etc_log)
 		ShowInfo("chrif_changeemail: account: %d, actual_email: '%s', new_email: '%s'.\n", id, actual_email, new_email);
 
@@ -778,7 +778,7 @@ int chrif_changeemail(int id, const char *actual_email, const char *new_email) {
  *   1: block, 2: ban, 3: unblock, 4: unban, 5: changesex (use next function for 5)
  *------------------------------------------*/
 int chrif_char_ask_name(int acc, const char* character_name, unsigned short operation_type, int year, int month, int day, int hour, int minute, int second) {
-	
+
 	chrif_check(-1);
 
 	WFIFOHEAD(char_fd,44);
@@ -786,7 +786,7 @@ int chrif_char_ask_name(int acc, const char* character_name, unsigned short oper
 	WFIFOL(char_fd,2) = acc;
 	safestrncpy((char*)WFIFOP(char_fd,6), character_name, NAME_LENGTH);
 	WFIFOW(char_fd,30) = operation_type;
-	
+
 	if ( operation_type == 2 ) {
 		WFIFOW(char_fd,32) = year;
 		WFIFOW(char_fd,34) = month;
@@ -795,14 +795,14 @@ int chrif_char_ask_name(int acc, const char* character_name, unsigned short oper
 		WFIFOW(char_fd,40) = minute;
 		WFIFOW(char_fd,42) = second;
 	}
-	
+
 	WFIFOSET(char_fd,44);
 	return 0;
 }
 
 int chrif_changesex(struct map_session_data *sd) {
 	chrif_check(-1);
-	
+
 	WFIFOHEAD(char_fd,44);
 	WFIFOW(char_fd,0) = 0x2b0e;
 	WFIFOL(char_fd,2) = sd->status.account_id;
@@ -834,9 +834,9 @@ static void chrif_char_ask_name_answer(int acc, const char* player_name, uint16
 	struct map_session_data* sd;
 	char action[25];
 	char output[256];
-	
+
 	sd = map_id2sd(acc);
-	
+
 	if( acc < 0 || sd == NULL ) {
 		ShowError("chrif_char_ask_name_answer failed - player not online.\n");
 		return;
@@ -854,7 +854,7 @@ static void chrif_char_ask_name_answer(int acc, const char* player_name, uint16
 		case 3 : sprintf(output, msg_txt(427), action, NAME_LENGTH, player_name); break;
 		default: output[0] = '\0'; break;
 	}
-	
+
 	clif_displaymessage(sd->fd, output);
 }
 
@@ -867,13 +867,13 @@ int chrif_changedsex(int fd) {
 
 	acc = RFIFOL(fd,2);
 	sex = RFIFOL(fd,6);
-	
+
 	if ( battle_config.etc_log )
 		ShowNotice("chrif_changedsex %d.\n", acc);
-	
+
 	sd = map_id2sd(acc);
 	if ( sd ) { //Normally there should not be a char logged on right now!
-		if ( sd->status.sex == sex ) 
+		if ( sd->status.sex == sex )
 			return 0; //Do nothing? Likely safe.
 		sd->status.sex = !sd->status.sex;
 
@@ -952,7 +952,7 @@ int chrif_divorceack(int char_id, int partner_id) {
 			if (sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F)
 				pc_delitem(sd, i, 1, 0, 0, LOG_TYPE_OTHER);
 	}
-	
+
 	return 0;
 }
 /*==========================================
@@ -965,7 +965,7 @@ int chrif_deadopt(int father_id, int mother_id, int child_id) {
 		sd->status.child = 0;
 		sd->status.skill[WE_CALLBABY].id = 0;
 		sd->status.skill[WE_CALLBABY].lv = 0;
-		sd->status.skill[WE_CALLBABY].flag = 0;
+		sd->status.skill[WE_CALLBABY].flag = SKILL_FLAG_PERMANENT;
 		clif_deleteskill(sd,WE_CALLBABY);
 	}
 
@@ -973,7 +973,7 @@ int chrif_deadopt(int father_id, int mother_id, int child_id) {
 		sd->status.child = 0;
 		sd->status.skill[WE_CALLBABY].id = 0;
 		sd->status.skill[WE_CALLBABY].lv = 0;
-		sd->status.skill[WE_CALLBABY].flag = 0;
+		sd->status.skill[WE_CALLBABY].flag = SKILL_FLAG_PERMANENT;
 		clif_deleteskill(sd,WE_CALLBABY);
 	}
 
@@ -988,10 +988,10 @@ int chrif_accountban(int fd) {
 	struct map_session_data *sd;
 
 	acc = RFIFOL(fd,2);
-	
+
 	if ( battle_config.etc_log )
 		ShowNotice("chrif_accountban %d.\n", acc);
-	
+
 	sd = map_id2sd(acc);
 
 	if ( acc < 0 || sd == NULL ) {
@@ -1006,8 +1006,8 @@ int chrif_accountban(int fd) {
                     clif_displaymessage(sd->fd, msg_txt(411+ret_status));
                 else if(ret_status==100)
                     clif_displaymessage(sd->fd, msg_txt(421));
-                else    
-                    clif_displaymessage(sd->fd, msg_txt(420)); //"Your account has not more authorised." 
+                else
+                    clif_displaymessage(sd->fd, msg_txt(420)); //"Your account has not more authorised."
 	} else if (RFIFOB(fd,6) == 1) { // 0: change of statut, 1: ban
 		time_t timestamp;
 		char tmpstr[2048];
@@ -1031,10 +1031,10 @@ int chrif_disconnectplayer(int fd) {
 	sd = map_id2sd(account_id);
 	if( sd == NULL ) {
 		struct auth_node* auth = chrif_search(account_id);
-		
+
 		if( auth != NULL && chrif_auth_delete(account_id, auth->char_id, ST_LOGIN) )
 			return 0;
-		
+
 		return -1;
 	}
 
@@ -1061,7 +1061,7 @@ int chrif_disconnectplayer(int fd) {
  *------------------------------------------*/
 int chrif_updatefamelist(struct map_session_data* sd) {
 	char type;
-	
+
 	chrif_check(-1);
 
 	switch(sd->class_ & MAPID_UPPERMASK) {
@@ -1101,30 +1101,30 @@ int chrif_recvfamelist(int fd) {
 	memset (taekwon_fame_list, 0, sizeof(taekwon_fame_list));
 
 	size = RFIFOW(fd, 6); //Blacksmith block size
-	
+
 	for (num = 0; len < size && num < MAX_FAME_LIST; num++) {
 		memcpy(&smith_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list));
  		len += sizeof(struct fame_list);
 	}
-	
+
 	total += num;
 
 	size = RFIFOW(fd, 4); //Alchemist block size
-	
+
 	for (num = 0; len < size && num < MAX_FAME_LIST; num++) {
 		memcpy(&chemist_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list));
  		len += sizeof(struct fame_list);
 	}
-	
+
 	total += num;
 
 	size = RFIFOW(fd, 2); //Total packet length
-	
+
 	for (num = 0; len < size && num < MAX_FAME_LIST; num++) {
 		memcpy(&taekwon_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list));
  		len += sizeof(struct fame_list);
 	}
-	
+
 	total += num;
 
 	ShowInfo("Received Fame List of '"CL_WHITE"%d"CL_RESET"' characters.\n", total);
@@ -1137,21 +1137,21 @@ int chrif_recvfamelist(int fd) {
 int chrif_updatefamelist_ack(int fd) {
 	struct fame_list* list;
 	uint8 index;
-	
+
 	switch (RFIFOB(fd,2)) {
 		case 1: list = smith_fame_list;   break;
 		case 2: list = chemist_fame_list; break;
 		case 3: list = taekwon_fame_list; break;
 		default: return 0;
 	}
-	
+
 	index = RFIFOB(fd, 3);
-	
+
 	if (index >= MAX_FAME_LIST)
 		return 0;
-	
+
 	list[index].fame = RFIFOL(fd,4);
-	
+
 	return 1;
 }
 
@@ -1166,12 +1166,12 @@ int chrif_save_scdata(struct map_session_data *sd) { //parses the sc_data of the
 
 	chrif_check(-1);
 	tick = gettick();
-	
+
 	WFIFOHEAD(char_fd, 14 + SC_MAX*sizeof(struct status_change_data));
 	WFIFOW(char_fd,0) = 0x2b1c;
 	WFIFOL(char_fd,4) = sd->status.account_id;
 	WFIFOL(char_fd,8) = sd->status.char_id;
-	
+
 	for (i = 0; i < SC_MAX; i++) {
 		if (!sc->data[i])
 			continue;
@@ -1191,15 +1191,15 @@ int chrif_save_scdata(struct map_session_data *sd) { //parses the sc_data of the
 			&data, sizeof(struct status_change_data));
 		count++;
 	}
-	
+
 	if (count == 0)
 		return 0; //Nothing to save.
-	
+
 	WFIFOW(char_fd,12) = count;
 	WFIFOW(char_fd,2) = 14 +count*sizeof(struct status_change_data); //Total packet size
 	WFIFOSET(char_fd,WFIFOW(char_fd,2));
 #endif
-	
+
 	return 0;
 }
 
@@ -1213,27 +1213,27 @@ int chrif_load_scdata(int fd) {
 
 	aid = RFIFOL(fd,4); //Player Account ID
 	cid = RFIFOL(fd,8); //Player Char ID
-	
+
 	sd = map_id2sd(aid);
-	
+
 	if ( !sd ) {
 		ShowError("chrif_load_scdata: Player of AID %d not found!\n", aid);
 		return -1;
 	}
-	
+
 	if ( sd->status.char_id != cid ) {
 		ShowError("chrif_load_scdata: Receiving data for account %d, char id does not matches (%d != %d)!\n", aid, sd->status.char_id, cid);
 		return -1;
 	}
-	
+
 	count = RFIFOW(fd,12); //sc_count
-	
+
 	for (i = 0; i < count; i++) {
 		data = (struct status_change_data*)RFIFOP(fd,14 + i*sizeof(struct status_change_data));
 		status_change_start(NULL,&sd->bl, (sc_type)data->type, 10000, data->val1, data->val2, data->val3, data->val4, data->tick, 15);
 	}
 #endif
-	
+
 	return 0;
 }
 
@@ -1250,7 +1250,7 @@ int chrif_ragsrvinfo(int base_rate, int job_rate, int drop_rate) {
 	WFIFOL(char_fd,6) = job_rate;
 	WFIFOL(char_fd,10) = drop_rate;
 	WFIFOSET(char_fd,14);
-	
+
 	return 0;
 }
 
@@ -1329,7 +1329,7 @@ void chrif_on_disconnect(void) {
 	if( chrif_connected != 1 )
 		ShowWarning("Connection to Char Server lost.\n\n");
 	chrif_connected = 0;
-	
+
  	other_mapserver_count = 0; //Reset counter. We receive ALL maps from all map-servers on reconnect.
 	map_eraseallipport();
 
@@ -1340,19 +1340,19 @@ void chrif_on_disconnect(void) {
 
 void chrif_update_ip(int fd) {
 	uint32 new_ip;
-	
+
 	WFIFOHEAD(fd,6);
-	
+
 	new_ip = host2ip(char_ip_str);
-	
+
 	if (new_ip && new_ip != char_ip)
 		char_ip = new_ip; //Update char_ip
 
 	new_ip = clif_refresh_ip();
-	
+
 	if (!new_ip)
 		return; //No change
-	
+
 	WFIFOW(fd,0) = 0x2736;
 	WFIFOL(fd,2) = htonl(new_ip);
 	WFIFOSET(fd,6);
@@ -1400,7 +1400,7 @@ int chrif_parse(int fd) {
 		if (cmd < 0x2af8 || cmd >= 0x2af8 + ARRAYLENGTH(packet_len_table) || packet_len_table[cmd-0x2af8] == 0) {
 			int r = intif_parse(fd); // Passed on to the intif
 
-			if (r == 1) continue;	// Treated in intif 
+			if (r == 1) continue;	// Treated in intif
 			if (r == 2) return 0;	// Didn't have enough data (len==-1)
 
 			ShowWarning("chrif_parse: session #%d, intif_parse failed (unrecognized command 0x%.4x).\n", fd, cmd);
@@ -1477,20 +1477,20 @@ int send_users_tochar(void) {
 	chrif_check(-1);
 
 	users = map_usercount();
-	
+
 	WFIFOHEAD(char_fd, 6+8*users);
 	WFIFOW(char_fd,0) = 0x2aff;
-	
+
 	iter = mapit_getallusers();
-	
+
 	for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) ) {
 		WFIFOL(char_fd,6+8*i) = sd->status.account_id;
 		WFIFOL(char_fd,6+8*i+4) = sd->status.char_id;
 		i++;
 	}
-	
+
 	mapit_free(iter);
-	
+
 	WFIFOW(char_fd,2) = 6 + 8*users;
 	WFIFOW(char_fd,4) = users;
 	WFIFOSET(char_fd, 6+8*users);
@@ -1512,7 +1512,7 @@ static int check_connect_char_server(int tid, unsigned int tick, int id, intptr_
 
 		chrif_state = 0;
 		char_fd = make_connection(char_ip, char_port,false);
-		
+
 		if (char_fd == -1)//Attempt to connect later. [Skotlex]
 			return 0;
 
@@ -1546,7 +1546,7 @@ int chrif_removefriend(int char_id, int friend_id) {
 	WFIFOL(char_fd,2) = char_id;
 	WFIFOL(char_fd,6) = friend_id;
 	WFIFOSET(char_fd,10);
-	
+
 	return 0;
 }
 
@@ -1554,13 +1554,13 @@ void chrif_send_report(char* buf, int len) {
 
 #ifndef STATS_OPT_OUT
 	WFIFOHEAD(char_fd,len + 2);
-	
+
 	WFIFOW(char_fd,0) = 0x3008;
-	
+
 	memcpy(WFIFOP(char_fd,2), buf, len);
-	
+
 	WFIFOSET(char_fd,len + 2);
-	
+
 	flush_fifo(char_fd); /* ensure it's sent now. */
 #endif
 
@@ -1571,15 +1571,15 @@ void chrif_send_report(char* buf, int len) {
  */
 int auth_db_final(DBKey key, DBData *data, va_list ap) {
 	struct auth_node *node = db_data2ptr(data);
-	
+
 	if (node->char_dat)
 		aFree(node->char_dat);
-	
+
 	if (node->sd)
 		aFree(node->sd);
-	
+
 	ers_free(auth_db_ers, node);
-	
+
 	return 0;
 }
 
@@ -1587,16 +1587,16 @@ int auth_db_final(DBKey key, DBData *data, va_list ap) {
  * Destructor
  *------------------------------------------*/
 int do_final_chrif(void) {
-	
+
 	if( char_fd != -1 ) {
 		do_close(char_fd);
 		char_fd = -1;
 	}
 
 	auth_db->destroy(auth_db, auth_db_final);
-	
+
 	ers_destroy(auth_db_ers);
-	
+
 	return 0;
 }
 
@@ -1604,7 +1604,7 @@ int do_final_chrif(void) {
  *
  *------------------------------------------*/
 int do_init_chrif(void) {
-	
+
 	auth_db = idb_alloc(DB_OPT_BASE);
 	auth_db_ers = ers_new(sizeof(struct auth_node),"chrif.c::auth_db_ers",ERS_OPT_NONE);
 

+ 66 - 68
src/map/clif.c

@@ -1481,9 +1481,10 @@ int clif_homskillinfoblock(struct map_session_data *sd)
 	WFIFOW(fd,0)=0x235;
 	for ( i = 0; i < MAX_HOMUNSKILL; i++){
 		if( (id = hd->homunculus.hskill[i].id) != 0 ){
+			int combo = (hd->homunculus.hskill[i].flag)&SKILL_FLAG_TMP_COMBO;
 			j = id - HM_SKILLBASE;
 			WFIFOW(fd,len  ) = id;
-			WFIFOW(fd,len+2) = skill_get_inf(id);
+			WFIFOW(fd,len+2) = ((combo)?INF_SELF_SKILL:skill_get_inf(id));
 			WFIFOW(fd,len+4) = 0;
 			WFIFOW(fd,len+6) = hd->homunculus.hskill[j].lv;
 			WFIFOW(fd,len+8) = skill_get_sp(id,hd->homunculus.hskill[j].lv);
@@ -4362,46 +4363,68 @@ void clif_getareachar_item(struct map_session_data* sd,struct flooritem_data* fi
 }
 
 
+
 /// Notifies the client of a skill unit.
 /// 011f <id>.L <creator id>.L <x>.W <y>.W <unit id>.B <visible>.B (ZC_SKILL_ENTRY)
 /// 01c9 <id>.L <creator id>.L <x>.W <y>.W <unit id>.B <visible>.B <has msg>.B <msg>.80B (ZC_SKILL_ENTRY2)
-static void clif_getareachar_skillunit(struct map_session_data *sd, struct skill_unit *unit)
+/// 08c7 <lenght>.W <id> L <creator id>.L <x>.W <y>.W <unit id>.B <range>.W <visible>.B (ZC_SKILL_ENTRY3)
+/// 099f <lenght>.W <id> L <creator id>.L <x>.W <y>.W <unit id>.L <range>.W <visible>.B (ZC_SKILL_ENTRY4)
+static void clif_getareachar_skillunit(int type,struct map_session_data *sd, struct skill_unit *unit)
 {
 	int fd = sd->fd;
+	int header=0, unit_id=0, pos=0;
 
 	if( unit->group->state.guildaura )
 		return;
+	if (battle_config.traps_setting&1 && skill_get_inf2(unit->group->skill_id)&INF2_TRAP)
+		unit_id=UNT_DUMMYSKILL; //Use invisible unit id for traps.
+	else if (skill_get_unit_flag(unit->group->skill_id) & UF_RANGEDSINGLEUNIT && !(unit->val2 & UF_RANGEDSINGLEUNIT))
+		unit_id=UNT_DUMMYSKILL; //Use invisible unit id for other case of rangedsingle unit
+	else
+		unit_id=unit->group->unit_id;
+
+	switch(type){
+		case 2: header=0x1c9; break;
+		case 3: header=0x8c7; break;
+		case 4: header=0x99f; break;
+		default:
+		case 1: header=0x11f; break;
+	}
 
 #if PACKETVER >= 3
 	if(unit->group->unit_id==UNT_GRAFFITI)	{ // Graffiti [Valaris]
-		WFIFOHEAD(fd,packet_len(0x1c9));
-		WFIFOW(fd, 0)=0x1c9;
-		WFIFOL(fd, 2)=unit->bl.id;
-		WFIFOL(fd, 6)=unit->group->src_id;
-		WFIFOW(fd,10)=unit->bl.x;
-		WFIFOW(fd,12)=unit->bl.y;
-		WFIFOB(fd,14)=unit->group->unit_id;
-		WFIFOB(fd,15)=1;
-		WFIFOB(fd,16)=1;
-		safestrncpy((char*)WFIFOP(fd,17),unit->group->valstr,MESSAGE_SIZE);
-		WFIFOSET(fd,packet_len(0x1c9));
-		return;
+		clif_getareachar_skillunit(2,sd,unit);
 	}
 #endif
-	WFIFOHEAD(fd,packet_len(0x11f));
-	WFIFOW(fd, 0)=0x11f;
-	WFIFOL(fd, 2)=unit->bl.id;
-	WFIFOL(fd, 6)=unit->group->src_id;
-	WFIFOW(fd,10)=unit->bl.x;
-	WFIFOW(fd,12)=unit->bl.y;
-	if (battle_config.traps_setting&1 && skill_get_inf2(unit->group->skill_id)&INF2_TRAP)
-		WFIFOB(fd,14)=UNT_DUMMYSKILL; //Use invisible unit id for traps.
-    else if (skill_get_unit_flag(unit->group->skill_id) & UF_RANGEDSINGLEUNIT && !(unit->val2 & UF_RANGEDSINGLEUNIT))
-		WFIFOB(fd,14)=UNT_DUMMYSKILL; //Use invisible unit id for traps.
-	else
-		WFIFOB(fd,14)=unit->group->unit_id;
-	WFIFOB(fd,15)=1; // ignored by client (always gets set to 1)
-	WFIFOSET(fd,packet_len(0x11f));
+	WFIFOHEAD(fd,packet_len(header));
+	WFIFOW(fd,pos)=header;
+	if(type==3 || type==4){
+		WFIFOW(fd, pos+2)=packet_len(header);
+		pos +=2;
+	}
+	WFIFOL(fd,pos+2)=unit->bl.id;
+	WFIFOL(fd,pos+6)=unit->group->src_id;
+	WFIFOW(fd,pos+10)=unit->bl.x;
+	WFIFOW(fd,pos+12)=unit->bl.y;
+	switch(type){
+		case 1: WFIFOB(fd,pos+14)=unit_id;
+			WFIFOB(fd,pos+15)=1;
+			break;
+		case 2: WFIFOB(fd,pos+14)=unit_id;
+			WFIFOB(fd,pos+15)=1;
+			WFIFOB(fd,pos+16)=1;
+			safestrncpy((char*)WFIFOP(fd,pos+17),unit->group->valstr,MESSAGE_SIZE);
+			break;
+		case 3: WFIFOB(fd,pos+14)=unit_id;
+			WFIFOW(fd,pos+15)=unit->range;
+			WFIFOB(fd,pos+17)=1; //visible
+			break;
+		case 4: WFIFOL(fd,pos+14)=unit_id; pos += 3;
+			WFIFOW(fd,pos+15)=unit->range;
+			WFIFOB(fd,pos+17)=1;
+			break;
+	}
+	WFIFOSET(fd,packet_len(header));
 
 	if(unit->group->skill_id == WZ_ICEWALL)
 		clif_changemapcell(fd,unit->bl.m,unit->bl.x,unit->bl.y,5,SELF);
@@ -4473,7 +4496,7 @@ static int clif_getareachar(struct block_list* bl,va_list ap)
 		clif_getareachar_item(sd,(struct flooritem_data*) bl);
 		break;
 	case BL_SKILL:
-		clif_getareachar_skillunit(sd,(TBL_SKILL*)bl);
+		clif_getareachar_skillunit(1,sd,(TBL_SKILL*)bl);
 		break;
 	default:
 		if(&sd->bl == bl)
@@ -4557,7 +4580,7 @@ int clif_insight(struct block_list *bl,va_list ap)
 			clif_getareachar_item(tsd,(struct flooritem_data*)bl);
 			break;
 		case BL_SKILL:
-			clif_getareachar_skillunit(tsd,(TBL_SKILL*)bl);
+			clif_getareachar_skillunit(1,tsd,(TBL_SKILL*)bl);
 			break;
 		default:
 			clif_getareachar_unit(tsd,bl);
@@ -4672,24 +4695,22 @@ void clif_deleteskill(struct map_session_data *sd, int id)
 	clif_skillinfoblock(sd);
 }
 
-
 /// Updates a skill in the skill tree (ZC_SKILLINFO_UPDATE).
 /// 010e <skill id>.W <level>.W <sp cost>.W <attack range>.W <upgradable>.B
-void clif_skillup(struct map_session_data *sd,uint16 skill_id)
-{
-	int fd;
+void clif_skillup(struct map_session_data *sd, uint16 skill_id, int lv, int range, int upgradable) {
+    int fd;
 
 	nullpo_retv(sd);
 
-	fd=sd->fd;
-	WFIFOHEAD(fd,packet_len(0x10e));
-	WFIFOW(fd,0) = 0x10e;
-	WFIFOW(fd,2) = skill_id;
-	WFIFOW(fd,4) = sd->status.skill[skill_id].lv;
-	WFIFOW(fd,6) = skill_get_sp(skill_id,sd->status.skill[skill_id].lv);
-	WFIFOW(fd,8) = skill_get_range2(&sd->bl,skill_id,sd->status.skill[skill_id].lv);
-	WFIFOB(fd,10) = (sd->status.skill[skill_id].lv < skill_tree_get_max(sd->status.skill[skill_id].id, sd->status.class_)) ? 1 : 0;
-	WFIFOSET(fd,packet_len(0x10e));
+	fd = sd->fd;
+	WFIFOHEAD(fd, packet_len(0x10e));
+	WFIFOW(fd, 0) = 0x10e;
+	WFIFOW(fd, 2) = skill_id;
+	WFIFOW(fd, 4) = lv;
+	WFIFOW(fd, 6) = skill_get_sp(skill_id, lv);
+	WFIFOW(fd, 8) = range;
+	WFIFOB(fd, 10) = upgradable;
+	WFIFOSET(fd, packet_len(0x10e));
 }
 
 
@@ -5378,7 +5399,7 @@ void clif_displaymessage(const int fd, const char* mes)
 				WFIFOHEAD(fd, 5 + len);
 				WFIFOW(fd,0) = 0x8e;
 				WFIFOW(fd,2) = 5 + len; // 4 + len + NULL teminate
-                safestrncpy((char *)WFIFOP(fd,4), line, len + 1);
+				safestrncpy((char *)WFIFOP(fd,4), line, len + 1);
 				WFIFOSET(fd, 5 + len);
 			}
 			line = strtok(NULL, "\n");
@@ -7878,29 +7899,6 @@ void clif_guild_message(struct guild *g,int account_id,const char *mes,int len)
 		clif_send(buf, WBUFW(buf,2), &sd->bl, GUILD_NOBG);
 }
 
-
-/*==========================================
- * Server tells client 'sd' that his guild skill 'skill_id' gone to level 'lv'
- *------------------------------------------*/
-int clif_guild_skillup(struct map_session_data *sd,uint16 skill_id,int lv)
-{// TODO: Merge with clif_skillup (same packet).
-	int fd;
-
-	nullpo_ret(sd);
-
-	fd=sd->fd;
-	WFIFOHEAD(fd,11);
-	WFIFOW(fd,0) = 0x10e;
-	WFIFOW(fd,2) = skill_id;
-	WFIFOW(fd,4) = lv;
-	WFIFOW(fd,6) = skill_get_sp(skill_id,lv);
-	WFIFOW(fd,8) = skill_get_range(skill_id,lv);
-	WFIFOB(fd,10) = 1;
-	WFIFOSET(fd,11);
-	return 0;
-}
-
-
 /// Request for guild alliance (ZC_REQ_ALLY_GUILD).
 /// 0171 <inviter account id>.L <guild name>.24B
 void clif_guild_reqalliance(struct map_session_data *sd,int account_id,const char *name)
@@ -12267,7 +12265,7 @@ void clif_parse_GuildRequestEmblem(int fd,struct map_session_data *sd)
 	struct guild* g;
 	int guild_id = RFIFOL(fd,2);
 
-	if( (g = guild_search(guild_id)) != NULL )
+	if( (g = sd->guild) != NULL )
 		clif_guild_emblem(sd,g);
 }
 

+ 1 - 2
src/map/clif.h

@@ -426,7 +426,7 @@ void clif_class_change(struct block_list *bl,int class_,int type);
 #define clif_mob_class_change(md, class_) clif_class_change(&md->bl, class_, 1)
 
 void clif_skillinfoblock(struct map_session_data *sd);
-void clif_skillup(struct map_session_data *sd,uint16 skill_id);
+void clif_skillup(struct map_session_data *sd, uint16 skill_id, int lv, int range, int upgradable);
 void clif_skillinfo(struct map_session_data *sd,int skill, int inf);
 void clif_addskill(struct map_session_data *sd, int id);
 void clif_deleteskill(struct map_session_data *sd, int id);
@@ -541,7 +541,6 @@ void clif_guild_emblem(struct map_session_data *sd,struct guild *g);
 void clif_guild_emblem_area(struct block_list* bl);
 void clif_guild_notice(struct map_session_data* sd, struct guild* g);
 void clif_guild_message(struct guild *g,int account_id,const char *mes,int len);
-int clif_guild_skillup(struct map_session_data *sd,uint16 skill_id,int lv);
 void clif_guild_reqalliance(struct map_session_data *sd,int account_id,const char *name);
 void clif_guild_allianceack(struct map_session_data *sd,int flag);
 void clif_guild_delalliance(struct map_session_data *sd,int guild_id,int flag);

+ 55 - 54
src/map/guild.c

@@ -261,7 +261,7 @@ int guild_getposition(struct guild* g, struct map_session_data* sd)
 
 	if( g == NULL && (g=sd->guild) == NULL )
 		return -1;
-	
+
 	ARR_FIND( 0, g->max_member, i, g->member[i].account_id == sd->status.account_id && g->member[i].char_id == sd->status.char_id );
 	return( i < g->max_member ) ? g->member[i].position : -1;
 }
@@ -297,7 +297,7 @@ int guild_payexp_timer_sub(DBKey key, DBData *data, va_list ap) {
 	struct guild *g;
 
 	c = db_data2ptr(data);
-	
+
 	if (
 		(g = guild_search(c->guild_id)) == NULL ||
 		(i = guild_getindex(g, c->account_id, c->char_id)) < 0
@@ -567,7 +567,7 @@ int guild_recv_info(struct guild *sg)
 		ShowError("guild_recv_info: Received guild with %d members, but MAX_GUILD is only %d. Extra guild-members have been lost!\n", g->max_member, MAX_GUILD);
 		g->max_member = MAX_GUILD;
 	}
-	
+
 	for(i=bm=m=0;i<g->max_member;i++){
 		if(g->member[i].account_id>0){
 			sd = g->member[i].sd = guild_sd_check(g->guild_id, g->member[i].account_id, g->member[i].char_id);
@@ -579,30 +579,30 @@ int guild_recv_info(struct guild *sg)
 			bm++;
 	}
 
-    for (i = 0; i < g->max_member; i++) { //Transmission of information at all members
+	for (i = 0; i < g->max_member; i++) { //Transmission of information at all members
 		sd = g->member[i].sd;
 		if( sd==NULL )
 			continue;
 
-        if (before.guild_lv != g->guild_lv || bm != m ||
-                before.max_member != g->max_member) {
-            clif_guild_basicinfo(sd); //Submit basic information
-            clif_guild_emblem(sd, g); //Submit emblem
-        }
+		if (before.guild_lv != g->guild_lv || bm != m ||
+			before.max_member != g->max_member) {
+			clif_guild_basicinfo(sd); //Submit basic information
+			clif_guild_emblem(sd, g); //Submit emblem
+		}
 
-        if (bm != m) { //Send members information
-            clif_guild_memberlist(g->member[i].sd);
-        }
+		if (bm != m) { //Send members information
+			clif_guild_memberlist(g->member[i].sd);
+		}
 
-        if (before.skill_point != g->skill_point)
-            clif_guild_skillinfo(sd); //Submit information skills
+		if (before.skill_point != g->skill_point)
+			clif_guild_skillinfo(sd); //Submit information skills
 
-        if (guild_new) { // Send information and affiliation if unsent
-            clif_guild_belonginfo(sd, g);
-            clif_guild_notice(sd, g);
-            sd->guild_emblem_id = g->emblem_id;
-        }
-    }
+		if (guild_new) { // Send information and affiliation if unsent
+			clif_guild_belonginfo(sd, g);
+			clif_guild_notice(sd, g);
+			sd->guild_emblem_id = g->emblem_id;
+		}
+	}
 
     //Occurrence of an event
 	if (guild_infoevent_db->remove(guild_infoevent_db, db_i2key(sg->guild_id), &data)) {
@@ -622,8 +622,8 @@ int guild_recv_info(struct guild *sg)
  * Player sd send a guild invatation to player tsd to join his guild
  *--------------------------------------------*/
 int guild_invite(struct map_session_data *sd, struct map_session_data *tsd) {
-    struct guild *g;
-    int i;
+	struct guild *g;
+	int i;
 
 	nullpo_ret(sd);
 
@@ -641,7 +641,7 @@ int guild_invite(struct map_session_data *sd, struct map_session_data *tsd) {
 			return 0;
 		}
 	}
-	
+
 	if (!tsd->fd) { //You can't invite someone who has already disconnected.
 		clif_guild_inviteack(sd,1);
 		return 0;
@@ -717,7 +717,6 @@ int guild_reply_invite(struct map_session_data* sd, int guild_id, int flag)
 			if( tsd ) clif_guild_inviteack(tsd,3);
 			return 0;
 		}
-
 		sd->guild = g;
 		guild_makemember(&m,sd);
 		intif_guild_addmember(guild_id, &m);
@@ -882,7 +881,7 @@ int guild_member_withdraw(int guild_id, int account_id, int char_id, int flag, c
 
 	if(g == NULL)
 		return 0; // no such guild (error!)
-	
+
 	i = guild_getindex(g, account_id, char_id);
 	if( i == -1 )
 		return 0; // not a member (inconsistency!)
@@ -916,7 +915,7 @@ int guild_member_withdraw(int guild_id, int account_id, int char_id, int flag, c
 		sd->status.guild_id = 0;
 		sd->guild = NULL;
 		sd->guild_emblem_id = 0;
-		
+
 		clif_charnameupdate(sd); //Update display name [Skotlex]
 		//TODO: send emblem update to self and people around
 	}
@@ -926,9 +925,9 @@ int guild_member_withdraw(int guild_id, int account_id, int char_id, int flag, c
 int guild_send_memberinfoshort(struct map_session_data *sd,int online)
 { // cleaned up [LuzZza]
 	struct guild *g;
-	
+
 	nullpo_ret(sd);
-		
+
 	if(sd->status.guild_id <= 0)
 		return 0;
 
@@ -946,7 +945,7 @@ int guild_send_memberinfoshort(struct map_session_data *sd,int online)
 			ShowError("guild_send_memberinfoshort: Failed to locate member %d:%d in guild %d!\n", sd->status.account_id, sd->status.char_id, g->guild_id);
 		return 0;
 	}
-	
+
 	if(sd->state.connect_new)
 	{	//Note that this works because it is invoked in parse_LoadEndAck before connect_new is cleared.
 		clif_guild_belonginfo(sd,g);
@@ -958,13 +957,13 @@ int guild_send_memberinfoshort(struct map_session_data *sd,int online)
 
 int guild_recv_memberinfoshort(int guild_id,int account_id,int char_id,int online,int lv,int class_)
 { // cleaned up [LuzZza]
-	
+
 	int i,alv,c,idx=-1,om=0,oldonline=-1;
 	struct guild *g = guild_search(guild_id);
-	
+
 	if(g == NULL)
 		return 0;
-	
+
 	for(i=0,alv=0,c=0,om=0;i<g->max_member;i++){
 		struct guild_member *m=&g->member[i];
 		if(!m->account_id) continue;
@@ -980,7 +979,7 @@ int guild_recv_memberinfoshort(int guild_id,int account_id,int char_id,int onlin
 		if(m->online)
 			om++;
 	}
-	
+
 	if(idx == -1 || c == 0) {
         //Treat char_id who doesn't match guild_id (not found as member)
 		struct map_session_data *sd = map_id2sd(account_id);
@@ -991,23 +990,23 @@ int guild_recv_memberinfoshort(int guild_id,int account_id,int char_id,int onlin
 		ShowWarning("guild: not found member %d,%d on %d[%s]\n",	account_id,char_id,guild_id,g->name);
 		return 0;
 	}
-	
+
 	g->average_lv=alv/c;
 	g->connect_member=om;
 
 	//Ensure validity of pointer (ie: player logs in/out, changes map-server)
 	g->member[idx].sd = guild_sd_check(guild_id, account_id, char_id);
 
-	if(oldonline!=online) 
+	if(oldonline!=online)
 		clif_guild_memberlogin_notice(g, idx, online);
-	
+
 	if(!g->member[idx].sd)
 		return 0;
 
 	//Send XY dot updates. [Skotlex]
 	//Moved from guild_send_memberinfoshort [LuzZza]
 	for(i=0; i < g->max_member; i++) {
-		
+
 		if(!g->member[i].sd || i == idx ||
 			g->member[i].sd->bl.m != g->member[idx].sd->bl.m)
 			continue;
@@ -1066,7 +1065,7 @@ int guild_memberposition_changed(struct guild *g,int idx,int pos)
 
 	g->member[idx].position=pos;
 	clif_guild_memberpositionchanged(g,idx);
-	
+
 	// Update char position in client [LuzZza]
 	if(g->member[idx].sd != NULL)
 		clif_charnameupdate(g->member[idx].sd);
@@ -1101,7 +1100,7 @@ int guild_position_changed(int guild_id,int idx,struct guild_position *p)
 		return 0;
 	memcpy(&g->position[idx],p,sizeof(struct guild_position));
 	clif_guild_positionchanged(g,idx);
-	
+
 	// Update char name in client [LuzZza]
 	for(i=0;i<g->max_member;i++)
 		if(g->member[i].position == idx && g->member[i].sd != NULL)
@@ -1244,29 +1243,29 @@ unsigned int guild_payexp(struct map_session_data *sd,unsigned int exp)
 	struct guild *g;
 	struct guild_expcache *c;
 	int per;
-	
+
 	nullpo_ret(sd);
 
 	if (!exp) return 0;
-	
+
 	if (sd->status.guild_id == 0 ||
 		(g = sd->guild) == NULL ||
 		(per = guild_getposition(g,sd)) < 0 ||
 		(per = g->position[per].exp_mode) < 1)
 		return 0;
-	
+
 
 	if (per < 100)
 		exp = exp * per / 100;
 	//Otherwise tax everything.
-	
+
 	c = db_data2ptr(guild_expcache_db->ensure(guild_expcache_db, db_i2key(sd->status.char_id), create_expcache, sd));
 
 	if (c->exp > UINT64_MAX - exp)
 		c->exp = UINT64_MAX;
 	else
 		c->exp += exp;
-	
+
 	return exp;
 }
 
@@ -1326,7 +1325,9 @@ int guild_skillupack(int guild_id,uint16 skill_id,int account_id)
 	if(g==NULL)
 		return 0;
 	if( sd != NULL ) {
-		clif_guild_skillup(sd,skill_id,g->skill[skill_id-GD_SKILLBASE].lv);
+		int lv = g->skill[skill_id-GD_SKILLBASE].lv;
+		int range = skill_get_range(skill_id, lv);
+		clif_skillup(sd,skill_id,lv,range,1);
 
 		/* Guild Aura handling */
 		switch( skill_id ) {
@@ -1763,7 +1764,7 @@ int guild_gm_change(int guild_id, struct map_session_data *sd)
 
 	if (sd->status.guild_id != guild_id)
 		return 0;
-	
+
 	g=guild_search(guild_id);
 
 	nullpo_ret(g);
@@ -1807,7 +1808,7 @@ int guild_gm_changed(int guild_id, int account_id, int char_id)
 		clif_displaymessage(g->member[pos].sd->fd, msg_txt(678)); //"You no longer are the Guild Master."
 		g->member[pos].sd->state.gmaster_flag = 0;
 	}
-	
+
 	if (g->member[0].sd && g->member[0].sd->fd) {
 		clif_displaymessage(g->member[0].sd->fd, msg_txt(679)); //"You have become the Guild Master!"
 		g->member[0].sd->state.gmaster_flag = g;
@@ -1961,7 +1962,7 @@ void guild_castle_reconnect_sub(void *key, void *data, va_list ap)
 }
 
 /**
- * Saves pending guild castle data changes when char-server is 
+ * Saves pending guild castle data changes when char-server is
  * disconnected.
  * On reconnect pushes all changes to char-server for saving.
  */
@@ -2088,7 +2089,7 @@ bool guild_isallied(int guild_id, int guild_id2)
 
 void guild_flag_add(struct npc_data *nd) {
 	int i;
-	
+
 	/* check */
 	for( i = 0; i < guild_flags_count; i++ ) {
 		if( guild_flags[i] && guild_flags[i]->bl.id == nd->bl.id ) {
@@ -2119,11 +2120,11 @@ void guild_flag_remove(struct npc_data *nd) {
 	for( i = 0, cursor = 0; i < guild_flags_count; i++ ) {
 		if( guild_flags[i] == NULL )
 			continue;
-		
+
 		if( cursor != i ) {
 			memmove(&guild_flags[cursor], &guild_flags[i], sizeof(struct npc_data*));
 		}
-		
+
 		cursor++;
 	}
 
@@ -2169,7 +2170,7 @@ void guild_flags_clear(void) {
 		if( guild_flags[i] )
 			guild_flags[i] = NULL;
 	}
-	
+
 	guild_flags_count = 0;
 }
 
@@ -2179,9 +2180,9 @@ void do_init_guild(void) {
 	guild_expcache_db  = idb_alloc(DB_OPT_BASE);
 	guild_infoevent_db = idb_alloc(DB_OPT_BASE);
 	expcache_ers = ers_new(sizeof(struct guild_expcache),"guild.c::expcache_ers",ERS_OPT_NONE);
-	
+
 	guild_flags_count = 0;
-	
+
 	sv_readdb(db_path, "castle_db.txt", ',', 4, 5, -1, &guild_read_castledb);
 
 	memset(guild_skill_tree,0,sizeof(guild_skill_tree));

+ 2 - 0
src/map/map.c

@@ -379,6 +379,8 @@ int map_moveblock(struct block_list *bl, int x1, int y1, unsigned int tick)
 		skill_unit_move(bl,tick,2);
 		status_change_end(bl, SC_CLOSECONFINE, INVALID_TIMER);
 		status_change_end(bl, SC_CLOSECONFINE2, INVALID_TIMER);
+		status_change_end(bl, SC_TINDER_BREAKER, INVALID_TIMER);
+		status_change_end(bl, SC_TINDER_BREAKER2, INVALID_TIMER);
 //		status_change_end(bl, SC_BLADESTOP, INVALID_TIMER); //Won't stop when you are knocked away, go figure...
 		status_change_end(bl, SC_TATAMIGAESHI, INVALID_TIMER);
 		status_change_end(bl, SC_MAGICROD, INVALID_TIMER);

+ 8 - 3
src/map/mob.c

@@ -2549,7 +2549,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 	}
 
 	if (type&2 && !sd && md->class_ == MOBID_EMPERIUM)
-	  	//Emperium destroyed by script. Discard mvp character. [Skotlex]
+		//Emperium destroyed by script. Discard mvp character. [Skotlex]
 		mvp_sd = NULL;
 
 	rebirth =  ( md->sc.data[SC_KAIZEL] || (md->sc.data[SC_REBIRTH] && !md->state.rebirth) );
@@ -2639,8 +2639,13 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 		mvptomb_create(md, mvp_sd ? mvp_sd->status.name : NULL, time(NULL));
 
 	// Remove all status changes before creating a respawn
-	if( sc )
+	if( sc ){
+		for(i=0; i<SC_MAX; i++){
+			if(sc->data[i] && (sc->data[i]->timer != INVALID_TIMER))
+				delete_timer(sc->data[i]->timer, status_change_timer);
+		}
 		memset( sc, 0, sizeof( struct status_change ) );
+	}
 
 	if( !rebirth )
 		mob_setdelayspawn(md); //Set respawning.
@@ -2934,7 +2939,7 @@ int mob_summonslave(struct mob_data *md2,int *value,int amount,uint16 skill_id)
 
 		//Inherit the aggressive mode of the master.
 		if (battle_config.slaves_inherit_mode && md->master_id)
-	  	{
+		{
 			switch (battle_config.slaves_inherit_mode) {
 			case 1: //Always aggressive
 				if (!(md->status.mode&MD_AGGRESSIVE))

+ 14 - 10
src/map/pc.c

@@ -1291,7 +1291,7 @@ int pc_calc_skilltree(struct map_session_data *sd)
 					if( (sd->class_&MAPID_UPPERMASK) != MAPID_NOVICE ) {
 							sd->status.skill[i].id = 0;
 							sd->status.skill[i].lv = 0;
-							sd->status.skill[i].flag = 0;
+							sd->status.skill[i].flag = SKILL_FLAG_PERMANENT;
 					}
 					break;
 			}
@@ -1520,12 +1520,12 @@ int pc_clean_skilltree(struct map_session_data *sd)
 		{
 			sd->status.skill[i].id = 0;
 			sd->status.skill[i].lv = 0;
-			sd->status.skill[i].flag = 0;
+			sd->status.skill[i].flag = SKILL_FLAG_PERMANENT;
 		}
 		else
 		if (sd->status.skill[i].flag == SKILL_FLAG_REPLACED_LV_0){
 			sd->status.skill[i].lv = sd->status.skill[i].flag - SKILL_FLAG_REPLACED_LV_0;
-			sd->status.skill[i].flag = 0;
+			sd->status.skill[i].flag = SKILL_FLAG_PERMANENT;
 		}
 	}
 
@@ -6042,6 +6042,7 @@ int pc_skillup(struct map_session_data *sd,uint16 skill_id)
 		sd->status.skill[skill_id].flag == SKILL_FLAG_PERMANENT && //Don't allow raising while you have granted skills. [Skotlex]
 		sd->status.skill[skill_id].lv < skill_tree_get_max(skill_id, sd->status.class_) )
 	{
+		int lv,range, upgradable;
 		sd->status.skill[skill_id].lv++;
 		sd->status.skill_point--;
 		if( !skill_get_inf(skill_id) )
@@ -6051,7 +6052,10 @@ int pc_skillup(struct map_session_data *sd,uint16 skill_id)
 		else
 			pc_check_skilltree(sd, skill_id); // Check if a new skill can Lvlup
 
-		clif_skillup(sd,skill_id);
+		lv = sd->status.skill[skill_id].lv;
+		range = skill_get_range2(&sd->bl, skill_id, lv);
+		upgradable = (lv < skill_tree_get_max(sd->status.skill[skill_id].id, sd->status.class_)) ? 1 : 0;
+		clif_skillup(sd,skill_id,lv,range,upgradable);
 		clif_updatestatus(sd,SP_SKILLPOINT);
 		if( skill_id == GN_REMODELING_CART ) /* cart weight info was updated by status_calc_pc */
 			clif_updatestatus(sd,SP_CARTINFO);
@@ -6337,7 +6341,7 @@ int pc_resetskill(struct map_session_data* sd, int flag)
 		if( i == NV_TRICKDEAD && (sd->class_&MAPID_UPPERMASK) != MAPID_NOVICE )
 		{
 			sd->status.skill[i].lv = 0;
-			sd->status.skill[i].flag = 0;
+			sd->status.skill[i].flag = SKILL_FLAG_PERMANENT;
 			continue;
 		}
 
@@ -6356,7 +6360,7 @@ int pc_resetskill(struct map_session_data* sd, int flag)
 			if( battle_config.quest_skill_reset && !(flag&2) )
 			{	//Wipe them
 				sd->status.skill[i].lv = 0;
-				sd->status.skill[i].flag = 0;
+				sd->status.skill[i].flag = SKILL_FLAG_PERMANENT;
 			}
 			continue;
 		}
@@ -6369,7 +6373,7 @@ int pc_resetskill(struct map_session_data* sd, int flag)
 		if( !(flag&2) )
 		{// reset
 			sd->status.skill[i].lv = 0;
-			sd->status.skill[i].flag = 0;
+			sd->status.skill[i].flag = SKILL_FLAG_PERMANENT;
 		}
 	}
 
@@ -7364,7 +7368,7 @@ int pc_jobchange(struct map_session_data *sd,int job, int upper)
 		if( sd->status.skill[sd->cloneskill_id].flag == SKILL_FLAG_PLAGIARIZED ) {
 			sd->status.skill[sd->cloneskill_id].id = 0;
 			sd->status.skill[sd->cloneskill_id].lv = 0;
-			sd->status.skill[sd->cloneskill_id].flag = 0;
+			sd->status.skill[sd->cloneskill_id].flag = SKILL_FLAG_PERMANENT;
 			clif_deleteskill(sd,sd->cloneskill_id);
 		}
 		sd->cloneskill_id = 0;
@@ -7376,7 +7380,7 @@ int pc_jobchange(struct map_session_data *sd,int job, int upper)
 		if( sd->status.skill[sd->reproduceskill_id].flag == SKILL_FLAG_PLAGIARIZED ) {
 			sd->status.skill[sd->reproduceskill_id].id = 0;
 			sd->status.skill[sd->reproduceskill_id].lv = 0;
-			sd->status.skill[sd->reproduceskill_id].flag = 0;
+			sd->status.skill[sd->reproduceskill_id].flag = SKILL_FLAG_PERMANENT;
 			clif_deleteskill(sd,sd->reproduceskill_id);
 		}
 		sd->reproduceskill_id = 0;
@@ -9382,7 +9386,7 @@ int pc_level_penalty_mod(struct map_session_data *sd, int mob_level, uint32 mob_
 	int diff, rate = 100, i;
 
 	nullpo_ret(sd);
-	
+
 	diff = mob_level - sd->status.base_level;
 
 	if( diff < 0 )

+ 312 - 327
src/map/skill.c

@@ -657,91 +657,35 @@ int skillnotok_hom(uint16 skill_id, struct homun_data *hd)
 	if (hd->blockskill[idx] > 0)
 		return 1;
 	switch(skill_id){
-	    case MH_LIGHT_OF_REGENE:
-		if(hd->homunculus.intimacy <= 750) //if not cordial
-		    return 1;
-		break;
-	    case MH_OVERED_BOOST:
-		if(hd->homunculus.hunger <= 1) //if we starving
-		    return 1;
-		break;
-	    case MH_GOLDENE_FERSE: //cant be used with angriff
-		if(hd->sc.data[SC_ANGRIFFS_MODUS])
-		    return 1;
-		break;
-	    case MH_ANGRIFFS_MODUS:
-		if(hd->sc.data[SC_GOLDENE_FERSE])
-		    return 1;
-		break;
-	    case MH_TINDER_BREAKER:
-	    case MH_CBC:
-	    case MH_EQC:
-	    case MH_SONIC_CRAW:
-	    case MH_SILVERVEIN_RUSH:
-	    case MH_MIDNIGHT_FRENZY: {
-		    struct status_change_entry *sce = hd->sc.data[SC_STYLE_CHANGE];
-		    TBL_PC *sd;
-		    if(!(sd=hd->master) || !sce) return 1; //homon doesn't have status or a master
-		    if((!sce->val3) && (skill_id != MH_SONIC_CRAW && skill_id != MH_TINDER_BREAKER))
-			    return 1; // or it's not a combo
-
-		    switch(skill_id){
-			case MH_SONIC_CRAW:
-			case MH_SILVERVEIN_RUSH:
-			case MH_TINDER_BREAKER:
-			case MH_CBC:
-			    if(!hd->homunculus.spiritball) {
-				clif_colormes(sd,COLOR_RED,"Homon need some spiritballs");
-				return 1;
-			    }
-			    break;
-
-			case MH_MIDNIGHT_FRENZY:
-			case MH_EQC:
-			    if(hd->homunculus.spiritball < 2) {
-				clif_colormes(sd,COLOR_RED,"Homon need at least 2 spiritballs");
-				return 1;
-			    }
-			    break;
-		    }
-
-		    switch(skill_id){
-			case MH_SONIC_CRAW:
-			case MH_SILVERVEIN_RUSH:
-			case MH_MIDNIGHT_FRENZY:
-			    if (!(sce->val1 == MH_MD_FIGHTING)){
-				    clif_colormes(sd,COLOR_RED,"Homon need to be in fighting mode to use that skill");
-				    return 1;
-			    }
-			    break;
-
-			case MH_TINDER_BREAKER:
-			case MH_CBC:
-			case MH_EQC:
-			    if (!(sce->val1 == MH_MD_GRAPPLING)){
-				    clif_colormes(sd,COLOR_RED,"Homon need to be in grappling mode to use that skill");
-				    return 1;
-			    }
-			    break;
-		    }
-
-		    //now let really be specific
-		    switch(skill_id){
-			case MH_TINDER_BREAKER:
-			    if(sce->val3 == MH_EQC && (gettick() - sce->val4 <= 2000)) break;
-			    else break; //im not a combo what should I do ??
-			case MH_CBC: if(sce->val3 == MH_TINDER_BREAKER && (gettick() - sce->val4 <= 2000)) break;
-			case MH_EQC: if(sce->val3 == MH_CBC && (gettick() - sce->val4 <= 2000)) break;
-
-			case MH_SONIC_CRAW:
-			    if(sce->val3 == MH_MIDNIGHT_FRENZY && (gettick() - sce->val4 <= 2000)) break;
-			    else break; //im not a combo what should I do ??
-			case MH_SILVERVEIN_RUSH: if(sce->val3 == MH_SONIC_CRAW && (gettick() - sce->val4 <= 2000)) break;
-			case MH_MIDNIGHT_FRENZY: if(sce->val3 == MH_SILVERVEIN_RUSH && (gettick() - sce->val4 <= 2000)) break;
-			default:
-			    return 1;
-		    }
-		}
+	case MH_LIGHT_OF_REGENE: //must be cordial
+		if(hd->homunculus.intimacy <= 750) return 1;
+		break;
+	case MH_OVERED_BOOST: //if we starving
+		if(hd->homunculus.hunger <= 1) return 1;
+		break;
+	case MH_GOLDENE_FERSE: //cant be used with angriff
+		if(hd->sc.data[SC_ANGRIFFS_MODUS]) return 1;
+		break;
+	case MH_ANGRIFFS_MODUS:
+		if(hd->sc.data[SC_GOLDENE_FERSE]) return 1;
+		break;
+	case MH_TINDER_BREAKER: //must be in grappling mode
+		if(!(hd->sc.data[SC_STYLE_CHANGE] && hd->sc.data[SC_STYLE_CHANGE]->val1 == MH_MD_GRAPPLING)) return 1;
+		break;
+	case MH_SONIC_CRAW: //must be in fighting mode
+		if(!(hd->sc.data[SC_STYLE_CHANGE] && hd->sc.data[SC_STYLE_CHANGE]->val1 == MH_MD_FIGHTING)) return 1;
+		break;
+	case MH_SILVERVEIN_RUSH:
+		if(!(hd->sc.data[SC_COMBO] && hd->sc.data[SC_COMBO]->val1 == MH_SONIC_CRAW)) return 1;
+		break;
+	case MH_MIDNIGHT_FRENZY:
+		if(!(hd->sc.data[SC_COMBO] && hd->sc.data[SC_COMBO]->val1 == MH_SILVERVEIN_RUSH)) return 1;
+		break;
+	case MH_CBC:
+		if(!(hd->sc.data[SC_COMBO] && hd->sc.data[SC_COMBO]->val1 == MH_TINDER_BREAKER)) return 1;
+		break;
+	case MH_EQC:
+		if(!(hd->sc.data[SC_COMBO] && hd->sc.data[SC_COMBO]->val1 == MH_CBC)) return 1;
 		break;
 	}
 
@@ -1499,6 +1443,9 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, uint
 	case MH_XENO_SLASHER:
 		sc_start4(src,bl,SC_BLEEDING,skill_lv,skill_lv,src->id,0,0,skill_get_time2(skill_id,skill_lv)); //@TODO need real duration
 		break;
+	case WL_HELLINFERNO:
+		sc_start4(src,bl,SC_BURNING,55+5*skill_lv,skill_lv,1000,src->id,0,skill_get_time(skill_id,skill_lv));
+		break;
 	}
 
 	if (md && battle_config.summons_trigger_autospells && md->master_id && md->special_state.ai)
@@ -2257,6 +2204,141 @@ static int skill_magic_reflect(struct block_list* src, struct block_list* bl, in
 	return 0;
 }
 
+/*
+ * Combo handler, start stop combo status
+ */
+void skill_combo_toogle_inf(struct block_list* bl, uint16 skill_id, int inf){
+	TBL_PC *sd = BL_CAST(BL_PC, bl);
+	switch (skill_id) {
+	case MH_MIDNIGHT_FRENZY:
+	case MH_EQC:{
+		int skill_id2 = ((skill_id==MH_EQC)?MH_TINDER_BREAKER:MH_SONIC_CRAW);
+		int idx = skill_get_index(skill_id2);
+		int flag = (inf?SKILL_FLAG_TMP_COMBO:SKILL_FLAG_PERMANENT);
+		TBL_HOM *hd = BL_CAST(BL_HOM, bl);
+		sd = hd->master;
+//		if (sd) clif_skillinfo(sd,skill_id2, inf);
+		hd->homunculus.hskill[idx].flag= SKILL_FLAG_TMP_COMBO;
+		if(sd) clif_homskillinfoblock(sd); //refresh info //@FIXME we only want to refresh one skill
+	}
+	break;
+	case MO_COMBOFINISH:
+	case CH_TIGERFIST:
+	case CH_CHAINCRUSH:
+		if (sd) clif_skillinfo(sd,MO_EXTREMITYFIST, inf);
+		break;
+	case TK_JUMPKICK:
+		if (sd) clif_skillinfo(sd,TK_JUMPKICK, inf);
+		break;
+	case MO_TRIPLEATTACK:
+		if (sd && pc_checkskill(sd, SR_DRAGONCOMBO) > 0)
+			clif_skillinfo(sd,SR_DRAGONCOMBO, inf);
+		break;
+	case SR_FALLENEMPIRE:
+		if (sd){
+			clif_skillinfo(sd,SR_GATEOFHELL, inf);
+			clif_skillinfo(sd,SR_TIGERCANNON, inf);
+		}
+		break;
+	}
+}
+
+void skill_combo(struct block_list* src,struct block_list *dsrc, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int tick){
+	int duration = 0, delay=0; //Used to signal if this skill can be combo'ed later on.
+	struct status_change_entry *sce;
+	TBL_PC *sd = BL_CAST(BL_PC,src);
+	TBL_HOM *hd = BL_CAST(BL_HOM,src);
+	struct status_change *sc = status_get_sc(src);
+
+	if(sc == NULL) return;
+
+	//End previous combo state after skill is invoked
+	if ((sce = sc->data[SC_COMBO]) != NULL) {
+		switch (skill_id) {
+		case TK_TURNKICK:
+		case TK_STORMKICK:
+		case TK_DOWNKICK:
+		case TK_COUNTER:
+			if (sd && pc_famerank(sd->status.char_id,MAPID_TAEKWON)) {//Extend combo time.
+				sce->val1 = skill_id; //Update combo-skill
+				sce->val3 = skill_id;
+				if( sce->timer != INVALID_TIMER )
+					delete_timer(sce->timer, status_change_timer);
+				sce->timer = add_timer(tick+sce->val4, status_change_timer, src->id, SC_COMBO);
+				break;
+			}
+			unit_cancel_combo(src); // Cancel combo wait
+			break;
+		default:
+			if( src == dsrc ) // Ground skills are exceptions. [Inkfish]
+				status_change_end(src, SC_COMBO, INVALID_TIMER);
+		}
+	}
+
+	//start new combo
+	if(sd){ //player only
+		switch(skill_id) {
+		case MO_TRIPLEATTACK:
+			if (pc_checkskill(sd, MO_CHAINCOMBO) > 0 || pc_checkskill(sd, SR_DRAGONCOMBO) > 0)
+				duration=1;
+			break;
+		case MO_CHAINCOMBO:
+			if(pc_checkskill(sd, MO_COMBOFINISH) > 0 && sd->spiritball > 0)
+				duration=1;
+			break;
+		case MO_COMBOFINISH:
+			if (sd->status.party_id>0) //bonus from SG_FRIEND [Komurka]
+				party_skill_check(sd, sd->status.party_id, MO_COMBOFINISH, skill_lv);
+			if (pc_checkskill(sd, CH_TIGERFIST) > 0 && sd->spiritball > 0)
+				duration=1;
+		case CH_TIGERFIST:
+			if (!duration && pc_checkskill(sd, CH_CHAINCRUSH) > 0 && sd->spiritball > 1)
+				duration=1;
+		case CH_CHAINCRUSH:
+			if (!duration && pc_checkskill(sd, MO_EXTREMITYFIST) > 0 && sd->spiritball > 0 && sd->sc.data[SC_EXPLOSIONSPIRITS])
+				duration=1;
+			break;
+		case AC_DOUBLE: {
+			unsigned char race = status_get_race(bl);
+			if( (race == RC_BRUTE || race == RC_INSECT) && pc_checkskill(sd, HT_POWER))
+			    duration = 2000;
+			break;
+		}
+		case SR_DRAGONCOMBO:
+			if( pc_checkskill(sd, SR_FALLENEMPIRE) > 0 )
+				duration = 1;
+			break;
+		case SR_FALLENEMPIRE:
+			if( pc_checkskill(sd, SR_TIGERCANNON) > 0 || pc_checkskill(sd, SR_GATEOFHELL) > 0 )
+				duration = 1;
+			break;
+		}
+	}
+	else { //other
+		switch(skill_id) {
+		case MH_TINDER_BREAKER:
+		case MH_CBC:
+		case MH_SONIC_CRAW:
+		case MH_SILVERVEIN_RUSH:
+			if(hd->homunculus.spiritball > 0) duration = 2000;
+				delay=1;
+			break;
+		case MH_EQC:
+		case MH_MIDNIGHT_FRENZY:
+			if(hd->homunculus.spiritball >= 2) duration = 6000;
+				delay=1;
+			break;
+		}
+	}
+
+	if (duration) { //Possible to chain
+		if(sd) duration = DIFF_TICK(sd->ud.canact_tick, tick);
+		if (duration < 1) duration = 1;
+		sc_start4(src,src,SC_COMBO,100,skill_id,bl->id,delay,0,duration);
+		clif_combo_delay(src, duration);
+	}
+}
+
 /*
  * =========================================================================
  * Does a skill attack with the given properties.
@@ -2405,107 +2487,46 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds
 	//Skill hit type
 	type=(skill_id==0)?5:skill_get_hit(skill_id);
 
-	if(damage < dmg.div_
-		//Only skills that knockback even when they miss. [Skotlex]
-		&& skill_id != CH_PALMSTRIKE)
-		dmg.blewcount = 0;
-
-	if(skill_id == CR_GRANDCROSS||skill_id == NPC_GRANDDARKNESS) {
+	switch(skill_id){
+	case SC_TRIANGLESHOT:
+		if(rnd()%100 > (1 + skill_lv) ) dmg.blewcount = 0;
+		break;
+	default:
+		if(damage < dmg.div_ && skill_lv != CH_PALMSTRIKE)
+			dmg.blewcount = 0; //only pushback when it hit
+		break;
+	}
+	switch(skill_id){
+	case CR_GRANDCROSS:
+	case NPC_GRANDDARKNESS:
 		if(battle_config.gx_disptype) dsrc = src;
 		if(src == bl) type = 4;
 		else flag|=SD_ANIMATION;
-	}
-	if(skill_id == NJ_TATAMIGAESHI) {
-		dsrc = src; //For correct knockback.
+		break;
+	case NJ_TATAMIGAESHI: //For correct knockback.
+		dsrc = src;
 		flag|=SD_ANIMATION;
-	}
-
-	if(sd) {
-		int flag = 0; //Used to signal if this skill can be combo'ed later on.
-		struct status_change_entry *sce;
-		if ((sce = sd->sc.data[SC_COMBO])) {//End combo state after skill is invoked. [Skotlex]
-			switch (skill_id) {
-			case TK_TURNKICK:
-			case TK_STORMKICK:
-			case TK_DOWNKICK:
-			case TK_COUNTER:
-				if (pc_famerank(sd->status.char_id,MAPID_TAEKWON)) {//Extend combo time.
-					sce->val1 = skill_id; //Update combo-skill
-					sce->val3 = skill_id;
-					if( sce->timer != INVALID_TIMER )
-						delete_timer(sce->timer, status_change_timer);
-					sce->timer = add_timer(tick+sce->val4, status_change_timer, src->id, SC_COMBO);
-					break;
-				}
-				unit_cancel_combo(src); // Cancel combo wait
-				break;
-			default:
-				if( src == dsrc ) // Ground skills are exceptions. [Inkfish]
-					status_change_end(src, SC_COMBO, INVALID_TIMER);
-			}
-		}
-		switch(skill_id) {
-			case MO_TRIPLEATTACK:
-				if (pc_checkskill(sd, MO_CHAINCOMBO) > 0 || pc_checkskill(sd, SR_DRAGONCOMBO) > 0)
-					flag=1;
-				break;
-			case MO_CHAINCOMBO:
-				if(pc_checkskill(sd, MO_COMBOFINISH) > 0 && sd->spiritball > 0)
-					flag=1;
-				break;
-			case MO_COMBOFINISH:
-				if (sd->status.party_id>0) //bonus from SG_FRIEND [Komurka]
-					party_skill_check(sd, sd->status.party_id, MO_COMBOFINISH, skill_lv);
-				if (pc_checkskill(sd, CH_TIGERFIST) > 0 && sd->spiritball > 0)
-					flag=1;
-			case CH_TIGERFIST:
-				if (!flag && pc_checkskill(sd, CH_CHAINCRUSH) > 0 && sd->spiritball > 1)
-					flag=1;
-			case CH_CHAINCRUSH:
-				if (!flag && pc_checkskill(sd, MO_EXTREMITYFIST) > 0 && sd->spiritball > 0 && sd->sc.data[SC_EXPLOSIONSPIRITS])
-					flag=1;
-				break;
-			case AC_DOUBLE:
-				if( (tstatus->race == RC_BRUTE || tstatus->race == RC_INSECT) && pc_checkskill(sd, HT_POWER))
-				{
-					//TODO: This code was taken from Triple Blows, is this even how it should be? [Skotlex]
-					sc_start2(src,src,SC_COMBO,100,HT_POWER,bl->id,2000);
-					clif_combo_delay(src,2000);
-				}
-				break;
-			case TK_COUNTER:
-			{	//bonus from SG_FRIEND [Komurka]
-				int level;
-				if(sd->status.party_id>0 && (level = pc_checkskill(sd,SG_FRIEND)))
-					party_skill_check(sd, sd->status.party_id, TK_COUNTER,level);
-			}
-				break;
-			case SL_STIN:
-			case SL_STUN:
-				if (skill_lv >= 7 && !sd->sc.data[SC_SMA])
-					sc_start(src,src,SC_SMA,100,skill_lv,skill_get_time(SL_SMA, skill_lv));
-				break;
-			case GS_FULLBUSTER:
-				//Can't attack nor use items until skill's delay expires. [Skotlex]
-				sd->ud.attackabletime = sd->canuseitem_tick = sd->ud.canact_tick;
-				break;
-			case SR_DRAGONCOMBO:
-				if( pc_checkskill(sd, SR_FALLENEMPIRE) > 0 )
-					flag = 1;
-				break;
-			case SR_FALLENEMPIRE:
-				if( pc_checkskill(sd, SR_TIGERCANNON) > 0 || pc_checkskill(sd, SR_GATEOFHELL) > 0 )
-					flag = 1;
-				break;
-		}	//Switch End
-		if (flag) { //Possible to chain
-			flag = DIFF_TICK(sd->ud.canact_tick, tick);
-			if (flag < 1) flag = 1;
-			sc_start2(src,src,SC_COMBO,100,skill_id,bl->id,flag);
-			clif_combo_delay(src, flag);
+		break;
+	case TK_COUNTER: {	//bonus from SG_FRIEND [Komurka]
+		int level;
+		if(sd->status.party_id>0 && (level = pc_checkskill(sd,SG_FRIEND)))
+			party_skill_check(sd, sd->status.party_id, TK_COUNTER,level);
 		}
+		break;
+	case SL_STIN:
+	case SL_STUN:
+		if (skill_lv >= 7 && !sd->sc.data[SC_SMA])
+			sc_start(src,src,SC_SMA,100,skill_lv,skill_get_time(SL_SMA, skill_lv));
+		break;
+	case GS_FULLBUSTER:
+		//Can't attack nor use items until skill's delay expires. [Skotlex]
+		sd->ud.attackabletime = sd->canuseitem_tick = sd->ud.canact_tick;
+		break;
 	}
 
+	//combo handling
+	skill_combo(src,dsrc,bl,skill_id,skill_lv,tick);
+
 	//Display damage.
 	switch( skill_id )
 	{
@@ -2659,7 +2680,7 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds
 				if( tsd->reproduceskill_id && tsd->status.skill[tsd->reproduceskill_id].flag == SKILL_FLAG_PLAGIARIZED ) {
 					tsd->status.skill[tsd->reproduceskill_id].id = 0;
 					tsd->status.skill[tsd->reproduceskill_id].lv = 0;
-					tsd->status.skill[tsd->reproduceskill_id].flag = 0;
+					tsd->status.skill[tsd->reproduceskill_id].flag = SKILL_FLAG_PERMANENT;
 					clif_deleteskill(tsd,tsd->reproduceskill_id);
 				}
 
@@ -2676,7 +2697,7 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds
 				if (tsd->cloneskill_id && tsd->status.skill[tsd->cloneskill_id].flag == SKILL_FLAG_PLAGIARIZED){
 					tsd->status.skill[tsd->cloneskill_id].id = 0;
 					tsd->status.skill[tsd->cloneskill_id].lv = 0;
-					tsd->status.skill[tsd->cloneskill_id].flag = 0;
+					tsd->status.skill[tsd->cloneskill_id].flag = SKILL_FLAG_PERMANENT;
 					clif_deleteskill(tsd,tsd->cloneskill_id);
 				}
 
@@ -2712,12 +2733,6 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds
 		if( damage > 0 ) //Counter status effects [Skotlex]
 			skill_counter_additional_effect(src,bl,skill_id,skill_lv,dmg.flag,tick);
 	}
-	// Hell Inferno burning status only starts if Fire part hits.
-	if( skill_id == WL_HELLINFERNO && dmg.damage > 0 )
-		sc_start4(src,bl,SC_BURNING,55+5*skill_lv,skill_lv,1000,src->id,0,skill_get_time(skill_id,skill_lv));
-	// Apply knock back chance in SC_TRIANGLESHOT skill.
-	else if( skill_id == SC_TRIANGLESHOT && rnd()%100 > (1 + skill_lv) )
-		dmg.blewcount = 0;
 
 	//Only knockback if it's still alive, otherwise a "ghost" is left behind. [Skotlex]
 	//Reflected spells do not bounce back (bl == dsrc since it only happens for direct skills)
@@ -4720,67 +4735,38 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint
 
 	case MH_STAHL_HORN:
 	case MH_NEEDLE_OF_PARALYZE:
+	case MH_SONIC_CRAW:
 		skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag);
 		break;
-	case MH_SONIC_CRAW:
-	case MH_TINDER_BREAKER:
 	case MH_MIDNIGHT_FRENZY:
-	case MH_SILVERVEIN_RUSH:
-	case MH_CBC:
-	case MH_EQC: {
+	case MH_SILVERVEIN_RUSH:{
 		TBL_HOM *hd = BL_CAST(BL_HOM,src);
-		int8 k=0;
-		int duration=0;
-		struct status_change_entry *sce;
-		struct block_list *tbl = NULL; //target
-
-		if(!hd){
-		    clif_colormes(sd,COLOR_RED,"Only homon are support this skill atm, can't used it by other");
-		    map_freeblock_unlock();
-		    return 1;
-		}
-		if(hd->sc.count && (sce=hd->sc.data[SC_STYLE_CHANGE])){
-		    //val1 = mode
-		    if(!sce->val2) sce->val2 = bl->id; //memo target (only sonic slaw and tinder should)
-		    tbl = map_id2bl(sce->val2);
-		    sce->val3 = skill_id;
-		    sce->val4 = gettick();
-		}
-		switch(skill_id){
-		    case MH_SONIC_CRAW: {
-			    int nb_sphere = hd->homunculus.spiritball;
-			    for(k=0; k<=nb_sphere; k++){ //attack for each sphere active
-				skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag);
-			    }
-	//		    hom_delspiritball(hd, nb_sphere, 0); //remove them all if we remove can't coninue combo
-			    break;
-		    }
-		    case MH_SILVERVEIN_RUSH:
-		    case MH_MIDNIGHT_FRENZY:
-			    hom_delspiritball(hd,skill_id==MH_SILVERVEIN_RUSH?1:2,0);
-			    skill_attack(skill_get_type(skill_id),src,src,tbl,skill_id,skill_lv,tick,flag);
-			    break;
-		    case MH_TINDER_BREAKER:
-			if (unit_movepos(src, bl->x, bl->y, 1, 1)) {
+		hom_delspiritball(hd,skill_id==MH_SILVERVEIN_RUSH?1:2,0);
+		skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag);
+		break;
+	}
+	case MH_TINDER_BREAKER:
+		if (unit_movepos(src, bl->x, bl->y, 1, 1)) {
 #if PACKETVER >= 20111005
-				clif_snap(src, bl->x, bl->y);
+			clif_snap(src, bl->x, bl->y);
 #else
-				clif_skill_poseffect(src,skill_id,skill_lv,bl->x,bl->y,tick);
+			clif_skill_poseffect(src,skill_id,skill_lv,bl->x,bl->y,tick);
 #endif
-			}
-		    case MH_CBC:
-		    case MH_EQC:
-			    duration = max(skill_lv,(status_get_str(src)/7 - status_get_str(bl)/10))*1000; //Yommy formula
-			    hom_delspiritball(hd,skill_id==MH_EQC?2:1,0); //only EQC consume 2 in grp 2
-			    if(skill_id==MH_TINDER_BREAKER)
-				sc_start2(src,src,status_skill2sc(skill_id),100,skill_lv,bl->id,duration);
-			    else
-				sc_start(src,bl,status_skill2sc(skill_id),100,skill_lv,duration);
-			    skill_attack(skill_get_type(skill_id),src,src,tbl,skill_id,skill_lv,tick,flag);
-			    break;
+
 		}
+	case MH_CBC:
+	case MH_EQC: {
+		int duration=0;
+		TBL_HOM *hd = BL_CAST(BL_HOM,src);
+		duration = max(skill_lv,(status_get_str(src)/7 - status_get_str(bl)/10))*1000; //Yommy formula
+		hom_delspiritball(hd,skill_id==MH_EQC?2:1,0); //only EQC consume 2 in grp 2
+		skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag);
+		clif_skill_nodamage(src,bl,skill_id,skill_lv,
+			sc_start4(src,bl,status_skill2sc(skill_id),100,skill_lv,src->id,0,0,duration));
 		break;
 	}
+
+
 	case 0:/* no skill - basic/normal attack */
 		if(sd) {
 			if (flag & 3){
@@ -9136,61 +9122,61 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 		}
 		break;
 
-       case MH_SILENT_BREEZE: {
-            struct status_change *ssc = status_get_sc(src);
-	    struct block_list *m_bl = battle_get_master(src);
-            const enum sc_type scs[] = {
-                    SC_MANDRAGORA, SC_HARMONIZE, SC_DEEPSLEEP, SC_VOICEOFSIREN, SC_SLEEP, SC_CONFUSION, SC_HALLUCINATION
-            };
-            int heal;
-            if(tsc){
-                for (i = 0; i < ARRAYLENGTH(scs); i++) {
-                    if (tsc->data[scs[i]]) status_change_end(bl, scs[i], INVALID_TIMER);
-                }
-                if (!tsc->data[SC_SILENCE]) //put inavoidable silence on target
-                        status_change_start(src,bl, SC_SILENCE, 100, skill_lv, 0,0,0, skill_get_time(skill_id, skill_lv),1|2|8);
-	    }
-	    heal = status_get_sp(src) + status_get_lv(src); //cur_sp+blvl @TODO need real value
-            status_heal(bl, heal, 0, 7);
-
-	    //now inflict silence on everyone
-	    if(ssc && !ssc->data[SC_SILENCE]) //put inavoidable silence on homun
-		status_change_start(src, src, SC_SILENCE, 100, skill_lv, 0,0,0, skill_get_time(skill_id, skill_lv),1|2|8);
-	    if(m_bl){
-		struct status_change *msc = status_get_sc(m_bl);
-		if(msc && !msc->data[SC_SILENCE]) //put inavoidable silence on master
-		    status_change_start(src, m_bl, SC_SILENCE, 100, skill_lv, 0,0,0, skill_get_time(skill_id, skill_lv),1|2|8);
-	    }
-            if (hd)
-                skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
-       }
-       break;
-       case MH_OVERED_BOOST:
-            if (hd){
-                struct block_list *s_bl = battle_get_master(src);
-                if(hd->homunculus.hunger>50) //reduce hunger
-                    hd->homunculus.hunger = hd->homunculus.hunger/2;
-                else
-                    hd->homunculus.hunger = min(1,hd->homunculus.hunger);
-                if(s_bl && s_bl->type==BL_PC){
-                    status_set_sp(s_bl,status_get_max_sp(s_bl)/2,0); //master drain 50% sp
-                    clif_send_homdata(((TBL_PC *)s_bl), SP_HUNGRY, hd->homunculus.hunger); //refresh hunger info
-                    sc_start(src,s_bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)); //gene bonus
-                }
-                sc_start(src,bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv));
-		skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
-            }
-            break;
-       case MH_GRANITIC_ARMOR:
-       case MH_PYROCLASTIC: {
-                struct block_list *s_bl = battle_get_master(src);
-                if(s_bl) sc_start2(src, s_bl, type, 100, skill_lv, hd->homunculus.level, skill_get_time(skill_id, skill_lv)); //start on master
-                sc_start2(src, bl, type, 100, skill_lv, hd->homunculus.level, skill_get_time(skill_id, skill_lv));
-		if (hd) skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
-            }
-            break;
-
-        case MH_LIGHT_OF_REGENE: //self
+	case MH_SILENT_BREEZE: {
+	     struct status_change *ssc = status_get_sc(src);
+	     struct block_list *m_bl = battle_get_master(src);
+	     const enum sc_type scs[] = {
+		     SC_MANDRAGORA, SC_HARMONIZE, SC_DEEPSLEEP, SC_VOICEOFSIREN, SC_SLEEP, SC_CONFUSION, SC_HALLUCINATION
+	     };
+	     int heal;
+	     if(tsc){
+		 for (i = 0; i < ARRAYLENGTH(scs); i++) {
+		     if (tsc->data[scs[i]]) status_change_end(bl, scs[i], INVALID_TIMER);
+		 }
+		 if (!tsc->data[SC_SILENCE]) //put inavoidable silence on target
+			 status_change_start(src,bl, SC_SILENCE, 100, skill_lv, 0,0,0, skill_get_time(skill_id, skill_lv),1|2|8);
+	     }
+	     heal = status_get_sp(src) + status_get_lv(src); //cur_sp+blvl @TODO need real value
+	     status_heal(bl, heal, 0, 7);
+
+	     //now inflict silence on everyone
+	     if(ssc && !ssc->data[SC_SILENCE]) //put inavoidable silence on homun
+		 status_change_start(src, src, SC_SILENCE, 100, skill_lv, 0,0,0, skill_get_time(skill_id, skill_lv),1|2|8);
+	     if(m_bl){
+		 struct status_change *msc = status_get_sc(m_bl);
+		 if(msc && !msc->data[SC_SILENCE]) //put inavoidable silence on master
+		     status_change_start(src, m_bl, SC_SILENCE, 100, skill_lv, 0,0,0, skill_get_time(skill_id, skill_lv),1|2|8);
+	     }
+	     if (hd)
+		 skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
+	}
+	break;
+	case MH_OVERED_BOOST:
+	     if (hd){
+		 struct block_list *s_bl = battle_get_master(src);
+		 if(hd->homunculus.hunger>50) //reduce hunger
+		     hd->homunculus.hunger = hd->homunculus.hunger/2;
+		 else
+		     hd->homunculus.hunger = min(1,hd->homunculus.hunger);
+		 if(s_bl && s_bl->type==BL_PC){
+		     status_set_sp(s_bl,status_get_max_sp(s_bl)/2,0); //master drain 50% sp
+		     clif_send_homdata(((TBL_PC *)s_bl), SP_HUNGRY, hd->homunculus.hunger); //refresh hunger info
+		     sc_start(src,s_bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)); //gene bonus
+		 }
+		 sc_start(src,bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+		 skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
+	     }
+	     break;
+	case MH_GRANITIC_ARMOR:
+	case MH_PYROCLASTIC: {
+		 struct block_list *s_bl = battle_get_master(src);
+		 if(s_bl) sc_start2(src, s_bl, type, 100, skill_lv, hd->homunculus.level, skill_get_time(skill_id, skill_lv)); //start on master
+		 sc_start2(src, bl, type, 100, skill_lv, hd->homunculus.level, skill_get_time(skill_id, skill_lv));
+		 if (hd) skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
+	     }
+	     break;
+
+	case MH_LIGHT_OF_REGENE: //self
 		sc_start2(src, src, type, 100, skill_lv, hd->homunculus.level, skill_get_time(skill_id, skill_lv));
 		if(hd){
 		    hd->homunculus.intimacy = 251; //change to neutral (can't be cast if < 750)
@@ -9212,40 +9198,39 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 	    }
 	    break;
 	}
-        case MH_MAGMA_FLOW:
-        case MH_PAIN_KILLER:
-           sc_start(src,bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv));
-           if (hd)
-                skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
-           break;
+	case MH_MAGMA_FLOW:
+	case MH_PAIN_KILLER:
+	   sc_start(src,bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+	   if (hd)
+		skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
+	   break;
 
-        case MH_SUMMON_LEGION:
-            {
-                int summons[5] = {2158, 2159, 2159, 2160, 2160};
-                int qty[5] =     {3   , 3   , 4   , 4   , 5};
-                struct mob_data *sum_md;
-                int i,c=0;
+	case MH_SUMMON_LEGION: {
+		int summons[5] = {2158, 2159, 2159, 2160, 2160};
+		int qty[5] =     {3   , 3   , 4   , 4   , 5};
+		struct mob_data *sum_md;
+		int i,c=0;
 
 		int maxcount = qty[skill_lv-1];
 		i = map_foreachinmap(skill_check_condition_mob_master_sub ,hd->bl.m, BL_MOB, hd->bl.id, summons[skill_lv-1], skill_id, &c);
 		if(c >= maxcount) return 0; //max qty already spawned
 
-                for(i=0; i<qty[skill_lv - 1]; i++){ //easy way
-                    sum_md = mob_once_spawn_sub(src, src->m, src->x, src->y, status_get_name(src), summons[skill_lv - 1], "", SZ_SMALL, AI_ATTACK);
-                    if (sum_md) {
-                        sum_md->master_id =  src->id;
-			sum_md->special_state.ai = 5;
-                        if (sum_md->deletetimer != INVALID_TIMER)
-                            delete_timer(sum_md->deletetimer, mob_timer_delete);
-                        sum_md->deletetimer = add_timer(gettick() + skill_get_time(skill_id, skill_lv), mob_timer_delete, sum_md->bl.id, 0);
-                        mob_spawn(sum_md); //Now it is ready for spawning.
-                        sc_start4(&sum_md->bl,&sum_md->bl, SC_MODECHANGE, 100, 1, 0, MD_CANATTACK|MD_AGGRESSIVE, 0, 60000);
-                    }
-                }
+		for(i=0; i<qty[skill_lv - 1]; i++){ //easy way
+			sum_md = mob_once_spawn_sub(src, src->m, src->x, src->y, status_get_name(src), summons[skill_lv - 1], "", SZ_SMALL, AI_ATTACK);
+			if (sum_md) {
+				sum_md->master_id =  src->id;
+				sum_md->special_state.ai = 5;
+				if (sum_md->deletetimer != INVALID_TIMER)
+					delete_timer(sum_md->deletetimer, mob_timer_delete);
+				sum_md->deletetimer = add_timer(gettick() + skill_get_time(skill_id, skill_lv), mob_timer_delete, sum_md->bl.id, 0);
+				mob_spawn(sum_md); //Now it is ready for spawning.
+				sc_start4(&sum_md->bl,&sum_md->bl, SC_MODECHANGE, 100, 1, 0, MD_CANATTACK|MD_AGGRESSIVE, 0, 60000);
+			}
+		}
 		if (hd)
 			skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
-            }
-            break;
+		}
+		break;
 	default:
 		ShowWarning("skill_castend_nodamage_id: Unknown skill used:%d\n",skill_id);
 		clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
@@ -9480,10 +9465,10 @@ int skill_castend_id(int tid, unsigned int tick, int id, intptr_t data)
 			break;
 
 		if (ud->state.running && ud->skill_id == TK_JUMPKICK)
-        {
-            ud->state.running = 0;
-            status_change_end(src, SC_RUN, INVALID_TIMER);
-			flag = 1;
+		{
+		    ud->state.running = 0;
+		    status_change_end(src, SC_RUN, INVALID_TIMER);
+				flag = 1;
 		}
 
 		if (ud->walktimer != INVALID_TIMER && ud->skill_id != TK_RUN && ud->skill_id != RA_WUGDASH)
@@ -13978,7 +13963,7 @@ int skill_castfix (struct block_list *bl, uint16 skill_id, uint16 skill_lv) {
 	if (battle_config.cast_rate != 100)
 		time = time * battle_config.cast_rate / 100;
 	// return final cast time
-        time = max(time, 0);
+	time = max(time, 0);
 
 //        ShowInfo("Castime castfix = %d\n",time);
 	return time;
@@ -13997,8 +13982,8 @@ int skill_castfix_sc (struct block_list *bl, int time)
 	if (sc && sc->count) {
 		if (sc->data[SC_SLOWCAST])
 			time += time * sc->data[SC_SLOWCAST]->val2 / 100;
-        if (sc->data[SC_PARALYSIS])
-            time += sc->data[SC_PARALYSIS]->val3;
+	if (sc->data[SC_PARALYSIS])
+		time += sc->data[SC_PARALYSIS]->val3;
 		if (sc->data[SC_SUFFRAGIUM]) {
 			time -= time * sc->data[SC_SUFFRAGIUM]->val2 / 100;
 			status_change_end(bl, SC_SUFFRAGIUM, INVALID_TIMER);
@@ -14013,7 +13998,7 @@ int skill_castfix_sc (struct block_list *bl, int time)
 		if (sc->data[SC_IZAYOI])
 			time -= time * 50 / 100;
 	}
-        time = max(time, 0);
+	time = max(time, 0);
 
 //        ShowInfo("Castime castfix_sc = %d\n",time);
 	return time;

+ 3 - 0
src/map/skill.h

@@ -1899,4 +1899,7 @@ int skill_elementalanalysis(struct map_session_data *sd, int n, uint16 skill_lv,
 int skill_changematerial(struct map_session_data *sd, int n, unsigned short *item_list);	// Genetic Change Material.
 int skill_get_elemental_type(uint16 skill_id, uint16 skill_lv);
 
+void skill_combo_toogle_inf(struct block_list* bl, uint16 skill_id, int inf);
+void skill_combo(struct block_list* src,struct block_list *dsrc, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int tick);
+
 #endif /* _SKILL_H_ */

+ 124 - 148
src/map/status.c

@@ -508,7 +508,8 @@ void initChangeTables(void) {
 	set_sc(MH_PAIN_KILLER, SC_PAIN_KILLER, SI_PAIN_KILLER, SCB_ASPD);
 
 	add_sc(MH_STYLE_CHANGE, SC_STYLE_CHANGE);
-	set_sc(MH_TINDER_BREAKER, SC_TINDER_BREAKER, SI_TINDER_BREAKER, SCB_FLEE);
+	set_sc(MH_TINDER_BREAKER, SC_TINDER_BREAKER2, SI_TINDER_BREAKER, SCB_FLEE);
+	set_sc(MH_TINDER_BREAKER, SC_TINDER_BREAKER, SI_TINDER_BREAKER_POSTDELAY, SCB_FLEE);
 	set_sc(MH_CBC, SC_CBC, SI_CBC, SCB_FLEE);
 	set_sc(MH_EQC, SC_EQC, SI_EQC, SCB_DEF2|SCB_BATK|SCB_MAXHP);
 
@@ -1006,6 +1007,8 @@ void initChangeTables(void) {
 	StatusChangeStateTable[SC_STOP]                |= SCS_NOMOVE;
 	StatusChangeStateTable[SC_CLOSECONFINE]        |= SCS_NOMOVE;
 	StatusChangeStateTable[SC_CLOSECONFINE2]       |= SCS_NOMOVE;
+	StatusChangeStateTable[SC_TINDER_BREAKER]     |= SCS_NOMOVE;
+	StatusChangeStateTable[SC_TINDER_BREAKER2]     |= SCS_NOMOVE;
 	StatusChangeStateTable[SC_MADNESSCANCEL]       |= SCS_NOMOVE;
 	StatusChangeStateTable[SC_GRAVITATION]         |= SCS_NOMOVE|SCS_NOMOVECOND;
 	StatusChangeStateTable[SC_WHITEIMPRISON]       |= SCS_NOMOVE;
@@ -1314,18 +1317,18 @@ int status_damage(struct block_list *src,struct block_list *target,int hp, int s
 
 		return hp+sp;
 	}
-    if(target->type == BL_PC){
-        TBL_PC *sd = BL_CAST(BL_PC,target);
-        TBL_HOM *hd = sd->hd;
-        if(hd && hd->sc.data[SC_LIGHT_OF_REGENE]){
-            clif_skillcasting(&hd->bl, hd->bl.id, target->id, 0,0, MH_LIGHT_OF_REGENE, skill_get_ele(MH_LIGHT_OF_REGENE, 1), 10); //just to display usage
-            clif_skill_nodamage(&sd->bl, target, ALL_RESURRECTION, 1, status_revive(&sd->bl,hd->sc.data[SC_LIGHT_OF_REGENE]->val2,0));
-            status_change_end(&sd->hd->bl,SC_LIGHT_OF_REGENE,INVALID_TIMER);
-            return hp + sp;
-        }
-    }
-    if (target->type == BL_MOB && sc && sc->data[SC_REBIRTH] && !((TBL_MOB*) target)->state.rebirth) {// Ensure the monster has not already rebirthed before doing so.
-        status_revive(target, sc->data[SC_REBIRTH]->val2, 0);
+	if(target->type == BL_PC){
+		TBL_PC *sd = BL_CAST(BL_PC,target);
+		TBL_HOM *hd = sd->hd;
+		if(hd && hd->sc.data[SC_LIGHT_OF_REGENE]){
+			clif_skillcasting(&hd->bl, hd->bl.id, target->id, 0,0, MH_LIGHT_OF_REGENE, skill_get_ele(MH_LIGHT_OF_REGENE, 1), 10); //just to display usage
+			clif_skill_nodamage(&sd->bl, target, ALL_RESURRECTION, 1, status_revive(&sd->bl,hd->sc.data[SC_LIGHT_OF_REGENE]->val2,0));
+			status_change_end(&sd->hd->bl,SC_LIGHT_OF_REGENE,INVALID_TIMER);
+			return hp + sp;
+		}
+	}
+	if (target->type == BL_MOB && sc && sc->data[SC_REBIRTH] && !((TBL_MOB*) target)->state.rebirth) {// Ensure the monster has not already rebirthed before doing so.
+		status_revive(target, sc->data[SC_REBIRTH]->val2, 0);
 		status_change_clear(target,0);
 		((TBL_MOB*)target)->state.rebirth = 1;
 
@@ -4813,6 +4816,8 @@ static signed short status_calc_flee(struct block_list *bl, struct status_change
 
 	if(!sc || !sc->count)
 		return cap_value(flee,1,SHRT_MAX);
+	if(sc->data[SC_TINDER_BREAKER] || sc->data[SC_TINDER_BREAKER2])
+		return 0; //0 flee
 
 	if(sc->data[SC_INCFLEE])
 		flee += sc->data[SC_INCFLEE]->val1;
@@ -7210,6 +7215,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 			case SC_BLEEDING:
 			case SC_DPOISON:
 			case SC_CLOSECONFINE2: //Can't be re-closed in.
+			case SC_TINDER_BREAKER2:
 			case SC_MARIONETTE:
 			case SC_MARIONETTE2:
 			case SC_NOCHAT:
@@ -7834,18 +7840,21 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 			status_zap(bl, status->hp-1, val2?0:status->sp);
 			return 1;
 			break;
+		case SC_TINDER_BREAKER2:
 		case SC_CLOSECONFINE2:
 		{
 			struct block_list *src = val2?map_id2bl(val2):NULL;
 			struct status_change *sc2 = src?status_get_sc(src):NULL;
-			struct status_change_entry *sce2 = sc2?sc2->data[SC_CLOSECONFINE]:NULL;
+			int type2 = ((type == SC_TINDER_BREAKER2)?SC_TINDER_BREAKER:SC_CLOSECONFINE);
+			struct status_change_entry *sce2 = sc2?sc2->data[type2]:NULL;
+
 			if (src && sc2) {
 				if (!sce2) //Start lock on caster.
-					sc_start4(src,src,SC_CLOSECONFINE,100,val1,1,0,0,tick+1000);
+					sc_start4(src,src,type2,100,val1,1,0,0,tick+1000);
 				else { //Increase count of locked enemies and refresh time.
 					(sce2->val2)++;
 					delete_timer(sce2->timer, status_change_timer);
-					sce2->timer = add_timer(gettick()+tick+1000, status_change_timer, src->id, SC_CLOSECONFINE);
+					sce2->timer = add_timer(gettick()+tick+1000, status_change_timer, src->id, type2);
 				}
 			} else //Status failed.
 				return 0;
@@ -8682,69 +8691,69 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 					status_zap(bl, hp * (lv*4) / 100, status_get_sp(bl) * (lv*3) / 100);
 			}
 			break;
-			case SC_ANGRIFFS_MODUS:
-			    val2 = 50 + 20 * val1; //atk bonus
-			    val3 = 40 + 20 * val1; // Flee reduction.
-			    val4 = tick/1000; // hp/sp reduction timer
-			    tick_time = 1000;
-			    break;
-			case SC_GOLDENE_FERSE:
-			    val2 = 10 + 10*val1; //flee bonus
-			    val3 = 6 + 4 * val1; // Aspd Bonus
-			    val4 = 2 + 2 * val1; // Chance of holy attack
-			    break;
-			case SC_OVERED_BOOST:
-			    val2 = 300 + 40*val1; //flee bonus
-			    val3 = 179 + 2*val1; //aspd bonus
-			    val4 = 50; //def reduc %
-			    break;
-			case SC_GRANITIC_ARMOR:
-			    val2 = 2*val1; //dmg hp reduction
-			    val3 = (6*status_get_max_hp(src))/100; //dmg hp on status end
-			    val4 = 5 * val1; //unknow formula
-			    break;
-			case SC_MAGMA_FLOW:
-			    val2 = 3*val1; //activation chance
-			    break;
-			case SC_PYROCLASTIC:
-			    val2 += 10*val1*status_get_lv(src); //atk bonus
-			    val3 = 2*val1;//Chance To AutoCast Hammer Fall %
-			    break;
-			case SC_PARALYSIS: //[Lighta] need real info
-			    val2 = 2*val1; //def reduction
-			    val3 = 500*val1; //varcast augmentation
-			    break;
-			case SC_LIGHT_OF_REGENE: //Yommy leak need confirm
-			    val2 = 20 * val1; //hp reco on death %
-			    break;
-			case SC_PAIN_KILLER: //Yommy leak need confirm
-			    val2 = 10 * val1; //aspd reduction %
-			    val3 = (( 200 * val1 ) * status_get_lv(src)) / 150; //dmg reduction linear
-			    if(sc->data[SC_PARALYSIS])
+		case SC_ANGRIFFS_MODUS:
+			val2 = 50 + 20 * val1; //atk bonus
+			val3 = 40 + 20 * val1; // Flee reduction.
+			val4 = tick/1000; // hp/sp reduction timer
+			tick_time = 1000;
+			break;
+		case SC_GOLDENE_FERSE:
+			val2 = 10 + 10*val1; //flee bonus
+			val3 = 6 + 4 * val1; // Aspd Bonus
+			val4 = 2 + 2 * val1; // Chance of holy attack
+			break;
+		case SC_OVERED_BOOST:
+			val2 = 300 + 40*val1; //flee bonus
+			val3 = 179 + 2*val1; //aspd bonus
+			val4 = 50; //def reduc %
+			break;
+		case SC_GRANITIC_ARMOR:
+			val2 = 2*val1; //dmg hp reduction
+			val3 = (6*status_get_max_hp(src))/100; //dmg hp on status end
+			val4 = 5 * val1; //unknow formula
+			break;
+		case SC_MAGMA_FLOW:
+			val2 = 3*val1; //activation chance
+			break;
+		case SC_PYROCLASTIC:
+			val2 += 10*val1*status_get_lv(src); //atk bonus
+			val3 = 2*val1;//Chance To AutoCast Hammer Fall %
+			break;
+		case SC_PARALYSIS: //[Lighta] need real info
+			val2 = 2*val1; //def reduction
+			val3 = 500*val1; //varcast augmentation
+			break;
+		case SC_LIGHT_OF_REGENE: //Yommy leak need confirm
+			val2 = 20 * val1; //hp reco on death %
+			break;
+		case SC_PAIN_KILLER: //Yommy leak need confirm
+			val2 = 10 * val1; //aspd reduction %
+			val3 = (( 200 * val1 ) * status_get_lv(src)) / 150; //dmg reduction linear
+			if(sc->data[SC_PARALYSIS])
 				sc_start(src,bl, SC_ENDURE, 100, val1, tick); //start endure for same duration
-			    break;
-                        case SC_STYLE_CHANGE: //[Lighta] need real info
-                            tick = -1;
-                            break;
-			case SC_CBC:
-			    val3 = 10; //drain sp % dmg
-			    val4 = tick/1000; //dmg each sec
-			    tick = 1000;
-			    break;
-			case SC_EQC:
-			    val2 = 5 * val1; //def % reduc
-			    val3 = 5 * val1; //atk % reduc
-			    val4 = 2 * val1; //maxhp % reduc
-			    break;
-			case SC_ASH:
-			    val2 = 50; //hit % reduc
-			    val3 = 0;//def % reduc
-			    val4 = 0;//atk flee & reduc
-			    if(status_get_race(bl) == RC_PLANT) //plant type
+			break;
+		case SC_STYLE_CHANGE:
+			tick = -1; //infinite duration
+			break;
+		case SC_CBC:
+			val3 = 10; //drain sp % dmg
+			val4 = tick/1000; //dmg each sec
+			tick = 1000;
+			break;
+		case SC_EQC:
+			val2 = 5 * val1; //def % reduc
+			val3 = 5 * val1; //atk % reduc
+			val4 = 2 * val1; //maxhp % reduc
+			break;
+		case SC_ASH:
+			val2 = 50; //hit % reduc
+			val3 = 0;//def % reduc
+			val4 = 0;//atk flee & reduc
+			if(status_get_race(bl) == RC_PLANT) //plant type
 				val3 = 50;
-			    if(status_get_element(bl) == ELE_WATER) // defense water type
+			if(status_get_element(bl) == ELE_WATER) // defense water type
 				val4 = 50;
-			    break;
+			break;
 		default:
 			if( calc_flag == SCB_NONE && StatusSkillChangeTable[type] == 0 && StatusIconChangeTable[type] == 0 )
 			{	//Status change with no calc, no icon, and no skill associated...?
@@ -8788,6 +8797,8 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 		case SC_CONFUSION:
 		case SC_CLOSECONFINE:
 		case SC_CLOSECONFINE2:
+		case SC_TINDER_BREAKER:
+		case SC_TINDER_BREAKER2:
 		case SC_SPIDERWEB:
 		case SC_ELECTRICSHOCKER:
 		case SC_BITE:
@@ -9064,7 +9075,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 			}
 			break;
 		case SC_COMBO:
-			switch (sce->val1) {
+			switch(sce->val1){
 				case TK_STORMKICK:
 					clif_skill_nodamage(bl,bl,TK_READYSTORM,1,1);
 					break;
@@ -9077,37 +9088,17 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 				case TK_COUNTER:
 					clif_skill_nodamage(bl,bl,TK_READYCOUNTER,1,1);
 					break;
-				case MO_COMBOFINISH:
-				case CH_TIGERFIST:
-				case CH_CHAINCRUSH:
-					if (sd)
-						clif_skillinfo(sd,MO_EXTREMITYFIST, INF_SELF_SKILL);
-					break;
-				case TK_JUMPKICK:
-					if (sd)
-						clif_skillinfo(sd,TK_JUMPKICK, INF_SELF_SKILL);
-					break;
-				case MO_TRIPLEATTACK:
-					if (sd && pc_checkskill(sd, SR_DRAGONCOMBO) > 0)
-						clif_skillinfo(sd,SR_DRAGONCOMBO, INF_SELF_SKILL);
-					break;
-				case SR_FALLENEMPIRE:
-					if (sd){
-						clif_skillinfo(sd,SR_GATEOFHELL, INF_SELF_SKILL);
-						clif_skillinfo(sd,SR_TIGERCANNON, INF_SELF_SKILL);
-					}
+				default: //rest just toogle inf to enable autotarget
+					skill_combo_toogle_inf(bl,sce->val1,INF_SELF_SKILL);
 					break;
 			}
 			break;
 		case SC_RAISINGDRAGON:
 			sce->val2 = status->max_hp / 100;// Officially tested its 1%hp drain. [Jobbie]
 			break;
-		case SC_TINDER_BREAKER:
-			sc_start2(src, map_id2bl(val2),SC_CLOSECONFINE2,100,val1,bl->id,tick);
-			break;
 		case SC_EQC:
 			sc_start2(src, bl,SC_STUN,100,val1,bl->id,(1000*status_get_lv(src))/50+500*val1);
-			status_change_end(bl,SC_TINDER_BREAKER,INVALID_TIMER);
+			status_change_end(bl,SC_TINDER_BREAKER2,INVALID_TIMER);
 			break;
 	}
 
@@ -9292,17 +9283,17 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const
 	vd = status_get_viewdata(bl);
 	calc_flag = StatusChangeFlagTable[type];
 	switch(type){
-        case SC_GRANITIC_ARMOR:{
-            int dammage = status->max_hp*sce->val3/100;
-            if(status->hp < dammage) //to not kill him
-                dammage = status->hp-1;
-            status_damage(NULL, bl, dammage,0,0,1);
-            break;
-        }
-        case SC_PYROCLASTIC:
-            if(bl->type == BL_PC)
-                skill_break_equip(bl,bl,EQP_WEAPON,10000,BCT_SELF);
-            break;
+		case SC_GRANITIC_ARMOR:{
+		    int dammage = status->max_hp*sce->val3/100;
+		    if(status->hp < dammage) //to not kill him
+			dammage = status->hp-1;
+		    status_damage(NULL, bl, dammage,0,0,1);
+		    break;
+		}
+		case SC_PYROCLASTIC:
+		    if(bl->type == BL_PC)
+			skill_break_equip(bl,bl,EQP_WEAPON,10000,BCT_SELF);
+		    break;
 		case SC_WEDDING:
 		case SC_XMAS:
 		case SC_SUMMER:
@@ -9480,18 +9471,19 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const
 					skill_castend_damage_id(src, bl, sce->val2, sce->val1, gettick(), SD_LEVEL );
 			}
 			break;
-		case SC_TINDER_BREAKER:
-		case SC_CLOSECONFINE2:
-			{
-				struct block_list *src = sce->val2?map_id2bl(sce->val2):NULL;
-				struct status_change *sc2 = src?status_get_sc(src):NULL;
-				if (src && sc2 && sc2->data[SC_CLOSECONFINE]) {
-					//If status was already ended, do nothing.
-					//Decrease count
-					if (--(sc2->data[SC_CLOSECONFINE]->val1) <= 0) //No more holds, free him up.
-						status_change_end(src, SC_CLOSECONFINE, INVALID_TIMER);
-				}
+		case SC_TINDER_BREAKER2:
+		case SC_CLOSECONFINE2:{
+			struct block_list *src = sce->val2?map_id2bl(sce->val2):NULL;
+			struct status_change *sc2 = src?status_get_sc(src):NULL;
+			int type2 = ((type==SC_CLOSECONFINE2)?SC_CLOSECONFINE:SC_TINDER_BREAKER);
+			if (src && sc2 && sc2->data[type2]) {
+				//If status was already ended, do nothing.
+				//Decrease count
+				if (type==SC_TINDER_BREAKER2 || (--(sc2->data[type2]->val1) <= 0)) //No more holds, free him up.
+					status_change_end(src, type2, INVALID_TIMER);
 			}
+		}
+		case SC_TINDER_BREAKER:
 		case SC_CLOSECONFINE:
 			if (sce->val2 > 0) {
 				//Caster has been unlocked... nearby chars need to be unlocked.
@@ -9503,27 +9495,8 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const
 			}
 			break;
 		case SC_COMBO:
-			if( sd )
-			switch (sce->val1) {
-				case MO_COMBOFINISH:
-				case CH_TIGERFIST:
-				case CH_CHAINCRUSH:
-					clif_skillinfo(sd, MO_EXTREMITYFIST, 0);
-					break;
-				case TK_JUMPKICK:
-					clif_skillinfo(sd, TK_JUMPKICK, 0);
-					break;
-				case MO_TRIPLEATTACK:
-					if (pc_checkskill(sd, SR_DRAGONCOMBO) > 0)
-						clif_skillinfo(sd, SR_DRAGONCOMBO, 0);
-					break;
-				case SR_FALLENEMPIRE:
-					clif_skillinfo(sd, SR_GATEOFHELL, 0);
-					clif_skillinfo(sd, SR_TIGERCANNON, 0);
-					break;
-			}
+			skill_combo_toogle_inf(bl,sce->val1,0);
 			break;
-
 		case SC_MARIONETTE:
 		case SC_MARIONETTE2:	/// Marionette target
 			if (sce->val1)
@@ -10799,7 +10772,7 @@ int status_change_timer_sub(struct block_list* bl, va_list ap)
 	tsc = status_get_sc(bl);
 
 	switch( type ) {
-    case SC_SIGHT: /* Reveal hidden ennemy on 3*3 range */
+	case SC_SIGHT: /* Reveal hidden ennemy on 3*3 range */
 		if( tsc && tsc->data[SC__SHADOWFORM] && (sce && sce->val4 >0 && sce->val4%2000 == 0) && // for every 2 seconds do the checking
 			rnd()%100 < 100-tsc->data[SC__SHADOWFORM]->val1*10 ) // [100 - (Skill Level x 10)] %
 				status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER);
@@ -10809,7 +10782,7 @@ int status_change_timer_sub(struct block_list* bl, va_list ap)
 		status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER);
 		status_change_end(bl, SC_CAMOUFLAGE, INVALID_TIMER);
 		break;
-    case SC_RUWACH: /* Reveal hidden target and deal little dammages if ennemy */
+	case SC_RUWACH: /* Reveal hidden target and deal little dammages if ennemy */
 		if (tsc && (tsc->data[SC_HIDING] || tsc->data[SC_CLOAKING] ||
 				tsc->data[SC_CAMOUFLAGE] || tsc->data[SC_CLOAKINGEXCEED] ||
 					tsc->data[SC__INVISIBILITY])) { //this sc should hit only
@@ -10834,13 +10807,16 @@ int status_change_timer_sub(struct block_list* bl, va_list ap)
 			}
 		}
 		break;
-	case SC_CLOSECONFINE:
+	case SC_TINDER_BREAKER:
+	case SC_CLOSECONFINE:{
+		int type2 = ((type==SC_CLOSECONFINE)?SC_CLOSECONFINE2:SC_TINDER_BREAKER2);
 		//Lock char has released the hold on everyone...
-		if (tsc && tsc->data[SC_CLOSECONFINE2] && tsc->data[SC_CLOSECONFINE2]->val2 == src->id) {
-			tsc->data[SC_CLOSECONFINE2]->val2 = 0;
-			status_change_end(bl, SC_CLOSECONFINE2, INVALID_TIMER);
+		if (tsc && tsc->data[type2] && tsc->data[type2]->val2 == src->id) {
+			tsc->data[type2]->val2 = 0;
+			status_change_end(bl, type2, INVALID_TIMER);
 		}
 		break;
+	}
 	case SC_CURSEDCIRCLE_TARGET:
 		if( tsc && tsc->data[SC_CURSEDCIRCLE_TARGET] && tsc->data[SC_CURSEDCIRCLE_TARGET]->val2 == src->id ) {
 			clif_bladestop(bl, tsc->data[SC_CURSEDCIRCLE_TARGET]->val2, 0);

+ 25 - 13
src/map/unit.c

@@ -1022,7 +1022,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
 	struct map_session_data *sd = NULL;
 	struct block_list * target = NULL;
 	unsigned int tick = gettick();
-	int temp = 0, range;
+	int combo = 0, range;
 
 	nullpo_ret(src);
 	if(status_isdead(src))
@@ -1046,14 +1046,14 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
 
 		if( skill_get_inf(skill_id)&INF_SELF_SKILL && skill_get_nk(skill_id)&NK_NO_DAMAGE )// exploit fix
 			target_id = src->id;
-		temp = 1;
+		combo = 1;
 	} else
 	if ( target_id == src->id &&
 		skill_get_inf(skill_id)&INF_SELF_SKILL &&
 		skill_get_inf2(skill_id)&INF2_NO_TARGET_SELF )
 	{
 		target_id = ud->target; //Auto-select target. [Skotlex]
-		temp = 1;
+		combo = 1;
 	}
 
 	if (sd) {
@@ -1083,7 +1083,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
 		if (target)
 			target_id = target->id;
 	}
-	if (src->type==BL_HOM)
+	else if (src->type==BL_HOM)
 	switch(skill_id)
 	{ //Homun-auto-target skills.
 		case HLIF_HEAL:
@@ -1093,6 +1093,16 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
 			target = battle_get_master(src);
 			if (!target) return 0;
 			target_id = target->id;
+			break;
+		case MH_SONIC_CRAW:
+		case MH_TINDER_BREAKER: {
+			int skill_id2 = ((skill_id==MH_SONIC_CRAW)?MH_MIDNIGHT_FRENZY:MH_EQC);
+			if(sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == skill_id2){ //it,s a combo
+				target_id = sc->data[SC_COMBO]->val2;
+				combo = 1;
+			}
+			break;
+		}
 	}
 
 	if( !target ) // choose default target
@@ -1166,7 +1176,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
 
 	//Check range when not using skill on yourself or is a combo-skill during attack
 	//(these are supposed to always have the same range as your attack)
-	if( src->id != target_id && (!temp || ud->attacktimer == INVALID_TIMER) ) {
+	if( src->id != target_id && (!combo || ud->attacktimer == INVALID_TIMER) ) {
 		if( skill_get_state(ud->skill_id) == ST_MOVE_ENABLE ) {
 			if( !unit_can_reach_bl(src, target, range + 1, 1, NULL, NULL) )
 				return 0; // Walk-path check failed.
@@ -1178,7 +1188,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
 		}
 	}
 
-	if (!temp) //Stop attack on non-combo skills [Skotlex]
+	if (!combo) //Stop attack on non-combo skills [Skotlex]
 		unit_stop_attack(src);
 	else if(ud->attacktimer != INVALID_TIMER) //Elsewise, delay current attack sequence
 		ud->attackabletime = tick + status_get_adelay(src);
@@ -1186,12 +1196,12 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
 	ud->state.skillcastcancel = castcancel;
 
 	//temp: Used to signal force cast now.
-	temp = 0;
+	combo = 0;
 
 	switch(skill_id){
 	case ALL_RESURRECTION:
 		if(battle_check_undead(tstatus->race,tstatus->def_ele)) {
-			temp = 1;
+			combo = 1;
 		} else if (!status_isdead(target))
 			return 0; //Can't cast on non-dead characters.
 	break;
@@ -1205,17 +1215,17 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
 			sc->data[SC_COMBO]->val1 == CH_TIGERFIST ||
 			sc->data[SC_COMBO]->val1 == CH_CHAINCRUSH))
 			casttime = -1;
-		temp = 1;
+		combo = 1;
 	break;
 	case SR_GATEOFHELL:
 	case SR_TIGERCANNON:
 		if (sc && sc->data[SC_COMBO] &&
 		   sc->data[SC_COMBO]->val1 == SR_FALLENEMPIRE)
 			casttime = -1;
-		temp = 1;
+		combo = 1;
 	break;
 	case SA_SPELLBREAKER:
-		temp = 1;
+		combo = 1;
 	break;
 	case ST_CHASEWALK:
 		if (sc && sc->data[SC_CHASEWALK])
@@ -1243,7 +1253,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
 	case RA_WUGDASH:
 		if (sc && sc->data[SC_WUGDASH])
 			casttime = -1;
-        break;
+		break;
 	case EL_WIND_SLASH:
 	case EL_HURRICANE:
 	case EL_TYPOON_MIS:
@@ -1279,7 +1289,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
 		unit_stop_walking(src,1);// eventhough this is not how official works but this will do the trick. bugreport:6829
 	// in official this is triggered even if no cast time.
 	clif_skillcasting(src, src->id, target_id, 0,0, skill_id, skill_get_ele(skill_id, skill_lv), casttime);
-	if( casttime > 0 || temp )
+	if( casttime > 0 || combo )
 	{
 		if (sd && target->type == BL_MOB)
 		{
@@ -2034,6 +2044,8 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file,
 		status_change_end(bl, SC_MARIONETTE2, INVALID_TIMER);
 		status_change_end(bl, SC_CLOSECONFINE, INVALID_TIMER);
 		status_change_end(bl, SC_CLOSECONFINE2, INVALID_TIMER);
+		status_change_end(bl, SC_TINDER_BREAKER, INVALID_TIMER);
+		status_change_end(bl, SC_TINDER_BREAKER2, INVALID_TIMER);
 		status_change_end(bl, SC_HIDING, INVALID_TIMER);
 		// Ensure the bl is a PC; if so, we'll handle the removal of cloaking and cloaking exceed later
 		if ( bl->type != BL_PC )