瀏覽代碼

- Implemented Mercenary Scrolls (item_db)
- There are some little problems, like remaining time going to 0, but you can summon it, make it walk, follow you.
- Feel free to test it.

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

zephyrus 16 年之前
父節點
當前提交
5df63da5e7
共有 9 個文件被更改,包括 211 次插入92 次删除
  1. 31 31
      db/item_db.txt
  2. 5 5
      src/char_sql/int_homun.c
  3. 47 41
      src/map/battle.c
  4. 32 7
      src/map/clif.c
  5. 1 0
      src/map/clif.h
  6. 65 2
      src/map/mercenary.c
  7. 4 1
      src/map/mercenary.h
  8. 24 0
      src/map/script.c
  9. 2 5
      src/map/unit.c

+ 31 - 31
db/item_db.txt

@@ -3497,36 +3497,36 @@
 12150,Bullet_Case_Blood,Shell of Blood Casing,2,2,,250,,,,,0xFFFFFFFF,7,2,,,,,,{ getitem 13202,500; },{},{}
 12151,Bullet_Case_Silver,Silver Bullet Casing,2,2,,250,,,,,0xFFFFFFFF,7,2,,,,,,{ getitem 13201,500; },{},{}
 12152,Special_Present,Special Gift,2,0,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12153,Bow_Mercenary_Scroll1,Archer Mercenary Summon Scroll Level 1,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12154,Bow_Mercenary_Scroll2,Archer Mercenary Summon Scroll Level 2,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12155,Bow_Mercenary_Scroll3,Archer Mercenary Summon Scroll Level 3,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12156,Bow_Mercenary_Scroll4,Archer Mercenary Summon Scroll Level 4,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12157,Bow_Mercenary_Scroll5,Archer Mercenary Summon Scroll Level 5,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12158,Bow_Mercenary_Scroll6,Archer Mercenary Summon Scroll Level 6,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12159,Bow_Mercenary_Scroll7,Archer Mercenary Summon Scroll Level 7,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12160,Bow_Mercenary_Scroll8,Archer Mercenary Summon Scroll Level 8,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12161,Bow_Mercenary_Scroll9,Archer Mercenary Summon Scroll Level 9,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12162,Bow_Mercenary_Scroll10,Archer Mercenary Summon Scroll Level 10,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12163,SwordMercenary_Scroll1,Swordsman Mercenary Summon Scroll Level 1,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12164,SwordMercenary_Scroll2,Swordsman Mercenary Summon Scroll Level 2,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12165,SwordMercenary_Scroll3,Swordsman Mercenary Summon Scroll Level 3,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12166,SwordMercenary_Scroll4,Swordsman Mercenary Summon Scroll Level 4,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12167,SwordMercenary_Scroll5,Swordsman Mercenary Summon Scroll Level 5,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12168,SwordMercenary_Scroll6,Swordsman Mercenary Summon Scroll Level 6,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12169,SwordMercenary_Scroll7,Swordsman Mercenary Summon Scroll Level 7,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12170,SwordMercenary_Scroll8,Swordsman Mercenary Summon Scroll Level 8,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12171,SwordMercenary_Scroll9,Swordsman Mercenary Summon Scroll Level 9,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12172,SwordMercenary_Scroll10,Swordsman Mercenary Summon Scroll Level 10,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12173,SpearMercenary_Scroll1,Spearman Mercenary Summon Scroll Level 1,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12174,SpearMercenary_Scroll2,Spearman Mercenary Summon Scroll Level 2,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12175,SpearMercenary_Scroll3,Spearman Mercenary Summon Scroll Level 3,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12176,SpearMercenary_Scroll4,Spearman Mercenary Summon Scroll Level 4,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12177,SpearMercenary_Scroll5,Spearman Mercenary Summon Scroll Level 5,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12178,SpearMercenary_Scroll6,Spearman Mercenary Summon Scroll Level 6,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12179,SpearMercenary_Scroll7,Spearman Mercenary Summon Scroll Level 7,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12180,SpearMercenary_Scroll8,Spearman Mercenary Summon Scroll Level 8,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12181,SpearMercenary_Scroll9,Spearman Mercenary Summon Scroll Level 9,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
