/// <summary> /// Add entity to the map /// </summary> /// <param name="entity"></param> /// <param name="x"></param> /// <param name="y"></param> public void AddEntity(AdventureEntity entity, int x, int y) { // can't add an entity to invalid position if (!WalkabilityMap[x, y]) { throw new Exception(); } if (!entity.IsPassable) { WalkabilityMap[x, y] = false; } entity.Map = this; entity.Coordinate = new Coord(x, y); if (EntityMap[x, y] == null) { EntityMap[x, y] = new AdventureEntityContainer(); } EntityMap[x, y].Add(entity); if (entity is Combatant combatant) { // calculate fov combatant.FovCoords = CalculateFoV(entity.Coordinate, entity is Mogwai); } // add entity to list Entities.Add(entity); Adventure.Enqueue(AdventureLog.EntityCreated(entity)); }
/// <summary> /// /// </summary> /// <param name="entity"></param> public void Loot(AdventureEntity entity) { // only allow mogwais to loot if (!(this is Mogwai.Mogwai mogwai)) { return; } if (entity.LootState == LootState.None || entity.LootState == LootState.Looted) { return; } var activity = ActivityLog.Create(ActivityLog.ActivityType.Loot, ActivityLog.ActivityState.None, new int[] { }, null); Mogwai.Mogwai.History.Add(LogType.Info, activity); Adventure?.Enqueue(AdventureLog.Info(this, entity, activity)); if (entity.Treasure != null) { activity = ActivityLog.Create(ActivityLog.ActivityType.Treasure, ActivityLog.ActivityState.Success, new int[] { }, null); Mogwai.Mogwai.History.Add(LogType.Info, activity); Adventure?.Enqueue(AdventureLog.Info(this, entity, activity)); mogwai.AddGold(entity.Treasure.Gold); } else { activity = ActivityLog.Create(ActivityLog.ActivityType.Treasure, ActivityLog.ActivityState.Fail, new int[] { }, null); Mogwai.Mogwai.History.Add(LogType.Info, activity); Adventure?.Enqueue(AdventureLog.Info(this, entity, activity)); } entity.LootState = LootState.Looted; }
public override CombatAction Executable(AdventureEntity target) { if (!Spell.CanCast(Owner, target)) { return(null); } return(new SpellCast(Owner, Spell, target, ActionType)); }
public override CombatAction Executable(AdventureEntity target) { if (!InWeaponRange(target)) { return(null); } return(new RangedAttack(Owner, Weapon, target, ActionType == ActionType.Full)); }
public virtual bool InWeaponRange(AdventureEntity target) { if (Owner == null || target == null) { return(false); } return((int)Distance.EUCLIDEAN.Calculate(target.Coordinate - Owner.Coordinate) <= GetRange()); }
public virtual bool ExecuteSpell(AdventureEntity me, AdventureEntity target = null) { if (!CanExecuteSpell(me, target)) { return(false); } SpellEffect(me, target); return(true); }
private List <Coord> GetIntersections(Entity entity, AdventureEntity target) { var moveRange = entity.Speed / 5; var intersects = new List <Coord>(); foreach (var weaponAttack in entity.CombatActions.OfType <WeaponAttack>()) { var attackRadius = new RadiusAreaProvider(target.Coordinate, weaponAttack.GetRange(), Radius.CIRCLE).CalculatePositions().ToArray(); var moveRadius = new RadiusAreaProvider(entity.Coordinate, moveRange, Radius.CIRCLE).CalculatePositions().ToArray(); var map = Map.WalkabilityMap; intersects.AddRange(from t in attackRadius from coord in moveRadius where coord.X >= 0 && coord.X < map.Width && coord.Y >= 0 && coord.Y < map.Height && map[coord] && coord == t select coord); } return(intersects); }
private Move(Entity owner, AdventureEntity target) : base(owner, target, true) { IsExecutable = true; }
public override CombatAction Executable(AdventureEntity target) { return(new Move(Owner, target)); }
private SpellCast(Entity owner, Spell spell, AdventureEntity target, ActionType actionType) : base(actionType, owner, target, true) { IsExecutable = true; Spell = spell; }
protected MoveAction(Entity owner, AdventureEntity target, bool provokesAttacksofOpportunity) : base(ActionType.Move, owner, target, provokesAttacksofOpportunity) { IsExecutable = true; }
public override bool CanSee(AdventureEntity entity) { return(entity != null && FovCoords.Any(p => entity.Coordinate == p)); }
private RangedAttack(Entity owner, Weapon weapon, AdventureEntity target, bool fullRound) : base(fullRound ? ActionType.Full : ActionType.Standard, owner, target, weapon, true) { IsExecutable = true; }
public override bool IsInReach(AdventureEntity entity) { return(Distance.EUCLIDEAN.Calculate(entity.Coordinate - Coordinate) <= 2d); }
public abstract CombatAction Executable(AdventureEntity target);
public override CombatAction Executable(AdventureEntity target) { return(null); }
public bool CanExecuteSpell(AdventureEntity me, AdventureEntity target = null) { return(true); }
public void ExploreDungeon(bool checkEnemies) { // expMap // -1 : impassable // 0 : uncharted (WHITE) // 1 : observed through FOV (GREY) // 2 : Visited or having no explorable tiles (BLACK) var expMap = Map.ExplorationMap; var moveRange = Speed / 5; var diagonalCount = 0; //bool foundLootable = false; //AdventureEntity currentLoot = null; while (moveRange > 0) { // check if we have an enemy in fov if (checkEnemies && CombatState == CombatState.None) { // check field of view positions for enemies foreach (var entity in Map.EntitiesOnCoords(FovCoords).Where(p => p.IsAlive)) { if (entity.Faction == Faction.None) { continue; } if (Faction != entity.Faction) { CombatState = CombatState.Initiation; entity.CombatState = CombatState.Initiation; } } // break if we have found an enemy if (CombatState == CombatState.Initiation) { break; } } // check if we have lootable entities in fov if (!_foundLootable && LootablesInSight(out var lootableEntities)) { // Searching for the nearest reachable loot. Coord[] nearestPath = null; foreach (var loot in lootableEntities) { //System.Console.WriteLine($"Is in reach {loot.Name}? {IsInReach(loot)}"); if (IsInReach(loot)) { //System.Console.WriteLine($"looting {loot.Name}"); Loot(loot); continue; } var path = Algorithms.AStar(Coordinate, loot.Coordinate, Map); if (nearestPath == null) { nearestPath = path; _currentLoot = loot; } else if (path != null && nearestPath.Length > path.Length) { nearestPath = path; _currentLoot = loot; } } if (nearestPath != null) { // Set a path for the objective. _foundLootable = true; _lastPath = nearestPath; _pathIndex = 2; if (!MoveAtomic(nearestPath[1], ref moveRange, ref diagonalCount)) { return; } continue; } } else if (_foundLootable && IsInReach(_currentLoot)) { Loot(_currentLoot); _foundLootable = false; _currentLoot = null; } // check if already have a path if (_pathIndex > 0) { if (_pathIndex != _lastPath.Length) { if (Coordinate == _lastPath[_pathIndex - 1]) { if (!MoveAtomic(_lastPath[_pathIndex++], ref moveRange, ref diagonalCount)) { return; } continue; } } else if (_foundLootable) { // This branch means this entity follows a proper path to the current loot, // and in terms of path there is only 1 step left to the loot // but the one step is diagonal so that it is not reachable from the current coordinate. // To handle this problem, set the next coordinate as one of the cardinally adjacent tiles // of the loot. if (Math.Abs(Coord.EuclideanDistanceMagnitude(_currentLoot.Coordinate - Coordinate) - 2) < float.Epsilon) { var loot = _currentLoot.Coordinate; var projected = loot.Translate(0, -(loot - Coordinate).Y); // Prefer X direction for now; can be randomized if (Map.WalkabilityMap[projected]) { MoveAtomic(projected, ref moveRange, ref diagonalCount); } else { projected = loot.Translate(-(loot - Coordinate).X, 0); if (Map.WalkabilityMap[projected]) { MoveAtomic(projected, ref moveRange, ref diagonalCount); } else { _foundLootable = false; _currentLoot = null; } } } else { _foundLootable = false; _currentLoot = null; } } } _pathIndex = -1; // Atomic movement; consider adjacent tiles first var adjs = Algorithms.GetReachableNeighbors(Map.WalkabilityMap, Coordinate); var max = 0; var maxIndex = -1; for (var i = 0; i < adjs.Length; i++) { // Not consider Black tiles if (expMap[adjs[i]] == 2) { continue; } var c = Map.ExpectedFovNum[adjs[i].X, adjs[i].Y]; // Calculate which adjacent tile has the greatest number of expected FOV tiles if (max < c) { max = c; maxIndex = i; } } Coord next; // If all adjacent tiles are Black, explorer have to find the nearest grey tile. if (maxIndex < 0) { Coord[] nearest; // Consider grey tiles in FOV first. If not any, check the whole map var inFov = FovCoords .Where(c => expMap[c] == 1) .ToArray(); if (inFov.Length > 0) { nearest = inFov .Select(c => Algorithms.AStar(Coordinate, c, Map)) .OrderBy(p => p.Length) .First(); } else { nearest = expMap.Positions() .Where(c => expMap[c] == 1) .OrderBy(p => Distance.EUCLIDEAN.Calculate(Coordinate, p)) .Take(5) .Where(p => p != Coord.NONE) .Select(p => Algorithms.AStar(Coordinate, p, Map)) .OrderBy(p => p.Length) .First(); } if (nearest?.Length < 2) { for (int i = 0; i < expMap.Width; i++) { for (int j = 0; j < expMap.Height; j++) { expMap[i, j] = 2; } } return; } next = nearest[1]; // Save the path _lastPath = nearest; _pathIndex = 2; } else { next = adjs[maxIndex]; } if (!MoveAtomic(next, ref moveRange, ref diagonalCount)) { return; } } }
protected CombatAction(ActionType actionType, Entity owner, AdventureEntity target, bool provokesAttackOfOpportunity) : base(owner) { ActionType = actionType; ProvokesAttackOfOpportunity = provokesAttackOfOpportunity; Target = target; }
internal bool CanCast(Entity owner, AdventureEntity target) { // TODO implement conditions return(true); }
protected WeaponAttack(ActionType actionType, Entity owner, AdventureEntity target, Weapon weapon, bool provokesAttackOfOpportunity) : base(actionType, owner, target, provokesAttackOfOpportunity) { Weapon = weapon; }
public void DrawEntity(AdventureEntity adventureEntity) { int glyph; Color color; switch (adventureEntity) { case Mogwai _: glyph = 1; color = Color.DarkOrange; break; case Monster _: glyph = 135; //64; color = Color.SandyBrown; break; case Chest _: glyph = 146; //64; color = Color.Pink; break; default: throw new NotImplementedException(); } // TODO: rotating symbols for multiple mogwais var defaultAnim = new Animated("default", 1, 1, _adventureFont); BasicNoDraw frame = defaultAnim.CreateFrame(); frame[0].Glyph = glyph; frame[0].Foreground = color; Coord pos = adventureEntity.Coordinate; var entity = new ConsoleEntity(defaultAnim) { Position = new Point(pos.X, pos.Y), }; // damage animation var defAnimated = new Animated("default", 1, 1, _adventureFont); var animEntity = new ConsoleEntity(defAnimated); var damageAnim = new Animated("damage", 1, 1, _adventureFont) { AnimationDuration = 1 }; BasicNoDraw damageFrame = damageAnim.CreateFrame(); damageAnim.CreateFrame(); damageFrame[0].Glyph = 15; damageFrame[0].Foreground = Color.Red; animEntity.Animations.Add("damage", damageAnim); // add animation entity entity.Children.Add(animEntity); // TODO change this ... to a more appropriate handling // do not revive ... dead shapes if (adventureEntity is Combatant combatant && combatant.IsDead) { DiedEntity(entity); } entity.IsVisible = false; _entities.Add(adventureEntity.AdventureEntityId, entity); _entityManager.Entities.Add(entity); }