starlit

resolver and events.on_shot

Give the ragebot a yaw hint for an enemy, and read the result of every shot fired with it.

Some players use anti-aim: the pose the game shows for them is turned away from the pose their body really has. A resolver is a script that works out how far, and tells the ragebot. The resolver module is how a script does that. A script sends one number for an enemy and gets back the outcome of the shots. The other ways to tune the ragebot, hit chance, minimum damage, the fire gate, targets, double precision and the stops, are on the rage page.

What a hint does

A hint is a number of degrees for one enemy, set with resolver.set_yaw(slot, degrees).

While you hold a hint for an enemy, the ragebot treats that enemy's recorded pose as turned by that many degrees around the enemy's origin, in the horizontal plane, when it scans, aims and fires. The stored records are not changed. The turned pose is a copy that the ragebot uses for that enemy. For an enemy without a hint the ragebot behaves exactly as it does when no script is loaded.

Positive and negative hints turn the pose in opposite directions. A positive hint turns it counter-clockwise as seen from above, the way a yaw angle grows. A resolver finds out from the results of its shots, and the example on the examples page does exactly that.

A hint applies to enemies only: players that are alive in this frame, not the local player, and on another team. When the server runs with mp_teammates_are_enemies, every other player is an enemy.

A hint is limited to 60 degrees, to enemies, and to two seconds, so a hint that stops being right fades out by itself.

The life of a hint

A hint dies by itself two seconds after the last resolver.set_yaw call for that enemy. A script that wants to keep a hint calls set_yaw again every frame.

We also remove it at once when:

  • the script is unloaded, or we stop it
  • the enemy dies, leaves the match, or is no longer an enemy
  • the level changes

The ragebot keeps one value for each slot. If two scripts hint the same enemy, the value of the last call is the one used. A script that calls resolver.clear removes its own hint. If another script still holds one, the ragebot keeps using that one. resolver.get_yaw only ever shows what the calling script set.

Results

The ragebot reports a finished shot only for an enemy that carried a hint when the shot was fired. A hint of 0 counts. If you clear the hint of an enemy, you stop hearing about shots at that enemy. A resolver that wants to learn from misses at 0 has to hold a hint of 0 and not clear it.

Results arrive through events.on_shot, each one as a table with exactly six fields. The fields are described under events.on_shot below.

Functions

resolver.set_yaw

resolver.set_yaw(slot, degrees) -> boolean
argumenttypemeaning
slotnumbera number with an exact whole value from 1 to 64. 2 and 2.0 pass. "2", 2.5, NaN, infinity and nil do not.
degreesnumberfinite. It is clamped to -60 to 60 and kept to a resolution of 0.01 degree.

Returns true when the hint was handed to the ragebot, false when it was refused. A refusal is not an error. We refuse when:

  • slot is not a valid slot as described above
  • the player in that slot is not an alive enemy in this frame: the local player, a teammate, a dead player, or nobody
  • degrees is not a number, or is NaN or infinite
  • the script has already made 256 accepted calls in this frame

Errors leaving out an argument raises number expected. Extra arguments are ignored.

Cost 20 instructions. Refused calls cost the same but do not count towards the 256.

events.on_paint(function()
  for _, enemy in ipairs(entity.players(true)) do
    resolver.set_yaw(enemy:index(), 0)
  end
end)

resolver.get_yaw

resolver.get_yaw(slot) -> number or nil

Returns the degrees this script last set for the slot, as applied (clamped and rounded to 0.01), while that hint is still alive. Otherwise nil. It returns only what this script set, never a value set by another script. An invalid slot gives nil.

Errors leaving out the argument raises number expected.

Cost 10 instructions.

local applied = resolver.get_yaw(3)
if applied then log.info("hint for slot 3 is " .. applied) end

resolver.clear

resolver.clear(slot)

Removes this script's hint for the slot. The ragebot goes back to its normal behaviour for that enemy, unless another script still holds a hint for it. It returns nothing. An invalid slot does nothing.

Errors leaving out the argument raises number expected.

Cost 20 instructions.

resolver.clear(3)

events.on_shot

events.on_shot(fn)    fn(shot)

fn is called once for every finished ragebot shot at an enemy that carried a hint. Registering again replaces the function, and nil removes it.

fieldtypemeaning
targetintegerthe slot of the enemy, 1 to 64
hitbooleantrue if the shot hit
hitgroupinteger0 none, 1 head, 2 chest, 3 stomach, 4 left arm, 5 right arm, 6 left leg, 7 right leg, 8 neck. Anything else is 0.
damageintegerhealth damage, 0 or more. 0 for a miss.
hintnumberthe degrees that were applied to the pose the shot was aimed at, -60 to 60. A value that is not a number becomes 0.
ageintegerthe age of the recorded pose that was aimed at, in ticks, 0 or more

The table has exactly these fields and no metatable.

Delivery

  • Results are delivered oldest first, before events.on_paint in the same frame, so a belief that on_shot updates is already in the hint that on_paint sets.
  • A script gets at most 16 results in a frame, within 1.5 ms. What does not fit waits for the next frame. A result older than five seconds is dropped. The host keeps 64 results for scripts that are behind.
  • Each call has 50,000 instructions and 0.5 ms. An error or an overrun in it is a strike. It ends the delivery for that frame and does not stop on_paint from running.
  • A script that loads or registers late sees only shots from then on. A change of level throws the queue away.
  • Every script that has registered gets every result.

Notes hint can differ from what your script holds now, because a result arrives some frames after the shot. Compare it with the hint you set before you count the result towards that hint.

local misses = {}
events.on_shot(function(shot)
  if shot.hit then
    misses[shot.target] = 0
  else
    misses[shot.target] = (misses[shot.target] or 0) + 1
  end
  log.info(string.format("slot %d: %s, hint %.2f, record age %d", shot.target, shot.hit and "hit" or "miss", shot.hint, shot.age))
end)

previous: rage · next: settings