We're looking for talented people — C++ only, prior LoL experience required. Apply at apply@rito.sh
rito.sh← HOME
Core
GameObject
objManager
Helpers
Prediction
Damage
Targeting
Spells
Geometry
Events
Utilities
Cache
Buffs
Items
LINQ
Evade
Draw
Game
Log
GUI

API Documentation

Complete reference for rito.sh scripting

GameObject

Properties

Identity

boolvalid
stringname
stringcharName
intnetworkID
inttype
intteam

Position & Movement

Vector3pos
Vector3dir
Vector3velocity
floatx, y, z
floatmoveSpeed

Stats

floathealth
floatmaxHealth
floatmana
floatmaxMana
floatarmor
floatspellBlock
floattotalAttackDamage
floattotalAbilityPower
floatattackSpeed
floatattackRange
floatcritChance
intlevel

Status Checks

boolisDead
boolisVisible
boolisTargetable
boolisEnemy
boolisAlly
boolisMoving
boolisDashing
boolisCasting
boolisStunned
boolisRooted
boolisSilenced
boolisInvulnerable

Methods

isValidTarget(range, onlyEnemyTeam) bool
float range = -1 — Maximum range (-1 for no limit)
bool onlyEnemyTeam = true — Only enemy team
Check if object is a valid target for attacks/spells
dist(target) float
Vector3|GameObject target — Target position or object
Calculate distance to target (3D)
move(x, y, z) bool
float x, y, z — Target position
Issue move command (player only)
attack(target) bool
GameObject target — Target to attack
Issue attack command (player only)
castSpell(mode, slot, target) bool
string mode — "self", "pos", or "obj"
int slot — Spell slot (0=Q, 1=W, 2=E, 3=R)
Vector3|GameObject target — Cast target
Cast a spell (player only)
Usage
-- Cast on position
player
:castSpell("pos", 0, enemy.pos)
-- Cast on target
player
:castSpell("obj", 1, enemy)
-- Cast on self
player
:castSpell("self", 2)
hasBuff(buffName) bool
string buffName — Buff name to check
Check if unit has specific buff
Usage
if enemy:hasBuff("zedpassivecd") then
-- Passive on cooldown
end
getBuffTime(buffName) float
string buffName — Buff name
Get remaining buff duration in seconds
healthPercent() float
Get health percentage (0.0 - 1.0)
predictPosition(time) Vector3
float time — Seconds into future
Predict where unit will be after time

Examples

Iterate enemies and attack lowest HP
for enemy in objManager.ienemies do
if enemy:isValidTarget(600) then
if enemy.healthPercent < 0.5 then
player:attack(enemy)
end
end
end
Buff tracking and combo execution
for enemy in objManager.ienemies do
if enemy:isValidTarget(1200) then
-- Check if enemy has our mark
if enemy:hasBuff("zedpassivecd") then
local buffTime = enemy:getBuffTime("zedpassivecd")
if buffTime > 2 then
-- Safe to engage
player:attack(enemy)
end
end
end
end
Kiting with movement prediction
local target = TargetSelector.getLowHP(600)
if target then
-- Predict where they'll be in 0.5s
local predictedPos = target:predictPosition(0.5)
-- Attack them
player:attack(target)
-- Move away from their position
local awayPos = player.pos + (player.pos - predictedPos):normalize() * 100
player
:move(awayPos.x, awayPos.y, awayPos.z)
end

objManager

Object Iterators

objManager.ienemies
Enemy champions
objManager.iallies
Ally champions (excluding player)
objManager.iheroes
All champions
objManager.iminions
All minions
objManager.ienemyminions
Enemy minions
objManager.iturrets
All turrets
objManager.imonsters
Jungle monsters
Farm minions with health threshold
for minion in objManager.ienemyminions do
if minion:isValidTarget(600) then
local aaDamage = damage.autoattack(player, minion)
-- Last hit if we can kill it
if minion.health < aaDamage then
player:attack(minion)
break
end
end
end
AOE ability optimization
local bestPosition = nil
local maxHits = 0
for enemy in objManager.ienemies do
if enemy:isValidTarget(1000) then
-- Count enemies in AOE range
local nearbyEnemies = objManager.getEnemiesInRange(300, enemy)
if #nearbyEnemies > maxHits then
maxHits = #nearbyEnemies
bestPosition = enemy.pos
end
end
end
if maxHits >= 2 then
player
:castSpell("pos", 3, bestPosition)
end

Query Functions

getEnemiesInRange(range, pos) GameObject[]
Get all enemies within range of position
getClosestEnemy(pos) GameObject
Get closest enemy to position
Count enemies in range
local enemies = objManager.getEnemiesInRange(1000, player)
if #enemies >= 3 then
-- Too many enemies, retreat
player:move(safePosition)
end

pred (Prediction)

Functions

pred.get_prediction(target, input) PredictionOutput
Get advanced prediction with collision detection
pred.predict_position(unit, time) Vector3
Predict where unit will be after time
pred.get_collisions(from, to, radius, flags) GameObject[]
Check for collisions along path

Types

PredictionInput

GameObject*target
floatdelay
floatradius
floatrange
floatspeed
SkillshotTypetype
intcollisionFlags

HitChance Values

0Impossible
1OutOfRange
2Collision
3Low
4Medium
5High
6VeryHigh
7Dashing
8Immobile

Examples

Skillshot prediction with collision check
local input = {
delay = 0.25,
radius = 70,
range = 1000,
speed = 1400,
type = SkillshotType.Line,
collisionFlags = Collision_Minions
}
local prediction = pred.get_prediction(target, input)
if prediction.hitChance >= HitChance_High then
player
:castSpell("pos", 0, prediction.castPosition)
end
Only cast on immobile targets
for enemy in objManager.ienemies do
if enemy:isValidTarget(900) then
-- Check if they're stunned/rooted/snared
if pred.is_immobile(enemy) then
local timeImmobile = pred.time_until_mobile(enemy)
-- If they're immobile for at least 1s, guaranteed hit
if timeImmobile >= 1.0 then
player
:castSpell("pos", 3, enemy.pos)
end
end
end
end

damage

Damage Calculation

damage.spell(src, target, slot) float
Calculate spell damage with scaling and resistances
damage.autoattack(src, target, addBonus) float
Calculate auto-attack damage
damage.calc(src, target, physical, magical, true) float
Calculate custom damage with resistances
damage.isKillable(target, spells, includeAA) bool
Check if target is killable with combo
damage.predict(target, time) float
Predict damage target will take over time

Examples

Calculate full combo damage
local target = TargetSelector.getLowHP(1000)
if target then
local qDmg = damage.spell(player, target, 0)
local wDmg = damage.spell(player, target, 1)
local eDmg = damage.spell(player, target, 2)
local rDmg = damage.spell(player, target, 3)
local aaDmg = damage.autoattack(player, target) * 2
local totalDamage = qDmg + wDmg + eDmg + rDmg + aaDmg
if totalDamage > target.health * 1.2 then
-- We can kill with 20% safety margin
print("All-in: " .. totalDamage .. " > " .. target.health)
end
end
Use isKillable helper function
-- Check if we can kill with Q+W+E combo
local spellMask = (1<<0) | (1<<1) | (1<<2) -- Q | W | E
for enemy in objManager.ienemies do
if enemy:isValidTarget(800) then
if damage.isKillable(enemy, spellMask, true) then
-- Execute combo
player:castSpell("obj", 0, enemy)
player:castSpell("obj", 1, enemy)
player:castSpell("obj", 2, enemy)
end
end
end