-12182,SpearMercenary_Scroll10,Spearman Mercenary Summon Scroll Level 10,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
+12153,Bow_Mercenary_Scroll1,Archer Mercenary Summon Scroll Level 1,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6017,1800000; },{},{}
+12154,Bow_Mercenary_Scroll2,Archer Mercenary Summon Scroll Level 2,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6018,1800000; },{},{}
+12155,Bow_Mercenary_Scroll3,Archer Mercenary Summon Scroll Level 3,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6019,1800000;},{},{}
+12156,Bow_Mercenary_Scroll4,Archer Mercenary Summon Scroll Level 4,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6020,1800000;},{},{}
+12157,Bow_Mercenary_Scroll5,Archer Mercenary Summon Scroll Level 5,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6021,1800000;},{},{}
+12158,Bow_Mercenary_Scroll6,Archer Mercenary Summon Scroll Level 6,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6022,1800000;},{},{}
+12159,Bow_Mercenary_Scroll7,Archer Mercenary Summon Scroll Level 7,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6023,1800000;},{},{}
+12160,Bow_Mercenary_Scroll8,Archer Mercenary Summon Scroll Level 8,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6024,1800000;},{},{}
+12161,Bow_Mercenary_Scroll9,Archer Mercenary Summon Scroll Level 9,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6025,1800000;},{},{}
+12162,Bow_Mercenary_Scroll10,Archer Mercenary Summon Scroll Level 10,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6026,1800000;},{},{}
+12163,SwordMercenary_Scroll1,Swordsman Mercenary Summon Scroll Level 1,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6027,1800000;},{},{}
+12164,SwordMercenary_Scroll2,Swordsman Mercenary Summon Scroll Level 2,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6028,1800000;},{},{}
+12165,SwordMercenary_Scroll3,Swordsman Mercenary Summon Scroll Level 3,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6029,1800000;},{},{}
+12166,SwordMercenary_Scroll4,Swordsman Mercenary Summon Scroll Level 4,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6030,1800000;},{},{}
+12167,SwordMercenary_Scroll5,Swordsman Mercenary Summon Scroll Level 5,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6031,1800000;},{},{}
+12168,SwordMercenary_Scroll6,Swordsman Mercenary Summon Scroll Level 6,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6032,1800000;},{},{}
+12169,SwordMercenary_Scroll7,Swordsman Mercenary Summon Scroll Level 7,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6033,1800000;},{},{}
+12170,SwordMercenary_Scroll8,Swordsman Mercenary Summon Scroll Level 8,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6034,1800000;},{},{}
+12171,SwordMercenary_Scroll9,Swordsman Mercenary Summon Scroll Level 9,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6035,1800000;},{},{}
+12172,SwordMercenary_Scroll10,Swordsman Mercenary Summon Scroll Level 10,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6036,1800000;},{},{}
+12173,SpearMercenary_Scroll1,Spearman Mercenary Summon Scroll Level 1,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6037,1800000;},{},{}
+12174,SpearMercenary_Scroll2,Spearman Mercenary Summon Scroll Level 2,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6038,1800000;},{},{}
+12175,SpearMercenary_Scroll3,Spearman Mercenary Summon Scroll Level 3,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6039,1800000;},{},{}
+12176,SpearMercenary_Scroll4,Spearman Mercenary Summon Scroll Level 4,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6040,1800000;},{},{}
+12177,SpearMercenary_Scroll5,Spearman Mercenary Summon Scroll Level 5,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6041,1800000;},{},{}
+12178,SpearMercenary_Scroll6,Spearman Mercenary Summon Scroll Level 6,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6042,1800000;},{},{}
+12179,SpearMercenary_Scroll7,Spearman Mercenary Summon Scroll Level 7,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6043,1800000;},{},{}
+12180,SpearMercenary_Scroll8,Spearman Mercenary Summon Scroll Level 8,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6044,1800000;},{},{}
+12181,SpearMercenary_Scroll9,Spearman Mercenary Summon Scroll Level 9,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6045,1800000;},{},{}
+12182,SpearMercenary_Scroll10,Spearman Mercenary Summon Scroll Level 10,2,2,,100,,,,,0xFFFFFFFF,7,2,,,,,,{ createmercenary 6046,1800000;},{},{}
 12183,Holy_Arrow_Quiver,Holy Arrow Quiver,2,2,,250,,,,,0xFFFFFFFF,7,2,,,,,,{ getitem 1772,500; },{},{}
 12184,Mercenary_Red_Potion,Mercenary's Red Potion,2,500,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
 12185,Mercenary_Blue_Potion,Mercenary's Blue Potion,2,1000,,100,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
