///- --package .
client (test)
test client: prelude
///- --test static --symbol
:< testing
:< sm'_operators
:! native_position = $'any'
:! creature = $'any'
:! crowd = $'any'
:== spot = { x : i32; y : i32; z : i32 }
:! host_value = $'any'
:= host_text (v : host_value) : string = $'tostring(!v)' : string
:= after (ms : i32) (f : () -> ()) : () = $'scheduleEvent(!f, !ms)' : ()
:= now_ms () : i32 = $'g_clock.millis()' : i32
:= wall_seconds () : i32 = $'os.time()' : i32
:~ env_text (name : string) (fallback : string) : string =
:= value = env::get_environment_variable name
? value = "" ?> fallback |: value
:~ env_i32 (name : string) (fallback : i32) : i32 =
optionm'::default_value fallback (convert::try_convert (sm'::trim (env::get_environment_variable name)))
:= seed_random_from_clock () : () = $'math.randomseed(os.time())' : ()
:= env_is (name : string) (value : string) : bool = env::get_environment_variable name = value
:= spot_of (p : native_position) : spot = { x = $'!p.x' : i32; y = $'!p.y' : i32; z = $'!p.z' : i32 }
:= player_spot () : spot = spot_of ($'g_game.getLocalPlayer():getPosition()' : native_position)
:= player_positioned () : bool = $'g_game.getLocalPlayer():getPosition() ~= nil' : bool
:= player_exists () : bool = $'g_game.getLocalPlayer() ~= nil' : bool
:= game_online () : bool = $'g_game.isOnline()' : bool
:~ player_present () : bool = player_exists () && game_online ()
:= player_name () : string = $'g_game.getLocalPlayer():getName()' : string
:= player_health () : i32 = $'g_game.getLocalPlayer():getHealth()' : i32
:= player_max_health () : i32 = $'g_game.getLocalPlayer():getMaxHealth()' : i32
:= player_shield_skill () : i32 = $'g_game.getLocalPlayer():getSkillLevel(Skill.Shielding)' : i32
:= player_mana () : i32 = $'g_game.getLocalPlayer():getMana()' : i32
:= player_experience () : i32 = $'g_game.getLocalPlayer():getExperience()' : i32
:= player_walking () : bool = $'g_game.getLocalPlayer():isWalking()' : bool
:= local_player () : creature = $'g_game.getLocalPlayer()' : creature
:= creature_has_zone_check (c : creature) : bool = $'(!c).isInProtectionZone ~= nil' : bool
:= creature_in_zone (c : creature) : bool = $'(!c):isInProtectionZone()' : bool
:~ player_zone () : string =
:= player = local_player ()
? creature_has_zone_check player ?> (? creature_in_zone player ?> "true" |: "false") |: "?"
:| footing =
| Free
| Ramp
| Occupied
| Blocked
| Unknown
:! map_tile = $'any'
:= tile_at (x : i32) (y : i32) (z : i32) : map_tile = $'g_map.getTile({ x = !x, y = !y, z = !z })' : map_tile
:= tile_present (tile : map_tile) : bool = $'(!tile) ~= nil' : bool
:= tile_walkable (tile : map_tile) : bool = $'(!tile):isWalkable()' : bool
:= tile_floor_change (tile : map_tile) : bool = $'(!tile):hasFloorChange()' : bool
:= tile_walkable_through_creatures (tile : map_tile) : bool = $'(!tile):isWalkable(true)' : bool
:~ footing_of (present : bool) (walkable : bool) (floor_change : bool) (through_creatures : bool) : footing =
? present = !- ?> Unknown
|? walkable && floor_change = !- ?> Free
|? walkable ?> Ramp
|? through_creatures ?> Occupied
|: Blocked
:~ footing_at (s : spot) : footing =
:= tile = tile_at s::x s::y s::z
? tile_present tile
?> footing_of !+ (tile_walkable tile) (tile_floor_change tile) (tile_walkable_through_creatures tile)
|: Unknown
:= spectators (s : spot) : crowd =
:= x, y, z = s::x, s::y, s::z
$'g_map.getSpectators({ x = !x, y = !y, z = !z }, false)' : crowd
:= crowd_size (c : crowd) : i32 = $'##(!c)' : i32
:= member (c : crowd) (index : i32) : creature = $'(!c)[!index]' : creature
:= creature_spot (c : creature) : spot = spot_of ($'(!c):getPosition()' : native_position)
:= creature_name (c : creature) : string = $'(!c):getName()' : string
:= creature_health_percent (c : creature) : i32 = $'(!c):getHealthPercent()' : i32
:= same_creature (a : creature) (b : creature) : bool = $'(!a) == (!b)' : bool
:= attacking () : creature = $'g_game.getAttackingCreature()' : creature
:= attacking_anyone () : bool = $'g_game.isAttacking()' : bool
:= creature_present (c : creature) : bool = $'(!c) ~= nil' : bool
:= spot_text (s : spot) : string = "" ++#? s::x ++# "," ++#? s::y ++# "," ++#? s::z
:= target_line (a : creature) : string = creature_name a ++# " " ++#? creature_health_percent a ++# "%"
:~ attacking_short () : string =
:= a = attacking ()
? creature_present a ?> target_line a |: "nothing"
:~ attacking_described () : string =
:= a = attacking ()
? creature_present a ?> target_line a ++# " at " ++# spot_text (creature_spot a) |: "nothing"
:= attack (c : creature) : () = $'g_game.attack(!c)' : ()
:= cancel_attack () : () = $'g_game.cancelAttack()' : ()
:= chase_on () : bool = $'g_game.getChaseMode() == ChaseOpponent' : bool
:= chase_opponent_mode () : i32 = $'ChaseOpponent' : i32
:= dont_chase_mode () : i32 = $'DontChase' : i32
:= chase_mode () : i32 = $'g_game.getChaseMode()' : i32
:= set_chase_mode (mode : i32) : () = $'g_game.setChaseMode(!mode)' : ()
:~ chasing (on : bool) : () =
:= wanted = ? on ?> chase_opponent_mode () |: dont_chase_mode ()
? chase_mode () <> wanted ?> set_chase_mode wanted
:= fight_balanced () : () = $'g_game.setFightMode(FightBalanced)' : ()
:= fight_mode_text () : string = host_text ($'g_game.getFightMode()' : host_value)
:= following_someone () : bool = $'g_game.getFollowingCreature() ~= nil' : bool
:! item = $'any'
:= inventory_item (p : creature) (slot : i32) : item = $'(!p):getInventoryItem(!slot)' : item
:= item_present (i : item) : bool = $'(!i) ~= nil' : bool
:= item_id (i : item) : i32 = $'(!i):getId()' : i32
:= left_hand_slot () : i32 = $'InventorySlotLeft' : i32
:= right_hand_slot () : i32 = $'InventorySlotRight' : i32
:= drink (item : i32) : bool = $'g_game.useInventoryItemWith(!item, g_game.getLocalPlayer()) ~= false' : bool
:! direction = $'any'
:= north () : direction = $'North' : direction
:= east () : direction = $'East' : direction
:= south () : direction = $'South' : direction
:= west () : direction = $'West' : direction
:= north_east () : direction = $'NorthEast' : direction
:= south_east () : direction = $'SouthEast' : direction
:= south_west () : direction = $'SouthWest' : direction
:= north_west () : direction = $'NorthWest' : direction
:= walk_direction (d : direction) : bool = $'g_game.walk(!d) ~= false' : bool
:= heading (dx : i32) (dy : i32) (x : i32) (y : i32) : bool = dx = x && dy = y
:~ diagonal_south (dx : i32) (dy : i32) : bool =
? heading dx dy 1 1 ?> walk_direction (south_east ())
|? heading dx dy (0 - 1) 1 ?> walk_direction (south_west ())
|: !-
:~ diagonal_step (dx : i32) (dy : i32) : bool =
? heading dx dy 1 (0 - 1) ?> walk_direction (north_east ())
|? heading dx dy (0 - 1) (0 - 1) ?> walk_direction (north_west ())
|: diagonal_south dx dy
:~ south_or_west_step (dx : i32) (dy : i32) : bool =
? heading dx dy 0 1 ?> walk_direction (south ())
|? heading dx dy (0 - 1) 0 ?> walk_direction (west ())
|: diagonal_step dx dy
:~ step (dx : i32) (dy : i32) : bool =
? heading dx dy 0 (0 - 1) ?> walk_direction (north ())
|? heading dx dy 1 0 ?> walk_direction (east ())
|: south_or_west_step dx dy
:= talk (text : string) : () = $'g_game.talk(!text)' : ()
:! settings_node = $'any'
:= bot_settings_node () : settings_node = $'g_settings.getNode("bot")' : settings_node
:= settings_node_present (s : settings_node) : bool = $'(!s) ~= nil' : bool
:= empty_settings_node () : settings_node = $'{}' : settings_node
:= mark_bot_disabled (s : settings_node) (key : string) : () = $'(!s)[!key] = { enabled = false }' : ()
:= mark_bot_enabled (s : settings_node) (key : string) (config : string) : () =
$'(!s)[!key] = { enabled = true, config = !config }' : ()
:= store_bot_settings (s : settings_node) : () = $'g_settings.setNode("bot", !s)' : ()
:= save_settings () : () = $'g_settings.save()' : ()
:= current_bot_settings () : settings_node =
:= s = bot_settings_node ()
? settings_node_present s ?> s |: empty_settings_node ()
:~ disable_client_bot (character_key : string) : () =
:= s = current_bot_settings ()
mark_bot_disabled s character_key
store_bot_settings s
save_settings ()
:~ enable_client_bot (character_key : string) (config : string) : () =
:= s = current_bot_settings ()
mark_bot_enabled s character_key config
store_bot_settings s
save_settings ()
:= start_client_bot (character_key : string) (config : string) : () =
enable_client_bot character_key config
$'modules.game_bot.refresh()' : ()
:= parity_present () : bool = $'ArenaParity ~= nil' : bool
:= parity_text () : string = $'ArenaParity.summary()' : string
:~ parity_summary () : string = ? parity_present () ?> parity_text () |: ""
:== engaged = mut (list (i32 * creature))
:= no_engaged () : engaged = mut []
:== creature_slot = mut (option creature)
:= no_creature () : creature = $'nil' : creature
:= new_creature_slot () : creature_slot = mut None
:= slot_occupied (slot : creature_slot) : bool = ?= *slot ?| Some _ => !+ | None => !-
:= slot_creature (slot : creature_slot) : creature = ?= *slot ?| Some c => c | None => no_creature ()
:= occupy_slot (slot : creature_slot) (c : creature) : () = slot <- Some c
:= empty_slot (slot : creature_slot) : () = slot <- None
:= creature_id (c : creature) : i32 = $'(!c):getId()' : i32
:= creature_is_monster (c : creature) : bool = $'(!c):isMonster()' : bool
:= creature_is_local_player (c : creature) : bool = $'(!c):isLocalPlayer()' : bool
:= creature_positioned (c : creature) : bool = $'(!c):getPosition() ~= nil' : bool
:= creature_dead (c : creature) : bool = $'(!c):isDead()' : bool
:~ positioned_monster (c : creature) : bool = creature_is_monster c && creature_positioned c
:~ live_monster (c : creature) : bool = creature_is_monster c && creature_dead c = !- && creature_positioned c
:~ ^ remember (t : engaged) (c : creature) : () =
:= id = creature_id c
t <- (id, c) :: listm'::filter (\ (seen, _) => seen <> id) *t
:~ ^ forget_dead (t : engaged) : i32 =
:= dead = listm'::filter (\ (_, c) => creature_dead c) *t
t <- listm'::filter (\ (_, c) => creature_dead c = !-) *t
listm::length dead
:= monster_line (c : creature) : string =
:= at = creature_spot c
target_line c ++# " " ++#? at::x ++# "," ++#? at::y
:~ ^ monster_lines (seen : crowd) (i : i32) (acc : list string) : list string =
? i < 1 ?> acc
|:
:= c = member seen i
monster_lines seen (i - 1) (? live_monster c ?> monster_line c :: acc |: acc)
:~ ^ monsters_described (s : spot) : string =
:= seen = spectators s
?= monster_lines seen (crowd_size seen) [] ?| [] => "none" | lines => sm'::concat_list "; " lines
:= position_known (p : native_position) : bool = $'(!p) ~= nil' : bool
:= player_speed () : string = host_text ($'g_game.getLocalPlayer():getSpeed()' : host_value)
:! position_handler = $'any'
:= position_handler_of (f : creature -> native_position -> native_position -> ()) : position_handler =
$'function(c, n, o) (!f)(c)(n)(o) end' : position_handler
:= connect_creature_moves (h : position_handler) : () = $'connect(Creature, { onPositionChange = !h })' : ()
:= connect_player_moves (h : position_handler) : () = $'connect(LocalPlayer, { onPositionChange = !h })' : ()
:~ on_position_change (f : creature -> native_position -> native_position -> ()) : () =
:= handler = position_handler_of f
connect_creature_moves handler
connect_player_moves handler
:= online () : bool = $'g_game.isOnline()' : bool
:= log_out () : () = $'g_game.safeLogout()' : ()
:= exit_client () : () = $'g_app.exit()' : ()
:= hidden_requested () : bool = env_is "TIBIA_PROBE_HIDDEN" "1"
:= hide_window () : () =
$'g_window.move({ x = -32000, y = -32000 })' : ()
$'g_window.hide()' : ()
:= sounds_present () : bool = $'g_sounds ~= nil' : bool
:= sounds_disable () : () = $'g_sounds.setAudioEnabled(false)' : ()
:= sounds_stop () : () = $'g_sounds.stopAll()' : ()
:~ mute () : () =
$'g_settings.set("enableAudio", false)' : ()
? sounds_present () ?>
sounds_disable ()
sounds_stop ()
:= probe_log_missing () : bool = $'TibiaProbeLog == nil' : bool
:= probe_log_start () : () = $'TibiaProbeLog = {}' : ()
:= probe_log_text () : string = $'table.concat(TibiaProbeLog, string.char(10))' : string
:~ log_flush () : () =
:= joined = probe_log_text () ++# "\n"
$'g_resources.writeFileContents("/tibia-probe.log", !joined)' : ()
:~ write_log_line (line : string) : () =
? probe_log_missing () ?> probe_log_start ()
$'table.insert(TibiaProbeLog, !line)' : ()
$'g_logger.info(!line)' : ()
log_flush ()
:! game_handler = $'any'
:= login_error_handler (f : string -> ()) : game_handler =
:= typed (message : host_value) : () = f (host_text message)
$'function(message) (!typed)(message) end' : game_handler
:= text_message_handler (f : string -> string -> ()) : game_handler =
:= typed (mode : host_value) (text : host_value) : () = f (host_text mode) (host_text text)
$'function(mode, text) (!typed)(mode)(text) end' : game_handler
:= connect_game_events (on_start : () -> ()) (on_error : game_handler) (on_text : game_handler) : () =
$'connect(g_game, { onGameStart = !on_start, onLoginError = !on_error, onTextMessage = !on_text })' : ()
:== session = { character : string; on_start : () -> (); on_error : string -> (); on_text : string -> string -> () }
:= client_version_set () : () =
$'g_game.setClientVersion(1511)' : ()
$'g_game.setProtocolVersion(g_game.getClientProtocolVersion(1511))' : ()
$'g_game.chooseRsa("127.0.0.1")' : ()
:= world_login (character : string) : () =
:= account = "@god"
:= session_key = "@god\ngod"
$'g_game.loginWorld(!account, "god", "OTServBR-Global", "127.0.0.1", 7172, !character, "", !session_key)' : ()
:= log_in (s : session) : () =
:= on_start, on_error, on_text, c = s::on_start, s::on_error, s::on_text, s::character
client_version_set ()
connect_game_events on_start (login_error_handler on_error) (text_message_handler on_text)
world_login c
footing_follows_the_host_tile_facts (test)
test footing_follows_the_host_tile_facts: passed
///- --test --symbol
///> lua
///> c
(footing_of !+ !+ !- !+, footing_of !+ !+ !+ !+, footing_of !+ !- !- !+) |> _assert_eq (Free, Ramp, Occupied)
(footing_of !+ !- !- !-, footing_of !- !+ !- !+) |> _assert_eq (Blocked, Unknown)
__assert_eq { expected = (Free, Ramp, Occupied) }
__assert_eq { expected = (Blocked, Unknown) }
lua: unavailable on this machine (`spiral lua --lua-path /tmp/spiral_cells/1793692148353-6210/main.lua` was not found)