AI.fs 3.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. namespace RL
  2. open Garnet.Composition
  3. open RL.Action
  4. open RL.Components
  5. open RL.FieldOfVision
  6. open RL.Mapping
  7. open RL.Pathfinding
  8. module AI =
  9. type BaseAI() =
  10. member val Target : Option<Eid> = None with get,set
  11. member val TargetDmap : Option<DijkstraMap> = None with get,set
  12. let DetermineAIAction(c : Container, entity : Entity) =
  13. let mutable determinedAction = { actionType = ActionType.ShortWait; cost = 25; initialCost = 25 }
  14. let pos = entity.Get<Point2D>()
  15. let ai = entity.Get<BaseAI>()
  16. let fov = entity.Get<FieldOfVision>()
  17. let action = entity.Get<Action>()
  18. // Set the AI's target if it doesn't have one yet.
  19. if ai.Target.IsNone then
  20. for e in c.Query<Eid, Player, Point2D>() do
  21. if fov.PointSet |> List.contains e.Value3 then
  22. ai.Target <- Some(e.Value1)
  23. // Only process if we have a target.
  24. if ai.Target.IsSome then
  25. let target = c.Get(ai.Target.Value)
  26. // Modify the target's position if they're moving.
  27. let t_pos =
  28. let t_act = target.Get<Action>()
  29. match t_act.actionType with
  30. | ActionType.Move(d) -> d
  31. | _ -> target.Get<Point2D>()
  32. let distance = Helpers.distance2D(pos.x, t_pos.x, pos.y, t_pos.y)
  33. // Lose the target if it ends up outside the FOV.
  34. if not (fov.PointSet |> List.contains t_pos) then
  35. ai.Target <- None
  36. else
  37. // Attack if we're within one tile of the target.
  38. if distance <= 1.45f then
  39. determinedAction <- ActionDefs.Attack target.Id
  40. // Otherwise, figure out how far we need to move.
  41. else
  42. let result, map : bool * RLMap = c.TryGetResource("Map")
  43. if result then
  44. // Set up a new Dijkstra map if we need to. Otherwise clear the current one.
  45. if ai.TargetDmap.IsNone then
  46. ai.TargetDmap <- Some(DijkstraMap(map.Width, map.Height, 40.0f))
  47. else
  48. ai.TargetDmap.Value.Clear()
  49. let dmap = ai.TargetDmap.Value
  50. let blockList =
  51. GetBlockingPositionsExcept(c, ai.Target.Value)
  52. |> List.map (fun p -> map.Index p.x p.y)
  53. // Rebuild the Dijkstra map to path to our target position.
  54. // Then move to the nearest exiting tile (if possible).
  55. dmap.Build([(map.Index t_pos.x t_pos.y)], blockList, map)
  56. let exit = dmap.FindLowestExit((map.Index pos.x pos.y), blockList, map)
  57. if exit.IsSome then
  58. let exit, _ = exit.Value
  59. determinedAction <- ActionDefs.Move (map.ReverseIndex exit)
  60. // Final pass to see if we're changing our course of action. If so, reduce the cost of this new action.
  61. match action.actionType with
  62. | ActionType.Move(_) | ActionType.Attack(_) ->
  63. determinedAction <- { actionType = determinedAction.actionType;
  64. cost = determinedAction.cost - min action.cost action.initialCost;
  65. initialCost = determinedAction.initialCost}
  66. | _ -> ()
  67. // Return the new action.
  68. determinedAction
  69. type Container with
  70. member c.RegisterAISystem() =
  71. c.On<AIProcessActionTrigger> <| fun r ->
  72. let entity = r.e
  73. if entity.Has<BaseAI>() then
  74. entity.Set(DetermineAIAction(c, entity))
  75. let init(c : Container) =
  76. Disposable.Create[c.RegisterAISystem()] |> ignore