Prechádzať zdrojové kódy

Implemented Basic Unit Controller Script Commands
* Added getunitname, setunitname, getunitdata, and setunitdata script commands.
- getunitdata and setunitdata are open to suggestions for more values.
* Split unitstop to unitstopattack and unitstopwalk.
* Expanded unitskilluseid and unitskillusepos to be able to adjust casttime of skill.
* Added two new monster modes: MD_NORANDOM_WALK and MD_NOCAST_SKILL.
* The areamonster script command now saves spawned monster game IDs in an array called $@mobid[].
* Adjusted other monster spawning script commands to return game ID.
* Updated documentation to reflect changes.

aleos89 10 rokov pred
rodič
commit
2cee5b6ff1
11 zmenil súbory, kde vykonal 840 pridanie a 121 odobranie
  1. 2 0
      doc/mob_db_mode_list.txt
  2. 147 30
      doc/script_commands.txt
  3. 8 1
      src/map/battle.c
  4. 5 0
      src/map/map.c
  5. 2 0
      src/map/map.h
  6. 5 1
      src/map/mob.c
  7. 1 2
      src/map/mob.h
  8. 665 87
      src/map/script.c
  9. 2 0
      src/map/status.c
  10. 2 0
      src/map/status.h
  11. 1 0
      src/map/unit.h

+ 2 - 0
doc/mob_db_mode_list.txt

@@ -33,6 +33,8 @@ MD_IGNORERANGED       | 0x040000 |   262144
 MD_MVP                | 0x080000 |   524288
 MD_IGNOREMISC         | 0x100000 |  1048576
 MD_KNOCKBACK_IMMUNE   | 0x200000 |  2097152
+MD_NORANDOM_WALK      | 0x400000 |  4194304
+MD_NOCAST_SKILL       | 0x800000 |  8388608
 
 
 Explanation for modes

+ 147 - 30
doc/script_commands.txt

@@ -2334,11 +2334,11 @@ character name is specified, of that player.
 
 Type is the kind of associated ID number required:
 
- 0 - Character ID number.
- 1 - Party ID number.
- 2 - Guild ID number.
- 3 - Account ID number.
- 4 - Battle ground ID
+ 0 - Character ID
+ 1 - Party ID
+ 2 - Guild ID
+ 3 - Account ID
+ 4 - Battle Ground ID
 
 For most purposes other than printing it, a number is better to have than a name 
 (people do horrifying things to their character names).
@@ -2363,7 +2363,7 @@ Retrieves IDs of the currently invoked NPC. If a unique npc name is
 given, IDs of that NPC are retrieved instead. Type specifies what ID
 to retrieve and can be one of the following:
 
-    0 - Unit ID (GID)
+    0 - NPC Game ID
 
 If an invalid type is given or the NPC does not exist, 0 is returned.
 
@@ -5588,6 +5588,9 @@ The 'areamonster' command works much like the 'monster' command and is not
 significantly different, but spawns the monsters within a square defined by 
 x1/y1-x2/y2.
 
+Returned value is an array with the game ID of the spawned monster(s) depending
+on the amount spawned. Array is stored in $@mobid[].
+
 Simple monster killing script:
 
         <Normal NPC object definition. Let's assume you called him NPCNAME.>
@@ -5757,6 +5760,8 @@ and it is not possible to create a spawn that lasts forever.
 If an event label is given, upon the monster being killed, the event label will
 run as if by 'donpcevent'.
 
+Returned value is the game ID of the spawned monster.
+
 // Will summon a dead branch-style monster to fight for the character.
 summon "--ja--",-1;
 
@@ -6999,9 +7004,15 @@ OnTouch:
 
 ---------------------------------------
 
-*unitstop <GID>;
+*unitstopattack <GID>;
+
+This command will make a <GID> stop attacking.
+
+---------------------------------------
+
+*unitstopwalk <GID>;
 
-This command will make a <GID> stop attacking and moving.
+This command will make a <GID> stop moving.
 
 ---------------------------------------
 
@@ -7018,16 +7029,123 @@ For a full list of emotion numbers, see 'db/const.txt' under 'e_'.
 
 ---------------------------------------
 
-*unitskilluseid <GID>,<skill id>,<skill lvl>{,<target id>};
-*unitskilluseid <GID>,"<skill name>",<skill lvl>{,<target id>};
-*unitskillusepos <GID>,<skill id>,<skill lvl>,<x>,<y>;
-*unitskillusepos <GID>,"<skill name>",<skill lvl>,<x>,<y>;
+*unitskilluseid <GID>,<skill id>,<skill lvl>{,<target id>,<casttime>};
+*unitskilluseid <GID>,"<skill name>",<skill lvl>{,<target id>,<casttime>};
+*unitskillusepos <GID>,<skill id>,<skill lvl>,<x>,<y>{,<casttime>};
+*unitskillusepos <GID>,"<skill name>",<skill lvl>,<x>,<y>{,<casttime>};
 
 This is the replacement of the older commands, these use the same values for
 GID as the other unit* commands (See 'GID').
 
 Skill ID is the ID of the skill, skill level is the level of the skill.
-For the position, the x and y are given in the unitSkillUsePos.
+Cast time is the amount of seconds to add or remove from the skill. Use a positive value to
+add and negative value to subtract. Using 0 or no value will use the default skill cast time.
+For the position, the x and y are given in the UnitSkillUsePos.
+
+---------------------------------------
+
+*getunitname <GID>;
+
+Gets the name of the given unit. Supported types are monster, homunculus, pet, and NPC.
+Mercenary and Elemental don't support custom names.
+
+Returns "Unknown" if unit is not found.
+
+---------------------------------------
+
+*setunitname <GID>,"<new name>";
+
+Changes the name of the given unit to the new name given. Supported types are monster,
+homunculus, and pet. To change an NPC's name, see 'setnpcdisplay'. Mercenary and
+Elemental don't support custom names.
+
+Changing a homunculus or pet name will be permanent.
+
+---------------------------------------
+
+*getunitdata <GID>,<arrayname>;
+*setsetdata <GID>,<parameter>,<new value>;
+
+This is used to get and set special data related to the unit.
+With getunitdata, the array given will be filled with the current data. In setunitdata
+the indexes in the array would be used to set that data on the unit. 
+
+Parameters (indexes) for monsters are:
+
+	0 - size (big, small, normal)	7  - y							14 - hair style			21 - weapon			28 - DEX
+	1 - level						8  - speed						15 - hair color			22 - shield (again)	29 - LUK
+	2 - HP							9  - mode 						16 - head gear bottom	23 - looking dir	30 - for slave to copy master's mode
+	3 - max HP						10 - special AI state	 		17 - head gear middle	24 - STR			31 - immune from attacks state
+	4 - master AID					11 - SC option					18 - head gear top		25 - AGI
+	5 - map index					12 - sex						19 - cloth color		26 - VIT
+	6 - x							13 - class (Monster ID, Job ID)	20 - shield				27 - INT
+
+Parameter (indexes) for homunculi are:
+
+	0 - size (big, small, normal)	7  - map index		14 - canmove_tick	21 - immune from attacks state
+	1 - level						8  - x				15 - STR
+	2 - HP							9  - y				16 - AGI
+	3 - max HP						10 - hunger			17 - VIT
+	4 - SP							11 - intimacy		18 - INT
+	5 - max SP						12 - speed			19 - DEX
+	6 - master Character ID			13 - looking dir	20 - LUK
+
+Parameter (indexes) for pets are:
+
+	0 - size (big, small, normal)	7  - y				14 - AGI
+	1 - level						8  - hunger			15 - VIT
+	2 - HP							9  - intimacy		16 - INT
+	3 - max HP						10 - speed			17 - DEX
+	4 - master AID					11 - looking dir	18 - LUK
+	5 - map index					12 - canmove_tick	19 - name
+	6 - x							13 - STR
+
+Parameter (indexes) for mercenaries are:
+
+	0 - size (big, small, normal)	7  - y				14 - AGI
+	1 - level						8  - kill count		15 - VIT
+	2 - HP							9  - life time		16 - INT
+	3 - max HP						10 - speed			17 - DEX
+	4 - master Character ID			11 - looking dir	18 - LUK
+	5 - map index					12 - canmove_tick	19 - immune from attacks state
+	6 - x							13 - STR
+
+Parameter (indexes) for elementals are:
+
+	0 - size (big, small, normal)	7  - map index		14 - canmove_tick	21 - immune from attacks state
+	1 - level						8  - x				15 - STR
+	2 - HP							9  - y				16 - AGI
+	3 - max HP						10 - life time		17 - VIT
+	4 - SP							11 - mode			18 - INT
+	5 - max SP						12 - speed			19 - DEX
+	6 - master Character ID			13 - looking dir	20 - LUK
+
+Parameter (indexes) for NPCs are:
+
+	0 - display		7  - looking dir
+	1 - level		8  - STR
+	2 - HP			9  - AGI
+	3 - max HP		10 - VIT
+	4 - map index	11 - INT
+	5 - x			12 - DEX
+	6 - y			13 - LUK
+
+*Note: For mode, see doc/mob_db_mode_list.txt
+
+Example:
+	// Spawn some Porings and save the Game ID.
+	// - Keep in mind, when the 'monster' script command is used,
+	// - all the spawned monster GID's are stored in an array
+	// - called $@mobid[].
+	monster "prontera",123,42,"Poring",1002,10;
+	.GID = $@mobid[9]; // Store and modify the 10th Poring spawned to make him stronger!
+
+	// Save the strong Poring's mob data in the @por_arr[] variable. (@por_arr[1] being level, @por_arr[13] being class, etc.)
+	getunitdata .GID,@por_arr;
+
+	// Set the max HP of the Poring to 1000 and update the current HP to match.
+	setunitdata .GID,3,1000;
+	setunitdata .GID,2,1000;
 
 ---------------------------------------
 \\