TargetSelector

Quick Access Methods

TargetSelector.getLowHP(range) GameObject
Get enemy with lowest HP
TargetSelector.getClosest(range) GameObject
Get closest enemy
TargetSelector.getPriority(range) GameObject
Get highest priority target (ADC > Mid > etc)

Target Modes

LowHPLowest health
LowHPPercentLowest %
ClosestClosest to player
ClosestToMouseClosest to cursor
MostADHighest AD
HighPriorityPriority list
EasiestKillMost killable

Examples

Builder pattern for complex targeting
local selector = TargetSelector()
:setMode(TargetMode.LowHP)
:range(1000)
:onlyKillable()
:excludeClones()
:notUnderTower()
local target = selector:execute()
if target then
player:attack(target)
end
Custom scoring function
-- Prioritize low HP enemies that are closer
local target = TargetSelector()
:setMode(TargetMode.Custom)
:range(1000)
:customScore(function(enemy)
local hpScore = (1 - enemy:healthPercent()) * 100
local distScore = (1000 - enemy:dist(player)) / 10
return hpScore + distScore
end)
:execute()
if target then
player:attack(target)
end

Spells

Spell Slots

0Q
1W
2E
3R
4Summoner1
5Summoner2

Spell States

0Ready
1NotAvailable
2NotLearned
5Cooldown
6NoMana

Examples

Check spell state before casting
if player:spellState(0) == SpellState.Ready then
local target = TargetSelector.getClosest(1000)
if target then
player
:castSpell("pos", 0, target.pos)
end
end
Wait for cooldowns before combo
local qReady = player:spellState(0) == SpellState.Ready
local wReady = player:spellState(1) == SpellState.Ready
local eReady = player:spellState(2) == SpellState.Ready
if qReady and wReady and eReady then
local target = TargetSelector.getLowHP(800)
if target then
player:castSpell("obj", 0, target)
player:castSpell("obj", 1, target)
player:castSpell("obj", 2, target)
end
end

geometry

Vector Operations

distance(v1, v2) float
Calculate distance between two vectors
normalize(v) Vector3
Normalize vector to unit length
dot(v1, v2) float
Calculate dot product

Intersection Tests

intersectCircle(center, radius, point) bool
Check if point is in circle
intersectLine(start, end, point, radius) bool
Check if point is near line segment

Examples

Calculate safe escape position
-- Find direction away from enemies
local enemyDir = Vector3(0, 0, 0)
local enemyCount = 0
for enemy in objManager.ienemies do
if enemy:dist(player) < 1000 then
enemyDir = enemyDir + (enemy.pos - player.pos)
enemyCount = enemyCount + 1
end
end
if enemyCount > 0 then
-- Normalize and go opposite direction
local escapeDir = geometry.normalize(-enemyDir)
local escapePos = player.pos + escapeDir * 400
player
:move(escapePos.x, escapePos.y, escapePos.z)
end
Check if enemy is in skillshot path
local target = TargetSelector.getClosest(1000)
if target then
local castPos = target.pos
local skillshotWidth = 80
-- Check if other enemies are in the line
local hitCount = 0
for enemy in objManager.ienemies do
if geometry.intersectLine(player.pos, castPos,
enemy.pos, skillshotWidth) then
hitCount = hitCount + 1
end
end
print("Can hit " .. hitCount .. " enemies")
end

events

Event System

events::on<EventType>(handler) int
Subscribe to an event
events::emit(event) void
Emit an event to all subscribers

Common Events

OnTickEvent
OnDrawEvent
OnLevelUpEvent
OnSpellCastEvent
OnDamageEvent
OnDeathEvent
OnBuffAddEvent
OnBuffRemoveEvent

Examples

React to spell casts
events::on<OnSpellCastEvent>(function(event)
if event.caster.isEnemy then
print("Enemy cast: " .. event.spellName)
-- Dodge if it's a dangerous spell
if event.spellName == "LuxLightBinding" then
local dodgePos = player.pos + player.dir * 200
player
:move(dodgePos.x, dodgePos.y, dodgePos.z)
end
end
end)
Game tick for main logic loop
events::on<OnTickEvent>(function()
-- Main script logic runs every tick
local target = TargetSelector.getClosest(1000)
if target and target:isValidTarget(900) then
-- Combo logic
if player:spellState(0) == SpellState.Ready then
local pred = pred.get_prediction(target, qInput)
if pred.hitChance >= HitChance_High then
player
:castSpell("pos", 0, pred.castPosition)
end
end
end
end)
Track buff application
events::on<OnBuffAddEvent>(function(event)
if event.target.isMe and event.buffName == "zhonyasringshield" then
print("Zhonya's active! Invulnerable for 2.5s")
end
-- Track enemy buffs
if event.target.isEnemy and event.buffName == "summonerexhaust" then
print(event.target.charName .. " exhausted!")
end
end)

cache

High-performance caching with automatic expiration and O(1) lookups.

Overview

Thread-safe key-value storage for frequently accessed game data.

  • Automatic expiration with configurable TTL
  • O(1) hash-based lookups
  • Built-in statistics and monitoring

API Reference

cache.set(key, value, ttl) void
Store a value in the cache with optional TTL (time to live) in seconds
cache.get(key) value
Retrieve a cached value by key, returns nil if not found or expired
cache.has(key) bool
Check if a key exists in the cache and has not expired
cache.remove(key) void
Remove a specific entry from the cache
cache.clear() void
Clear all entries from the cache
cache.size() int
Get the number of entries currently in the cache
cache.stats() table
Get cache statistics (hits, misses, evictions, etc.)

Examples

Cache enemy positions for path prediction
-- Cache enemy positions with 2 second TTL
cb.add(cb.CB_TICK, function()
for enemy in objManager.ienemies do
if enemy:isValidTarget() then
local key = "pos_" .. enemy.networkID
cache.set(key, enemy.pos, 2.0)
end
end
end)
-- Later, retrieve cached position
local cachedPos = cache.get("pos_" .. enemy.networkID)
if cachedPos then
-- Use cached position for calculations
local distance = player.pos:dist(cachedPos)
end
Cache expensive damage calculations
-- Calculate and cache combo damage
function getComboDamage(target)
local cacheKey = "combo_" .. target.networkID
if cache.has(cacheKey) then
return cache.get(cacheKey)
end
-- Expensive calculation
local qDmg = damage.spell(player, target, 0)
local wDmg = damage.spell(player, target, 1)
local eDmg = damage.spell(player, target, 2)
local total = qDmg + wDmg + eDmg
-- Cache for 0.5 seconds
cache.set(cacheKey, total, 0.5)
return total
end
-- Use cached damage
local dmg = getComboDamage(enemy)
if dmg > enemy.health then
-- Execute combo
end
Cache spell database lookups
-- Cache spell data to avoid repeated lookups
function getSpellData(spellName)
local cacheKey = "spell_" .. spellName
if cache.has(cacheKey) then
return cache.get(cacheKey)
end
local spellData = SpellDB.get(spellName)
cache.set(cacheKey, spellData, 60) -- Cache for 1 minute
return spellData
end
-- Monitor cache performance
local stats = cache.stats()
print("Cache hit rate: " .. (stats.hits / (stats.hits + stats.misses) * 100) .. "%")