@@ -3651,7 +3651,7 @@
 12313,Guardian_Angel,Guardian Angel,2,0,,0,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
 //New Novice Ground Items
 12323,Novice_Wing_Of_Fly,Novice Fly Wing,2,1,,50,,,,,0xFFFFFFFF,7,2,,,,,,{ warp "Random",0,0; },{},{}
-12324,Novice_Wing_Of_Butterfly,NOvice Butterfly Wing,2,1,,50,,,,,0xFFFFFFFF,7,2,,,,,,{ warp "SavePoint",0,0; },{},{}
+12324,Novice_Wing_Of_Butterfly,Novice Butterfly Wing,2,1,,50,,,,,0xFFFFFFFF,7,2,,,,,,{ warp "SavePoint",0,0; },{},{}
 12325,Novice_Spectacles,Novice Magnifier,11,1,,50,,,,,0xFFFFFFFF,7,2,,,,,,{ itemskill "MC_IDENTIFY",1; },{},{}
 //
 12701,Old_Blue_Box_F,Old Blue Box,2,,,200,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}

+ 5 - 5
src/char_sql/int_homun.c

@@ -342,11 +342,11 @@ bool mapif_mercenary_load(int merc_id, int char_id, struct s_mercenary *merc)
 		return false;
 	}
 
-	Sql_GetData(sql_handle,  1, &data, NULL); merc->class_ = atoi(data);
-	Sql_GetData(sql_handle,  2, &data, NULL); merc->hp = atoi(data);
-	Sql_GetData(sql_handle,  3, &data, NULL); merc->sp = atoi(data);
-	Sql_GetData(sql_handle,  4, &data, NULL); merc->kill_count = atoi(data);
-	Sql_GetData(sql_handle,  5, &data, NULL); merc->remain_life_time = atoi(data);
+	Sql_GetData(sql_handle,  0, &data, NULL); merc->class_ = atoi(data);
+	Sql_GetData(sql_handle,  1, &data, NULL); merc->hp = atoi(data);
+	Sql_GetData(sql_handle,  2, &data, NULL); merc->sp = atoi(data);
+	Sql_GetData(sql_handle,  3, &data, NULL); merc->kill_count = atoi(data);
+	Sql_GetData(sql_handle,  4, &data, NULL); merc->remain_life_time = atoi(data);
 	Sql_FreeResult(sql_handle);
 	if( save_log )
 		ShowInfo("Mercenary loaded (%d - %d).\n", merc->mercenary_id, merc->char_id);

+ 47 - 41
src/map/battle.c

@@ -16,6 +16,7 @@
 #include "status.h"
 #include "skill.h"
 #include "homunculus.h"
+#include "mercenary.h"
 #include "mob.h"
 #include "itemdb.h"
 #include "clif.h"
@@ -3027,6 +3028,10 @@ struct block_list* battle_get_master(struct block_list *src)
 				if (((TBL_HOM*)src)->master)
 					src = (struct block_list*)((TBL_HOM*)src)->master;
 				break;
+			case BL_MER:
+				if (((TBL_MER*)src)->master)
+					src = (struct block_list*)((TBL_MER*)src)->master;
+				break;
 			case BL_SKILL:
 				if (((TBL_SKILL*)src)->group && ((TBL_SKILL*)src)->group->src_id)
 					src = map_id2bl(((TBL_SKILL*)src)->group->src_id);
