--.--.-- *--*
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

取り巻きモンスターの挙動

2012.08.12 *Sun*
だいぶ前にBlazeさんがAthenaの取り巻きモンスターの挙動の記事を書いておられました。
http://www.usamimi.info/~blaze/cgi-bin/diarypro/diary.cgi?no=286

私はこれ以前のAthenaの取り巻きの挙動は知らないのですが、
このBlazeさんのコードの入っている現在のAurigaの取り巻きの動きは、
やはり本鯖のBOSSの取り巻きのそれとは随分違う動きをしています。

Aurigaの場合、移動の最中にランダムでジグザグに移動して
最終的に散らばったように見える感じです。また重なってしまう子もいます。

本鯖は、BOSSが移動すると、まずBOSSの周囲に向かって束になって移動し、
近づき終わると、ばらばらと回りに散らばる感じです。
また障害物が邪魔しない限り、取り巻き同士が重なることはありません。

本鯖の取り巻きを観察すると、綺麗に散らばる現象は
特別なAIとして組み込まれているわけではなく
MOBやPCの移動後、同じセルに他のユニットが存在すると自動的に周囲に
強制移動する「重なり判定」による結果のようです。

しかし、Aurigaにはそのような処理は入っていないので
お手軽に再現するなら、AIに「重なり判定」を入れてあげればいいじゃない。
ということで組み込んでみました。


dracula.png

……いい。伯爵様かっこいい。

↓というわけで以下修正コードです。
map.h

// block関連に追加
int map_count_oncell(int m,int x,int y,int type);
+ int map_unit_exists_oncell(int m,int x,int y,int type,int srcid,int master);
struct skill_unit *map_find_skill_unit_oncell(struct block_list *,int x,int y,int skill_id,struct skill_unit *);


map.c (int map_count_oncell()関数の下あたりに追加)

/*==========================================
* セル上に他のUNITがいるか?
*
* type: BL_TYPE
* srcid: 除外するbl
* master: 取り巻きAI用主人bl
*------------------------------------------
*/
int map_unit_exists_oncell(int m,int x,int y,int type,int srcid,int master)
{
int bx, by;
struct block_list *bl;
struct mob_data *md;

if(x < 0 || y < 0 || x >= map[m].xs || y >= map[m].ys)
return 1;

bx = x / BLOCK_SIZE;
by = y / BLOCK_SIZE;

if(type & ~BL_MOB) {
bl = map[m].block[bx+by*map[m].bxs];
for( ; bl; bl = bl->next) {
if(bl->id != srcid && (bl->type & type) && bl->x == x && bl->y == y)
return 1;
}
}
if(type & BL_MOB) {
bl = map[m].block_mob[bx+by*map[m].bxs];
for( ; bl; bl = bl->next) {
struct unit_data *ud = NULL;
if((md = BL_DOWNCAST(BL_MOB, bl))) ud = &md->ud;
if(md && bl->id != srcid && (!master || (master && (bl->id == master || md->master_id == master)))){
if(bl->x == x && bl->y == y)
return 1;
//移動先予測
if(ud && ud->walktimer!=-1){
int dx, dy, dir;
dir = ud->walkpath.path[ud->walkpath.path_pos];
dx = bl->x + dirx[dir];
dy = bl->y + diry[dir];
if(dx == x && dy == y)
return 1;
}
}
}
}
return 0;
}


mob.c (mob_ai_sub_hard_slavemob()関数)