@@ -8044,9 +8162,7 @@ server and the egg will disappear when anyone tries to hatch it.
 This function will return pet information for the pet the invoking character 
 currently has active. Valid types are:
 
- 0 - Unique pet ID number as stored by the char server and distinguishing it 
-     from all other pets the characters actually have. This value is currently 
-     useless, at most you can use it to tell pets apart reliably.
+ 0 - Pet Game ID
  1 - Pet class number as per 'db/pet_db.txt' - will tell you what kind of a pet it 
      is.
  2 - Pet name. Will return "null" if there's no pet. 
@@ -8055,7 +8171,6 @@ currently has active. Valid types are:
  5 - Pet rename flag. 0 means this pet has not been named yet.
  6 - Pet level
 
-
 ---------------------------------------
 
 =============================
@@ -8217,12 +8332,12 @@ invoking character, regardless of its vaporize state. It returns zero or
 "null" if the player does not own a Homunculus.
 
 Valid types are:
- 0 - Homunculus unique ID
+ 0 - Homunculus Game ID
  1 - Homunculus Class
- 2 - Name
- 3 - Friendly level (intimacy score). 100000 is full loyalty.
- 4 - Hungry level. 100 is completely full.
- 5 - Rename flag. 0 means this homunculus has not been named yet.
+ 2 - Homunculus Name
+ 3 - Homunculus friendly level (intimacy score). 100000 is full loyalty.
+ 4 - Homunculus hungry level. 100 is completely full.
+ 5 - Homunculus rename flag. 0 means this homunculus has not been named yet.
  6 - Homunculus level
 
 ---------------------------------------
@@ -8246,6 +8361,8 @@ list of all available classes, see 'db/mercenary_db.txt'.
 
 This command is typically used in item scripts of mercenary scrolls.
 
+Returned value is the game ID of the spawned mercenary.
+
 ---------------------------------------
 
 *mercenary_heal <hp>,<sp>;
@@ -8293,14 +8410,14 @@ character. If char id is given, the information of that character is
 retrieved instead. Type specifies what information to retrieve and
 can be one of the following:
 
-    0 - Database ID
-    1 - Class
-    2 - Name
-    3 - Faith value for this mercenary's guild, if any
-    4 - Calls value for this mercenary's guild, if any
-    5 - Kill count
-    6 - Remaining life time in msec
-    7 - Level
+    0 - Mercenary Game ID
+    1 - Mercenary Class
+    2 - Mercenary Name
+    3 - Mercenary faith value for this mercenary's guild, if any
+    4 - Mercenary calls value for this mercenary's guild, if any
+    5 - Mercenary kill count
+    6 - Mercenary remaining life time in msec
+    7 - Mercenary level
 
 If the character does not have a mercenary, the command returns ""
 for name and 0 for all other types.

+ 8 - 1
src/map/battle.c

@@ -7142,10 +7142,12 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
 	int 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 unit_data *ud = NULL;
 
 	nullpo_ret(src);
 	nullpo_ret(target);
 
+	ud = unit_bl2ud(target);
 	m = target->m;
 
 	//t_bl/s_bl hold the 'master' of the attack, while src/target are the actual
@@ -7174,6 +7176,7 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
 	switch( target->type ) { // Checks on actual target
 		case BL_PC: {
 				struct status_change* sc = status_get_sc(src);
+
 				if (((TBL_PC*)target)->invincible_timer != INVALID_TIMER || pc_isinvisible((TBL_PC*)target))
 					return -1; //Cannot be targeted yet.
 				if( sc && sc->count ) {
@@ -7185,6 +7188,9 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
 		case BL_MOB:
 		{
 			struct mob_data *md = ((TBL_MOB*)target);
+
+			if (ud && ud->immune_attack)
+				return 0;
 			if(((md->special_state.ai == AI_SPHERE || //Marine Spheres
 				(md->special_state.ai == AI_FLORA && battle_config.summon_flora&1)) && s_bl->type == BL_PC && src->type != BL_MOB) || //Floras
 				(md->special_state.ai == AI_ZANZOU && t_bl->id != s_bl->id) || //Zanzoe
@@ -7256,10 +7262,11 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
 				return 0;
 		}
 			break;
-		//Valid targets with no special checks here.
 		case BL_MER:
 		case BL_HOM:
 		case BL_ELEM:
+			if (ud && ud->immune_attack)
+				return 0;
 			break;
 		//All else not specified is an invalid target.
 		default:

+ 5 - 0
src/map/map.c

@@ -1903,6 +1903,11 @@ struct pet_data* map_id2pd(int id){
 	return BL_CAST(BL_PET, bl);
 }
 
+struct elemental_data* map_id2ed(int id) {
+	struct block_list* bl = map_id2bl(id);
+	return BL_CAST(BL_ELEM, bl);
+}
+
 struct chat_data* map_id2cd(int id){
 	struct block_list* bl = map_id2bl(id);
 	return BL_CAST(BL_CHAT, bl);

+ 2 - 0
src/map/map.h

@@ -836,6 +836,8 @@ struct mob_data * map_id2md(int id);
 struct npc_data * map_id2nd(int id);
 struct homun_data* map_id2hd(int id);
 struct mercenary_data* map_id2mc(int id);
+struct pet_data* map_id2pd(int id);
+struct elemental_data* map_id2ed(int id);
 struct chat_data* map_id2cd(int id);
 struct block_list * map_id2bl(int id);
 bool map_blid_exists( int id );

+ 5 - 1
src/map/mob.c

@@ -1353,6 +1353,7 @@ int mob_randomwalk(struct mob_data *md,unsigned int tick)
 	nullpo_ret(md);
 
 	if(DIFF_TICK(md->next_walktime,tick)>0 ||
+	   (status_get_mode(&md->bl)&MD_NORANDOM_WALK) ||
 	   !unit_can_move(&md->bl) ||
 	   !(status_get_mode(&md->bl)&MD_CANMOVE))
 		return 0;
@@ -3048,6 +3049,9 @@ int mob_summonslave(struct mob_data *md2,int *value,int amount,uint16 skill_id)
 			}
 		}
 
+		if (md2->state.copy_master_mode)
+			md->status.mode = md2->status.mode;
+
 		clif_skill_nodamage(&md->bl,&md->bl,skill_id,amount,1);
 	}
 
@@ -3185,7 +3189,7 @@ int mobskill_use(struct mob_data *md, unsigned int tick, int event)
 	nullpo_ret(md);
 	nullpo_ret(ms = md->db->skill);
 
-	if (!battle_config.mob_skill_rate || md->ud.skilltimer != INVALID_TIMER || !md->db->maxskill)
+	if (!battle_config.mob_skill_rate || md->ud.skilltimer != INVALID_TIMER || !md->db->maxskill || (status_get_mode(&md->bl)&MD_NOCAST_SKILL))
 		return 0;
 
 	if (event == -1 && DIFF_TICK(md->ud.canact_tick, tick) > 0)

+ 1 - 2
src/map/mob.h

@@ -143,6 +143,7 @@ struct mob_data {
 		unsigned int npc_killmonster: 1; //for new killmonster behavior
 		unsigned int rebirth: 1; // NPC_Rebirth used
 		unsigned int boss : 1;
+		unsigned int copy_master_mode : 1; ///< Whether the spawned monster should copy the master's mode.
 		enum MobSkillState skillstate;
 		unsigned char steal_flag; //number of steal tries (to prevent steal exploit on mobs with few items) [Lupus]
 		unsigned char attacked_count; //For rude attacked.
@@ -187,8 +188,6 @@ struct mob_data {
 	int tomb_nid;
 };
 
-
-
 enum {
 	MST_TARGET	=	0,
 	MST_RANDOM,	//Random Target!

+ 665 - 87
src/map/script.c

@@ -7480,6 +7480,7 @@ BUILDIN_FUNC(getcharid)
 	}
 	return SCRIPT_CMD_SUCCESS;
 }
+
 /*==========================================
  * returns the GID of an NPC
  *------------------------------------------*/
@@ -9263,6 +9264,7 @@ BUILDIN_FUNC(cooking)
 	clif_cooking_list(sd, trigger, AM_PHARMACY, 1, 1);
 	return SCRIPT_CMD_SUCCESS;
 }
+
 /*==========================================
  * Create a pet
  *------------------------------------------*/
@@ -9291,6 +9293,7 @@ BUILDIN_FUNC(makepet)
 
 	return SCRIPT_CMD_SUCCESS;
 }
