// 前進する場合の移動先の候補を返す。進めない場合は後退する。 private static Loc[] Advance(Loc fm, Dir dir) { var locs = new[] { fm.Forward(dir), fm.Forward(dir.Clockwise()), fm.Forward(dir.Anticlockwise()), fm.Forward(dir.Clockwise().Clockwise()), fm.Forward(dir.Anticlockwise().Anticlockwise()), fm.Backward(dir), }; return(locs); }
public bool CanAdvance(Loc fm, Dir dir) { Loc to = fm.Forward(dir); if (!(_map.IsRoomOrPassage(fm) && _map.IsRoomOrPassage(to))) { return(false); } // 障害物が配置されているなら進めない if (ExistsObstacle(to)) { return(false); } if (fm.IsDiagonal(to)) // 斜め移動のときは、左右に障害物があるなら進めない { if (_map.IsWall(fm.Row, to.Col) || _map.IsWall(to.Row, fm.Col)) { return(false); } return(true); } return(true); // to に移動できる }
public Loc FindHitTarget(Loc src, Dir front, out CharacterBase hitTarget) { hitTarget = null; Loc loc = src; bool update = true; while (update) { update = false; var next = loc.Forward(front); if (_floor.IsWall(next) || _floor.ExistsObstacle(next)) { return(next); } else { int p = _enemies.FindIndex(e => e.Loc == next); if (p != -1) { hitTarget = _enemies[p]; return(next); } update = true; loc = next; } } Assert.IsTrue(false); return(Loc.Zero); }
private void ExecutePlayerThrowFootItem(FieldItem fieldItem) { // TODO:石を投げる場合は別処理 Assert.IsTrue(_acts.Count == 0); Loc loc = _player.Loc; bool update = true; while (update) { update = false; var next = loc.Forward(_player.Dir); if (_floor.IsWall(next) || _floor.ExistsObstacle(next)) { _acts.Add(new ActPlayerThrowFootItem(_player, fieldItem, next, loc, null)); } else { int p = _enemies.FindIndex(e => e.Loc == next); if (p != -1) // 敵にヒット { var target = _enemies[p]; _acts.Add(new ActPlayerThrowFootItem(_player, fieldItem, next, next, target)); } else { update = true; loc = next; } } } ChangeGameState(GameState.Act); }
// to に近づく private static Loc[] Approach(Loc fm, Loc to) { Dir dir = fm.Toward(to); var locs = new[] { fm.Forward(dir), fm.Forward(dir.Clockwise()), fm.Forward(dir.Anticlockwise()), fm.Forward(dir.Clockwise().Clockwise()), fm.Forward(dir.Anticlockwise().Anticlockwise()), }; Array.Sort(locs, (a, b) => { var x = a.SquareDistance(to); var y = b.SquareDistance(to); return(x.CompareTo(y)); }); return(locs); }
public static List <Act> Detect(List <Enemy> enemies, Player player, Loc playerNextLoc, Floor floor) { // プレイヤーが見えない場合の行動 if (player.IsInvisible()) { return(Detect2(enemies, player, playerNextLoc, floor)); } // 敵をプレイヤーに近い距離順にソートする // TODO:プレイヤーから逃げる行動をとる場合は、プレイヤーから遠い順に行動を決める enemies.Sort((a, b) => { var x = a.Loc.SquareDistance(playerNextLoc); var y = b.Loc.SquareDistance(playerNextLoc); if (x == y) // 距離が等しい場合は、row の大きい方を優先する { return(b.Row.CompareTo(a.Row)); } return(x.CompareTo(y)); }); var q = new List <Act>(); var locs = new bool[floor.Rows, floor.Cols]; // キャラクターの位置 Func <Loc, bool> existsEnemy = (loc) => { int rows = locs.GetLength(0); int cols = locs.GetLength(1); if (0 <= loc.Row && loc.Row < rows && 0 <= loc.Col && loc.Col < cols) { return(locs[loc.Row, loc.Col]); } return(false); }; var used = new bool[enemies.Count]; for (int i = 0; i < enemies.Count; i++) { if (enemies[i].ActCount <= 0) // 行動済み { used[i] = true; // 行動済み } else if (enemies[i].IsBehavioralIncapacitation()) { used[i] = true; } Loc loc = enemies[i].Loc; locs[loc.Row, loc.Col] = true; } locs[playerNextLoc.Row, playerNextLoc.Col] = true; // 遠距離攻撃をする敵 // - プレイヤーと同じ部屋にいる // - プレイヤーは直線上に位置する // - プレイヤーとの間に障害物や敵は存在しない for (int i = 0; i < enemies.Count; i++) { if (used[i]) { continue; } if (!enemies[i].CanLongDistanceAttack) { continue; } if (floor.InSight(enemies[i].Loc, playerNextLoc)) { if (enemies[i].Loc.Row == playerNextLoc.Row || enemies[i].Loc.Col == playerNextLoc.Col || Math.Abs(enemies[i].Loc.Row - playerNextLoc.Row) == Math.Abs(enemies[i].Loc.Col - playerNextLoc.Col)) { Dir front = enemies[i].Loc.Toward(playerNextLoc); Loc loc = enemies[i].Loc.Forward(front); bool ok = true; while (loc != playerNextLoc) { // 敵が存在する。障害物があるなら遠距離攻撃しない if (locs[loc.Row, loc.Col] || floor.ExistsObstacle(loc) || floor.IsWall(loc)) { ok = false; break; } loc = loc.Forward(front); } Debug.Log("ok = " + ok); if (ok) // 遠距離攻撃を行う { q.Add(new ActEnemyLongDistanceAttack(enemies[i], player, playerNextLoc)); used[i] = true; } } } } // 移動するキャラ bool updated = true; while (updated) { updated = false; for (int i = 0; i < enemies.Count; i++) { if (used[i]) { continue; } var enemy = enemies[i]; Assert.IsTrue(locs[enemy.Loc.Row, enemy.Loc.Col]); if (CanDirectAttack(enemy.Loc, playerNextLoc, floor)) { continue; } // 移動先を決める if (floor.InSight(enemy.Loc, playerNextLoc)) // プレイヤーが視界内 // 敵が部屋にいて、プレイヤーが部屋の入り口にいるならターゲットとして追尾 { if (floor.IsRoom(enemy.Loc) && floor.IsEntrance(playerNextLoc)) { enemy.LockOn(playerNextLoc); } else { enemy.CancelTarget(); } // プレイヤーに近づく foreach (var loc in Approach(enemy.Loc, playerNextLoc)) { Dir dir = enemy.Loc.Toward(loc); if (!existsEnemy(loc) && floor.CanAdvance(enemy.Loc, dir)) { q.Add(new ActEnemyMove(enemy, loc)); locs[loc.Row, loc.Col] = true; locs[enemy.Row, enemy.Col] = false; updated = true; used[i] = true; break; } } } else // プレイヤーが視界にいない // 巡回モード { if (enemy.IsLockedOn) { foreach (var loc in Approach(enemy.Loc, enemy.Target)) { Dir dir = enemy.Loc.Toward(loc); if (!existsEnemy(loc) && floor.CanAdvance(enemy.Loc, dir)) { q.Add(new ActEnemyMove(enemy, loc)); locs[loc.Row, loc.Col] = true; locs[enemy.Row, enemy.Col] = false; updated = true; used[i] = true; break; } } } else if (floor.IsPassage(enemy.Loc)) // 通路にいるなら前進する { enemy.CancelTarget(); foreach (var loc in Advance(enemy.Loc, enemy.Dir)) { Dir dir = enemy.Loc.Toward(loc); if (!existsEnemy(loc) && floor.CanAdvance(enemy.Loc, dir)) { q.Add(new ActEnemyMove(enemy, loc)); locs[loc.Row, loc.Col] = true; locs[enemy.Row, enemy.Col] = false; updated = true; used[i] = true; break; } } } else if (floor.IsRoom(enemy.Loc)) // 部屋にいるなら入り口に向かう { Room room = floor.FindRoom(enemy.Loc); Debug.Log("room = " + room); Debug.Log("room.Entrances.Length = " + room.Entrances.Length); if (room.Entrances.Length == 0) // 入り口がないなら何もしない { updated = true; used[i] = true; break; } Assert.IsTrue(room != null && room.Entrances.Length > 0); Loc[] entrances = room.Entrances; if (entrances.Length > 1) { // 隣接する入り口には向かわない(通路から部屋に入って、また通路に戻るパターンは除外) entrances = entrances.Where(e => !e.IsNeighbor(enemy.Loc)).ToArray(); } Loc target = entrances.Choice(); enemy.LockOn(target); foreach (var loc in Approach(enemy.Loc, enemy.Target)) { Dir dir = enemy.Loc.Toward(loc); if (!existsEnemy(loc) && floor.CanAdvance(enemy.Loc, dir)) { q.Add(new ActEnemyMove(enemy, loc)); locs[loc.Row, loc.Col] = true; locs[enemy.Row, enemy.Col] = false; updated = true; used[i] = true; break; } } } } } } // 攻撃(移動以外)するキャラ for (int i = 0; i < enemies.Count; i++) { if (used[i]) { continue; } if (CanDirectAttack(enemies[i].Loc, playerNextLoc, floor)) { q.Add(new ActEnemyAttack(enemies[i], player, playerNextLoc)); used[i] = true; } } // 移動も行動も出来ないキャラは待機 for (int i = 0; i < enemies.Count; i++) { if (used[i]) { continue; } // q.Add(new ActEnemyWait(enemies[i])); used[i] = true; } return(q); }
public Loc Front() { return(Loc.Forward(Dir)); }
public Map(char[,] mapData) { MapData = mapData; int rows = mapData.GetLength(0); int cols = mapData.GetLength(1); _rooms = GetRooms(); _spotlight = new MapSpotlight(rows, cols, _rooms); // _roomMap に各マスがどの部屋に所属しているのかを記録する _roomMap = Utils.CreateArray2D(rows, cols, NoRoom); foreach (var room in _rooms) { for (int r = 0; r < room.Height; r++) { for (int c = 0; c < room.Width; c++) { _roomMap[room.Row + r, room.Col + c] = room.Id; } } } _mapLayer = LayerManager.GetLayer(LayerName.Map); var flat = Resources.Load("Prefabs/MapChip/pipo-map001_0"); var mountain = Resources.Load("Prefabs/MapChip/pipo-map001_at-yama2_0"); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { if (MapData[i, j] == MapChar.Water || MapData[i, j] == MapChar.Sand) { var dir = Dir.N; var xs = new List <int>(); var loc = new Loc(i, j); for (int k = 0; k < 8; k++) { var chip = GetMapChar(loc.Forward(dir)); xs.Add(chip == MapData[i, j] ? 0 : 1); // 隣接するマップチップが同じか dir = dir.Clockwise(); } // Debug.Log(">> " + string.Join(" ", xs.Select(e => e.ToString()).ToArray())); var offsets = new[, ] { { -1, 1, }, { 1, 1 }, { -1, -1 }, { 1, -1 } }; var pathnames = MapData[i, j] == MapChar.Water ? MapChipUtils.GetSeaMapChipName(xs.ToArray()) : MapChipUtils.GetSandMapChipName(xs.ToArray()); for (int k = 0; k < pathnames.Length; k++) { // Debug.Log("pathname: " + pathnames[k]); var pos = loc.ToPosition(); var obj = Resources.Load("Prefabs/MapChip/" + pathnames[k]); pos.x += offsets[k, 0] * Config.ChipSize / 4; pos.y += offsets[k, 1] * Config.ChipSize / 4; var gobj = (GameObject)GameObject.Instantiate(obj, pos, Quaternion.identity); gobj.transform.SetParent(_mapLayer.transform); } } else { var pos = new Loc(i, j).ToPosition(); var floatObj = (GameObject)GameObject.Instantiate(flat, pos, Quaternion.identity); floatObj.transform.SetParent(_mapLayer.transform); switch (MapData[i, j]) { case MapChar.Wall: var mtObj = (GameObject)GameObject.Instantiate(mountain, pos, Quaternion.identity); mtObj.transform.SetParent(_mapLayer.transform); break; } } } } }