Beispiel #1
0
    public void Update()
    {
        UpdateAttack();
        UpdateMovement();

        void UpdateWeapon()
        {
            if (weapon == null || !actor.Inventory.Contains(weapon) || weapon.Gun.AmmoLeft + weapon.Gun.ClipLeft == 0)
            {
                weapon = actor.Inventory.FirstOrDefault(i => i.Gun != null && i.Gun.AmmoLeft + i.Gun.ClipLeft > 0);
            }
        }

        void UpdateAttack()
        {
            if (attack == null || attack.Done())
            {
                UpdateWeapon();
                if (weapon == null)
                {
                    return;
                }
                var enemies = new HashSet <Entity>();
                foreach (var point in actor.World.entities.space.Keys)
                {
                    if (((XYZ)point - actor.Position).Magnitude < 100)
                    {
                        enemies.UnionWith(actor.World.entities[point].OfType <ICharacter>());
                    }
                }
                enemies.Remove(actor);
                enemies = enemies.Where(e => (e.Position - actor.Position).Magnitude < weapon.Gun.range).ToHashSet();
                if (enemies.Any())
                {
                    var target = enemies.First();
                    attack = new ShootAction(actor, weapon, new TargetEntity(target));
                    actor.Actions.Add(attack);
                }
            }
        }

        void UpdateMovement()
        {
            if (movement == null || movement.Done())
            {
                UpdateWeapon();
                if (weapon == null)
                {
                    var weapons = new HashSet <IItem>();
                    foreach (var point in actor.World.entities.space.Keys)
                    {
                        if (((XYZ)point - actor.Position).Magnitude < 100)
                        {
                            weapons.UnionWith(actor.World.entities[point].OfType <IItem>());
                        }
                    }
                    if (!weapons.Any())
                    {
                        UpdateWander();
                        return;
                    }
                    var target = weapons.OrderBy(w => (w.Position - actor.Position).Magnitude2).First();

                    Dictionary <(int, int, int), (int, int, int)> prev = new Dictionary <(int, int, int), (int, int, int)>();
                    var points = new SimplePriorityQueue <XYZ, double>();
                    //Truncate to integer coordinates so we don't get confused by floats
                    (int, int, int)start    = target.Position.i;
                    (int, int, int)actorPos = actor.Position.i;
                    prev[start]             = start;
                    points.Enqueue(start, 0);
                    int  seen    = 0;
                    bool success = false;
                    while (points.Any() && seen < 500 && !success)
                    {
                        var point = points.Dequeue();

                        foreach (var offset in new XYZ[] { new XYZ(0, 1), new XYZ(1, 0), new XYZ(0, -1), new XYZ(-1, 0) })
                        {
                            var next = point + offset;
                            if (prev.ContainsKey(next))
                            {
                                continue;
                            }
                            else if (CanOccupy(next))
                            {
                                prev[next] = point;
                                //Truncate to integer coordinates so we don't get confused by floats
                                if (next.Equals(actorPos))
                                {
                                    success = true;
                                    break;
                                }
                                else
                                {
                                    points.Enqueue(next, (next - actorPos).Magnitude);
                                }
                            }
                        }
                    }

                    if (success)
                    {
                        LinkedList <XYZ> path = new LinkedList <XYZ>();

                        XYZ p = prev[actor.Position];
                        path.AddLast(p);
                        while (!p.Equals(start))
                        {
                            p = prev[p];
                            path.AddLast(p);
                        }

                        movement = new CompoundAction(new FollowPath(actor, path), new TakeItem(actor, target));
                        actor.Actions.Add(movement);
                    }
                    else
                    {
                        UpdateWander();
                    }
                }
                else
                {
                    UpdateWander();
                }
            }
        }

        void UpdateWander()
        {
            HashSet <(int, int, int)> known               = new HashSet <(int, int, int)>();
            HashSet <XYZ>             accessible          = new HashSet <XYZ>();
            Dictionary <(int, int, int), (XYZ, int)> prev = new Dictionary <(int, int, int), (XYZ, int)>();
            Queue <XYZ> points = new Queue <XYZ>();

            var start = actor.Position.i;

            points.Enqueue(start);
            prev[start] = (null, 0);
            int seen = 0;

            while (points.Count > 0 && seen < 500)
            {
                var point = points.Dequeue().i;
                known.Add(point);
                seen++;
                if (CanOccupy(point))
                {
                    accessible.Add(point);
                    foreach (var offset in new XYZ[] { new XYZ(0, 1), new XYZ(1, 0), new XYZ(0, -1), new XYZ(-1, 0) })
                    {
                        var next = point + offset;
                        var dist = prev[point].Item2 + 1;
                        if (known.Add(next))
                        {
                            prev[next] = (point, dist);
                            points.Enqueue(next);
                        }
                        else if (prev.TryGetValue(next, out (XYZ, int)v) && v.Item2 > dist)
                        {
                            prev[next] = (point, dist);
                        }
                    }
                }
            }

            var dest = accessible.OrderByDescending(xyz => (actor.Position - xyz).Magnitude2).ElementAt(new Random().Next(0, 4));
            var path = new LinkedList <XYZ>();

            while (dest != null)
            {
                path.AddFirst(dest);
                XYZ next;
                (next, _) = prev[dest];
                dest      = next;
            }
            movement = new FollowPath(actor, path);
            actor.Actions.Add(movement);
        }

        bool CanOccupy(XYZ position)
        {
            var v     = actor.World.voxels.Try(position);
            var below = actor.World.voxels.Try(position.PlusZ(-1));

            return(v is Air || v is Floor || below?.Collision == VoxelType.Solid);
        }
    }