+
 /*==========================================
  * Give player exp base,job * quest_exp_rate/100
  *------------------------------------------*/
@@ -9362,13 +9365,8 @@ BUILDIN_FUNC(guildchangegm)
 }
 
 /*==========================================
- * Spawn a monster :
+ * Spawn a monster:
  * *monster "<map name>",<x>,<y>,"<name to show>",<mob id>,<amount>{,"<event label>",<size>,<ai>};
- @mapn,x,y : location
- @str : monster name
- @class_ : mob_id
- @amount : nb to spawn
- @event : event to attach to mob
  *------------------------------------------*/
 BUILDIN_FUNC(monster)
 {
@@ -9419,12 +9417,16 @@ BUILDIN_FUNC(monster)
 	else
 		m = map_mapname2mapid(mapn);
 
-	for(i=0; i<amount; i++){ //not optimised
+	for(i = 0; i < amount; i++) { //not optimised
 		int mobid = mob_once_spawn(sd, m, x, y, str, class_, 1, event, size, ai);
-		if(mobid) mapreg_setreg(reference_uid(add_str("$@mobid"), i),mobid);
+
+		if (mobid)
+			mapreg_setreg(reference_uid(add_str("$@mobid"), i), mobid);
 	}
+
 	return SCRIPT_CMD_SUCCESS;
 }
+
 /*==========================================
  * Request List of Monster Drops
  *------------------------------------------*/
@@ -9460,8 +9462,10 @@ BUILDIN_FUNC(getmobdrops)
 
 	return SCRIPT_CMD_SUCCESS;
 }
+
 /*==========================================
- * Same as monster but randomize location in x0,x1,y0,y1 area
+ * Spawn a monster in a random location
+ * in x0,x1,y0,y1 area.
  *------------------------------------------*/
 BUILDIN_FUNC(areamonster)
 {
@@ -9479,6 +9483,7 @@ BUILDIN_FUNC(areamonster)
 
 	struct map_session_data* sd;
 	int16 m;
+	int i;
 
 	if (script_hasdata(st,10)) {
 		event = script_getstr(st, 10);
@@ -9508,9 +9513,16 @@ BUILDIN_FUNC(areamonster)
 	else
 		m = map_mapname2mapid(mapn);
 
-	mob_once_spawn_area(sd, m, x0, y0, x1, y1, str, class_, amount, event, size, ai);
+	for(i = 0; i < amount; i++) { //not optimised
+		int mobid = mob_once_spawn_area(sd, m, x0, y0, x1, y1, str, class_, 1, event, size, ai);
+
+		if (mobid)
+			mapreg_setreg(reference_uid(add_str("$@mobid"), i), mobid);
+	}
+
 	return SCRIPT_CMD_SUCCESS;
 }
+
 /*==========================================
  * KillMonster subcheck, verify if mob to kill ain't got an even to handle, could be force kill by allflag
  *------------------------------------------*/
@@ -13550,6 +13562,7 @@ BUILDIN_FUNC(recovery)
 	script_pushint(st,1); //Successfully executed without errors
 	return SCRIPT_CMD_SUCCESS;
 }