@@ -3050,7 +3055,7 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
 {
 	int m,state = 0; //Initial state none
 	int strip_enemy = 1; //Flag which marks whether to remove the BCT_ENEMY status if it's also friend/ally.
-	struct block_list *s_bl= src, *t_bl= target;
+	struct block_list *s_bl = src, *t_bl = target;
 
 	nullpo_retr(0, src);
 	nullpo_retr(0, target);
@@ -3059,13 +3064,13 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
 
 	//t_bl/s_bl hold the 'master' of the attack, while src/target are the actual
 	//objects involved.
-	if ((t_bl = battle_get_master(target)) == NULL)
+	if( (t_bl = battle_get_master(target)) == NULL )
 		t_bl = target;
 
-	if ((s_bl = battle_get_master(src)) == NULL)
+	if( (s_bl = battle_get_master(src)) == NULL )
 		s_bl = src;
 
-	switch (target->type)
+	switch( target->type )
 	{	//Checks on actual target
 		case BL_PC:
 			if (((TBL_PC*)target)->invincible_timer != -1 || pc_isinvisible((TBL_PC*)target))
@@ -3109,6 +3114,7 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
 		}
 			break;
 		//Valid targets with no special checks here.
+		case BL_MER:
 		case BL_HOM:
 			break;
 		//All else not specified is an invalid target.
@@ -3116,34 +3122,35 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
 			return 0;
 	}
 
-	switch (t_bl->type)
+	switch( t_bl->type )
 	{	//Checks on target master
 		case BL_PC:
 		{
-			TBL_PC *sd = (TBL_PC*)t_bl;
-			if (sd->status.karma && t_bl != s_bl && s_bl->type == BL_PC &&
-				((TBL_PC*)s_bl)->status.karma)
-				state |= BCT_ENEMY; //Characters with bad karma may fight amongst them.
-			if (sd->state.monster_ignore && t_bl != s_bl && flag&BCT_ENEMY)
-				return 0; //Global inmunity to attacks.
-			if (sd->state.killable && t_bl != s_bl)
-			{
-				state |= BCT_ENEMY; //Universal Victim
+			struct map_session_data *sd;
+			if( t_bl == s_bl ) break;
+			sd = BL_CAST(BL_PC, t_bl);
+
+			if( sd->state.monster_ignore && flag&BCT_ENEMY )
+				return 0; // Global inminuty only to Attacks
+			if( sd->status.karma && s_bl->type == BL_PC && ((TBL_PC*)s_bl)->status.karma )
+				state |= BCT_ENEMY; // Characters with bad karma may fight amongst them
+			if( sd->state.killable ) {
+				state |= BCT_ENEMY; // Everything can kill it
 				strip_enemy = 0;
 			}
 			break;
 		}
 		case BL_MOB:
 		{
-			TBL_MOB *md = (TBL_MOB*)t_bl;
+			struct mob_data *md = BL_CAST(BL_MOB, t_bl);
 
-			if (!(agit_flag && map[m].flag.gvg_castle) && md->guardian_data && md->guardian_data->guild_id)
-				return 0; //Disable guardians/emperiums owned by Guilds on non-woe times.
+			if( !(agit_flag && map[m].flag.gvg_castle) && md->guardian_data && md->guardian_data->guild_id )
+				return 0; // Disable guardians/emperiums owned by Guilds on non-woe times.
 			break;
 		}
 	}
 
-	switch(src->type)
+	switch( src->type )
   	{	//Checks on actual src type
 		case BL_PET:
 			if (t_bl->type != BL_MOB && flag&BCT_ENEMY)
@@ -3170,26 +3177,24 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
 		}
 	}
 
