summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorskotlex <skotlex@54d463be-8e91-2dee-dedb-b68131a5f0ec>2006-12-06 18:21:32 +0000
committerskotlex <skotlex@54d463be-8e91-2dee-dedb-b68131a5f0ec>2006-12-06 18:21:32 +0000
commitf4edc7e4431b929acc62e4c2427e815ba9d69779 (patch)
treeac82447101de9f643bef0fa7a5b3a56bd1e37194
parentf7ff1f208a2ce086fe6636b8359c65cf14ab68f4 (diff)
downloadhercules-f4edc7e4431b929acc62e4c2427e815ba9d69779.tar.gz
hercules-f4edc7e4431b929acc62e4c2427e815ba9d69779.tar.bz2
hercules-f4edc7e4431b929acc62e4c2427e815ba9d69779.tar.xz
hercules-f4edc7e4431b929acc62e4c2427e815ba9d69779.zip
- Cleaned up the implementation of map_foreachinmovearea so that the number of arguments passed is less.
- Moved setting a mob's chase/attack states from the mob_ai to unit_attack and unit_walktobl. - Cleaned the change-target/cast-sensor code to account for the new mob modes. - Cleaned up a bit the mob ai sub hard function. - Made the monster_active_enable config setting take effect on mob load. - Updated the doc explaining mob modes. git-svn-id: https://rathena.svn.sourceforge.net/svnroot/rathena/trunk@9422 54d463be-8e91-2dee-dedb-b68131a5f0ec
-rw-r--r--Changelog-Trunk.txt5
-rw-r--r--doc/mob_db_mode_list.txt102
-rw-r--r--src/map/map.c69
-rw-r--r--src/map/map.h2
-rw-r--r--src/map/mob.c233
-rw-r--r--src/map/skill.c10
-rw-r--r--src/map/status.c4
-rw-r--r--src/map/status.h29
-rw-r--r--src/map/unit.c50
9 files changed, 272 insertions, 232 deletions
diff --git a/Changelog-Trunk.txt b/Changelog-Trunk.txt
index abf49c893..5edf458ab 100644
--- a/Changelog-Trunk.txt
+++ b/Changelog-Trunk.txt
@@ -4,6 +4,11 @@ AS OF SVN REV. 5091, WE ARE NOW USING TRUNK. ALL UNTESTED BUGFIXES/FEATURES GO
IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK.
2004/12/06
+ * Moved setting a mob's chase/attack states from the mob_ai to unit_attack
+ and unit_walktobl for better state-handling precision. [Skotlex]
+ * Cleaned the change-target/cast-sensor code to account for the new mob
+ modes. [Skotlex]
+ * Updated the doc explaining mob modes. [Skotlex]
* When GM skill unconditional is set, the arrow state of the skill will be
calculated, this fixes all skills getting stuck on arrow-type after you
attack normally. [Skotlex]
diff --git a/doc/mob_db_mode_list.txt b/doc/mob_db_mode_list.txt
index 3fa713575..7b2974c41 100644
--- a/doc/mob_db_mode_list.txt
+++ b/doc/mob_db_mode_list.txt
@@ -1,50 +1,58 @@
Bit Legend:
----------
-MD_CANMOVE | 0x001 | 1
-MD_LOOTER | 0x002 | 2
-MD_AGGRESSIVE | 0x004 | 4
-MD_ASSIST | 0x008 | 8
-MD_CASTSENSOR | 0x010 | 16
-MD_BOSS | 0x020 | 32
-MD_PLANT | 0x040 | 64
-MD_CANATTACK | 0x080 | 128
-MD_DETECTOR | 0x100 | 256
-MD_CHANGETARGET | 0x200 | 512 //removed, not needed
-MD_CHANGECHASE | 0x400 | 1024
-MD_ANGRY | 0x800 | 2048
+MD_CANMOVE | 0x0001 | 1
+MD_LOOTER | 0x0002 | 2
+MD_AGGRESSIVE | 0x0004 | 4
+MD_ASSIST | 0x0008 | 8
+MD_CASTSENSOR_MELEE | 0x0010 | 16
+MD_BOSS | 0x0020 | 32
+MD_PLANT | 0x0040 | 64
+MD_CANATTACK | 0x0080 | 128
+MD_DETECTOR | 0x0100 | 256
+MD_CASTSENSOR_CHASE | 0x0200 | 512
+MD_CHANGECHASE | 0x0400 | 1024
+MD_ANGRY | 0x0800 | 2048
+MD_CHANGETARGET_MELEE | 0x1000 | 4096
+MD_CHANGETARGET_CHASE | 0x2000 | 8192
-----------
-1: Can Move.
-2: looter.
-4: Aggressive: normal aggressive mob, will look for a close-by player and charge him until death. Aggressive mobs also can change target if they are hit by someone else while chasing someone else.
-8: Assist
-16: Cast Sensor: If mob is aggressive, it will also change targets while chasing/following someone else, otherwise, it'll also be cast-sensitive while idle/random-walking.
-32: Boss. Special flag which makes mobs inmune to certain stuff like status-changes and basilica.
-64: Plant. Always receives 1 damage from stuff.
-128: Can Attack. Everyone with a few exceptions has this. Only prevents normal attacks, not skills.
-256: Detector: can see hidden players. Insects, Demons, and Bosses automatically receive this.
-1024: ChangeChase: When the mob is chasing a player and it passes nearby another player, it will switch targets and hit this other player. Note this only applies if the new player is withing attack range, it won't change it's chase-path to another player!
-2048: Angry: These mobs are "hyper-active". They will change to the closest player while they are following up, They also have skill states "follow" and "angry" (instead of "chase"/"attack") when they pick a new target by their own (ie: not when they change targets due to being attacked). If attacked even once, they loose their Angry mode.
-
-Change Target: (coded)
-Only Assist, Angry or Aggressive+CastSensor mobs can change target while attacking. Target must be no more than 3 cells away.
-Mob in Hyper-active mode can change target unconditionally.
-Mob that chases player can change target only, when he's Aggresive and get attacked.
-
-Old -> New
-----------
-64 -> 64: Plants
-128 -> 0: Passive immobile beings (pupa)
-129 -> 129: Normal enemies
-131 -> 131: looters (porings)
-132 -> 132: Immobile attackers (hydra)
-133 -> 133: Aggressive
-137 -> 137: Assists others (wolves)
-139 -> 139: Assists & loots (thief bugs)
-141 -> 141: Aggressive & Assists (no idea how these would differ from plain aggressive, but there's a lot of enemies defined this way)
-145 -> 145: Detectors (Giearth)
-149 -> 149: Aggressive & Detector (Hunter Fly)
-159 -> 159: Cast sensor, Assist, Agressive, Looter
-171 -> 171: Boss, summons, assists & loots (Golden Thief Bug)
-181 -> 1461: Boss, summons, detector & aggressive (pretty much every MVP and miniboss)
-none -> 2181 : Hyper-active (Familiar) \ No newline at end of file
+Explanation for modes:
+---------------------
+
+CanMove: Enables the mob to move/chase characters.
+
+CanAttack: Enables the mob to attack/retaliate when you are within attack
+ range. Note that this only enables them to use normal attacks, skills are
+ always allowed.
+
+Looter: The mob will loot up nearby items on the ground when it's on idle state.
+
+Aggressive: normal aggressive mob, will look for a close-by player to attack.
+
+Assist: When a nearby mob of the same class attacks, assist types will join them.
+
+Cast Sensor Melee: Will go after characters who start casting on them if idle
+ or during melee attacks (they will leave their current target alone)
+
+Cast Sensor Chase: Will go after characters who start casting on them if idle
+ or chasing other players (they switch chase targets)
+
+Boss: Special flag which makes mobs inmune to certain status changes and skills.
+
+Plant: Always receives 1 damage from attacks.
+
+Detector: Enables mob to detect and attack characters who are in hiding/cloak.
+
+ChangeChase: Allows chasing mobs to switch targets if another player happens
+ to be within attack range (handy on ranged attackers, for example)
+
+Angry: These mobs are "hyper-active". Apart from "chase"/"attack", they have
+ the states "follow"/"angry". Once hit, they stop using these states and use
+ the normal ones. The new states are used to determine a different skill-set
+ for their "before attacked" and "after attacked" states. Also, when
+ "following", they automatically switch to whoever character is closest.
+
+Change Target Melee: Enables a mob to switch targets when attacked while
+ attacking someone else.
+
+Change Target Chase: Enables a mob to switch targets when attacked while
+ chasing another character.
diff --git a/src/map/map.c b/src/map/map.c
index 3a321fc68..1247206ab 100644
--- a/src/map/map.c
+++ b/src/map/map.c
@@ -629,10 +629,8 @@ int map_foreachinrange(int (*func)(struct block_list*,va_list),struct block_list
struct block_list *bl=NULL;
int blockcount=bl_list_count,i,c;
int x0,x1,y0,y1;
- m = center->m;
- if (m < 0)
- return 0;
va_start(ap,type);
+ m = center->m;
x0 = center->x-range;
x1 = center->x+range;
y0 = center->y-range;
@@ -854,14 +852,23 @@ int map_foreachinarea(int (*func)(struct block_list*,va_list),int m,int x0,int y
* dx,dyは-1,0,1のみとする(どんな値でもいいっぽい?)
*------------------------------------------
*/
-int map_foreachinmovearea(int (*func)(struct block_list*,va_list),int m,int x0,int y0,int x1,int y1,int dx,int dy,int type,...) {
- int bx,by;
+int map_foreachinmovearea(int (*func)(struct block_list*,va_list),struct block_list *center,int range, int dx,int dy,int type,...) {
+ int bx,by,m;
int returnCount =0; //total sum of returned values of func() [Skotlex]
struct block_list *bl=NULL;
va_list ap;
int blockcount=bl_list_count,i,c;
+ int x0, x1, y0, y1;
+ if (!range) return 0;
+ if (!dx && !dy) return 0; //No movement.
va_start(ap,type);
+ m = center->m;
+ x0 = center->x-range;
+ x1 = center->x+range;
+ y0 = center->y-range;
+ y1 = center->y+range;
+
if (x1 < x0)
{ //Swap range
bx = x0;
@@ -875,19 +882,17 @@ int map_foreachinmovearea(int (*func)(struct block_list*,va_list),int m,int x0,i
y1 = bx;
}
if(dx==0 || dy==0){
- // 矩形領域の場合
+ //Movement along one axis only.
if(dx==0){
- if(dy<0){
+ if(dy<0) //Moving south
y0=y1+dy+1;
- } else {
+ else //North
y1=y0+dy-1;
- }
- } else if(dy==0){
- if(dx<0){
+ } else { //dy == 0
+ if(dx<0) //West
x0=x1+dx+1;
- } else {
+ else //East
x1=x0+dx-1;
- }
}
if(x0<0) x0=0;
if(y0<0) y0=0;
@@ -899,7 +904,10 @@ int map_foreachinmovearea(int (*func)(struct block_list*,va_list),int m,int x0,i
bl = map[m].block[bx+by*map[m].bxs];
c = map[m].block_count[bx+by*map[m].bxs];
for(i=0;i<c && bl;i++,bl=bl->next){
- if(bl && bl->type&type && bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1 && bl_list_count<BL_LIST_MAX)
+ if(bl->type&type &&
+ bl->x>=x0 && bl->x<=x1 &&
+ bl->y>=y0 && bl->y<=y1 &&
+ bl_list_count<BL_LIST_MAX)
bl_list[bl_list_count++]=bl;
}
}
@@ -907,15 +915,16 @@ int map_foreachinmovearea(int (*func)(struct block_list*,va_list),int m,int x0,i
bl = map[m].block_mob[bx+by*map[m].bxs];
c = map[m].block_mob_count[bx+by*map[m].bxs];
for(i=0;i<c && bl;i++,bl=bl->next){
- if(bl && bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1 && bl_list_count<BL_LIST_MAX)
+ if(bl->x>=x0 && bl->x<=x1 &&
+ bl->y>=y0 && bl->y<=y1 &&
+ bl_list_count<BL_LIST_MAX)
bl_list[bl_list_count++]=bl;
}
}
}
}
}else{
- // L字領域の場合
-
+ // Diagonal movement
if(x0<0) x0=0;
if(y0<0) y0=0;
if(x1>=map[m].xs) x1=map[m].xs-1;
@@ -926,23 +935,31 @@ int map_foreachinmovearea(int (*func)(struct block_list*,va_list),int m,int x0,i
bl = map[m].block[bx+by*map[m].bxs];
c = map[m].block_count[bx+by*map[m].bxs];
for(i=0;i<c && bl;i++,bl=bl->next){
- if(!bl || !(bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1))
+ if(!(bl->type&type &&
+ bl->x>=x0 && bl->x<=x1 &&
+ bl->y>=y0 && bl->y<=y1 &&
+ bl_list_count<BL_LIST_MAX))
continue;
- if(bl && bl->type&type && ((dx>0 && bl->x<x0+dx) || (dx<0 && bl->x>x1+dx) ||
- (dy>0 && bl->y<y0+dy) || (dy<0 && bl->y>y1+dy)) &&
- bl_list_count<BL_LIST_MAX)
- bl_list[bl_list_count++]=bl;
+ if((dx>0 && bl->x<x0+dx) ||
+ (dx<0 && bl->x>x1+dx) ||
+ (dy>0 && bl->y<y0+dy) ||
+ (dy<0 && bl->y>y1+dy))
+ bl_list[bl_list_count++]=bl;
}
}
if (type & BL_MOB) {
bl = map[m].block_mob[bx+by*map[m].bxs];
c = map[m].block_mob_count[bx+by*map[m].bxs];
for(i=0;i<c && bl;i++,bl=bl->next){
- if(!bl || !(bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1))
+ if(!(
+ bl->x>=x0 && bl->x<=x1 &&
+ bl->y>=y0 && bl->y<=y1 &&
+ bl_list_count<BL_LIST_MAX))
continue;
- if(bl && ((dx>0 && bl->x<x0+dx) || (dx<0 && bl->x>x1+dx) ||
- (dy>0 && bl->y<y0+dy) || (dy<0 && bl->y>y1+dy)) &&
- bl_list_count<BL_LIST_MAX)
+ if((dx>0 && bl->x<x0+dx) ||
+ (dx<0 && bl->x>x1+dx) ||
+ (dy>0 && bl->y<y0+dy) ||
+ (dy<0 && bl->y>y1+dy))
bl_list[bl_list_count++]=bl;
}
}
diff --git a/src/map/map.h b/src/map/map.h
index 2f61a07d5..5bf9ab006 100644
--- a/src/map/map.h
+++ b/src/map/map.h
@@ -1327,7 +1327,7 @@ int map_foreachinshootrange(int (*)(struct block_list*,va_list),struct block_lis
int map_foreachinarea(int (*)(struct block_list*,va_list),int,int,int,int,int,int,...);
// -- moonsoul (added map_foreachincell)
int map_foreachincell(int (*)(struct block_list*,va_list),int,int,int,int,...);
-int map_foreachinmovearea(int (*)(struct block_list*,va_list),int,int,int,int,int,int,int,int,...);
+int map_foreachinmovearea(int (*)(struct block_list*,va_list),struct block_list*,int,int,int,int,...);
int map_foreachinpath(int (*func)(struct block_list*,va_list),int m,int x0,int y0,int x1,int y1,int range,int type,...); // Celest
int map_foreachinmap(int (*)(struct block_list*,va_list),int,int,...);
int map_countnearpc(int,int,int);
diff --git a/src/map/mob.c b/src/map/mob.c
index 5f22c8212..fb26a9d26 100644
--- a/src/map/mob.c
+++ b/src/map/mob.c
@@ -726,13 +726,12 @@ static int mob_can_changetarget(struct mob_data* md, struct block_list* target,
}
switch (md->state.skillstate) {
- case MSS_BERSERK: //Only Assist, Angry or Aggressive+CastSensor mobs can change target while attacking.
- if (mode&(MD_ASSIST|MD_ANGRY|MD_CHANGETARGET) || (mode&(MD_AGGRESSIVE|MD_CASTSENSOR)) == (MD_AGGRESSIVE|MD_CASTSENSOR))
- return (battle_config.mob_ai&0x4 || check_distance_bl(&md->bl, target, 3));
- else
+ case MSS_BERSERK:
+ if (!mode&MD_CHANGETARGET_MELEE)
return 0;
+ return (battle_config.mob_ai&0x4 || check_distance_bl(&md->bl, target, 3));
case MSS_RUSH:
- return (mode&MD_AGGRESSIVE);
+ return (mode&MD_CHANGETARGET_CHASE);
case MSS_FOLLOW:
case MSS_ANGRY:
case MSS_IDLE:
@@ -1156,7 +1155,6 @@ static int mob_ai_sub_hard(struct block_list *bl,va_list ap)
{ //Change if the new target is closer than the actual one
//or if the previous target is not attacking the mob. [Skotlex]
md->target_id = md->attacked_id; // set target
- md->state.aggressive = 0; //Retaliating.
if (md->attacked_count)
md->attacked_count--; //Should we reset rude attack count?
md->min_chase = dist+md->db->range3;
@@ -1167,7 +1165,13 @@ static int mob_ai_sub_hard(struct block_list *bl,va_list ap)
}
}
if (md->state.aggressive && md->attacked_id == md->target_id)
- md->state.aggressive = 0; //No longer aggressive, change to retaliate AI.
+ { //No longer aggressive, change to retaliate AI.
+ md->state.aggressive = 0;
+ if(md->state.skillstate== MSS_ANGRY)
+ md->state.skillstate = MSS_BERSERK;
+ if(md->state.skillstate== MSS_FOLLOW)
+ md->state.skillstate = MSS_RUSH;
+ }
//Clear it since it's been checked for already.
md->attacked_players = 0;
md->attacked_id = 0;
@@ -1185,135 +1189,126 @@ static int mob_ai_sub_hard(struct block_list *bl,va_list ap)
view_range, BL_ITEM, md, &tbl);
}
- if ((!tbl && mode&MD_AGGRESSIVE && battle_config.monster_active_enable) ||
- (mode&MD_ANGRY && md->state.skillstate == MSS_FOLLOW)
- ) {
+ if ((!tbl && mode&MD_AGGRESSIVE) || md->state.skillstate == MSS_FOLLOW)
+ {
map_foreachinrange (mob_ai_sub_hard_activesearch, &md->bl,
view_range, md->special_state.ai?BL_CHAR:BL_PC|BL_HOM, md, &tbl);
- if(!tbl && mode&MD_ANGRY && !md->state.aggressive)
- md->state.aggressive = 1; //Restore angry state when no targets are visible.
- } else if (mode&MD_CHANGECHASE && (md->state.skillstate == MSS_RUSH || md->state.skillstate == MSS_FOLLOW)) {
+ } else
+ if (mode&MD_CHANGECHASE && (md->state.skillstate == MSS_RUSH || md->state.skillstate == MSS_FOLLOW))
+ {
search_size = view_range<md->status.rhw.range ? view_range:md->status.rhw.range;
map_foreachinrange (mob_ai_sub_hard_changechase, &md->bl,
search_size, (md->special_state.ai?BL_CHAR:BL_PC|BL_HOM), md, &tbl);
}
- if (tbl)
- { //Target exists, attack or loot as applicable.
- if (tbl->type != BL_ITEM)
- { //Attempt to attack.
- //At this point we know the target is attackable, we just gotta check if the range matches.
- if (md->ud.target == tbl->id && md->ud.attacktimer != -1)
- {
- if (md->state.skillstate!=(md->state.aggressive?MSS_ANGRY:MSS_BERSERK))
- md->state.skillstate = md->state.aggressive?MSS_ANGRY:MSS_BERSERK; //Correct the state.
- return 0; //Already locked.
- }
-
- if (!battle_check_range (&md->bl, tbl, md->status.rhw.range))
- { //Out of range...
- if (!(mode&MD_CANMOVE))
- { //Can't chase. Attempt to use a ranged skill at least?
- md->state.skillstate = MSS_IDLE;
- if (!mobskill_use(md, tick, -1))
- mob_unlocktarget(md,tick);
- return 0;
- }
+ if (!tbl) { //No targets available.
+ if (mode&MD_ANGRY && !md->state.aggressive)
+ md->state.aggressive = 1; //Restore angry state when no targets are available.
- if (!can_move)
- { //Stuck. Use an idle skill. o.O'
- md->state.skillstate = MSS_IDLE;
- if (!(++md->ud.walk_count%IDLE_SKILL_INTERVAL))
- mobskill_use(md, tick, -1);
- return 0;
- }
-
- md->state.skillstate = md->state.aggressive?MSS_FOLLOW:MSS_RUSH;
- if (md->ud.walktimer != -1 && md->ud.target == tbl->id &&
- (
- !(battle_config.mob_ai&0x1) ||
- check_distance_blxy(tbl, md->ud.to_x, md->ud.to_y, md->status.rhw.range)
- )) //Current target tile is still within attack range.
- return 0;
-
- //Follow up
- if (!mob_can_reach(md, tbl, md->min_chase, MSS_RUSH) ||
- !unit_walktobl(&md->bl, tbl, md->status.rhw.range, 2))
- //Give up.
- mob_unlocktarget(md,tick);
+ if(md->ud.walktimer == -1) {
+ // Idle skill.
+ md->state.skillstate = MSS_IDLE;
+ if (!(++md->ud.walk_count%IDLE_SKILL_INTERVAL) && mobskill_use(md, tick, -1))
return 0;
- }
- //Target within range, engage
- md->state.skillstate = md->state.aggressive?MSS_ANGRY:MSS_BERSERK;
- unit_attack(&md->bl,tbl->id,1);
+ }
+ // Random walk.
+ if (can_move && !md->master_id && DIFF_TICK(md->next_walktime, tick) <= 0)
+ mob_randomwalk(md,tick);
+ return 0;
+ }
+
+ //Target exists, attack or loot as applicable.
+ if (tbl->type == BL_ITEM)
+ { //Loot time.
+ struct flooritem_data *fitem;
+ if (md->ud.target == tbl->id && md->ud.walktimer != -1)
+ return 0; //Already locked.
+ if (md->lootitem == NULL)
+ { //Can't loot...
+ mob_unlocktarget (md, tick);
+ mob_stop_walking(md,0);
return 0;
- } else { //Target is BL_ITEM, attempt loot.
- struct flooritem_data *fitem;
- int i;
- if (md->ud.target == tbl->id && md->ud.walktimer != -1)
- return 0; //Already locked.
- if (md->lootitem == NULL)
- { //Can't loot...
- mob_unlocktarget (md, tick);
- mob_stop_walking(md,0);
+ }
+ if (!check_distance_bl(&md->bl, tbl, 1))
+ { //Still not within loot range.
+ if (!(mode&MD_CANMOVE))
+ { //A looter that can't move? Real smart.
+ mob_unlocktarget(md,tick);
return 0;
}
-
- if (!check_distance_bl(&md->bl, tbl, 1))
- { //Still not within loot range.
- if (!(mode&MD_CANMOVE))
- { //A looter that can't move? Real smart.
- mob_unlocktarget(md,tick);
- return 0;
- }
- if (!can_move) //Stuck. Wait before walking.
- return 0;
- md->state.skillstate = MSS_LOOT; // ルート時スキル使用
- if (!unit_walktobl(&md->bl, tbl, 0, 1))
- mob_unlocktarget(md, tick); //Can't loot...
+ if (!can_move) //Stuck. Wait before walking.
return 0;
- }
- //Within looting range.
- if (md->ud.attacktimer != -1)
- return 0; //Busy attacking?
-
- fitem = (struct flooritem_data *)tbl;
- if (md->lootitem_count < LOOTITEM_SIZE) {
- memcpy (&md->lootitem[md->lootitem_count++], &fitem->item_data, sizeof(md->lootitem[0]));
- if(log_config.enable_logs&0x10) //Logs items, taken by (L)ooter Mobs [Lupus]
- log_pick_mob(md, "L", md->lootitem[md->lootitem_count-1].nameid, md->lootitem[md->lootitem_count-1].amount, &md->lootitem[md->lootitem_count-1]);
- } else { //Destroy first looted item...
- if (md->lootitem[0].card[0] == (short)0xff00)
- intif_delete_petdata( MakeDWord(md->lootitem[0].card[1],md->lootitem[0].card[2]) );
- for (i = 0; i < LOOTITEM_SIZE - 1; i++)
- memcpy (&md->lootitem[i], &md->lootitem[i+1], sizeof(md->lootitem[0]));
- memcpy (&md->lootitem[LOOTITEM_SIZE-1], &fitem->item_data, sizeof(md->lootitem[0]));
- }
- //Clear item.
- if (pcdb_checkid(md->vd->class_))
- { //Give them walk act/delay to properly mimic players. [Skotlex]
- clif_takeitem(&md->bl,tbl);
- md->ud.canact_tick = tick + md->status.amotion;
- unit_set_walkdelay(&md->bl, tick, md->status.amotion, 1);
- }
- map_clearflooritem (tbl->id);
- mob_unlocktarget (md,tick);
+ md->state.skillstate = MSS_LOOT;
+ if (!unit_walktobl(&md->bl, tbl, 0, 1))
+ mob_unlocktarget(md, tick); //Can't loot...
return 0;
}
+ //Within looting range.
+ if (md->ud.attacktimer != -1)
+ return 0; //Busy attacking?
+
+ fitem = (struct flooritem_data *)tbl;
+ if(log_config.enable_logs&0x10) //Logs items, taken by (L)ooter Mobs [Lupus]
+ log_pick_mob(md, "L", fitem->item_data.nameid, fitem->item_data.amount, &fitem->item_data);
+
+ if (md->lootitem_count < LOOTITEM_SIZE) {
+ memcpy (&md->lootitem[md->lootitem_count++], &fitem->item_data, sizeof(md->lootitem[0]));
+ } else { //Destroy first looted item...
+ if (md->lootitem[0].card[0] == CARD0_PET)
+ intif_delete_petdata( MakeDWord(md->lootitem[0].card[1],md->lootitem[0].card[2]) );
+ memmove(&md->lootitem[0], &md->lootitem[1], sizeof(md->lootitem) - sizeof(md->lootitem[0]));
+ memcpy (&md->lootitem[LOOTITEM_SIZE-1], &fitem->item_data, sizeof(md->lootitem[0]));
+ }
+ if (pcdb_checkid(md->vd->class_))
+ { //Give them walk act/delay to properly mimic players. [Skotlex]
+ clif_takeitem(&md->bl,tbl);
+ md->ud.canact_tick = tick + md->status.amotion;
+ unit_set_walkdelay(&md->bl, tick, md->status.amotion, 1);
+ }
+ //Clear item.
+ map_clearflooritem (tbl->id);
+ mob_unlocktarget (md,tick);
+ return 0;
+ }
+ //Attempt to attack.
+ //At this point we know the target is attackable, we just gotta check if the range matches.
+ if (md->ud.target == tbl->id && md->ud.attacktimer != -1) //Already locked.
+ return 0;
+
+ if (battle_check_range (&md->bl, tbl, md->status.rhw.range))
+ { //Target within range, engage
+ unit_attack(&md->bl,tbl->id,1);
+ return 0;
}
- if(md->ud.walktimer == -1) {
- // When there's no target, it is idling.
- // Is it terribly exploitable to reuse the walkcounter for idle state skills? [Skotlex]
+ //Out of range...
+ if (!(mode&MD_CANMOVE))
+ { //Can't chase. Attempt an idle skill before unlocking.
md->state.skillstate = MSS_IDLE;
- if (!(++md->ud.walk_count%IDLE_SKILL_INTERVAL) && mobskill_use(md, tick, -1))
- return 0;
+ if (!mobskill_use(md, tick, -1))
+ mob_unlocktarget(md,tick);
+ return 0;
}
- // Nothing else to do... except random walking.
- // Slaves do not random walk! [Skotlex]
- if (can_move && !md->master_id && DIFF_TICK(md->next_walktime, tick) <= 0)
- mob_randomwalk(md,tick);
+ if (!can_move)
+ { //Stuck. Attempt an idle skill
+ md->state.skillstate = MSS_IDLE;
+ if (!(++md->ud.walk_count%IDLE_SKILL_INTERVAL))
+ mobskill_use(md, tick, -1);
+ return 0;
+ }
+
+ if (md->ud.walktimer != -1 && md->ud.target == tbl->id &&
+ (
+ !(battle_config.mob_ai&0x1) ||
+ check_distance_blxy(tbl, md->ud.to_x, md->ud.to_y, md->status.rhw.range)
+ )) //Current target tile is still within attack range.
+ return 0;
+
+ //Follow up if possible.
+ if (!mob_can_reach(md, tbl, md->min_chase, MSS_RUSH) ||
+ !unit_walktobl(&md->bl, tbl, md->status.rhw.range, 2))
+ mob_unlocktarget(md,tick);
return 0;
}
@@ -3315,7 +3310,9 @@ static int mob_readdb(void)
ShowWarning("Mob with ID: %d has invalid element level %d (max is 4)\n", class_, status->ele_lv);
status->ele_lv = 1;
}
- status->mode=atoi(str[25]);
+ status->mode=(int)strtol(str[25],NULL,0);
+ if (!battle_config.monster_active_enable)
+ status->mode&=~MD_AGGRESSIVE;
status->speed=atoi(str[26]);
status->aspd_rate = 1000;
db->min_thinktime=atoi(str[27]);
@@ -4002,6 +3999,8 @@ static int mob_read_sqldb(void)
status->ele_lv = 1;
}
status->mode = TO_INT(25);
+ if (!battle_config.monster_active_enable)
+ status->mode&=~MD_AGGRESSIVE;
status->speed = TO_INT(26);
status->aspd_rate = 1000;
db->min_thinktime = TO_INT(27);
diff --git a/src/map/skill.c b/src/map/skill.c
index 378e46796..daf0abc04 100644
--- a/src/map/skill.c
+++ b/src/map/skill.c
@@ -1780,18 +1780,16 @@ int skill_blown (struct block_list *src, struct block_list *target, int count)
if (!dx && !dy) //Could not knockback.
return 0;
- map_foreachinmovearea(clif_outsight,target->m,
- x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,
- dx,dy,target->type==BL_PC?BL_ALL:BL_PC,target);
+ map_foreachinmovearea(clif_outsight, target, AREA_SIZE,
+ dx, dy, target->type==BL_PC?BL_ALL:BL_PC, target);
if(su)
skill_unit_move_unit_group(su->group,target->m,dx,dy);
else
map_moveblock(target, nx, ny, gettick());
- map_foreachinmovearea(clif_insight,target->m,
- nx-AREA_SIZE,ny-AREA_SIZE,nx+AREA_SIZE,ny+AREA_SIZE,
- -dx,-dy,target->type==BL_PC?BL_ALL:BL_PC,target);
+ map_foreachinmovearea(clif_insight, target, AREA_SIZE,
+ -dx, -dy, target->type==BL_PC?BL_ALL:BL_PC, target);
if(!(count&0x20000))
clif_blown(target);
diff --git a/src/map/status.c b/src/map/status.c
index 6a2ef578f..397319d72 100644
--- a/src/map/status.c
+++ b/src/map/status.c
@@ -1633,7 +1633,7 @@ int status_calc_pc(struct map_session_data* sd,int first)
//FIXME: Most of these stuff should be calculated once, but how do I fix the malloc_set above to do that? [Skotlex]
status->speed = DEFAULT_WALK_SPEED;
- status->mode = MD_CANMOVE|MD_CANATTACK|MD_LOOTER|MD_ASSIST|MD_AGGRESSIVE|MD_CASTSENSOR;
+ status->mode = MD_CANMOVE|MD_CANATTACK|MD_LOOTER|MD_ASSIST|MD_AGGRESSIVE|MD_CASTSENSOR_MELEE|MD_CASTSENSOR_CHASE;
status->size = (sd->class_&JOBL_BABY)?0:1;
if (battle_config.character_size && pc_isriding(sd)) { //[Lupus]
if (sd->class_&JOBL_BABY) {
@@ -2373,7 +2373,7 @@ int status_calc_homunculus(struct homun_data *hd, int first)
status->race = hd->homunculusDB->race ;
status->size = hd->homunculusDB->size ;
status->rhw.range = 1 + status->size;
- status->mode = MD_CANMOVE|MD_CANATTACK|MD_ASSIST|MD_AGGRESSIVE|MD_CASTSENSOR;
+ status->mode = MD_CANMOVE|MD_CANATTACK;
status->speed = DEFAULT_WALK_SPEED;
if (battle_config.slaves_inherit_speed&1 &&
hd->master && hd->master->state.auth) //Master needs be authed to have valid speed.
diff --git a/src/map/status.h b/src/map/status.h
index c680ab89b..9986ad9a0 100644
--- a/src/map/status.h
+++ b/src/map/status.h
@@ -408,20 +408,21 @@ extern int current_equip_card_id;
extern int percentrefinery[5][MAX_REFINE+1]; //The last slot always has a 0% success chance [Skotlex]
//Mode definitions to clear up code reading. [Skotlex]
-#define MD_CANMOVE 0x001
-#define MD_LOOTER 0x002
-//MD_ANGRY mobs are also aggressive.
-#define MD_AGGRESSIVE 0x804
-#define MD_ASSIST 0x008
-#define MD_CASTSENSOR 0x010
-#define MD_BOSS 0x020
-#define MD_PLANT 0x040
-#define MD_CANATTACK 0x080
-#define MD_DETECTOR 0x100
-#define MD_CHANGETARGET 0x200
-#define MD_CHANGECHASE 0x400
-#define MD_ANGRY 0x800
-#define MD_MASK 0xFFF
+#define MD_CANMOVE 0x0001
+#define MD_LOOTER 0x0002
+#define MD_AGGRESSIVE 0x0004
+#define MD_ASSIST 0x0008
+#define MD_CASTSENSOR_MELEE 0x0010
+#define MD_BOSS 0x0020
+#define MD_PLANT 0x0040
+#define MD_CANATTACK 0x0080
+#define MD_DETECTOR 0x0100
+#define MD_CASTSENSOR_CHASE 0x0200
+#define MD_CHANGECHASE 0x0400
+#define MD_ANGRY 0x0800
+#define MD_CHANGETARGET_MELEE 0x1000
+#define MD_CHANGETARGET_CHASE 0x2000
+#define MD_MASK 0xFFFF
//Status change option definitions (options are what makes status changes visible to chars
//who were not on your field of sight when it happened)
diff --git a/src/map/unit.c b/src/map/unit.c
index 9b6e4e85c..ef60579b6 100644
--- a/src/map/unit.c
+++ b/src/map/unit.c
@@ -145,9 +145,8 @@ static int unit_walktoxy_timer(int tid,unsigned int tick,int id,int data)
// バシリカ判定
- map_foreachinmovearea(clif_outsight,bl->m,
- x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,
- dx,dy,sd?BL_ALL:BL_PC,bl);
+ map_foreachinmovearea(clif_outsight,bl, AREA_SIZE,
+ dx, dy, sd?BL_ALL:BL_PC, bl);
x += dx;
y += dy;
@@ -155,9 +154,8 @@ static int unit_walktoxy_timer(int tid,unsigned int tick,int id,int data)
ud->walk_count++; //walked cell counter, to be used for walk-triggered skills. [Skotlex]
ud->walktimer = 1;
- map_foreachinmovearea(clif_insight,bl->m,
- x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,
- -dx,-dy,sd?BL_ALL:BL_PC,bl);
+ map_foreachinmovearea(clif_insight, bl, AREA_SIZE,
+ -dx, -dy, sd?BL_ALL:BL_PC, bl);
ud->walktimer = -1;
if(sd) {
@@ -244,9 +242,10 @@ static int unit_walktoxy_timer(int tid,unsigned int tick,int id,int data)
}
if (tbl->m == bl->m && check_distance_bl(bl, tbl, ud->chaserange))
{ //Reached destination.
- if (ud->state.attack_continue) {
- clif_fixpos(bl); //Aegis uses one before every attack, we should
- //only need this one for syncing purposes. [Skotlex]
+ if (ud->state.attack_continue)
+ { //Aegis uses one before every attack, we should
+ //only need this one for syncing purposes. [Skotlex]
+ clif_fixpos(bl);
unit_attack(bl, tbl->id, ud->state.attack_continue);
}
} else { //Update chase-path
@@ -342,6 +341,10 @@ int unit_walktobl(struct block_list *bl, struct block_list *tbl, int range, int
if (sc && sc->count && sc->data[SC_CONFUSION].timer != -1) //Randomize the target position
map_random_dir(bl, &ud->to_x, &ud->to_y);
+ //Set Mob's CHASE/FOLLOW states.
+ if(bl->type == BL_MOB && flag&2)
+ ((TBL_MOB*)bl)->state.skillstate = ((TBL_MOB*)bl)->state.aggressive?MSS_FOLLOW:MSS_RUSH;
+
if(ud->walktimer != -1) {
ud->state.change_walk_target = 1;
return 1;
@@ -438,16 +441,14 @@ int unit_movepos(struct block_list *bl,int dst_x,int dst_y, int easy, int checkp
dx = dst_x - bl->x;
dy = dst_y - bl->y;
- map_foreachinmovearea(clif_outsight,bl->m,
- bl->x-AREA_SIZE,bl->y-AREA_SIZE,bl->x+AREA_SIZE,bl->y+AREA_SIZE,
- dx,dy,sd?BL_ALL:BL_PC,bl);
+ map_foreachinmovearea(clif_outsight, bl, AREA_SIZE,
+ dx, dy, sd?BL_ALL:BL_PC, bl);
map_moveblock(bl, dst_x, dst_y, gettick());
ud->walktimer = 1;
- map_foreachinmovearea(clif_insight,bl->m,
- bl->x-AREA_SIZE,bl->y-AREA_SIZE,bl->x+AREA_SIZE,bl->y+AREA_SIZE,
- -dx,-dy,sd?BL_ALL:BL_PC,bl);
+ map_foreachinmovearea(clif_insight, bl, AREA_SIZE,
+ -dx, -dy, sd?BL_ALL:BL_PC, bl);
ud->walktimer = -1;
if(sd) {
@@ -950,15 +951,22 @@ int unit_skilluse_id2(struct block_list *src, int target_id, int skill_num, int
TBL_MOB *md = (TBL_MOB*)target;
mobskill_event(md, src, tick, -1); //Cast targetted skill event.
//temp: used to store mob's mode now.
- if (tstatus->mode&MD_CASTSENSOR &&
+ if (tstatus->mode&(MD_CASTSENSOR_MELEE|MD_CASTSENSOR_CHASE) &&
battle_check_target(target, src, BCT_ENEMY) > 0)
{
switch (md->state.skillstate) {
- case MSS_ANGRY:
case MSS_RUSH:
case MSS_FOLLOW:
- if (!(tstatus->mode&(MD_AGGRESSIVE|MD_ANGRY)))
- break; //Only Aggressive mobs change target while chasing.
+ if (!(tstatus->mode&MD_CASTSENSOR_CHASE))
+ break;
+ md->target_id = src->id;
+ md->state.aggressive = (temp&MD_ANGRY)?1:0;
+ md->min_chase = md->db->range3;
+ break;
+ case MSS_ANGRY:
+ case MSS_BERSERK:
+ if (!(tstatus->mode&MD_CASTSENSOR_MELEE))
+ break;
case MSS_IDLE:
case MSS_WALK:
md->target_id = src->id;
@@ -1168,6 +1176,10 @@ int unit_attack(struct block_list *src,int target_id,int type)
if(ud->attacktimer != -1)
return 0;
+ //Set Mob's ANGRY/BERSERK states.
+ if(src->type == BL_MOB)
+ ((TBL_MOB*)src)->state.skillstate = ((TBL_MOB*)src)->state.aggressive?MSS_ANGRY:MSS_BERSERK;
+
if(DIFF_TICK(ud->attackabletime, gettick()) > 0)
//Do attack next time it is possible. [Skotlex]
ud->attacktimer=add_timer(ud->attackabletime,unit_attack_timer,src->id,0);