2 min read
Line of Sight Casting

With fog-of-war, lighting, and scrying implemented, determining line of sight becomes just a check:

  • Is there a static obstacle (box2d)?
  • Are they visible (not hidden or in fog of war)?

Spells and autoattacks silently failing isn’t a great UX. Mages should move into position after the AttackCommand or CastCommand has been given.

Since I already have a MoveCommand, I wrote a moveIntoRange function for my SpellCastSystem if the target was out of range or move within line of sight.

if (isTargetOutbounds(wantsToCast)) {
    moveIntoRange(wantsToCast, castCommand);
}
// moveIntoRange just sends a MoveCommand to the target.
// This could cause issues if pathfinding isn't possible to that location
void moveIntoRange(Entity wantsToCast, CastCommand castCommand) {
    Position target = Comps.position.get(castCommand.target);
    wantsToCast.add(
        createComponent(MoveCommand.class).setup(target.x, target.y)
    );
}

Line of sight fireball

However, for dynamic targets (like other entities), the MoveCommand wouldn’t update position until they arrived at the wrong location.

Line of sight old location

Spamming MoveCommands seemed like a bad idea given how expensive pathfinding could be, so I added a UpdatePathWithTolerance function that reissued a new MoveCommand when they drifted out of a static tolerance of ~0.5m.

Line of sight fixed

As a bonus, I created an AutoTargetSystem that issues AttackCommand when an enemy enters line of sight. So now boarding is the survival horror it was always meant to be.