-	switch (s_bl->type)
+	switch( s_bl->type )
 	{	//Checks on source master
 		case BL_PC:
 		{
-			TBL_PC *sd = (TBL_PC*) s_bl;
+			struct map_session_data *sd = BL_CAST(BL_PC, s_bl);
 			if( s_bl != t_bl )
 			{
 				if( sd->state.killer )
 				{
-					state |= BCT_ENEMY; //Is on a killing rampage :O
+					state |= BCT_ENEMY; // Can kill anything
 					strip_enemy = 0;
 				}
 				else if( sd->duel_group && !((!battle_config.duel_allow_pvp && map[m].flag.pvp) || (!battle_config.duel_allow_gvg && map_flag_gvg(m))) )
 		  		{
-					if (t_bl->type == BL_PC &&
-						(sd->duel_group == ((TBL_PC*)t_bl)->duel_group))
-						//Duel targets can ONLY be your enemy, nothing else.
-						return (BCT_ENEMY&flag)?1:-1;
-					else // You can't target anything out of your duel
-						return 0;
+					if( t_bl->type == BL_PC && (sd->duel_group == ((TBL_PC*)t_bl)->duel_group) )
+						return (BCT_ENEMY&flag)?1:-1; // Duel targets can ONLY be your enemy, nothing else.
+					else
+						return 0; // You can't target anything out of your duel
 				}
 			}
 			if (map_flag_gvg(m) && !sd->status.guild_id &&
@@ -3201,20 +3206,21 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
 		}
 		case BL_MOB:
 		{
-			TBL_MOB*md = (TBL_MOB*)s_bl;
-			if (!(agit_flag && map[m].flag.gvg_castle) && md->guardian_data && md->guardian_data->guild_id)
-				return 0; //Disable guardians/emperium owned by Guilds on non-woe times.
-
-			{	//Smart enemy criteria.
-				if (!md->special_state.ai) { //Normal mobs.
-					if (t_bl->type == BL_MOB && !((TBL_MOB*)t_bl)->special_state.ai)
-						state |= BCT_PARTY; //Normal mobs with no ai are friends.
-					else
-						state |= BCT_ENEMY; //However, all else are enemies.
-				} else {
-					if (t_bl->type == BL_MOB && !((TBL_MOB*)t_bl)->special_state.ai)
-						state |= BCT_ENEMY; //Natural enemy for AI mobs are normal mobs.
-				}
+			struct mob_data *md = BL_CAST(BL_MOB, s_bl);
+			if( !(agit_flag && map[m].flag.gvg_castle) && md->guardian_data && md->guardian_data->guild_id )
+				return 0; // Disable guardians/emperium owned by Guilds on non-woe times.
+
+			if( !md->special_state.ai )
+			{ //Normal mobs.
+				if( t_bl->type == BL_MOB && !((TBL_MOB*)t_bl)->special_state.ai )
+					state |= BCT_PARTY; //Normal mobs with no ai are friends.
+				else
+					state |= BCT_ENEMY; //However, all else are enemies.
+			}
+			else
+			{
+				if( t_bl->type == BL_MOB && !((TBL_MOB*)t_bl)->special_state.ai )
+					state |= BCT_ENEMY; //Natural enemy for AI mobs are normal mobs.
 			}
 			break;
 		}

+ 32 - 7
src/map/clif.c

@@ -11306,11 +11306,21 @@ void clif_parse_HomMoveTo(int fd, struct map_session_data *sd)
 }
 
 void clif_parse_HomAttack(int fd,struct map_session_data *sd)
-{	//[orn]
-	if(!merc_is_hom_active(sd->hd))
-		return;
-	
-	unit_attack(&sd->hd->bl,RFIFOL(fd,6),0) ;
+{
+	struct block_list *bl = NULL;
+	int id = RFIFOL(fd,2),
+		target_id = RFIFOL(fd,6),
+		action_type = RFIFOB(fd,10);
+
+	if( merc_is_hom_active(sd->hd) && sd->hd->bl.id == id )
+		bl = &sd->hd->bl;
+	else if( sd->md && sd->md->bl.id == id )
+		bl = &sd->md->bl;
+	else return;
+
+	unit_stop_walking(bl, 1);
+	unit_stop_attack(bl);
+	unit_attack(bl, target_id, action_type != 0);
 }
 
 void clif_parse_HomMenu(int fd, struct map_session_data *sd)
@@ -12386,10 +12396,10 @@ void clif_mercenary_info(struct map_session_data *sd)
 	WFIFOL(fd,52) = status->max_hp;
 	WFIFOL(fd,56) = status->sp;
 	WFIFOL(fd,60) = status->max_sp;
-	WFIFOL(fd,64) = 0; // Contract End
+	WFIFOL(fd,64) = (int)time(NULL) + (md->mercenary.remain_life_time / 1000);
 	WFIFOW(fd,68) = 0; // Loyalty
 	WFIFOL(fd,70) = 0; // Summon Count
-	WFIFOL(fd,74) = 0; // Kill Counter
+	WFIFOL(fd,74) = md->mercenary.kill_count;
 	WFIFOW(fd,78) = 0;
 	WFIFOSET(fd,80);
 }
@@ -12461,6 +12471,21 @@ void clif_parse_mercenary_action(int fd, struct map_session_data* sd)
 		return;
 }
 
+/*------------------------------------------
+ * Mercenary Message
+ * 0 = Mercenary soldier's duty hour is over.
+ * 1 = Your mercenary soldier has been killed.
+ * 2 = Your mercenary soldier has been fired.
+ * 3 = Your mercenary soldier has ran away.
+ *------------------------------------------*/
+void clif_mercenary_message(int fd, int message)
+{
+	WFIFOHEAD(fd,4);
+	WFIFOW(fd,0) = 0x0291;
+	WFIFOW(fd,2) = 1266 + message;
+	WFIFOSET(fd,4);
+}
+
 /*==========================================
  * ƒpƒPƒbƒgƒfƒoƒbƒO
  *------------------------------------------*/

+ 1 - 0
src/map/clif.h

@@ -442,5 +442,6 @@ void clif_Adopt_reply(struct map_session_data *sd, int type);
 // MERCENARIES
 void clif_mercenary_info(struct map_session_data *sd);
 void clif_mercenary_skillblock(struct map_session_data *sd);
+void clif_mercenary_message(int fd, int message);
 
 #endif /* _CLIF_H_ */

+ 65 - 2
src/map/mercenary.c

@@ -86,13 +86,75 @@ int merc_create(struct map_session_data *sd, int class_, unsigned int lifetime)
 
 int mercenary_save(struct mercenary_data *md)
 {
+	const struct TimerData * td = get_timer(md->contract_timer);
+
 	md->mercenary.hp = md->battle_status.hp;
 	md->mercenary.sp = md->battle_status.sp;
-	intif_mercenary_save(&md->mercenary);
+	if( td != NULL )
+		md->mercenary.remain_life_time = DIFF_TICK(td->tick, gettick());
+	else
+	{
+		md->mercenary.remain_life_time = 0;
+		ShowWarning("mercenary_save : mercenary without timer (tid %d)\n", md->contract_timer);
+	}
 
+	intif_mercenary_save(&md->mercenary);
 	return 1;
 }
 
+static int merc_contract_end(int tid, unsigned int tick, int id, intptr data)
+{
+	struct map_session_data *sd;
+	struct mercenary_data *md;
+
+	if( (sd = map_id2sd(id)) == NULL )
+		return 1;
+	if( (md = sd->md) == NULL )
+		return 1;
+
+	if( md->contract_timer != tid )
+	{
+		ShowError("merc_contract_end %d != %d.\n", md->contract_timer, tid);
+		return 0;
+	}
+
+	md->contract_timer = INVALID_TIMER;
+	merc_delete(md, 0); // Mercenary soldier's duty hour is over.
+
+	return 0;
+}
+
+int merc_delete(struct mercenary_data *md, int reply)
+{
+	struct map_session_data *sd = md->master;
+	md->mercenary.remain_life_time = 0;
+
+	if( md->contract_timer != INVALID_TIMER )
+		delete_timer(md->contract_timer, merc_contract_end);
+
+	if( !sd )
+		return unit_free(&md->bl, 1);
+	clif_mercenary_message(sd->fd, reply);
+	
+	return unit_remove_map(&md->bl, 1);
+}
+
+void merc_contract_stop(struct mercenary_data *md)
+{
+	nullpo_retv(md);
+	if( md->contract_timer != INVALID_TIMER )
+		delete_timer(md->contract_timer, merc_contract_end);
+	md->contract_timer = INVALID_TIMER;
+}
+
+void merc_contract_init(struct mercenary_data *md)
+{
+	if( md->contract_timer == INVALID_TIMER )
+		md->contract_timer = add_timer(gettick() + md->mercenary.remain_life_time, merc_contract_end, md->master->bl.id, 0);
+
+	md->regen.state.block = 0;
+}
+
 int merc_data_received(struct s_mercenary *merc, bool flag)
 {
 	struct map_session_data *sd;
@@ -134,6 +196,7 @@ int merc_data_received(struct s_mercenary *merc, bool flag)
 
 		map_addiddb(&md->bl);
 		status_calc_mercenary(md,1);
+		md->contract_timer = INVALID_TIMER;
 	}
 	else
 		memcpy(&sd->md->mercenary, merc, sizeof(struct s_mercenary));
@@ -145,7 +208,7 @@ int merc_data_received(struct s_mercenary *merc, bool flag)
 		clif_spawn(&md->bl);
 		clif_mercenary_info(sd);
 		clif_mercenary_skillblock(sd);
-		// init timers
+		merc_contract_init(md);
 	}
 
 	return 1;