/*==========================================
* 取り巻きモンスターの処理
*------------------------------------------
*/
static int mob_ai_sub_hard_slavemob(struct mob_data *md,unsigned int tick)
{
struct mob_data *mmd = NULL;
struct block_list *bl;
int old_dist;

nullpo_retr(0, md);

if((bl = map_id2bl(md->master_id)) == NULL || unit_isdead(bl)) { // 主が死亡しているか見つからない
if(md->state.special_mob_ai > 0)
unit_remove_map(&md->bl,3,0);
else
unit_remove_map(&md->bl,1,0);
return 0;
}
if(md->state.special_mob_ai > 0) // 主がPCの場合は、以降の処理は要らない
return 0;

if(bl->type == BL_MOB)
mmd = (struct mob_data *)bl; // 主の情報

// 主ではない
if(!mmd || mmd->bl.id != md->master_id)
return 0;
// 呼び戻し
if(mmd->state.recall_flag == 1) {
if(mmd->recallcount < mmd->recallmob_count + 2) {
int dx = atn_rand()%5-2+mmd->bl.x;
int dy = atn_rand()%5-2+mmd->bl.y;
mob_warp(md,-1,dx,dy,3);
mmd->recallcount += 1;
} else {
mmd->state.recall_flag = 0;
mmd->recallcount = 0;
}
md->state.master_check = 1;
return 0;
}
// 主が違うマップにいるのでテレポートして追いかける
if(mmd->bl.m != md->bl.m) {
mob_warp(md,mmd->bl.m,mmd->bl.x,mmd->bl.y,3);
md->state.master_check = 1;
return 0;
}

// 主との距離を測る
old_dist = md->master_dist;
md->master_dist = unit_distance(md->bl.x,md->bl.y,mmd->bl.x,mmd->bl.y);

// 直前まで主が近くにいたのでテレポートして追いかける
if(old_dist < 10 && md->master_dist > 18) {
mob_warp(md,-1,mmd->bl.x,mmd->bl.y,3);
md->state.master_check = 1;
return 0;
}

// 主がいるが、少し遠いので近寄る
- if(!md->target_id && unit_can_move(&md->bl) && !unit_isrunning(&md->bl) && md->ud.walktimer == -1 && md->master_dist < 15) {
- int i = 0, dx, dy, ret;
- if(md->master_dist > 2) {
- do {
- if(i <= 2) {
- dx = atn_rand()%5-2+mmd->bl.x - md->bl.x;
- dy = atn_rand()%5-2+mmd->bl.y - md->bl.y;
- } else {
- dx = mmd->bl.x - md->bl.x + atn_rand()%5 - 2;
- dy = mmd->bl.y - md->bl.y + atn_rand()%5 - 2;
- }
- ret = unit_walktoxy(&md->bl,md->bl.x+dx,md->bl.y+dy);
- i++;
- } while(ret == 0 && i < 5);
- } else {
- do {
- dx = atn_rand()%5 - 2;
- dy = atn_rand()%5 - 2;
- if(dx == 0 && dy == 0) {
- dx = (atn_rand()%2)? 1: -1;
- dy = (atn_rand()%2)? 1: -1;
- }
- dx += mmd->bl.x;
- dy += mmd->bl.y;
- ret = unit_walktoxy(&md->bl,mmd->bl.x+dx,mmd->bl.y+dy);
- i++;
- } while(ret == 0 && i < 5);
- }
- md->next_walktime = tick + 500;

+ if(!md->target_id && unit_can_move(&md->bl) && !unit_isrunning(&md->bl) && md->ud.walktimer == -1 && md->master_dist < AREA_SIZE+1) {
+ int i = 0, dx, dy, ret=0;
+ #define ABS(v) ( v>=0 ? v : -1*v )
+ #define SGN(v) ( v>=0 ? v>0 :-1 )
+ if(md->master_dist > 3) {
+ //まず近づく
+ do {
+ dx = md->bl.x + SGN(bl->x - md->bl.x) * (atn_rand()%3+1);
+ dy = md->bl.y + SGN(bl->y - md->bl.y) * (atn_rand()%3+1);
+ ret = unit_walktoxy(&md->bl, dx, dy);
+ i++;
+ } while(ret == 0 && i < 5);
+ } else {
+ //自分と同じセルに他の取り巻きがいる場合
+ if(map_unit_exists_oncell(md->bl.m,md->bl.x,md->bl.y,BL_MOB,md->bl.id,md->master_id)){
+ int r = atn_rand()%8;
+ do {
+ if(i<8){
+ dx = md->bl.x + dirx[(r+i)%8];
+ dy = md->bl.y + diry[(r+i)%8];
+ } else {
+ do{
+ dx = atn_rand()%7 - 3;
+ dy = atn_rand()%7 - 3;
+ }while(ABS(dx)<2 && ABS(dy)<2);
+ dx += bl->x;
+ dy += bl->y;
+ }
+ ret = (!map_unit_exists_oncell(md->bl.m,dx,dy,BL_MOB,md->bl.id,md->master_id));
+ if(ret) ret = unit_walktoxy(&md->bl,dx,dy);
+ i++;
+ } while(ret == 0 && i < 20);
+ }
+ }
+ #undef ABS
+ #undef SGN
md->state.master_check = 1;
}

// 主がいて、主がロックしていて自分はロックしていない
if(mmd->target_id > 0 && !md->target_id) {
struct block_list *tbl = map_id2bl(mmd->target_id);
if(tbl && (tbl->type != BL_ITEM || md->mode & MD_ITEMLOOT) && mob_can_lock(md,tbl)) {
md->target_id = tbl->id;
md->min_chase = 5 + unit_distance(md->bl.x,md->bl.y,tbl->x,tbl->y);
md->state.master_check = 1;
}
}

return 0;
}


■キーワード
md->target_id==0 : アイドル状態
md->master_dist : 主人との距離
ABS(v)      : 絶対値(符号なし値)を返すマクロ
SGN(v)      : 符号(1 or -1)を返すマクロ
unit_walktoxy() : unit移動関数
map_unit_exists_oncell() : 指定セルの重なりチェック
map[m].block_mob[] : MOB用の座標マトリクス。各座標にmobのblock_listが格納される。

■解説
mob.cの修正コードは、移動後、自分の周囲に8方向にランダムに移動させるというもの。

map_unit_exists_oncell()関数で他の取り巻きとの重なりチェック。
周囲がふさがっていた場合(乱数なので厳密には違うけど)、
主人の周囲7x7マスの空いているセルを探してループ回避させています。


このコードで一番悩んだ部分はmap.cの重なりチェック関数でした。

最初はmap_count_oncell()関数とほぼ同じ内容で、
自分以外&同じ主人の取り巻きのみバージョンだったのですが
unit_walktoxy()で移動させた直後、各unitは block_mob[]が更新されません。
移動前のままです。移動が完了したら更新されます。

ですので、この処理を block_mob[]だけで行うとMOBはお互いの移動先セルに移動しあってループとなってしまいます。

next_walktimerの時間をランダムにずらして同時実行を回避させる方法も考えましたが
ダサいのと、動きがもっさりするのとで却下。
地道にudから対象セルのmobが別セルへ移動中かどうかを調べています。
関連記事
| Auriga |

COMMENT

Re: タイトルなし
な、なんという釣り針臭!

これに関してはノーコメントでw
2012/08/14(火) 00:49:01 | URL | みのり #- [Edit

Comment Form


秘密にする
 


TRACKBACK

TrackBack List



プロフィール

Author:みのり

すごく面倒臭がりで大雑把です。
なので、すぐ楽をしようとして
ツールを作り始めます。
ツールを作る時間と手作業で費やす時間
はたしてどちらが短いのか……


-構築環境-

Auriga0945 MySQL 5.5
with 2011-12-20bRagexeRE

3CeAM rev525
with 2010-07-30aRagexeRE



カテゴリ

未分類 (9)
RagexeRE (9)
eAthena (1)
Auriga (5)
ツール:eAthena用 (4)
ツール:Auriga用 (6)
ツール:その他 (14)
はじめてのAurigaスクリプト (8)
はじめてのAurigaスクリプト基本編 (21)
はじめてのAurigaスクリプト応用編 (7)
Aurigaスクリプト (3)
本鯖 (7)
Aurigaスクリプト:冒険者アカデミー (6)
ドラクエ10 (2)
新生FF14 (3)
EVE Online (2)



最新記事



最新コメント



月別アーカイブ



検索フォーム



リンク

このブログをリンクに追加する



ブロとも申請フォーム

この人とブロともになる



QRコード

QR



12
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Copyright © Rocco di Forte All Rights Reserved.
テンプレート配布者: サリイ  ・・・  素材: HELIUM  ・・・ 
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。