12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788 |
- namespace RL
- open Garnet.Composition
- open RL.Action
- open RL.Components
- open RL.FieldOfVision
- open RL.Mapping
- open RL.Pathfinding
- module AI =
- type BaseAI() =
- member val Target : Option<Eid> = None with get,set
- member val TargetDmap : Option<DijkstraMap> = None with get,set
- let DetermineAIAction(c : Container, entity : Entity) =
- let mutable determinedAction = { actionType = ActionType.ShortWait; cost = 25; initialCost = 25 }
- let pos = entity.Get<Point2D>()
- let ai = entity.Get<BaseAI>()
- let fov = entity.Get<FieldOfVision>()
- let action = entity.Get<Action>()
- // Set the AI's target if it doesn't have one yet.
- if ai.Target.IsNone then
- for e in c.Query<Eid, Player, Point2D>() do
- if fov.PointSet |> List.contains e.Value3 then
- ai.Target <- Some(e.Value1)
- // Only process if we have a target.
- if ai.Target.IsSome then
- let target = c.Get(ai.Target.Value)
- // Modify the target's position if they're moving.
- let t_pos =
- let t_act = target.Get<Action>()
- match t_act.actionType with
- | ActionType.Move(d) -> d
- | _ -> target.Get<Point2D>()
- let distance = Helpers.distance2D(pos.x, t_pos.x, pos.y, t_pos.y)
- // Lose the target if it ends up outside the FOV.
- if not (fov.PointSet |> List.contains t_pos) then
- ai.Target <- None
- else
- // Attack if we're within one tile of the target.
- if distance <= 1.45f then
- determinedAction <- ActionDefs.Attack target.Id
- // Otherwise, figure out how far we need to move.
- else
- let result, map : bool * RLMap = c.TryGetResource("Map")
- if result then
- // Set up a new Dijkstra map if we need to. Otherwise clear the current one.
- if ai.TargetDmap.IsNone then
- ai.TargetDmap <- Some(DijkstraMap(map.Width, map.Height, 40.0f))
- else
- ai.TargetDmap.Value.Clear()
- let dmap = ai.TargetDmap.Value
- let blockList =
- GetBlockingPositionsExcept(c, ai.Target.Value)
- |> List.map (fun p -> map.Index p.x p.y)
- // Rebuild the Dijkstra map to path to our target position.
- // Then move to the nearest exiting tile (if possible).
- dmap.Build([(map.Index t_pos.x t_pos.y)], blockList, map)
- let exit = dmap.FindLowestExit((map.Index pos.x pos.y), blockList, map)
- if exit.IsSome then
- let exit, _ = exit.Value
- determinedAction <- ActionDefs.Move (map.ReverseIndex exit)
- // Final pass to see if we're changing our course of action. If so, reduce the cost of this new action.
- match action.actionType with
- | ActionType.Move(_) | ActionType.Attack(_) ->
- determinedAction <- { actionType = determinedAction.actionType;
- cost = determinedAction.cost - min action.cost action.initialCost;
- initialCost = determinedAction.initialCost}
- | _ -> ()
- // Return the new action.
- determinedAction
- type Container with
- member c.RegisterAISystem() =
- c.On<AIProcessActionTrigger> <| fun r ->
- let entity = r.e
- if entity.Has<BaseAI>() then
- entity.Set(DetermineAIAction(c, entity))
- let init(c : Container) =
- Disposable.Create[c.RegisterAISystem()] |> ignore
|