+ 4 - 1
src/map/mercenary.h

@@ -32,7 +32,8 @@ struct mercenary_data {
 	struct s_mercenary_db *db;
 	struct s_mercenary mercenary;
 
-	struct map_session_data *master; // Master of mercenary
+	struct map_session_data *master;
+	int contract_timer;
 };
 
 bool merc_class(int class_);
@@ -41,5 +42,7 @@ int merc_create(struct map_session_data *sd, int class_, unsigned int lifetime);
 int merc_data_received(struct s_mercenary *merc, bool flag);
 int mercenary_save(struct mercenary_data *md);
 int do_init_mercenary(void);
+int merc_delete(struct mercenary_data *md, int reply);
+void merc_contract_stop(struct mercenary_data *md);
 
 #endif /* _MERCENARY_H_ */

+ 24 - 0
src/map/script.c

@@ -28,6 +28,7 @@
 #include "pet.h"
 #include "mapreg.h"
 #include "homunculus.h"
+#include "mercenary.h"
 #include "intif.h"
 #include "skill.h"
 #include "status.h"
@@ -12792,6 +12793,28 @@ BUILDIN_FUNC(setcell)
 	return 0;
 }
 
+/*==========================================
+ * Mercenary Commands
+ *------------------------------------------*/
+BUILDIN_FUNC(createmercenary)
+{
+	struct map_session_data *sd;
+	int class_, contract_time;
+
+	if( (sd = script_rid2sd(st)) == NULL || sd->md || sd->status.mer_id != 0 )
+		return 0;
+	
+	class_ = script_getnum(st,2);
+
+	if( !merc_class(class_) )
+		return 0;
+
+	contract_time = script_getnum(st,3);
+	merc_create(sd, class_, contract_time);
+
+	return 0;
+}
+
 /******************
 Questlog script commands
 *******************/