+
 /*==========================================
  * Get your pet info: getpetinfo(n)
  * n -> 0:pet_id 1:pet_class 2:pet_name
@@ -14103,6 +14116,8 @@ BUILDIN_FUNC(summon)
 		clif_specialeffect(&md->bl,344,AREA);
 		sc_start4(NULL,&md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 60000);
 	}
+	script_pushint(st, md->bl.id);
+
 	return SCRIPT_CMD_SUCCESS;
 }
 
@@ -16165,10 +16180,546 @@ BUILDIN_FUNC(pcstopfollow)
 	return SCRIPT_CMD_SUCCESS;
 }
 // <--- [zBuffer] List of player cont commands
-// [zBuffer] List of mob control commands --->
+// [zBuffer] List of unit control commands --->
 
-/// Makes the unit walk to target position or map
-/// Returns if it was successfull
+/// Gets specific live information of a bl.
+///
+/// getunitdata <unit id>,<arrayname>;
+BUILDIN_FUNC(getunitdata)
+{
+	TBL_PC *sd = st->rid ? map_id2sd(st->rid) : NULL;
+	struct block_list* bl;
+	TBL_MOB* md = NULL;
+	TBL_HOM* hd = NULL;
+	TBL_MER* mc = NULL;
+	TBL_PET* pd = NULL;
+	TBL_ELEM* ed = NULL;
+	TBL_NPC* nd = NULL;
+	int num;
+	char* name;
+
+	if (!data_isreference(script_getdata(st, 3))) {
+		ShowWarning("buildin_getunitdata: Error in argument! Please give a variable to store values in.\n");
+		return SCRIPT_CMD_FAILURE;
+	}
+
+	bl = map_id2bl(script_getnum(st, 2));
+
+	if (!bl) {
+		ShowWarning("buildin_getunitdata: Error in finding object with given game ID %d!\n", script_getnum(st, 2));
+		return SCRIPT_CMD_FAILURE;
+	}
+
+	switch (bl->type) {
+		case BL_MOB:  md = map_id2md(bl->id); break;
+		case BL_HOM:  hd = map_id2hd(bl->id); break;
+		case BL_PET:  pd = map_id2pd(bl->id); break;
+		case BL_MER:  mc = map_id2mc(bl->id); break;
+		case BL_ELEM: ed = map_id2ed(bl->id); break;
+		case BL_NPC:  nd = map_id2nd(bl->id); break;
+	}
+
+	num = st->stack->stack_data[st->start+3].u.num;
+	name = (char *)(str_buf+str_data[num&0x00ffffff].str);
+
+	switch(bl->type) {
+		case BL_MOB:
+			if (!md) {
+				ShowWarning("buildin_getunitdata: Error in finding object BL_MOB!\n");
+				return SCRIPT_CMD_FAILURE;
+			}
+			setd_sub(st,sd,name,0,(void *)(int)md->status.size,script_getref(st,3));
+			setd_sub(st,sd,name,1,(void *)(int)md->level,script_getref(st,3));
+			setd_sub(st,sd,name,2,(void *)(int)md->status.hp,script_getref(st,3));
+			setd_sub(st,sd,name,3,(void *)(int)md->status.max_hp,script_getref(st,3));
+			setd_sub(st,sd,name,4,(void *)(int)md->master_id,script_getref(st,3));
+			setd_sub(st,sd,name,5,(void *)(int)md->bl.m,script_getref(st,3));
+			setd_sub(st,sd,name,6,(void *)(int)md->bl.x,script_getref(st,3));
+			setd_sub(st,sd,name,7,(void *)(int)md->bl.y,script_getref(st,3));
+			setd_sub(st,sd,name,8,(void *)(int)md->status.speed,script_getref(st,3));
+			setd_sub(st,sd,name,9,(void *)(int)md->status.mode,script_getref(st,3));
+			setd_sub(st,sd,name,10,(void *)(int)md->special_state.ai,script_getref(st,3));
+			setd_sub(st,sd,name,11,(void *)(int)md->sc.option,script_getref(st,3));
+			setd_sub(st,sd,name,12,(void *)(int)md->vd->sex,script_getref(st,3));
+			setd_sub(st,sd,name,13,(void *)(int)md->vd->class_,script_getref(st,3));
+			setd_sub(st,sd,name,14,(void *)(int)md->vd->hair_style,script_getref(st,3));
+			setd_sub(st,sd,name,15,(void *)(int)md->vd->hair_color,script_getref(st,3));
+			setd_sub(st,sd,name,16,(void *)(int)md->vd->head_bottom,script_getref(st,3));
+			setd_sub(st,sd,name,17,(void *)(int)md->vd->head_mid,script_getref(st,3));
+			setd_sub(st,sd,name,18,(void *)(int)md->vd->head_top,script_getref(st,3));
+			setd_sub(st,sd,name,19,(void *)(int)md->vd->cloth_color,script_getref(st,3));
+			setd_sub(st,sd,name,20,(void *)(int)md->vd->shield,script_getref(st,3));
+			setd_sub(st,sd,name,21,(void *)(int)md->vd->weapon,script_getref(st,3));
+			setd_sub(st,sd,name,22,(void *)(int)md->vd->shield,script_getref(st,3));
+			setd_sub(st,sd,name,23,(void *)(int)md->ud.dir,script_getref(st,3));
+			setd_sub(st,sd,name,24,(void *)(int)md->status.str, script_getref(st,3));
+			setd_sub(st,sd,name,25,(void *)(int)md->status.agi, script_getref(st,3));
+			setd_sub(st,sd,name,26,(void *)(int)md->status.vit, script_getref(st,3));
+			setd_sub(st,sd,name,27,(void *)(int)md->status.int_, script_getref(st,3));
+			setd_sub(st,sd,name,28,(void *)(int)md->status.dex, script_getref(st,3));
+			setd_sub(st,sd,name,29,(void *)(int)md->status.luk, script_getref(st,3));
+			setd_sub(st,sd,name,30,(void *)(int)md->state.copy_master_mode, script_getref(st,3));
+			setd_sub(st,sd,name,31,(void *)(int)md->ud.immune_attack, script_getref(st,3));
+			break;
+
+		case BL_HOM:
+			if (!hd) {
+				ShowWarning("buildin_getunitdata: Error in finding object BL_HOM!\n");
+				return SCRIPT_CMD_FAILURE;
+			}
+			setd_sub(st,sd,name,0,(void *)(int)hd->base_status.size,script_getref(st,3));
+			setd_sub(st,sd,name,1,(void *)(int)hd->homunculus.level,script_getref(st,3));
+			setd_sub(st,sd,name,2,(void *)(int)hd->homunculus.hp,script_getref(st,3));
+			setd_sub(st,sd,name,3,(void *)(int)hd->homunculus.max_hp,script_getref(st,3));
+			setd_sub(st,sd,name,4,(void *)(int)hd->homunculus.sp,script_getref(st,3));
+			setd_sub(st,sd,name,5,(void *)(int)hd->homunculus.max_sp,script_getref(st,3));
+			setd_sub(st,sd,name,6,(void *)(int)hd->homunculus.char_id,script_getref(st,3));
+			setd_sub(st,sd,name,7,(void *)(int)hd->bl.m,script_getref(st,3));
+			setd_sub(st,sd,name,8,(void *)(int)hd->bl.x,script_getref(st,3));
+			setd_sub(st,sd,name,9,(void *)(int)hd->bl.y,script_getref(st,3));
+			setd_sub(st,sd,name,10,(void *)(int)hd->homunculus.hunger,script_getref(st,3));
+			setd_sub(st,sd,name,11,(void *)(int)hd->homunculus.intimacy,script_getref(st,3));
+			setd_sub(st,sd,name,12,(void *)(int)hd->base_status.speed,script_getref(st,3));
+			setd_sub(st,sd,name,13,(void *)(int)hd->ud.dir,script_getref(st,3));
+			setd_sub(st,sd,name,14,(void *)(int)hd->ud.canmove_tick, script_getref(st,3));
+			setd_sub(st,sd,name,15,(void *)(int)hd->base_status.str, script_getref(st,3));
+			setd_sub(st,sd,name,16,(void *)(int)hd->base_status.agi, script_getref(st,3));
+			setd_sub(st,sd,name,17,(void *)(int)hd->base_status.vit, script_getref(st,3));
+			setd_sub(st,sd,name,18,(void *)(int)hd->base_status.int_, script_getref(st,3));
+			setd_sub(st,sd,name,19,(void *)(int)hd->base_status.dex, script_getref(st,3));
+			setd_sub(st,sd,name,20,(void *)(int)hd->base_status.luk, script_getref(st,3));
+			setd_sub(st,sd,name,21,(void *)(int)hd->ud.immune_attack, script_getref(st,3));
+			break;
+
+		case BL_PET:
+			if (!pd) {
+				ShowWarning("buildin_getunitdata: Error in finding object BL_PET!\n");
+				return SCRIPT_CMD_FAILURE;
+			}
+			setd_sub(st,sd,name,0,(void *)(int)pd->status.size,script_getref(st,3));
+			setd_sub(st,sd,name,1,(void *)(int)pd->pet.level,script_getref(st,3));
+			setd_sub(st,sd,name,2,(void *)(int)pd->status.hp,script_getref(st,3));
+			setd_sub(st,sd,name,3,(void *)(int)pd->status.max_hp,script_getref(st,3));
+			setd_sub(st,sd,name,4,(void *)(int)pd->pet.account_id,script_getref(st,3));
+			setd_sub(st,sd,name,5,(void *)(int)pd->bl.m,script_getref(st,3));
+			setd_sub(st,sd,name,6,(void *)(int)pd->bl.x,script_getref(st,3));
+			setd_sub(st,sd,name,7,(void *)(int)pd->bl.y,script_getref(st,3));
+			setd_sub(st,sd,name,8,(void *)(int)pd->pet.hungry,script_getref(st,3));
+			setd_sub(st,sd,name,9,(void *)(int)pd->pet.intimate,script_getref(st,3));
+			setd_sub(st,sd,name,10,(void *)(int)pd->status.speed,script_getref(st,3));
+			setd_sub(st,sd,name,11,(void *)(int)pd->ud.dir,script_getref(st,3));
+			setd_sub(st,sd,name,12,(void *)(int)pd->ud.canmove_tick, script_getref(st,3));
+			setd_sub(st,sd,name,13,(void *)(int)pd->status.str, script_getref(st,3));
+			setd_sub(st,sd,name,14,(void *)(int)pd->status.agi, script_getref(st,3));
+			setd_sub(st,sd,name,15,(void *)(int)pd->status.vit, script_getref(st,3));
+			setd_sub(st,sd,name,16,(void *)(int)pd->status.int_, script_getref(st,3));
+			setd_sub(st,sd,name,17,(void *)(int)pd->status.dex, script_getref(st,3));
+			setd_sub(st,sd,name,18,(void *)(int)pd->status.luk, script_getref(st,3));
+			break;
+
+		case BL_MER:
+			if (!mc) {
+				ShowWarning("buildin_getunitdata: Error in finding object BL_MER!\n");
+				return SCRIPT_CMD_FAILURE;
+			}
+			setd_sub(st,sd,name,0,(void *)(int)mc->base_status.size,script_getref(st,3));
+			setd_sub(st,sd,name,1,(void *)(int)mc->base_status.hp,script_getref(st,3));
+			setd_sub(st,sd,name,2,(void *)(int)mc->base_status.max_hp,script_getref(st,3));
+			setd_sub(st,sd,name,3,(void *)(int)mc->mercenary.char_id,script_getref(st,3));
+			setd_sub(st,sd,name,4,(void *)(int)mc->bl.m,script_getref(st,3));
+			setd_sub(st,sd,name,5,(void *)(int)mc->bl.x,script_getref(st,3));
+			setd_sub(st,sd,name,6,(void *)(int)mc->bl.y,script_getref(st,3));
+			setd_sub(st,sd,name,7,(void *)(int)mc->mercenary.kill_count,script_getref(st,3));
+			setd_sub(st,sd,name,8,(void *)(int)mc->mercenary.life_time,script_getref(st,3));
+			setd_sub(st,sd,name,9,(void *)(int)mc->base_status.speed,script_getref(st,3));
+			setd_sub(st,sd,name,10,(void *)(int)mc->ud.dir,script_getref(st,3));
+			setd_sub(st,sd,name,11,(void *)(int)mc->ud.canmove_tick, script_getref(st,3));
+			setd_sub(st,sd,name,12,(void *)(int)mc->base_status.str, script_getref(st,3));
+			setd_sub(st,sd,name,13,(void *)(int)mc->base_status.agi, script_getref(st,3));
+			setd_sub(st,sd,name,14,(void *)(int)mc->base_status.vit, script_getref(st,3));
+			setd_sub(st,sd,name,15,(void *)(int)mc->base_status.int_, script_getref(st,3));
+			setd_sub(st,sd,name,16,(void *)(int)mc->base_status.dex, script_getref(st,3));
+			setd_sub(st,sd,name,17,(void *)(int)mc->base_status.luk, script_getref(st,3));
+			setd_sub(st,sd,name,18,(void *)(int)mc->ud.immune_attack, script_getref(st,3));
+			break;
+
+		case BL_ELEM:
+			if (!ed) {
+				ShowWarning("buildin_getunitdata: Error in finding object BL_ELEM!\n");
+				return SCRIPT_CMD_FAILURE;
+			}
+			setd_sub(st,sd,name,0,(void *)(int)ed->base_status.size,script_getref(st,3));
+			setd_sub(st,sd,name,1,(void *)(int)ed->elemental.hp,script_getref(st,3));
+			setd_sub(st,sd,name,2,(void *)(int)ed->elemental.max_hp,script_getref(st,3));
+			setd_sub(st,sd,name,3,(void *)(int)ed->elemental.sp,script_getref(st,3));
+			setd_sub(st,sd,name,4,(void *)(int)ed->elemental.max_sp,script_getref(st,3));
+			setd_sub(st,sd,name,5,(void *)(int)ed->elemental.char_id,script_getref(st,3));
+			setd_sub(st,sd,name,6,(void *)(int)ed->bl.m,script_getref(st,3));
+			setd_sub(st,sd,name,7,(void *)(int)ed->bl.x,script_getref(st,3));
+			setd_sub(st,sd,name,8,(void *)(int)ed->bl.y,script_getref(st,3));
+			setd_sub(st,sd,name,9,(void *)(int)ed->elemental.life_time,script_getref(st,3));
+			setd_sub(st,sd,name,10,(void *)(int)ed->elemental.mode,script_getref(st,3));
+			setd_sub(st,sd,name,11,(void *)(int)ed->base_status.speed,script_getref(st,3));
+			setd_sub(st,sd,name,12,(void *)(int)ed->ud.dir,script_getref(st,3));
+			setd_sub(st,sd,name,13,(void *)(int)ed->ud.canmove_tick, script_getref(st,3));
+			setd_sub(st,sd,name,14,(void *)(int)ed->base_status.str, script_getref(st,3));
+			setd_sub(st,sd,name,15,(void *)(int)ed->base_status.agi, script_getref(st,3));
+			setd_sub(st,sd,name,16,(void *)(int)ed->base_status.vit, script_getref(st,3));
+			setd_sub(st,sd,name,17,(void *)(int)ed->base_status.int_, script_getref(st,3));
+			setd_sub(st,sd,name,18,(void *)(int)ed->base_status.dex, script_getref(st,3));
+			setd_sub(st,sd,name,19,(void *)(int)ed->base_status.luk, script_getref(st,3));
+			setd_sub(st,sd,name,20,(void *)(int)ed->ud.immune_attack, script_getref(st,3));
+			break;
+
+		case BL_NPC:
+			if (!nd) {
+				ShowWarning("buildin_getunitdata: Error in finding object BL_NPC!\n");
+				return SCRIPT_CMD_FAILURE;
+			}
+			setd_sub(st,sd,name,0,(void *)(int)nd->class_,script_getref(st,3));
+			setd_sub(st,sd,name,1,(void *)(int)nd->level,script_getref(st,3));
+			setd_sub(st,sd,name,2,(void *)(int)nd->status.hp,script_getref(st,3));
+			setd_sub(st,sd,name,3,(void *)(int)nd->status.max_hp,script_getref(st,3));
+			setd_sub(st,sd,name,4,(void *)(int)nd->bl.m,script_getref(st,3));
+			setd_sub(st,sd,name,5,(void *)(int)nd->bl.x,script_getref(st,3));
+			setd_sub(st,sd,name,6,(void *)(int)nd->bl.y,script_getref(st,3));
+			setd_sub(st,sd,name,7,(void *)(int)nd->ud.dir,script_getref(st,3));
+			setd_sub(st,sd,name,8,(void *)(int)nd->status.str, script_getref(st,3));
+			setd_sub(st,sd,name,9,(void *)(int)nd->status.agi, script_getref(st,3));
+			setd_sub(st,sd,name,10,(void *)(int)nd->status.vit, script_getref(st,3));
+			setd_sub(st,sd,name,11,(void *)(int)nd->status.int_, script_getref(st,3));
+			setd_sub(st,sd,name,12,(void *)(int)nd->status.dex, script_getref(st,3));
+			setd_sub(st,sd,name,13,(void *)(int)nd->status.luk, script_getref(st,3));
+			break;
+
+		default:
+			ShowWarning("buildin_getunitdata: Unknown object type!\n");
+			return SCRIPT_CMD_FAILURE;
+	}
+
+	return SCRIPT_CMD_SUCCESS;
+}
+
+/// Changes the live data of a bl.
+///
+/// setunitdata <unit id>,<type>,<value>;
+BUILDIN_FUNC(setunitdata)
+{
+	struct block_list* bl = NULL;
+	TBL_MOB* md = NULL;
+	TBL_HOM* hd = NULL;
+	TBL_MER* mc = NULL;
+	TBL_PET* pd = NULL;
+	TBL_ELEM* ed = NULL;
+	TBL_NPC* nd = NULL;
+	int type, value;
+
+	bl = map_id2bl(script_getnum(st, 2));
+
+	if (!bl) {
+		ShowWarning("buildin_setunitdata: Error in finding object with given game ID %d!\n", script_getnum(st, 2));
+		return SCRIPT_CMD_FAILURE;
+	}
+
+	switch (bl->type) {
+		case BL_MOB:  md = map_id2md(bl->id); break;
+		case BL_HOM:  hd = map_id2hd(bl->id); break;
+		case BL_PET:  pd = map_id2pd(bl->id); break;
+		case BL_MER:  mc = map_id2mc(bl->id); break;
+		case BL_ELEM: ed = map_id2ed(bl->id); break;
+		case BL_NPC:  nd = map_id2nd(bl->id); break;
+	}
+
+	type = script_getnum(st, 3);
+	value = script_getnum(st, 4);
+
+	switch (bl->type) {
+	case BL_MOB:
+		if (!md) {
+			ShowWarning("buildin_setunitdata: Error in finding object BL_MOB!\n");
+			return SCRIPT_CMD_FAILURE;
+		}
+		switch (type) {
+			case 0: md->status.size = (unsigned char)value; break;
+			case 1: md->level = (unsigned short)value; break;
+			case 2: md->status.hp = (unsigned int)value; break;
+			case 3: md->status.max_hp = (unsigned int)value; break;
+			case 4: md->master_id = value; break;
+			case 5: md->bl.m = (short)value; break;
+			case 6: md->bl.x = (short)value; break;
+			case 7: md->bl.y = (short)value; break;
+			case 8: md->status.speed = (unsigned short)value; break;
+			case 9: md->status.mode = (enum e_mode)value; break;
+			case 10: md->special_state.ai = (enum mob_ai)value; break;
+			case 11: md->sc.option = (unsigned short)value; break;
+			case 12: md->vd->sex = (char)value; break;
+			case 13: md->vd->class_ = (unsigned short)value; break;
+			case 14: md->vd->hair_style = (unsigned short)value; break;
+			case 15: md->vd->hair_color = (unsigned short)value; break;
+			case 16: md->vd->head_bottom = (unsigned short)value; break;
+			case 17: md->vd->head_mid = (unsigned short)value; break;
+			case 18: md->vd->head_top = (unsigned short)value; break;
+			case 19: md->vd->cloth_color = (unsigned short)value; break;
+			case 20: md->vd->shield = (unsigned short)value; break;
+			case 21: md->vd->weapon = (unsigned short)value; break;
+			case 22: md->vd->shield = (unsigned short)value; break;
+			case 23: md->ud.dir = (unsigned char)value; break;
+			case 24: md->status.str = (unsigned int)value; break;
+			case 25: md->status.agi = (unsigned int)value; break;
+			case 26: md->status.vit = (unsigned int)value; break;
+			case 27: md->status.int_ = (unsigned int)value; break;
+			case 28: md->status.dex = (unsigned int)value; break;
+			case 29: md->status.luk = (unsigned int)value; break;
+			case 30: md->state.copy_master_mode = value > 0 ? 1 : 0; break;
+			case 31: md->ud.immune_attack = (bool)value > 0 ? 1 : 0; break;
+			default:
+				ShowError("buildin_setunitdata: Unknown data identifier %d for BL_MOB.\n", type);
+				return SCRIPT_CMD_FAILURE;
+			}
+		break;
+
+	case BL_HOM:
+		if (!hd) {
+			ShowWarning("buildin_setunitdata: Error in finding object BL_HOM!\n");
+			return SCRIPT_CMD_FAILURE;
+		}
+		switch (type) {
+			case 0: hd->base_status.size = (unsigned char)value; break;
+			case 1: hd->homunculus.level = (unsigned short)value; break;
+			case 2: hd->homunculus.hp = (unsigned int)value; break;
+			case 3: hd->homunculus.max_hp = (unsigned int)value; break;
+			case 4: hd->homunculus.sp = (unsigned int)value; break;
+			case 5: hd->homunculus.max_sp; (unsigned int)value; break;
+			case 6: hd->homunculus.char_id = (unsigned int)value; break;
+			case 7: hd->bl.m = (short)value; break;
+			case 8: hd->bl.x = (short)value; break;
+			case 9: hd->bl.y = (short)value; break;
+			case 10: hd->homunculus.hunger = (short)value; break;
+			case 11: hd->homunculus.intimacy = (unsigned int)value; break;
+			case 12: hd->base_status.speed = (unsigned short)value; break;
+			case 13: hd->ud.dir = (unsigned char)value; break;
+			case 14: hd->ud.canmove_tick = value > 0 ? 1 : 0; break;
+			case 15: hd->base_status.str = (unsigned int)value; break;
+			case 16: hd->base_status.agi = (unsigned int)value; break;
+			case 17: hd->base_status.vit = (unsigned int)value; break;
+			case 18: hd->base_status.int_ = (unsigned int)value; break;
+			case 19: hd->base_status.dex = (unsigned int)value; break;
+			case 20: hd->base_status.luk = (unsigned int)value; break;
+			case 21: hd->ud.immune_attack = (bool)value > 0 ? 1 : 0; break;
+			default:
+				ShowError("buildin_setunitdata: Unknown data identifier %d for BL_HOM.\n", type);
+				return SCRIPT_CMD_FAILURE;
+			}
+		break;
+
+	case BL_PET:
+		if (!pd) {
+			ShowWarning("buildin_setunitdata: Error in finding object BL_PET!\n");
+			return SCRIPT_CMD_FAILURE;
+		}
+		switch (type) {
+			case 0: pd->status.size = (unsigned char)value; break;
+			case 1: pd->pet.level = (unsigned short)value; break;
+			case 2: pd->status.hp = (unsigned int)value; break;
+			case 3: pd->status.max_hp = (unsigned int)value; break;
+			case 4: pd->pet.account_id = (unsigned int)value; break;
+			case 5: pd->bl.m = (short)value; break;
+			case 6: pd->bl.x = (short)value; break;
+			case 7: pd->bl.y = (short)value; break;
+			case 8: pd->pet.hungry = (short)value; break;
+			case 9: pd->pet.intimate = (unsigned int)value; break;
+			case 10: pd->status.speed = (unsigned short)value; break;
+			case 11: pd->ud.dir = (unsigned char)value; break;
+			case 12: pd->ud.canmove_tick = value > 0 ? 1 : 0; break;
+			case 13: pd->status.str = (unsigned int)value; break;
+			case 14: pd->status.agi = (unsigned int)value; break;
+			case 15: pd->status.vit = (unsigned int)value; break;
+			case 16: pd->status.int_ = (unsigned int)value; break;
+			case 17: pd->status.dex = (unsigned int)value; break;
+			case 18: pd->status.luk = (unsigned int)value; break;
+			case 20: pd->ud.immune_attack = (bool)value > 0 ? 1 : 0; break;
+			default:
+				ShowError("buildin_setunitdata: Unknown data identifier %d for BL_PET.\n", type);
+				return SCRIPT_CMD_FAILURE;
+			}
+		break;
+
+	case BL_MER:
+		if (!mc) {
+			ShowWarning("buildin_setunitdata: Error in finding object BL_MER!\n");
+			return SCRIPT_CMD_FAILURE;
+		}
+		switch (type) {
+			case 0: mc->base_status.size = (unsigned char)value; break;
+			case 1: mc->base_status.hp = (unsigned int)value; break;
+			case 2: mc->base_status.max_hp = (unsigned int)value; break;
+			case 3: mc->mercenary.char_id = (unsigned int)value; break;
+			case 4: mc->bl.m = (short)value; break;
+			case 5: mc->bl.x = (short)value; break;
+			case 6: mc->bl.y = (short)value; break;
+			case 7: mc->mercenary.kill_count = (unsigned int)value; break;
+			case 8: mc->mercenary.life_time = (unsigned int)value; break;
+			case 9: mc->base_status.speed = (unsigned short)value; break;
+			case 10: mc->ud.dir = (unsigned char)value; break;
+			case 11: mc->ud.canmove_tick = value > 0 ? 1 : 0; break;
+			case 12: mc->base_status.str = (unsigned int)value; break;
+			case 13: mc->base_status.agi = (unsigned int)value; break;
+			case 14: mc->base_status.vit = (unsigned int)value; break;
+			case 15: mc->base_status.int_ = (unsigned int)value; break;
+			case 16: mc->base_status.dex = (unsigned int)value; break;
+			case 17: mc->base_status.luk = (unsigned int)value; break;
+			default:
+				ShowError("buildin_setunitdata: Unknown data identifier %d for BL_MER.\n", type);
+				return SCRIPT_CMD_FAILURE;
+			}
+		break;
+
+	case BL_ELEM:
+		if (!ed) {
+			ShowWarning("buildin_setunitdata: Error in finding object BL_ELEM!\n");
+			return SCRIPT_CMD_FAILURE;
+		}
+		switch (type) {
+			case 0: ed->base_status.size = (unsigned char)value; break;
+			case 1: ed->elemental.hp = (unsigned int)value; break;
+			case 2: ed->elemental.max_hp = (unsigned int)value; break;
+			case 3: ed->elemental.sp = (unsigned int)value; break;
+			case 4: ed->elemental.max_sp = (unsigned int)value; break;
+			case 5: ed->elemental.char_id = (unsigned int)value; break;
+			case 6: ed->bl.m = (short)value; break;
+			case 7: ed->bl.x = (short)value; break;
+			case 8: ed->bl.y = (short)value; break;
+			case 9: ed->elemental.life_time = (unsigned int)value; break;
+			case 10: ed->elemental.mode = (unsigned int)value; break;
+			case 11: ed->base_status.speed = (unsigned short)value; break;
+			case 12: ed->ud.dir = (unsigned char)value; break;
+			case 13: ed->ud.canmove_tick = value > 0 ? 1 : 0; break;
+			case 14: ed->base_status.str = (unsigned int)value; break;
+			case 15: ed->base_status.agi = (unsigned int)value; break;
+			case 16: ed->base_status.vit = (unsigned int)value; break;
+			case 17: ed->base_status.int_ = (unsigned int)value; break;
+			case 18: ed->base_status.dex = (unsigned int)value; break;
+			case 19: ed->base_status.luk = (unsigned int)value; break;
+			case 20: ed->ud.immune_attack = (bool)value > 0 ? 1 : 0; break;
+			default:
+				ShowError("buildin_setunitdata: Unknown data identifier %d for BL_ELEM.\n", type);
+				return SCRIPT_CMD_FAILURE;
+			}
+		break;
+
+	case BL_NPC:
+		if (!md) {
+			ShowWarning("buildin_setunitdata: Error in finding object BL_NPC!\n");
+			return SCRIPT_CMD_FAILURE;
+		}
+		switch (type) {
+			case 0: nd->class_ = (unsigned int)value; break;
+			case 1: nd->level = (unsigned int)value; break;
+			case 2: nd->status.hp = (unsigned int)value; break;
+			case 3: nd->status.max_hp = (unsigned int)value; break;
+			case 4: nd->bl.m = (short)value; break;
+			case 5: nd->bl.x = (short)value; break;
+			case 6: nd->bl.y = (short)value; break;
+			case 7: nd->ud.dir = (unsigned char)value; break;
+			case 8: nd->status.str = (unsigned int)value; break;
+			case 9: nd->status.agi = (unsigned int)value; break;
+			case 10: nd->status.vit = (unsigned int)value; break;
+			case 11: nd->status.int_ = (unsigned int)value; break;
+			case 12: nd->status.dex = (unsigned int)value; break;
+			case 13: nd->status.luk = (unsigned int)value; break;
+			default:
+				ShowError("buildin_setunitdata: Unknown data identifier %d for BL_NPC.\n", type);
+				return SCRIPT_CMD_FAILURE;
+			}
+		break;
+
+	default:
+		ShowWarning("buildin_setunitdata: Unknown object type!\n");
+		return SCRIPT_CMD_FAILURE;
+	}
+
+	return SCRIPT_CMD_SUCCESS;
+}
+
+/// Gets the name of a bl.
+/// Supported types are [MOB|HOM|PET|NPC].
+/// MER and ELEM don't support custom names.
+///
+/// getunitname <unit id>;
+BUILDIN_FUNC(getunitname)
+{
+	struct block_list* bl = NULL;
+
+	bl = map_id2bl(script_getnum(st, 2));
+
+	if (!bl) {
+		ShowWarning("buildin_getunitname: Error in finding object with given game ID %d!\n", script_getnum(st, 2));
+		return SCRIPT_CMD_FAILURE;
+	}
+
+	script_pushstrcopy(st, status_get_name(bl));
+
+	return SCRIPT_CMD_SUCCESS;
+}
+
+/// Changes the name of a bl.
+/// Supported types are [MOB|HOM|PET].
+/// For NPC see 'setnpcdisplay', MER and ELEM don't support custom names.
+///
+/// setunitname <unit id>,<name>;
+BUILDIN_FUNC(setunitname)
+{
+	struct block_list* bl = NULL;
+	TBL_MOB* md = NULL;
+	TBL_HOM* hd = NULL;
+	TBL_PET* pd = NULL;
+
+	bl = map_id2bl(script_getnum(st, 2));
+
+	if (!bl) {
+		ShowWarning("buildin_setunitname: Error in finding object with given game ID %d!\n", script_getnum(st, 2));
+		return SCRIPT_CMD_FAILURE;
+	}
+
+	switch (bl->type) {
+		case BL_MOB:  md = map_id2md(bl->id); break;
+		case BL_HOM:  hd = map_id2hd(bl->id); break;
+		case BL_PET:  pd = map_id2pd(bl->id); break;
+	}
+
+	switch (bl->type) {
+		case BL_MOB:
+			if (!md) {
+				ShowWarning("buildin_setunitname: Error in finding object BL_MOB!\n");
+				return SCRIPT_CMD_FAILURE;
+			}
+			safestrncpy(md->name, script_getstr(st, 3), NAME_LENGTH);
+			break;
+		case BL_HOM:
+			if (!hd) {
+				ShowWarning("buildin_setunitname: Error in finding object BL_HOM!\n");
+				return SCRIPT_CMD_FAILURE;
+			}
+			safestrncpy(hd->homunculus.name, script_getstr(st, 3), NAME_LENGTH);
+			break;
+		case BL_PET:
+			if (!pd) {
+				ShowWarning("buildin_setunitname: Error in finding object BL_PET!\n");
+				return SCRIPT_CMD_FAILURE;
+			}
+			safestrncpy(pd->pet.name, script_getstr(st, 3), NAME_LENGTH);
+			break;
+		default:
+			ShowWarning("buildin_setunitname: Unknown object type!\n");
+			return SCRIPT_CMD_FAILURE;
+	}
+	clif_charnameack(0, bl); // Send update to client.
+
+	return SCRIPT_CMD_SUCCESS;
+}
+
+/// Makes the unit walk to target position or map.
+/// Returns if it was successful.
 ///
 /// unitwalk(<unit_id>,<x>,<y>) -> <bool>
 /// unitwalk(<unit_id>,<target_id>) -> <bool>
@@ -16177,41 +16728,44 @@ BUILDIN_FUNC(unitwalk)
 	struct block_list* bl;
 
 	bl = map_id2bl(script_getnum(st,2));
-	if( bl == NULL )
+
+	if (!bl)
 		script_pushint(st, 0);
 	else if( script_hasdata(st,4) ) {
 		int x = script_getnum(st,3);
 		int y = script_getnum(st,4);
-		if( script_pushint(st, unit_can_reach_pos(bl,x,y,0)) )
+
+		if (script_pushint(st, unit_can_reach_pos(bl,x,y,0)))
 			add_timer(gettick()+50, unit_delay_walktoxy_timer, bl->id, (x<<16)|(y&0xFFFF)); // Need timer to avoid mismatches
 	} else {
 		struct block_list* tbl = map_id2bl(script_getnum(st,3));
-		if( tbl == NULL ) {
-			ShowError("script:unitwalk: bad target destination\n");
+
+		if (!tbl) {
+			ShowError("buildin_unitwalk: Bad target destination.\n");
 			script_pushint(st, 0);
-			return 1;
-		}
-		else if (script_pushint(st, unit_can_reach_bl(bl, tbl, distance_bl(bl, tbl)+1, 0, NULL, NULL)))
+			return SCRIPT_CMD_FAILURE;
+		} else if (script_pushint(st, unit_can_reach_bl(bl, tbl, distance_bl(bl, tbl)+1, 0, NULL, NULL)))
 			add_timer(gettick()+50, unit_delay_walktobl_timer, bl->id, tbl->id); // Need timer to avoid mismatches
 	}
 
 	return SCRIPT_CMD_SUCCESS;
 }
 
-/// Kills the unit
+/// Kills the unit.
 ///
 /// unitkill <unit_id>;
 BUILDIN_FUNC(unitkill)
 {
 	struct block_list* bl = map_id2bl(script_getnum(st,2));
-	if( bl != NULL )
+
+	if (bl != NULL)
 		status_kill(bl);
 
 	return SCRIPT_CMD_SUCCESS;
 }
 
-/// Warps the unit to the target position in the target map
-/// Returns if it was successfull
+/// Warps the unit to the target position in the target map.
+/// Returns if it was successful.
 ///
 /// unitwarp(<unit_id>,"<map name>",<x>,<y>) -> <bool>
 BUILDIN_FUNC(unitwarp)
@@ -16233,12 +16787,12 @@ BUILDIN_FUNC(unitwarp)
 	else
 		bl = map_id2bl(unit_id);
 
-	if( strcmp(mapname,"this") == 0 )
+	if (!strcmp(mapname,"this"))
 		map_idx = bl?bl->m:-1;
 	else
 		map_idx = map_mapname2mapid(mapname);
 
-	if( map_idx >= 0 && bl != NULL )
+	if (map_idx >= 0 && bl != NULL)
 		script_pushint(st, unit_warp(bl,map_idx,x,y,CLR_OUTSIGHT));
 	else
 		script_pushint(st, 0);
@@ -16260,58 +16814,56 @@ BUILDIN_FUNC(unitattack)
 	struct script_data* data;
 	int actiontype = 0;
 
-	// get unit
 	unit_bl = map_id2bl(script_getnum(st,2));
-	if( unit_bl == NULL ) {
+
+	if (!unit_bl) {
 		script_pushint(st, 0);
-		return 0;
+		return SCRIPT_CMD_FAILURE;
 	}
 
 	data = script_getdata(st, 3);
 	get_val(st, data);
-	if( data_isstring(data) )
-	{
+
+	if (data_isstring(data)) {
 		TBL_PC* sd = map_nick2sd(conv_str(st, data));
 		if( sd != NULL )
 			target_bl = &sd->bl;
 	} else
 		target_bl = map_id2bl(conv_num(st, data));
-	// request the attack
-	if( target_bl == NULL )
-	{
+
+	if (!target_bl) {
 		script_pushint(st, 0);
-		return 0;
+		return SCRIPT_CMD_FAILURE;
 	}
 
-	// get actiontype
-	if( script_hasdata(st,4) )
+	if (script_hasdata(st,4))
 		actiontype = script_getnum(st,4);
 
-	switch( unit_bl->type )
-	{
-	case BL_PC:
-		clif_parse_ActionRequest_sub(((TBL_PC *)unit_bl), actiontype > 0 ? 0x07 : 0x00, target_bl->id, gettick());
-		script_pushint(st, 1);
-		return 0;
-	case BL_MOB:
-		((TBL_MOB *)unit_bl)->target_id = target_bl->id;
-		break;
-	case BL_PET:
-		((TBL_PET *)unit_bl)->target_id = target_bl->id;
-		break;
-	default:
-		ShowError("script:unitattack: unsupported source unit type %d\n", unit_bl->type);
-		script_pushint(st, 0);
-		return 1;
+	switch(unit_bl->type) {
+		case BL_PC:
+			clif_parse_ActionRequest_sub(((TBL_PC *)unit_bl), actiontype > 0 ? 0x07 : 0x00, target_bl->id, gettick());
+			script_pushint(st, 1);
+			return SCRIPT_CMD_SUCCESS;
+		case BL_MOB:
+			((TBL_MOB *)unit_bl)->target_id = target_bl->id;
+			break;
+		case BL_PET:
+			((TBL_PET *)unit_bl)->target_id = target_bl->id;
+			break;
+		default:
+			ShowError("buildin_unitattack: Unsupported source unit type %d.\n", unit_bl->type);
+			script_pushint(st, 0);
+			return SCRIPT_CMD_FAILURE;
 	}
+
 	script_pushint(st, unit_walktobl(unit_bl, target_bl, 65025, 2));
 	return SCRIPT_CMD_SUCCESS;
 }
 
-/// Makes the unit stop attacking and moving
+/// Makes the unit stop attacking.
 ///
-/// unitstop <unit_id>;
-BUILDIN_FUNC(unitstop)
+/// unitstopattack <unit_id>;
+BUILDIN_FUNC(unitstopattack)
 {
 	int unit_id;
 	struct block_list* bl;
@@ -16319,18 +16871,38 @@ BUILDIN_FUNC(unitstop)
 	unit_id = script_getnum(st,2);
 
 	bl = map_id2bl(unit_id);
-	if( bl != NULL )
-	{
+
+	if (bl != NULL) {
 		unit_stop_attack(bl);
+		if (bl->type == BL_MOB)
+			((TBL_MOB*)bl)->target_id = 0;
+	}
+
+	return SCRIPT_CMD_SUCCESS;
+}
+
+/// Makes the unit stop walking.
+///
+/// unitstopwalk <unit_id>;
+BUILDIN_FUNC(unitstopwalk)
+{
+	int unit_id;
+	struct block_list* bl;
+
+	unit_id = script_getnum(st,2);
+
+	bl = map_id2bl(unit_id);
+
+	if (bl != NULL) {
 		unit_stop_walking(bl,4);
-		if( bl->type == BL_MOB )
+		if (bl->type == BL_MOB)
 			((TBL_MOB*)bl)->target_id = 0;
 	}
 
 	return SCRIPT_CMD_SUCCESS;
 }
 
-/// Makes the unit say the message
+/// Makes the unit say the given message.
 ///
 /// unittalk <unit_id>,"<message>";
 BUILDIN_FUNC(unittalk)
@@ -16343,9 +16915,10 @@ BUILDIN_FUNC(unittalk)
 	message = script_getstr(st, 3);
 
 	bl = map_id2bl(unit_id);
-	if( bl != NULL )
-	{
+
+	if (bl != NULL) {
 		struct StringBuf sbuf;
+
 		StringBuf_Init(&sbuf);
 		StringBuf_Printf(&sbuf, "%s : %s", status_get_name(bl), message);
 		clif_disp_overhead(bl, StringBuf_Value(&sbuf));
@@ -16355,7 +16928,7 @@ BUILDIN_FUNC(unittalk)
 	return SCRIPT_CMD_SUCCESS;
 }
 
-/// Makes the unit do an emotion
+/// Makes the unit do an emotion.
 ///
 /// unitemote <unit_id>,<emotion>;
 ///
@@ -16369,22 +16942,21 @@ BUILDIN_FUNC(unitemote)
 	unit_id = script_getnum(st,2);
 	emotion = script_getnum(st,3);
 	bl = map_id2bl(unit_id);
-	if( bl != NULL )
+
+	if (bl != NULL)
 		clif_emotion(bl, emotion);
 
 	return SCRIPT_CMD_SUCCESS;
 }
 
-/// Makes the unit cast the skill on the target or self if no target is specified
+/// Makes the unit cast the skill on the target or self if no target is specified.
 ///
-/// unitskilluseid <unit_id>,<skill_id>,<skill_lv>{,<target_id>};
-/// unitskilluseid <unit_id>,"<skill name>",<skill_lv>{,<target_id>};
+/// unitskilluseid <unit_id>,<skill_id>,<skill_lv>{,<target_id>,<casttime>};
+/// unitskilluseid <unit_id>,"<skill name>",<skill_lv>{,<target_id>,<casttime>};
 BUILDIN_FUNC(unitskilluseid)
 {
-	int unit_id;
-	uint16 skill_id;
-	uint16 skill_lv;
-	int target_id;
+	int unit_id, target_id, casttime;
+	uint16 skill_id, skill_lv;
 	struct block_list* bl;
 	struct script_data *data;
 
@@ -16394,25 +16966,23 @@ BUILDIN_FUNC(unitskilluseid)
 	skill_id = ( data_isstring(data) ? skill_name2id(script_getstr(st,3)) : script_getnum(st,3) );
 	skill_lv = script_getnum(st,4);
 	target_id = ( script_hasdata(st,5) ? script_getnum(st,5) : unit_id );
-
+	casttime = ( script_hasdata(st,6) ? script_getnum(st,6) : 0 );
 	bl = map_id2bl(unit_id);
-	if( bl != NULL )
-		unit_skilluse_id(bl, target_id, skill_id, skill_lv);
+
+	if (bl != NULL)
+		unit_skilluse_id2(bl, target_id, skill_id, skill_lv, (casttime * 1000) + skill_castfix(bl, skill_id, skill_lv), skill_get_castcancel(skill_id));
 
 	return SCRIPT_CMD_SUCCESS;
 }
 
 /// Makes the unit cast the skill on the target position.
 ///
-/// unitskillusepos <unit_id>,<skill_id>,<skill_lv>,<target_x>,<target_y>;
-/// unitskillusepos <unit_id>,"<skill name>",<skill_lv>,<target_x>,<target_y>;
+/// unitskillusepos <unit_id>,<skill_id>,<skill_lv>,<target_x>,<target_y>{,<casttime>};
+/// unitskillusepos <unit_id>,"<skill name>",<skill_lv>,<target_x>,<target_y>{,<casttime>};
 BUILDIN_FUNC(unitskillusepos)
 {
-	int unit_id;
-	uint16 skill_id;
-	uint16 skill_lv;
-	int skill_x;
-	int skill_y;
+	int unit_id, skill_x, skill_y, casttime;
+	uint16 skill_id, skill_lv;
 	struct block_list* bl;
 	struct script_data *data;
 
@@ -16423,10 +16993,11 @@ BUILDIN_FUNC(unitskillusepos)
 	skill_lv = script_getnum(st,4);
 	skill_x  = script_getnum(st,5);
 	skill_y  = script_getnum(st,6);
-
+	casttime = ( script_hasdata(st,7) ? script_getnum(st,7) : 0 );
 	bl = map_id2bl(unit_id);
-	if( bl != NULL )
-		unit_skilluse_pos(bl, skill_x, skill_y, skill_id, skill_lv);
+
+	if (bl != NULL)
+		unit_skilluse_pos2(bl, skill_x, skill_y, skill_id, skill_lv, (casttime * 1000) + skill_castfix(bl, skill_id, skill_lv), skill_get_castcancel(skill_id));
 
 	return SCRIPT_CMD_SUCCESS;
 }
@@ -16715,6 +17286,8 @@ BUILDIN_FUNC(mercenary_create)
 
 	contract_time = script_getnum(st,3);
 	mercenary_create(sd, class_, contract_time);
+	script_pushint(st, sd->md->bl.id);
+
 	return SCRIPT_CMD_SUCCESS;
 }
 
@@ -19598,15 +20171,20 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF(pcblockmove,"ii"),
 	// <--- [zBuffer] List of player cont commands
 	// [zBuffer] List of mob control commands --->
+	BUILDIN_DEF(getunitname,"i"),
+	BUILDIN_DEF(setunitname,"is"),
+	BUILDIN_DEF(getunitdata,"i*"),
+	BUILDIN_DEF(setunitdata,"iii"),
 	BUILDIN_DEF(unitwalk,"ii?"),
 	BUILDIN_DEF(unitkill,"i"),
 	BUILDIN_DEF(unitwarp,"isii"),
 	BUILDIN_DEF(unitattack,"iv?"),
-	BUILDIN_DEF(unitstop,"i"),
+	BUILDIN_DEF(unitstopattack,"i"),
+	BUILDIN_DEF(unitstopwalk,"i"),
 	BUILDIN_DEF(unittalk,"is"),
 	BUILDIN_DEF(unitemote,"ii"),
-	BUILDIN_DEF(unitskilluseid,"ivi?"), // originally by Qamera [Celest]
-	BUILDIN_DEF(unitskillusepos,"iviii"), // [Celest]
+	BUILDIN_DEF(unitskilluseid,"ivi??"), // originally by Qamera [Celest]
+	BUILDIN_DEF(unitskillusepos,"iviii?"), // [Celest]
 // <--- [zBuffer] List of mob control commands
 	BUILDIN_DEF(sleep,"i"),
 	BUILDIN_DEF(sleep2,"i"),

+ 2 - 0
src/map/status.c

@@ -6654,7 +6654,9 @@ const char* status_get_name(struct block_list *bl)
 		case BL_MOB:	return ((TBL_MOB*)bl)->name;
 		case BL_PET:	return ((TBL_PET*)bl)->pet.name;
 		case BL_HOM:	return ((TBL_HOM*)bl)->homunculus.name;
+		//case BL_MER: // They only have database names which are global, not specific to GID.
 		case BL_NPC:	return ((TBL_NPC*)bl)->name;
+		//case BL_ELEM: // They only have database names which are global, not specific to GID.
 	}
 	return "Unknown";
 }

+ 2 - 0
src/map/status.h

@@ -1621,6 +1621,8 @@ enum e_mode {
 	MD_MVP					= 0x080000,
 	MD_IGNOREMISC			= 0x100000,
 	MD_KNOCKBACK_IMMUNE		= 0x200000,
+	MD_NORANDOM_WALK		= 0x400000,
+	MD_NOCAST_SKILL			= 0x800000,
 };
 #define MD_MASK 0x00FFFF
 #define ATR_MASK 0xFF0000

+ 1 - 0
src/map/unit.h

@@ -40,6 +40,7 @@ struct unit_data {
 	unsigned int attackabletime;
 	unsigned int canact_tick;
 	unsigned int canmove_tick;
+	bool immune_attack; ///< Whether the unit is immune to attacks
 	uint8 dir;
 	unsigned char walk_count;
 	unsigned char target_count;