Post Wednesday, 8th July 2020, 22:56

Spell Targeting

Targeting in all roguelikes is clunky and unresponsive. I prefer casting spells in Hyperbol, or Pangya, where the trajectory of my shot can be lengthened and curved.
If spell targeting were convenient, then I imagine four methods of spell targeting which synergize with each other to designate a target mob, a target tile, and a curvature. Thus a 'previous' and 'next' hotkey for each method, where I can combine eight hotkeys to get the functionality I want! That said, the existing AI for spell-targeting is pretty decent.
tl;dr - Spell aiming should function like golfing.

Constants:
z = default primary mob targeted by existing AI for the spell being cast
Z = default tile targeted by existing AI for the spell being cast
Xₒ = tile targeted by previous spellcast
xₒ = previous location of mob targeted by previous spellcast

Variables:
n₁ = target mob
n₂ = target mob
n₃ = distancing
f = primary target mob, f = z
F = target tile, F = Z

This allows us to 'skip' redundancies in methods (3) and (4) by 'rotating' the orientation of the spell.

Output:
X = actual target tile

In current Roguelikes, when we use a spell/evocable/projectile, we have a flow chart with X=z if in range, X=Z[/b] if not in range, and then we orient X with the numpad or arrow keys. Repetitive, antifun, soulcrushing, and monotonous. No creativity. Instead, spell-targeting should do what I am thinking, without the need for repetitive input.

1) Projectiles
Mobs are ordered in the order that the 'Tab' macro prioritizes mobs (1 being the highest). The 'next' hotkey targets the mob that would be targeted if the current and previous mobs are dead. If targeting highest priority mob and pressing 'previous' then target cycles to lowest priority mob. If the spell and its blast radius are out of range of a mob then the 'next' and 'previous' hotkeys target the last sighted invisible enemy location, and otherwise, self-targets.
Inputs: z, n₁, n₂, f
Input: Default primary mob target = n₁
'next' output: n₁ = n+1 ; F,X = position of primary target
'previous' output: primary target = x ; Z = position of primary target

Outputs: n1, n2, f, F, X .......

Note: In practice, fulminant prism has many methods of use. To cope with the complexity, we will list our inputs and outputs.


Each spell already has an aiming AI, so primary target will vary by spell. Regardless of how targeting method orders mobs, if the primary target is nᵗʰ ordered mob then 'next' will cycle to n+1 and 'previous' will cycle to n-1.

2) Bolts
Mobs are ordered by distance. When using the 'next' hotkey for 'bolts', the next furthest mob is targeted. Mobs are ordered by distance from the player, and ties are broken by radial angle from the primary target (if two mobs are 4 tiles away from player then mob closest to primary target is ordered first).

Outputs: n1, n2, f, F, X .........

3) Fulminant
Basically, if we cast a spell next to a mob, and wanna blast the mob again, we wanna know which side of the mob we were shooting at. Now, I would like to convert all coordinates to angular vectors, and then convert them back to coordinates, but it is simpler to store the coordinates. We can calculate distances and projections, but all we really need is:
Xₒ = tile targeted by previous spellcast
xₒ = previous location of mob targeted by previous spellcast
Let Vₒ = the vector from xₒ to Xₒ
Now instead of calculating angles, we just add Vₒ to f to find F!
Now we set Z,X = z+Vₒ
Now we our aim is tracking the mob as it moves. If the mob moved one tile south, then this turn our default aim is set to one tile south of the last tile we shot at. The mob's new location z plus our previous mob-to-spell vector, Vₒ. Now, this only applies if our primary target/mob from last turn is still visible. What if we are just entering the fight? Well then we use the existing targeting AI. But what if the player has selected new target mob? Then we don't need any of this, because the player has already designated f and F. Note that manual targeting with the directional arrow/numpad keys will change f and F. Recall that little f is the primary targeted mob and big F is the tile we are aiming at. So why the difference between F and X? Because X can be modified by the arrow keys, but F is modified by targeting methods (1) (2) and (3). This allows us to use F as a reference point for method (4).

If we know what the player is doing, and how big the explosion will get, then we can deduce the context! When Vₒ is zero, we can just keep firing at primary target with hotkeys such as 'z' or 'f' or 'v' or 'Tab' targeting. However, when Vₒ is non-zero, it would be nice to retain the same vector when shooting twice! Therefore when using 'Z' or 'F' or 'V', we should have the aiming AI retain the same vector with respect to the mob as the last time. If the mob moves one tile to the left, then 'Z'/'F'/'V' hotkeys will shoot one tile to track the mob using the same vector as the last turn. Harder than it sounds.

Now, what if we actually are casting fulminant prism? Then we might be kiting a target mob which is out of range. Recall that xₒ is the last seen location of the mob who was our last primary target (of any action). If that one's dead then xₒ can even be the last seen location (after our latest kill) of the last seen mob or invisible outline or invisible water swirlies.
Recall 'F' is the tile of the mob of the 'Tab' AI for projectile weapons.
If f is not visible then set F = xₒ
When entering a level, xₒ = most recent noise source
Now we can use 'next' to cycle to the closest tile to xₒ (n₃ = #arbitrary) and 'previous' to target a blast radius adjacent to ourselves (n₃ = 1)! Then we cycle n₃ along the following list:
#-3. Aim at last heard mob shout (if blast then try to blast furthest tile in that direction) (if no shout and using projectile instead of blast, then target most recently created steam or translocational cloud)
#-1. Blast radius overlapping player by 1 tile. (if using projectile instead of blast then target in direction opposite of xₒ.
#1. Blast radius adjacent to player. (if this is adjacent to next option then skip #1 to go to #-1)
#arbitrary-2. Two tiles closer to player from f or xₒ
#arbitary.
#arbitrary+2 Two tiles further from player from f or xₒ
#arbitary+4. et cetera

We are moving the prism in increments of two tiles at a time. Ideally, this means we can press one button instead of two, move our reticle two tiles :D
This means that if we are fighting in melee on a diagonal, then #arbitrary+2 will be two tiles behind the primary target/mob. This is good because now we can use method (4) to place two non-overlapping fulminant prisms :ugeek:

Outputs: F, X, d₃, X

4) Curve
When golfing, we can add spin to curve a shot clockwise or counterclockwise. There are two ways to do this in DCSS: Rotating the orientation radially, or shifting the endpoint of the spell. Again we need to look at context, remembering the following priorities:
a) Doing lots of damage.
b) Hitting the primary target mob.