Performance Considerations

  • Fast lookups: O(1) average case using hash tables
  • Memory management: Automatic cleanup of expired entries
  • Thread safety: Minimal lock contention for concurrent access
  • Low overhead: Negligible performance impact for cache hits

Best used for: expensive calculations, database queries, position predictions, and frequently accessed data.

buffs

Buff/debuff detection with duration tracking and comprehensive database.

Overview

Detect, analyze, and query buffs/debuffs on any game unit.

  • Real-time detection with duration tracking
  • Stack counting and type classification
  • Extensive buff database with metadata

API Reference

unit:hasBuff(buffName) bool
Check if the unit has a specific buff active
unit:getBuffCount(buffName) int
Get the stack count of a buff (returns 0 if not present)
unit:getBuffDuration(buffName) float
Get the total duration of a buff in seconds
unit:getBuffRemaining(buffName) float
Get the remaining time of a buff in seconds
unit:getAllBuffs() table
Get a table of all active buffs on the unit
buffs.isCC(buffName) bool
Check if a buff is a crowd control effect
buffs.getType(buffName) string
Get buff type (buff, debuff, or neutral)

Examples

Check for dangerous CC buffs
cb.add(cb.CB_TICK, function()
local me = player
-- Check if we're stunned or rooted
if me:hasBuff("stun") or me:hasBuff("root") then
-- Cancel combo, we can't move
return
end
-- Check for silence (can't cast spells)
if me:hasBuff("silence") then
-- Skip spell casting logic
return
end
-- Proceed with normal logic
end)
Monitor buff stacks for champions
-- Track Vayne Silver Bolts stacks
for enemy in objManager.ienemies do
if enemy:isValidTarget(550) then
local stacks = enemy:getBuffCount("VayneSilverDebuff")
if stacks == 2 then
-- Enemy has 2 stacks, next AA will proc
print(enemy.charName .. " has 2 silver bolt stacks!")
end
end
end
-- Track Darius passive stacks
local target = TargetSelector.get(player, 400)
if target then
local hemorrhageStacks = target:getBuffCount("dariushemo")
if hemorrhageStacks >= 5 then
-- Full passive stacks - DON'T fight!
print("DANGER: Full Darius passive!")
end
end
Track buff durations and timing
-- Wait for enemy buff to expire before engaging
local target = TargetSelector.get(player, 1000)
if target then
-- Check if enemy has defensive buff
if target:hasBuff("summonerbarrier") then
local remaining = target:getBuffRemaining("summonerbarrier")
print("Barrier expires in " .. remaining .. "s")
if remaining < 0.5 then
-- Barrier about to expire, prepare combo
prepareCombo(target)
end
end
-- Check steroid buff duration
if player:hasBuff("LuluW") then
local duration = player:getBuffDuration("LuluW")
local remaining = player:getBuffRemaining("LuluW")
print("Whimsy: " .. remaining .. "/" .. duration .. "s")
if remaining > 2 then
-- Plenty of time, play aggressive
executeCombo(target)
end
end
end
Analyze all buffs on a unit
-- Debug: Print all buffs on player
local allBuffs = player:getAllBuffs()
print("Active buffs on player:")
for i, buff in ipairs(allBuffs) do
local remaining = player:getBuffRemaining(buff.name)
local stacks = player:getBuffCount(buff.name)
print(string.format(
"[%d] %s (x%d) - %.1fs remaining",
i, buff.name, stacks, remaining
))
if buffs.isCC(buff.name) then
print(" ^^ THIS IS CC!")
end
end

Buff Database

The module includes a comprehensive JSON database containing metadata for hundreds of League of Legends buffs:

  • Buff ID and name: Internal identifiers
  • Type classification: Buff, debuff, or neutral
  • Scope: Personal, team, or global effects
  • Effect type: Damage, healing, shield, movement, etc.
  • CC classification: Stun, root, slow, silence, etc.

Integration

The Buffs module integrates seamlessly with:

  • GameObject: Access buffs on any game unit
  • Events: CB_UPDATE_BUFF, CB_REMOVE_BUFF callbacks
  • Prediction: Factor in buff effects for movement speed
  • Damage: Calculate damage with buff modifiers

items

Item database access, inventory management, and active item usage.

Overview

Query item data, manage inventory, and use active items.

  • Item database with stats and effects
  • Inventory queries and purchases
  • Active item casting and cooldowns

API Reference

player:hasItem(itemId) bool
Check if player owns a specific item
player:getItemSlot(itemId) int
Get the slot number where item is equipped (returns -1 if not found)
player:buyItem(itemId) bool
Purchase an item from the shop
player:useItem(slot) bool
Use an active item in the specified slot
items.getInfo(itemId) table
Get detailed information about an item (stats, cost, effects)
items.isActive(itemId) bool
Check if an item has an active component
items.getCooldown(slot) float
Get cooldown remaining for active item in slot

Examples

Use active items in combo
-- Item IDs
local YOUMUUS = 3142
local ECLIPSE = 6692
local BOTRK = 3153
cb.add(cb.CB_TICK, function()
local target = TargetSelector.get(player, 1000)
if not target then return end
-- Use Youmuu's for speed boost
if player:hasItem(YOUMUUS) then
local slot = player:getItemSlot(YOUMUUS)
if items.getCooldown(slot) == 0 then
player:useItem(slot)
end
end
-- Use Eclipse when in range
if player:hasItem(ECLIPSE) and target.dist < 550 then
local slot = player:getItemSlot(ECLIPSE)
if items.getCooldown(slot) == 0 then
player:useItem(slot)
player:attack(target)
end
end
-- Use BotRK on low HP target
if player:hasItem(BOTRK) and target:healthPercent() < 50 then
local slot = player:getItemSlot(BOTRK)
if items.getCooldown(slot) == 0 then
player:castSpell("obj", slot, target)
end
end
end)
Check item build progress
-- Track mythic item completion
local MYTHIC_ITEMS = {
3152, -- Hextech Rocketbelt
6653, -- Liandry's Anguish
3068, -- Sunfire Aegis
6632, -- Divine Sunderer
}
local hasMythic = false
for _, itemId in ipairs(MYTHIC_ITEMS) do
if player:hasItem(itemId) then
hasMythic = true
local info = items.getInfo(itemId)
print("Mythic completed: " .. info.name)
break
end
end
if not hasMythic then
print("No mythic item yet - " .. player.gold .. "g available")
end
Smart item usage with conditions
-- Auto-use defensive items
local ZHONYAS = 3157
local STOPWATCH = 2420
local QSS = 3140
cb.add(cb.CB_TICK, function()
local me = player
-- Use Zhonya's when low HP and in danger
if me:hasItem(ZHONYAS) and me:healthPercent() < 20 then
local enemiesNearby = 0
for enemy in objManager.ienemies do
if enemy:isValidTarget(600) then
enemiesNearby = enemiesNearby + 1
end
end
if enemiesNearby >= 2 then
local slot = me:getItemSlot(ZHONYAS)
if items.getCooldown(slot) == 0 then
me:useItem(slot)
print("ZHONYAS ACTIVATED!")
end
end
end
-- Use QSS to cleanse CC
if me:hasItem(QSS) then
if me:hasBuff("stun") or me:hasBuff("root") or me:hasBuff("charm") then
local slot = me:getItemSlot(QSS)
if items.getCooldown(slot) == 0 then
me:useItem(slot)
print("QSS CLEANSED CC!")
end
end
end
end)
Item database queries
-- Query item information
local itemId = 3089 -- Rabadon's Deathcap
local info = items.getInfo(itemId)
if info then
print("Item: " .. info.name)
print("Cost: " .. info.gold.total)
print("AP: " .. info.stats.abilityPower)
if items.isActive(itemId) then
print("Has active component")
else
print("Passive item only")
end
end
-- Check inventory value
local totalGold = 0
for slot = 0, 6 do
local item = player:getItemInSlot(slot)
if item then
local info = items.getInfo(item.id)
totalGold = totalGold + info.gold.total
end
end
print("Inventory value: " .. totalGold .. "g")

Item Slots

Item slot numbering:

  • Slots 0-5: Main inventory slots (6 items)
  • Slot 6: Trinket slot
  • Active items: Use via SpellSlot.Item1 through SpellSlot.Item6

Note: Trinkets use SpellSlot.Trinket for casting

Integration

The Items module integrates with:

  • Damage: Calculate item damage (BotRK, Eclipse, etc.)
  • Spells: Cast active items like spells
  • GameObject: Query item stats on any unit
  • Events: React to item purchases and usage

linq

Chainable query operations for filtering, sorting, and aggregating game objects.

Overview

LINQ-style fluent API for powerful object collection queries.

  • Chainable filter, sort, and aggregate operations
  • Type-safe with automatic null filtering
  • Familiar syntax for C# and JavaScript developers

Filtering Operations

where(predicate) Query
Filter elements using a custom predicate function
validTargets(range?) Query
Filter for valid, targetable units within optional range
inRange(position, range) Query
Filter objects within range from a position
ofType(type) Query
Filter by object type (hero, minion, turret, etc.)
allies() Query
Filter for allied units
enemies() Query
Filter for enemy units
alive() Query
Filter for living objects
heroes() Query
Filter for hero/champion units
minions() Query
Filter for minion units

Sorting Operations

sortBy(selector) Query
Sort ascending by selector function result
sortByDescending(selector) Query
Sort descending by selector function result
sortByDistance(position) Query
Sort by distance from a position (nearest first)
sortByHealth() Query
Sort by health (lowest first)

Selection Operations

first() T
Get first valid element (throws if empty)
firstOrNull() T?
Get first element or null if empty
last() T
Get last valid element (throws if empty)
min(selector) T
Get element with minimum value from selector
max(selector) T
Get element with maximum value from selector

Aggregation Operations

count() int
Count valid elements in collection
any(predicate?) bool
Check if any elements exist (optionally matching predicate)
all(predicate) bool
Check if all elements match predicate
sum(selector) number
Sum values returned by selector function
average(selector) number
Average values returned by selector function

Transformation Operations

take(count) Query
Take first N elements
skip(count) Query
Skip first N elements
distinct() Query
Remove duplicate elements
toVector() vector<T>
Convert query result to vector

Examples

Find low HP enemies in range
-- Get 3 lowest HP enemies within 1000 range
local lowHpEnemies = objManager.getObjects()
:enemies()
:inRange(player.pos, 1000)
:sortByHealth()
:take(3)
:toVector()
for _, enemy in ipairs(lowHpEnemies) do
print(enemy.charName .. " has " .. enemy.health .. " HP")
end
Count enemies in danger zone
-- Count enemies in AOE spell area
local spellPos = Vector3(1000, 0, 2000)
local spellRadius = 400
local enemiesInAOE = objManager.getObjects()
:enemies()
:alive()
:inRange(spellPos, spellRadius)
:count()
if enemiesInAOE >= 3 then
-- Cast AOE spell
player:castSpell("pos", SpellSlot.R, spellPos)
end
Find nearest low HP minion
-- Find closest minion below 100 HP for last hitting
local lastHitTarget = objManager.getObjects()
:minions()
:enemies()
:where(function(m) return m.health < 100 end)
:sortByDistance(player.pos)
:firstOrNull()
if lastHitTarget then
player:attack(lastHitTarget)
end
Calculate total enemy damage in range
-- Sum total AD of all enemies nearby
local totalEnemyAD = objManager.getObjects()
:enemies()
:heroes()
:inRange(player.pos, 1500)
:sum(function(e) return e.totalAttackDamage end)
if totalEnemyAD > 500 then
print("DANGER: High enemy damage nearby!")
end
Check if all allies are safe
-- Check if all allies have sufficient HP
local allAlliesSafe = objManager.getObjects()
:allies()
:heroes()
:all(function(a) return a:healthPercent() > 30 end)
if not allAlliesSafe then
-- Play defensively
print("Team low on HP - defensive mode")
end
Advanced chaining example
-- Complex query: Find best gank target
-- (Enemy, isolated, low HP, high priority)
local gankTarget = objManager.getObjects()
:enemies()
:heroes()
:alive()
:validTargets(2000)
:where(function(e)
-- Check if isolated (no allies nearby)
local alliesNearby = objManager.getObjects()
:allies()
:heroes()
:inRange(e.pos, 800)
:count()
return alliesNearby == 0
end)
:sortBy(function(e)
-- Lower score = better target
return e:healthPercent() + (e.armor / 100)
end)
:firstOrNull()
if gankTarget then
print("Best gank target: " .. gankTarget.charName)
end

Performance Considerations

  • Lazy evaluation: Operations are chained but not executed until terminal operation
  • Functional style: Each operation creates a new result (immutable)
  • Automatic filtering: Null and invalid objects are automatically filtered
  • Type safety: Compile-time type checking for GameObject* operations

Note: For performance-critical paths with large collections, consider caching results or batching operations.

evade

Humanized skillshot dodging with realistic reaction times and learning systems.

Overview

Intelligent detection and evasion with advanced humanization.

  • Multi-source detection (missiles, casts, animations)
  • Smart pathfinding with multiple safe positions
  • Humanization: reaction times, fatigue, attention, learning
  • Skill profiles: Bronze through Challenger

Core API

evade.initialize(config?) bool
Initialize evade system with optional configuration
evade.update() void
Update evade system (call each frame)
evade.shouldEvade() bool
Check if we should evade based on current threats
evade.getSafePosition() Vector3
Get calculated safe position to move to
evade.executeEvade() bool
Execute evade to calculated safe position
evade.isSafe(position) bool
Check if a position is safe from skillshots
evade.getDangerAt(position) float
Get danger level at a specific position
evade.setEnabled(enabled) void
Enable or disable the evade system

Configuration

Configure evade behavior with extensive options:

config.mode
Evade mode: DodgeAll, DodgeOnly, DodgeCC, Custom, Disabled
config.skillLevel
Bronze, Silver, Gold, Platinum, Diamond, Master, Challenger, Inhuman
config.addReactionDelay
Add realistic reaction time delays (recommended)
config.allowFlashToEvade
Allow using Flash to evade (⚠️ risky, not recommended)

Skill Level Profiles

LevelReaction TimeFail Rate
Bronze300-500ms25%
Silver250-400ms18%
Gold200-350ms12%
Platinum150-280ms8%
Diamond100-220ms5%
Master80-180ms3%
Challenger50-120ms1.5%
Inhuman ⚠️0-50ms0%

⚠️ Inhuman level NOT recommended - instant reactions are detectable

Examples

Basic evade setup
-- Initialize evade with safe settings
local config = {
enabled = true,
mode = EvadeMode.DodgeAll,
skillLevel = SkillLevel.Diamond,
addReactionDelay = true,
addMouseMovementTime = true,
addRandomization = true,
allowFlashToEvade = false -- NEVER flash to evade
}
evade.initialize(config)
-- Main game loop
cb.add(cb.CB_TICK, function()
evade.update()
if evade.shouldEvade() then
local safePos = evade.getSafePosition()
evade.executeEvade()
end
end)
Custom evade with priority logic
-- Only evade high-priority spells
local config = {
mode = EvadeMode.Custom,
skillLevel = SkillLevel.Platinum,
minDangerLevel = DangerLevel.Medium,
dodgeCC = true, -- Always dodge CC
dodgeDangerous = true, -- Always dodge ults
-- Callback when skillshot detected
onSkillshotDetected = function(skillshot)
if skillshot.dangerLevel == DangerLevel.Extreme then
print("EXTREME DANGER: " .. skillshot.spellName)
return true -- Process this
end
-- Ignore low danger skillshots
return skillshot.dangerLevel >= DangerLevel.Medium
end
}
evade.initialize(config)
Check danger zones before moving
-- Validate positions before movement
cb.add(cb.CB_ISSUE_ORDER, function(order)
if order.type == OrderType.MoveTo then
local targetPos = order.targetPos
-- Check if position is safe
if not evade.isSafe(targetPos) then
local danger = evade.getDangerAt(targetPos)
print("UNSAFE POSITION: Danger level " .. danger)
-- Get alternative safe position
local safePos = evade.getSafePosition()
if safePos then
order.targetPos = safePos
else
-- Cancel movement if no safe path
return false
end
end
end
return true
end)
Get active threats
-- Display active skillshots and danger
local skillshots = evade.getActiveSkillshots()
for _, ss in ipairs(skillshots) do
print(string.format(
"Skillshot: %s from %s (Danger: %s)",
ss.spellName,
ss.caster.charName,
ss.dangerLevel
))
-- Draw skillshot hitbox
draw.circle(ss.position, ss.radius, 1, 0, 0)
end
-- Show stats
local stats = evade.getStats()
print(string.format(
"Evade Stats: %d dodged / %d total (%.1f%%)",
stats.successfulDodges,
stats.totalSkillshots,
stats.successfulDodges / stats.totalSkillshots * 100
))

Humanization Features

Advanced systems make evade behavior realistic and undetectable:

  • Reaction timing: Variable delays based on skill level
  • Attention system: Reacts faster when in combat or low HP
  • Fatigue simulation: Slower reactions over extended play
  • Learning system: Improves against familiar spells
  • Occasional mistakes: Configurable fail rate for realism
  • Mouse movement: Simulates cursor travel time
  • Team fight awareness: Less perfect in chaotic situations

⚠️ Safety Considerations

HIGH RISK:
  • SkillLevel.Inhuman - Instant reactions (0-50ms) are detectable
  • allowFlashToEvade = true - Flashing on cooldown looks suspicious
  • Disabling all humanization features
RECOMMENDED SAFE SETTINGS:
  • SkillLevel: Diamond or below
  • Enable all humanization (reaction delay, randomness)
  • allowFlashToEvade = false
  • considerTeamFight = true

draw

Rendering API for shapes, text, and overlays in world and screen space.

Overview

Visual debugging and custom overlay creation.

  • World space: circles, lines, rectangles, polygons
  • Screen space: text, HUD elements
  • Full RGB/RGBA color support

World Space Drawing

draw.circle(position, radius, r, g, b, a?) void
Draw a circle at world position with color (RGB 0-1)
draw.line(start, end, width, r, g, b, a?) void
Draw a line between two world positions
draw.rectangle(position, width, height, r, g, b, a?) void
Draw a rectangle in world space
draw.polygon(points, r, g, b, a?) void
Draw a polygon connecting multiple points
draw.text3D(position, text, size, r, g, b) void
Draw text at a world position (billboard)

Screen Space Drawing

draw.text(x, y, text, size, r, g, b) void
Draw text at screen coordinates
draw.rect(x, y, width, height, r, g, b, a?) void
Draw rectangle at screen coordinates
draw.line2D(x1, y1, x2, y2, width, r, g, b) void
Draw 2D line on screen

Utility Functions

draw.worldToScreen(worldPos) Vector2
Convert 3D world position to 2D screen coordinates
draw.screenToWorld(screenX, screenY) Vector3
Convert 2D screen coordinates to 3D world position
draw.getScreenSize() width, height
Get current screen resolution

Examples

Draw ability ranges
cb.add(cb.CB_DRAW_WORLD, function()
local me = player
-- Draw Q range (blue)
draw.circle(me.pos, 1000, 0.2, 0.5, 1.0, 0.3)
-- Draw W range (green)
draw.circle(me.pos, 800, 0.2, 1.0, 0.5, 0.3)
-- Draw R range (red)
draw.circle(me.pos, 1500, 1.0, 0.2, 0.2, 0.3)
-- Draw auto-attack range (white)
draw.circle(me.pos, me.attackRange, 1.0, 1.0, 1.0, 0.5)
end)
Visualize targeting and prediction
cb.add(cb.CB_DRAW_WORLD, function()
local target = TargetSelector.get(player, 1200)
if target then
-- Draw line to target
draw.line(player.pos, target.pos, 2, 1.0, 0.0, 0.0)
-- Draw target circle
draw.circle(target.pos, 100, 1.0, 1.0, 0.0, 0.8)
-- Draw predicted position
local predPos = target:predictPosition(0.5)
draw.circle(predPos, 80, 0.0, 1.0, 1.0, 0.5)
-- Draw line to prediction
draw.line(target.pos, predPos, 1, 0.5, 1.0, 0.5, 0.5)
-- Draw health bar
local screenPos = draw.worldToScreen(target.pos)
if screenPos then
local hpPercent = target:healthPercent()
draw.rect(
screenPos.x - 25, screenPos.y - 40,
50 * (hpPercent / 100), 5,
0.0, 1.0, 0.0
)
end
end
end)
Draw text overlays and HUD
cb.add(cb.CB_DRAW_HUD, function()
local width, height = draw.getScreenSize()
-- Draw script status
draw.text(10, 10, "rito.sh - ACTIVE", 14, 0.0, 1.0, 0.0)
-- Draw FPS
draw.text(10, 30, "FPS: " .. gameTime, 12, 1.0, 1.0, 1.0)
-- Draw combo status (top-center)
local comboKey = "Space"
local color = input.isKeyDown(comboKey) and {1, 0, 0} or {0.5, 0.5, 0.5}
draw.text(
width / 2 - 50, 10,
"COMBO: " .. (input.isKeyDown(comboKey) and "ON" or "OFF"),
16,
color[1], color[2], color[3]
)
-- Draw target info
local target = TargetSelector.get(player, 1000)
if target then
draw.text(10, 60, "Target: " .. target.charName, 12, 1, 1, 0)
draw.text(10, 75, "HP: " .. math.floor(target.health), 12, 1, 1, 0)
draw.text(10, 90, "Distance: " .. math.floor(target.dist), 12, 1, 1, 0)
end
end)
Draw skillshot hitbox visualization
-- Visualize Ezreal Q
cb.add(cb.CB_DRAW_WORLD, function()
if player.charName == "Ezreal" then
local qReady = player:spellState(SpellSlot.Q) == SpellState.Ready
if qReady then
local target = TargetSelector.get(player, 1150)
if target then
local pred = pred.get_prediction(target, 0.25, 60, 1150, 2000)
if pred.canHit then
-- Draw Q line
local endPos = player.pos:extended(pred.castPosition, 1150)
draw.line(player.pos, endPos, 60, 0.0, 0.8, 1.0, 0.3)
-- Draw hit indicator
draw.circle(pred.castPosition, 80, 0.0, 1.0, 0.0, 0.7)
-- Draw hitchance text
draw.text3D(
pred.castPosition,
"HIT: " .. pred.hitChance,
14, 1.0, 1.0, 1.0
)
end
end
end
end
end)
Draw danger zones and paths
-- Visualize evade danger zones
cb.add(cb.CB_DRAW_WORLD, function()
local skillshots = evade.getActiveSkillshots()
for _, ss in ipairs(skillshots) do
local color = {1, 0, 0}
if ss.dangerLevel == DangerLevel.Low then
color = {1, 1, 0}
elseif ss.dangerLevel == DangerLevel.Extreme then
color = {1, 0, 1}
end
-- Draw skillshot area
if ss.type == "line" then
draw.line(ss.start, ss.end, ss.radius * 2,
color[1], color[2], color[3], 0.4)
elseif ss.type == "circle" then
draw.circle(ss.position, ss.radius,
color[1], color[2], color[3], 0.4)
end
end
-- Draw safe position
if evade.shouldEvade() then
local safePos = evade.getSafePosition()
if safePos then
draw.circle(safePos, 100, 0.0, 1.0, 0.0, 0.7)
draw.line(player.pos, safePos, 3, 0.0, 1.0, 0.0, 0.9)
end
end
end)

Draw Callbacks

Use these callbacks for drawing:

  • CB_DRAW_WORLD: Draw in 3D world space (circles, ranges, skillshots)
  • CB_DRAW_HUD: Draw on 2D screen (text, overlays, UI elements)

Note: Drawing operations are batched and rendered efficiently. Heavy draw calls may impact performance.

game

Global game state, timing, terrain queries, and match information.

Overview

Access game state and utility functions.

  • Timing, map data, and match state
  • Terrain queries (walls, grass)
  • Cursor and input information

Global Variables

player() GameObject
Reference to the local player character
gameTime() float
Current game time in seconds
inGame() bool
True if currently in a game (not loading/lobby)
mousePos() Vector3
3D world position of mouse cursor
mousePos2D() Vector2
2D screen position of mouse cursor

Game Functions

game.getTime() float
Get current game time in seconds
game.getPing() int
Get current network latency in milliseconds
game.getMapId() int
Get current map ID (11 = SR, 12 = HA, etc.)
game.isWallAt(position) bool
Check if position is blocked by terrain
game.isGrass(position) bool
Check if position is in brush/grass
game.getCursorPos() Vector3
Get 3D world position of cursor

Examples

Game timing and delays
-- Track game time for objective timers
local dragonSpawnTime = 5 * 60 -- 5 minutes
local baronSpawnTime = 20 * 60 -- 20 minutes
cb.add(cb.CB_TICK, function()
local currentTime = game.getTime()
if currentTime > dragonSpawnTime then
-- Dragon is spawned
if currentTime % 60 < 1 then -- Print once per minute
print("Dragon available!")
end
end
if currentTime > baronSpawnTime then
print("Baron available!")
end
end)
-- Delay action with game time
local lastCastTime = 0
local cooldown = 1.0 -- 1 second cooldown
cb.add(cb.CB_TICK, function()
if game.getTime() - lastCastTime > cooldown then
-- Can cast again
lastCastTime = game.getTime()
player:castSpell("self", SpellSlot.Q)
end
end)
Check terrain and positioning
-- Validate position before movement
function isSafePosition(pos)
-- Check if it's a wall
if game.isWallAt(pos) then
return false
end
-- Check if too close to enemies
for enemy in objManager.ienemies do
if enemy:isValidTarget() and enemy.pos:dist(pos) < 400 then
return false
end
end
return true
end
-- Move to cursor if safe
cb.add(cb.CB_TICK, function()
if input.isKeyDown("Space") then
local cursorPos = game.getCursorPos()
if isSafePosition(cursorPos) then
player:move(cursorPos)
else
print("Unsafe position!")
end
end
end)
Brush detection and stealth play
-- Hide in brush when low HP
cb.add(cb.CB_TICK, function()
local me = player
if me:healthPercent() < 30 then
-- Find nearest brush
local nearestBrush = nil
local minDist = 999999
for x = -200, 200, 100 do
for y = -200, 200, 100 do
local checkPos = me.pos + Vector3(x, 0, y)
if game.isGrass(checkPos) then
local dist = me.pos:dist(checkPos)
if dist < minDist then
minDist = dist
nearestBrush = checkPos
end
end
end
end
if nearestBrush and minDist < 500 then
-- Move to brush
me:move(nearestBrush)
print("Hiding in brush!")
end
end
end)
Map-specific logic
-- Different logic for different maps
local MAP_SR = 11 -- Summoner's Rift
local MAP_ARAM = 12 -- Howling Abyss
local MAP_TFT = 22 -- TFT
cb.add(cb.CB_LOAD, function()
local mapId = game.getMapId()
if mapId == MAP_SR then
print("Summoner's Rift - Full feature set")
-- Enable jungle clear, objective tracking, etc.
elseif mapId == MAP_ARAM then
print("ARAM - Teamfight mode")
-- Disable jungle features, focus on teamfights
elseif mapId == MAP_TFT then
print("TFT detected - disabling script")
return
end
end)
Ping-based prediction
-- Adjust prediction based on ping
function getPredictionDelay()
local ping = game.getPing()
-- Add ping compensation to spell delay
local baseDelay = 0.25
local pingDelay = ping / 1000 -- Convert ms to seconds
return baseDelay + pingDelay
end
-- Use in prediction
local target = TargetSelector.get(player, 1000)
if target then
local delay = getPredictionDelay()
local pred = pred.get_prediction(target, delay, 70, 1000, 1400)
if pred.canHit then
player:castSpell("pos", SpellSlot.Q, pred.castPosition)
end
end

Map Constants

Common map IDs:

  • 11: Summoner's Rift
  • 12: Howling Abyss (ARAM)
  • 21: Nexus Blitz
  • 22: Teamfight Tactics
  • 30: Training Map

log

Logging and debugging with multiple levels and file output.

Overview

Debug and monitor scripts with structured logging.

  • Log levels: DEBUG, INFO, WARN, ERROR
  • Formatted output with timestamps
  • Optional file logging

Logging Functions

log.info(message, ...) void
Log informational message (standard output)
log.debug(message, ...) void
Log debug message (verbose, only shown if debug enabled)
log.warn(message, ...) void
Log warning message (highlighted in yellow)
log.error(message, ...) void
Log error message (highlighted in red)
log.trace(message, ...) void
Log with stack trace for debugging

Configuration

log.setLevel(level) void
Set minimum log level (DEBUG, INFO, WARN, ERROR)
log.setFile(filepath) void
Enable file logging to specified path
log.enable() void
Enable logging output
log.disable() void
Disable all logging output
log.clear() void
Clear console log (if supported)

Examples

Basic logging
-- Simple logging
log.info("Script loaded successfully")
log.info("Player: " .. player.charName)
-- Formatted logging
log.info(string.format(
"Position: (%.0f, %.0f, %.0f)",
player.x, player.y, player.z
))
-- Warnings and errors
if not target then
log.warn("No target found")
end
if player:healthPercent() < 10 then
log.error("CRITICAL HP: " .. player.health)
end
Debug logging with levels
-- Set log level (only show INFO and above)
log.setLevel("INFO")
-- These will be shown
log.info("Script initialized")
log.warn("Low mana: " .. player.mana)
log.error("Spell cast failed")
-- This will be hidden (below INFO level)
log.debug("Target health: " .. target.health)
-- Enable debug mode
log.setLevel("DEBUG")
-- Now debug messages are shown
log.debug("Checking target validity...")
log.debug("Prediction hit chance: " .. pred.hitChance)
File logging
-- Enable file logging
log.setFile("logs/script.log")
log.info("=== Script Started ===")
log.info("Time: " .. os.date("%Y-%m-%d %H:%M:%S"))
-- Log important events to file
cb.add(cb.CB_DEATH, function(unit)
if unit.isMe then
log.error("DEATH: Died at " .. game.getTime() .. "s")
elseif unit.isEnemy and unit.isHero then
log.info("KILL: " .. unit.charName .. " eliminated")
end
end)
cb.add(cb.CB_LEVEL_UP, function()
log.info("LEVEL UP: Now level " .. player.level)
end)
Debug tracing and diagnostics
-- Trace function calls
function performCombo(target)
log.trace("performCombo called")
log.debug("Target: " .. target.charName)
if player:spellState(SpellSlot.Q) ~= SpellState.Ready then
log.warn("Q not ready, aborting combo")
return false
end
log.debug("Casting Q...")
local result = player:castSpell("obj", SpellSlot.Q, target)
if result then
log.info("Combo executed successfully")
else
log.error("Combo failed - spell cast returned false")
end
return result
end
-- Comprehensive error logging
function safeCast(slot, target)
log.debug("Attempting to cast " .. slot)
if not target or not target.valid then
log.error("Invalid target for spell cast")
return false
end
if not target:isValidTarget() then
log.warn("Target not valid: " .. target.charName)
return false
end
local state = player:spellState(slot)
if state ~= SpellState.Ready then
log.debug("Spell not ready: " .. state)
return false
end
return player:castSpell("obj", slot, target)
end
Conditional and formatted logging
-- Log with conditions
local LOG_SPELLS = true
local LOG_MOVEMENT = false
local LOG_TARGETING = true
cb.add(cb.CB_CAST_SPELL, function(spell)
if LOG_SPELLS then
log.info(string.format(
"[SPELL] %s cast %s at %.0fs",
player.charName,
spell.name,
game.getTime()
))
end
end)
-- Periodic stats logging
local lastLogTime = 0
local LOG_INTERVAL = 60 -- Log every 60 seconds
cb.add(cb.CB_TICK, function()
if game.getTime() - lastLogTime > LOG_INTERVAL then
lastLogTime = game.getTime()
log.info("=== Status Update ===")
log.info(string.format(
"Level: %d | Gold: %d | KDA: %d/%d/%d",
player.level,
player.gold,
player.kills or 0,
player.deaths or 0,
player.assists or 0
))
end
end)
-- Performance logging
local frameCount = 0
local startTime = game.getTime()
cb.add(cb.CB_TICK, function()
frameCount = frameCount + 1
if frameCount % 1000 == 0 then
local elapsed = game.getTime() - startTime
local fps = frameCount / elapsed
log.info(string.format(
"Performance: %.1f FPS (avg over %d frames)",
fps, frameCount
))
end
end)

Log Levels

  • DEBUG: Verbose output for development (lowest priority)
  • INFO: Standard informational messages
  • WARN: Warning messages for potential issues
  • ERROR: Error messages for failures (highest priority)

When you set a log level, only messages at that level or higher are displayed. For example, setting level to INFO will show INFO, WARN, and ERROR, but hide DEBUG.

Best Practices

  • Use DEBUG for verbose development info
  • Use INFO for normal operation messages
  • Use WARN for recoverable issues
  • Use ERROR for critical failures
  • Enable file logging for debugging complex issues
  • Use string.format() for clean, readable output
  • Disable excessive logging in production for performance

gui

ImGui-based overlay system for creating in-game menus and ESP rendering.

Overview

Complete DirectX 11 overlay system with widgets and ESP rendering.

  • Window, button, checkbox, slider, text widgets
  • ESP drawing: boxes, lines, health bars, text
  • Input handling with game passthrough
  • Toggle support (default: F1)

Widgets

gui.window(title, callback) void
Create a window with custom content
gui.button(label) bool
Create a clickable button, returns true when clicked
gui.checkbox(label, value) bool, value
Create a checkbox, returns changed state and new value
gui.slider(label, value, min, max) value
Create a slider for numeric input
gui.sliderInt(label, value, min, max) int
Integer slider
gui.sliderFloat(label, value, min, max) float
Float slider
gui.text(text) void
Display text
gui.textColored(r, g, b, a, text) void
Display colored text
gui.combo(label, current, items) int
Create a combo/dropdown box

Layout & Structure

gui.separator() void
Add horizontal separator line
gui.spacing() void
Add vertical spacing
gui.sameLine() void
Place next widget on same line
gui.collapsingHeader(label) bool
Collapsible section header
gui.beginTabBar(id) bool
Begin tab bar container
gui.beginTabItem(label) bool
Begin individual tab

ESP Rendering

esp.drawBox(x, y, width, height, r, g, b, a) void
Draw rectangular box at screen position
esp.drawLine(x1, y1, x2, y2, r, g, b, thickness) void
Draw line between two points
esp.drawText(x, y, text, r, g, b) void
Draw text at screen position
esp.drawHealthBar(x, y, width, height, health, maxHealth) void
Draw health bar with fill
esp.drawCircle(x, y, radius, r, g, b, segments) void
Draw circle outline
esp.worldToScreen(worldX, worldY, worldZ) bool, x, y
Convert 3D world position to 2D screen coordinates

Examples

Simple menu with widgets
-- Create main menu window
gui.window("Script Menu", function()
gui.text("Rito.sh - Script Controls")
gui.separator()
-- Combat section
if gui.collapsingHeader("Combat") then
local changed, newValue = gui.checkbox("Enable Combo", comboEnabled)
if changed then
comboEnabled = newValue
end
comboRange = gui.sliderInt("Combo Range", comboRange, 400, 1200)
if gui.button("Reset Settings") then
comboEnabled = false
comboRange = 800
end
end
-- Drawing section
if gui.collapsingHeader("Drawing") then
drawRanges = gui.checkbox("Draw Ranges", drawRanges)
drawPrediction = gui.checkbox("Draw Prediction", drawPrediction)
gui.text("Circle Alpha:")
gui.sameLine()
circleAlpha = gui.sliderFloat("##alpha", circleAlpha, 0.0, 1.0)
end
end)
Tabbed menu interface
gui.window("Feature Menu", function()
if gui.beginTabBar("MainTabs") then
if gui.beginTabItem("Combo") then
gui.checkbox("Q in Combo", useQ)
gui.checkbox("W in Combo", useW)
gui.checkbox("E in Combo", useE)
gui.checkbox("R in Combo", useR)
gui.endTabItem()
end
if gui.beginTabItem("Harass") then
gui.checkbox("Q Harass", harassQ)
gui.checkbox("W Harass", harassW)
manaThreshold = gui.sliderInt("Min Mana %", manaThreshold, 0, 100)
gui.endTabItem()
end
if gui.beginTabItem("Drawing") then
drawColor = gui.combo("Draw Color", drawColor, {
"Red", "Green", "Blue", "White"
})
gui.endTabItem()
end
gui.endTabBar()
end
end)
ESP overlay rendering
-- Draw ESP on all enemies
cb.add(cb.CB_DRAW_WORLD, function()
for enemy in objManager.ienemies do
if enemy:isValidTarget() then
-- Convert world position to screen
local visible, screenX, screenY = esp.worldToScreen(
enemy.x, enemy.y, enemy.z
)
if visible then
-- Draw box around enemy
esp.drawBox(screenX - 25, screenY - 40, 50, 80,
1.0, 0.0, 0.0, 0.5) -- Red box
-- Draw name above head
esp.drawText(screenX, screenY - 50, enemy.charName,
1.0, 1.0, 1.0) -- White text
-- Draw health bar
esp.drawHealthBar(screenX - 25, screenY + 45,
50, 5, enemy.health, enemy.maxHealth)
-- Draw line from player to enemy
local _, myX, myY = esp.worldToScreen(
player.x, player.y, player.z
)
if myX then
esp.drawLine(myX, myY, screenX, screenY,
0.0, 1.0, 0.0, 2) -- Green line
end
end
end
end
end)
Advanced ESP with distance filtering
-- ESP with distance-based rendering
local MAX_DISTANCE = 3000
local NEAR_DISTANCE = 1000
cb.add(cb.CB_DRAW_WORLD, function()
for enemy in objManager.ienemies do
if enemy:isValidTarget() then
local distance = enemy.dist
-- Skip if too far
if distance > MAX_DISTANCE then
goto continue
end
local visible, sx, sy = esp.worldToScreen(
enemy.x, enemy.y, enemy.z
)
if visible then
-- Color based on distance
local r, g, b = 1.0, 0.0, 0.0
if distance < NEAR_DISTANCE then
-- Close = red
r, g, b = 1.0, 0.0, 0.0
else
-- Far = yellow
r, g, b = 1.0, 1.0, 0.0
end
-- Draw box with distance color
esp.drawBox(sx - 25, sy - 40, 50, 80, r, g, b, 0.7)
-- Draw champion name and distance
local text = string.format("%s [%dm]",
enemy.charName, math.floor(distance)
)
esp.drawText(sx, sy - 50, text, 1.0, 1.0, 1.0)
-- Draw health bar
esp.drawHealthBar(sx - 25, sy + 45, 50, 5,
enemy.health, enemy.maxHealth)
-- Draw circle at feet for close enemies
if distance < NEAR_DISTANCE then
esp.drawCircle(sx, sy + 40, 30, r, g, b, 32)
end
end
::continue::
end
end
end)
Custom styled widgets
-- Menu with custom colors and styling
gui.window("Styled Menu", function()
-- Push red color for critical button
gui.pushStyleColor(0, 1.0, 0.0, 0.0, 1.0) -- Button color
if gui.button("DANGER: Reset All") then
resetAllSettings()
end
gui.popStyleColor(1)
gui.spacing()
-- Success button in green
gui.pushStyleColor(0, 0.0, 1.0, 0.0, 1.0)
if gui.button("Save Configuration") then
saveConfig()
end
gui.popStyleColor(1)
gui.separator()
-- Colored status text
if scriptActive then
gui.textColored(0.0, 1.0, 0.0, 1.0, "Status: ACTIVE")
else
gui.textColored(1.0, 0.0, 0.0, 1.0, "Status: INACTIVE")
end
end)

Input Handling

The GUI system automatically handles input capture:

  • Menu is toggled with F1 by default
  • Mouse and keyboard input pass through to game when menu is hidden
  • Input is captured when interacting with GUI widgets
  • Use gui.wantCaptureMouse() and gui.wantCaptureKeyboard() to check capture state

Color Values

All color parameters use RGBA format with values from 0.0 to 1.0:

  • Red: (1.0, 0.0, 0.0, 1.0)
  • Green: (0.0, 1.0, 0.0, 1.0)
  • Blue: (0.0, 0.0, 1.0, 1.0)
  • White: (1.0, 1.0, 1.0, 1.0)
  • Yellow: (1.0, 1.0, 0.0, 1.0)
  • Cyan: (0.0, 1.0, 1.0, 1.0)
  • Magenta: (1.0, 0.0, 1.0, 1.0)

Alpha channel (4th value) controls transparency: 1.0 = opaque, 0.0 = transparent