@@ -13222,5 +13245,6 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF(hasquest, "i"),
 	BUILDIN_DEF(setwall,"siiiiis"),
 	BUILDIN_DEF(delwall,"s"),
+	BUILDIN_DEF(createmercenary,"ii"),
 	{NULL,NULL,NULL},
 };

+ 2 - 5
src/map/unit.c

@@ -1258,9 +1258,8 @@ int unit_attack(struct block_list *src,int target_id,int continuous)
 		}
 	}
 
-	if(battle_check_target(src,target,BCT_ENEMY)<=0 ||
-		!status_check_skilluse(src, target, 0, 0)
-	) {
+	if( battle_check_target(src,target,BCT_ENEMY) <= 0 || !status_check_skilluse(src, target, 0, 0) )
+	{
 		unit_unattackable(src);
 		return 1;
 	}
@@ -2065,7 +2064,6 @@ int unit_free(struct block_list *bl, int clrtype)
 		{
 			struct homun_data *hd = (TBL_HOM*)bl;
 			struct map_session_data *sd = hd->master;
-			// Desactive timers
 			merc_hom_hungry_timer_delete(hd);
 			if( clrtype >= 0 )
 			{
@@ -2086,7 +2084,6 @@ int unit_free(struct block_list *bl, int clrtype)
 		{
 			struct mercenary_data *md = (TBL_MER*)bl;
 			struct map_session_data *sd = md->master;
-			/* Stop Mercenary Timer */
 			if( clrtype >= 0 )
 			{
 				if( md->mercenary.remain_life_time > 0 )