If the primary target f is within range of spell range or blast radius range then we need to know if the spell is a bolt or projectile. If the spell is a bolt or projectile, then we have two options:
A) Spin counterclockwise from target
B) Spin clockwise from target

This means we need to know whether targeting another tile X will still pass through our mob f at tile F.

Note: If we lose line of sight via clouds or darkness or invis, then the default AI targeting should be updated to try aiming at the last known location, or at any invisible things like outlines and swirly water. You know how knights move in chess? I want the AI to be smart enough to know that if I read fog, net something at max range, drink a potion stabbing and move sideways so I can fire upon a 35° angle, that my angled shots will pass through the fog to hit the last known location of the last targeted mob. So when I press 'F' or 'Z' it's not self-targeted, but it shoots in the last known direction of my enemy. Because that's eloquent, and says to me that we can push the fog meta without worrying about nerfs. And more importantly, shoot invis mobs quicker. Also what's with Barachi hop to the furthest northwest tile highlighting multiple tiles?

If the spell is a cloud/blast thingie then we have different options:
A) Spin from player such that we do not hit ourselves.
B) Shift the blast radius onto additional mobs
C) shenanigans

Shenanigans could include rotating X with respect to the player, planning out fulminant placements, and skipping the angles we've already rotated to. For example, spamming 'next' could result in alternating between the two 'next'most angles at different spins. Ideally including a 'far' blast radius and a 'not even hitting the target' blast radius. The latter being niche for kiting diagonally with fulminant or hitting stuff outside line of sight. The point is that we are rotating the spell orientation rather than trying to make it pass through multiple targets. Once reason to rotate an actual fulminant prism would be to setup three prisms at once when tanking a target diagonally, such that the prisms do not explode on each other. The benefit is that once the spell is cast, we store θ₄ for the default AI targeting of the next spell (no we don't. Just store the coordinate of the tile he aim at Xₒ and the mob we were last targeting xₒ), such that the orientation of the blast radius with respect to the target mob retained. This means I can iceblast adjacent to a moving target without re-aiming. Maybe not that useful.

Therefore if:
a) the player is using a cloud. We can rotate by one tile at a time, radially, because close clouds are nicer than far clouds so we don't need to target those far-away tiles.

b) the player is placing a fulminant on top of another fulminant's blast radius, then we rotate the placement twice per hotkey click because we do not want the fulminant blast radii to overlap.

c) the player is using a bolt spell or penetrating ego. This one's ambiguous, because we don't know the primary target. We don't know if we're spinning around a close mob or a far mob or a summoner or a tormentor or trying to hit as many mobs as possible or corrode a high AC mob. Not as cool on paper since we don't actually throw curveballs. Something to hit as many mobs as possible with BVC would be okay, but this seems identical to (d). The only difference between throwing a javelin and throwing a projectile is that sometimes the javelin kills a summoner or a unique before its lesser mobs get out of the way. So functionally a bolt spell is targeted the same as a projectile. For BVC and acid wand maybe we cycle targets to find the summoner.

d) the player has used projectile targeting method, then we must spiral the 'spin' outward from the primary target without changing the target mob. Imagine hitting two yaks on an acute angle instead of one. You can't target either directly because then you'll only hit one. To hit both we select tiles which allow a projectile to pass through the target mob as well as a further mob. If there are no further mobs, or no further tiles in the direction we're rotating which still pass through the primary target f, then the 'next' hotkey will do 'spin' in the opposite direction to attempt to pass through a closer mob by moving X closer to θ. Ugh I didn't set that variable... Basically if we're rotating clockwise and can't rotate anymore, then we rotate counterclockwise to try and snag a closer mob along our line of fire. If I was spinning clockwise with 'next' and then I spin counterclockwise with 'previous' then all of my clockwise rotations are voided by returning to F and spinning the other way. This requires that method (3) not rewrite F and f but just give X as output.

e) the player using a blast. Same as (a) but making sure the target is within the blast radius rather than within the line of fire. When we can't rotate anymore we alternate between weak blast zone and strong blast zone.

tl;dr For projectiles and bolts, the 'previous' and 'next' hotkeys change the line of fire by a tiny angle by aiming far away. Small angular increments.

Outputs: X

P.S. Once the spell is cast, rewrite the constants d₃, Xₒ, xₒ.
P.S. Having a max aim distance feels somewhat limiting. I'm glad we can at least aim at walls now. It would be cool if like, I could set the curve on orb of destruction and boomerang, or dodging with amulet of the acrobat gave me increased crit chance against the attacker.
P.S. There are eight directional keys. I always remap my shift+numpad to auto-move in that direction (just as shift+directional key auto-moves in that direction). So maybe if the shift+auto-move were used as the hotkeys for targeting methods? I realize it wouldn't really help anyone, but it's apparently a hot-topic, and might make squishy casters bearable to play.
In Ziggurat:27 born and raised, on the Zot stairs where I spent most of my days chilling out maxing, relaxing all cool and all shooting some b-breath outside of lungs. When a couple CK - they were up to no good - started makin trouble in my neighbourhood!