Skip to content

Instantly share code, notes, and snippets.

@realmonster
Last active September 24, 2017 17:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save realmonster/20940bafbdfbbccca7968cf392953ed1 to your computer and use it in GitHub Desktop.
Save realmonster/20940bafbdfbbccca7968cf392953ed1 to your computer and use it in GitHub Desktop.
dune_tas.lua
-- Dune - The Battle For Arrakis (U)[!]
-- Script for TAS
-- 2017, r57shell
unit_bar = 3
building_bar = 4
show_buttons = true
sync = false
show_boxes = true
show_bars = true
show_stats = true
show_houses = false
show_units = false
show_builds = false
show_making = false
show_targets = true
show_cursor = true
show_sight = false
show_range = false
show_turrets = true
show_grid = false
show_fog = false
show_aim = true
function def_button(text, x1, y1, x2, y2, var)
return { text = text, x1 = x1, y1 = y1, x2 = x2, y2 = y2, down = false, var = var}
end
buttons = {
[0] = { text = "hide", x1 = 0, y1 = 0, x2 = 22, y2 = 8, down = false, proc=function()
show_buttons = not show_buttons
end},
[1] = def_button("sync", 24, 0, 51, 8, "sync"),
[2] = def_button("boxes", 53, 0, 81, 8, "show_boxes"),
[3] = def_button("bars", 83, 0, 104, 8, "show_bars"),
[4] = def_button("stats", 106, 0, 128, 8, "show_stats"),
[5] = def_button("houses", 130, 0, 160, 8, "show_houses"),
[6] = def_button("builds", 162, 0, 190, 8, "show_builds"),
[7] = def_button("units", 192, 0, 220, 8, "show_units"),
[8] = def_button("making", 222, 0, 250, 8, "show_making"),
[9] = def_button("targets", 252, 0, 285, 8, "show_targets"),
[10] = def_button("cursor", 287, 0, 319, 8, "show_cursor"),
[11] = def_button("turrets", 229, 10, 261, 18, "show_turrets"),
[12] = def_button("sight", 263, 10, 288, 18, "show_sight"),
[13] = def_button("range", 290, 10, 319, 18, "show_range"),
[14] = def_button("grid", 189, 10, 209, 18, "show_grid"),
[15] = def_button("fog", 211, 10, 227, 18, "show_fog"),
[16] = def_button("aim", 169, 10, 187, 18, "show_aim"),
}
local rb, rw, rws, rl, rls, AND,
registerbefore, registerafter,
white, hp_color, making_color, spice_color,
font_height
local floor, abs = math.floor, math.abs
Houses_colors = {
0xFF0000,
0x0000FF,
0x00FF00,
0xCCCCCC,
0xFF00FF,
0x666666,
}
if gens then
rb = memory.readbyte
rw = memory.readword
rws = memory.readwordsigned
rl = memory.readlong
rls = memory.readlongsigned
AND = bit.band
white = "#FFFFFFFF"
grey = "#AAAAAAFF"
hp_color = 0x00FF00FF
making_color = 0x0000FFFF
spice_color = "#FFCC00FF"
font_height = 8
for i = 1, 6 do
Houses_colors[i] = string.format("#%06XFF", Houses_colors[i])
end
registerbefore = gens.registerbefore
registerafter = gens.registerafter
else
rb = memory.read_u8
rw = memory.read_u16_be
rws = memory.read_s16_be
rl = memory.read_u32_be
rls = memory.read_s32_be
AND = bit.band
white = 0xFFFFFFFF
grey = 0xFFAAAAAA
hp_color = 0xFF00FF00
making_color = 0xFF0000FF
spice_color = 0xFFFFCC00
font_height = 7
for i = 1, 6 do
Houses_colors[i] = 0xFF000000 + Houses_colors[i]
end
registerbefore = event.onframestart
registerafter = event.onframeend
end
local Houses_info
local Build_info
local Build_column
local Units_info
local Units_column
function cstring(address)
s = {}
while true do
local c = rb(address)
if c == 0 then
break
end
table.insert(s, c)
address = address + 1
end
return string.char(unpack(s))
end
function make_info_houses()
local info = {}
local super_names = { "Death Hand", "Fremens", "Saboteur", "???", "Death Hand", "???" }
for i = 0, 5 do
local base = 0x6C75A + i*0x1E
local j = rl(base)
info[i] = {
id = i,
name = cstring(rl(base)),
super_weapon_time = rw(base + 0xC),
super_weapon_name = super_names[i + 1],
}
end
Houses_info = info
end
function make_info_buildings()
local info = {}
local column = ""
for i = 0, 0x12 do
local base = rl(0x6B954 + i*4)
local name = cstring(rl(base + 2))
name = name:gsub("^%s+", "")
name = name:gsub("%s+$", "")
local size = rw(base + 0x3C)
info[i] = {
id = i,
name = name,
armor = rw(base + 0x10),
cost = rw(base + 0x16),
build_speed = rw(base + 0x18),
electricity = rw(base + 0x3A),
size = size,
sx = rw(0x2E2D8 + size*4)/4,
sy = rw(0x2E2D6 + size*4)/4,
}
if i > 1 then
column = column .. string.format("%10s:", name) .. "\n"
end
end
Build_info = info
Build_column = column:sub(1, #column - 1)
end
function make_info_units()
local info = {}
local column = ""
for i = 0, 0x1A do
local base = rl(0x6C5BC + i*4)
info[i] = {
id = i,
name = cstring(rl(base + 2)),
armor = rw(base + 0x10),
sight = rw(base + 0x12),
cost = rw(base + 0x16),
build_speed = rw(base + 0x18),
missile = (AND(rw(base + 0x38), 2) == 2),
range = rw(base + 0x52),
}
if not info[i].missile then
column = column .. string.format("%12s:", info[i].name) .. "\n"
end
end
Units_info = info
Units_column = column:sub(1, #column - 1)
end
make_info_houses()
make_info_units()
make_info_buildings()
local Selectable_blocks = {}
for i = 0, 0x7F do
Selectable_blocks[i] = 1
end
for i = 0, 15 do
Selectable_blocks[0x7B - 0xF + i] = rb(0x45D0 + i)
end
local Cells_near = {}
for i = 0, 8 do
Cells_near[i] = rws(0xFEEC8 + i*2)
end
local Units_offs = {}
for id = 0, 0x65 do
Units_offs[id] = rl(0x4A85A + id*4)
end
function get_unit(id)
return Units_offs[id]
end
local Build_offs = {}
for id = 0, 0x48 do
Build_offs[id] = rl(0x4A71E + id*4)
end
function get_building(id)
return Build_offs[id]
end
function get_type(base)
return rb(base + 2)
end
function get_home(base)
return rb(base + 8)
end
function get_color(base)
return Houses_colors[rb(base + 8) + 1]
end
function get_hp(base)
return rw(base + 0x12)
end
function get_pos(base)
return rw(base + 0xC) / 8 - scrollx,
rw(base + 0xA) / 8 - scrolly
end
function get_pos_floor(base)
local posx, posy = get_pos(base)
return floor(posx), floor(posy)
end
function get_building_armor(base)
local t = rb(base + 2)
return Build_info[t].armor
end
function get_unit_armor(base)
local t = rb(base + 2)
return Units_info[t].armor
end
function get_building_size(base)
local t = rb(base + 0x2)
local info = Build_info[t]
return info.sx, info.sy
end
function get_making(base)
return rb(base + 3)
end
function target(word)
local v = AND(word, 0x3FFF)
if word >= 0xC000 then
return 3, v
elseif word >= 0x8000 then
return 2, v, get_building(v)
elseif word >= 0x4000 then
return 1, v, get_unit(v)
end
end
function target_pos(word)
local tt, tv, to = target(word)
if tt then
if tt == 3 then
return AND(tv, 0x7F)*16 - scrollx,
AND(tv, 0x3F80)/8 - scrolly
elseif tt == 2 then
local sx, sy = get_building_size(to)
local tx, ty = get_pos_floor(to)
return tx + sx/2, ty + sy/2
else
return get_pos_floor(to)
end
end
end
function get_cell_fog(id)
return floor(rb(id*4 + 0xFF7D9C)/2)
end
function get_cell_is_vis(id)
return Selectable_blocks[get_cell_fog(id)]
end
function get_cell_building(id)
if AND(rb(id*4 + 0xFF7D9E), 0x20) ~= 0 then
return get_building(rb(id*4 + 0xFF7D9F) - 1)
end
end
function get_cell_unit(id)
if AND(rb(id*4 + 0xFF7D9E), 0x10) ~= 0 then
return get_unit(rb(id*4 + 0xFF7D9F) - 1)
end
end
function draw_bar(xpos, ypos, width, height, percent, color)
gui.boxa(xpos, ypos, xpos + math.floor(width * percent), ypos + unit_bar, color, color)
end
function get_production(base)
local t = get_type(base)
local making = get_making(base)
if making ~= 0xFF then
local info, b
if t == 8 then
info = Build_info
b = get_building(making)
else
info = Units_info
b = get_unit(making)
end
if b then
local tt, left, total, color = get_type(b)
if t == 12 then -- refinery
left = rb(b + 0x5E) -- spice
total = 100
color = spice_color
else
left = rw(base + 0x56)
total = info[tt].build_speed*256
color = making_color
end
local percent = 1.0 - left/total
return info[tt].name, percent, floor(percent*100), color
end
end
if AND(rw(base + 6), 2) == 2 then -- upgrade
local left = rb(base + 0x55)
local percent = 100 - left
return "Upgrade", percent/100, percent, making_color
end
if t == 2 then -- palace
local timeout = rw(base + 0x4E)
if timeout ~= 0 then
local percent = timeout/0x12C
return "Timeout ", percent, timeout, making_color
else
local left = rw(base + 0x56)
local total = Houses_info[get_home(base)].super_weapon_time
local name = Houses_info[get_home(base)].super_weapon_name
local percent = 1.0 - left/total
return name, percent, floor(percent*100), making_color
end
end
end
function draw_building(base)
if AND(rw(base + 4), 7) ~= 3 then
return
end
local xpos, ypos = get_pos_floor(base)
if xpos > -32*3 and xpos <= 320 and ypos > -32*3 and ypos <= 224 then
local sx, sy = get_building_size(base)
if show_cursor
and xpos <= cursor.X and xpos + sx > cursor.X
and ypos <= cursor.Y and ypos + sy > cursor.Y then
cursor.type = 2
cursor.base = base
end
if show_boxes then
gui.boxa(xpos, ypos, xpos + sx, ypos + sy, get_color(base), 0)
end
if show_bars then
local hp = get_hp(base)
local armor = get_building_armor(base)
if armor ~= 0 then
local percent = hp / armor
draw_bar(xpos, ypos, sx, building_bar, percent, hp_color)
end
end
local name, percent, percent100, color = get_production(base)
if name then
if show_bars then
draw_bar(xpos, ypos + building_bar, sx, building_bar, percent, color)
end
gui.texta(xpos + 4, ypos + 4 + building_bar * 2, name.." "..percent100, get_color(base))
end
end
if show_turrets then
local t = get_type(base)
if t == 15 then -- Turret
circle(xpos, ypos, 5*32, get_color(base))
elseif t == 16 then -- R-Turret
local c = get_color(base)
circle(xpos, ypos, 3*32, c)
circle(xpos, ypos, 8*32, c)
end
end
end
function draw_unit(base)
if rw(base + 4) == 0 then
return
end
local xpos, ypos = get_pos_floor(base)
if xpos > -32 and xpos < 360 and ypos > -32 and ypos < 260 then
if show_cursor
and abs(xpos - cursor.X) <= 16
and abs(ypos - cursor.Y) <= 16 then
cursor.type = 1
cursor.base = base
end
local t = get_type(base)
local missile = Units_info[t].missile
if missile then
if show_boxes then
local color = get_color(base)
gui.boxa(xpos - 1, ypos - 1, xpos + 1, ypos + 1, color, color)
end
else
if show_boxes then
gui.boxa(xpos - 8, ypos - 8, xpos + 8, ypos + 8, get_color(base), 0)
end
if show_bars then
local armor = get_unit_armor(base)
local hp = get_hp(base)
local percent = hp / armor
draw_bar(xpos - 8, ypos - 8, 16, unit_bar, percent, hp_color)
if t == 16 then -- harvester
local spice = rb(base + 0x5E)
draw_bar(xpos - 8, ypos - 8 + unit_bar, 16, unit_bar, spice/100, spice_color)
end
end
end
end
if show_targets then
local tx, ty = target_pos(rw(base + 0x5C))
if tx then
gui.linea(xpos, ypos, tx, ty, get_color(base))
end
if rb(base + 2) ~= 16 then -- harvester
tx, ty = target_pos(rw(base + 0x5A))
if tx then
gui.linea(xpos, ypos, tx, ty, get_color(base))
end
end
if rl(base + 0x4E) ~= 0 then
tx, ty = get_pos_floor(base + 0x4E - 0xA)
gui.linea(xpos, ypos, tx, ty, "red")
end
end
if show_range or show_sight then
local info = Units_info[get_type(base)]
if not info.missile then
local r = info.sight*32
if show_sight then
circle(xpos, ypos, r, white)
end
r = info.range*32
if show_range then
circle(xpos, ypos, r, get_color(base))
end
end
end
end
function draw_houses_stats()
gui.texta(50, 0x10, " Money Power Units", "white")
for i = 0, 4 do
local base = rl(0xFFDC24) + i*0x46
gui.texta(50, 0x10 + (i + 1)*font_height,
string.format("%5d/%-5d %4d/%-4d %2d/%-2d",
rl(base + 0x12), rl(base + 0x16), -- money
rw(base + 0x1C), rw(base + 0x1A), -- power
rw(base + 6), rw(base + 8), -- Units
nil), Houses_colors[i + 1])
end
end
function draw_buildings_count()
gui.texta(40, 0x40, Build_column, "white")
for j = 2, 0x12 do
for i = 0, 4 do
local bb = rl(0x4A9FA + i*4)
gui.texta(89 + i*12, 0x40 + (j - 2)*font_height, string.format("%2d", rb(bb + j)), Houses_colors[i + 1])
end
end
end
function draw_units_count()
local k = 0
gui.texta(152, 0x20, Units_column, "white")
for j = 0, 0x1A do
if not Units_info[j].missile then
for i = 0, 4 do
local ba = rl(0x4AA0E + i*4)
gui.texta(209 + i*12, 0x20 + k*font_height, string.format("%2d", rb(ba + j)), Houses_colors[i + 1])
end
k = k + 1
end
end
end
function draw_making()
local k = 0
for id = 0, 0x48 do
local base = get_building(id)
if AND(rw(base + 4), 3) == 3 then
local name, percent, percent100, color = get_production(base)
if name and color ~= spice_color then
gui.texta(4, 16 + k*font_height, name.." "..percent100, get_color(base))
k = k + 1
end
end
end
end
function draw_cursor()
local t, base = cursor.type, cursor.base
if t == 2 then -- building
t = get_type(base)
local str = string.format("%s\n HP: %d/%d",
Build_info[t].name, get_hp(base), Build_info[t].armor)
gui.texta(cursor.X, cursor.Y, str, get_color(base))
elseif t == 1 then -- unit
local t = get_type(base)
local str = string.format("%s\n HP: %d/%d",
Units_info[t].name, get_hp(base), Units_info[t].armor)
gui.texta(cursor.X, cursor.Y, str, get_color(base))
else -- nothing
local str = string.format("(%d %d)", cursor.X + scrollx, cursor.Y + scrolly)
gui.texta(cursor.X, cursor.Y, str, white)
end
end
function draw_grid()
local sx = floor(scrollx/32)*32 - scrollx
local sy = floor(scrolly/32)*32 - scrolly
for px = sx, sx + 10*32, 32 do
gui.linea(px, 0, px, 224, white)
end
for py = sy, sy + 7*32, 32 do
gui.linea(0, py, 320, py, white)
end
end
function draw_fog()
local sx = floor(scrollx/32)
local sy = floor(scrolly/32)
for py = sy, sy + 7 do
for px = sx, sx + 10 do
local i = py*64 + px
local c = get_cell_is_vis(i)
local r = get_cell_is_vis(i+1)
local b = get_cell_is_vis(i+64)
local posx = px*32 - scrollx
local posy = py*32 - scrolly
if c ~= r then
gui.linea(posx + 32, posy, posx + 32, posy + 32, grey)
end
if c ~= b then
gui.linea(posx, posy + 32, posx + 32, posy + 32, grey)
end
end
end
end
function draw_aim()
local cursor = rw(0xFFC240)
if get_cell_is_vis(cursor) == 0 then
return
end
local posx = (cursor % 64)*32 + 16 - scrollx
local posy = floor(cursor / 64)*32 + 16 - scrolly
gui.boxa(posx - 1, posy - 1, posx + 1,posy + 1, white)
local base = get_cell_building(cursor)
if base then
xpos, ypos = get_pos_floor(base)
local sx, sy = get_building_size(base)
circle(xpos + sx/2, ypos + sy/2, sy*0.7071, white)
return
end
for i = 0, 8 do
local cell = cursor + Cells_near[i]
base = get_cell_unit(cell)
if base then
if get_cell_is_vis(cell) == 1 then
xpos, ypos = get_pos_floor(base)
circle(xpos, ypos, 14, white)
end
return
end
end
end
function draw_all()
gui_draw_info = { idx = 1 }
if rl(0xFFE002) ~= 0x6D10 and rl(0xFFE002) ~= 0x6092 then
return
end
scrollx = rw(0xFFE3BE)
scrolly = rw(0xFFE3C0)
if show_cursor then
cursor = mouse_get()
cursor.type = 0
end
if show_grid then
draw_grid()
end
if show_fog then
draw_fog()
end
for id = 0, 0x48 do
local base = get_building(id)
draw_building(base)
end
for id = 0, 0x65 do
local base = get_unit(id)
draw_unit(base)
end
if show_houses then
draw_houses_stats()
end
if show_builds then
draw_buildings_count()
end
if show_units then
draw_units_count()
end
if show_making then
draw_making()
end
if show_aim then
draw_aim()
end
if show_cursor then
draw_cursor()
end
end
registerbefore(function()
if sync then
draw_all()
end
end)
registerafter(function()
if not sync then
draw_all()
end
if not gens then
render()
end
end)
local gui_draw_info = { idx = 1 }
local gui_draw_info_prev = { idx = 1 }
local gui_text, gui_box, gui_line
if gens then
gui_text, gui_line = gui.text, gui.line
local gb = gui.box
function gui_box(x1, y1, x2, y2, c, o)
gb(x1, y1, x2, y2, o, c)
end
else
gui_text = gui.pixelText
gui_box = gui.drawBox
gui_line = gui.drawLine
gui_boxx = gui.drawBox
end
function gui_circle(x, y, r, color)
gui_line(x - r, y, x - r*0.7071, y - r*0.7071, color)
gui_line(x - r, y, x - r*0.7071, y + r*0.7071, color)
gui_line(x + r, y, x + r*0.7071, y - r*0.7071, color)
gui_line(x + r, y, x + r*0.7071, y + r*0.7071, color)
gui_line(x, y - r, x - r*0.7071, y - r*0.7071, color)
gui_line(x, y - r, x + r*0.7071, y - r*0.7071, color)
gui_line(x, y + r, x - r*0.7071, y + r*0.7071, color)
gui_line(x, y + r, x + r*0.7071, y + r*0.7071, color)
end
function gui_text_(v)
gui_text(v.x, v.y, v.text, v.c, v.o)
end
function gui_box_(v)
gui_box(v.x1, v.y1, v.x2, v.y2, v.c, v.o)
end
function gui_line_(v)
gui_line(v.x1, v.y1, v.x2, v.y2, v.c)
end
function gui_circle_(v)
gui_circle(v.x, v.y, v.r, v.c)
end
function gui.texta(x, y, text, c, o)
local info = gui_draw_info
local idx = info.idx
info.idx = idx + 1
info[idx] = { f = gui_text_, x = x, y = y, text = text, c = c, o = o }
end
function gui.boxa(x1, y1, x2, y2, c, o)
local info = gui_draw_info
local idx = info.idx
info.idx = idx + 1
info[idx] = { f = gui_box_, x1 = x1, y1 = y1, x2 = x2, y2 = y2, c = c, o = o }
end
function gui.linea(x1, y1, x2, y2, c)
local info = gui_draw_info
local idx = info.idx
info.idx = idx + 1
info[idx] = { f = gui_line_, x1 = x1, y1 = y1, x2 = x2, y2 = y2, c = c }
end
function circle(x, y, r, c)
if x + r >= 0 and x - r <= 320
and y + r >= 0 and y - r <= 224 then
local info = gui_draw_info
local idx = info.idx
info.idx = idx + 1
info[idx] = { f = gui_circle_, x = x, y = y, r = r, c = c }
end
end
inpt_prev = {}
if gens then
function mouse_get()
local inpt = input.get()
return { Left = inpt.leftclick, X = inpt.xmouse, Y = inpt.ymouse }
end
else
function mouse_get()
return input.getmouse()
end
end
function draw_button(b)
local inpt = mouse_get()
local xmouse = inpt.X
local ymouse = inpt.Y
if xmouse > b.x1 and xmouse < b.x2
and ymouse > b.y1 and ymouse < b.y2 then
if b.down
and inpt_prev.Left == true
and inpt.Left ~= true then
if b.var then
_G[b.var] = not _G[b.var]
else
b:proc()
end
end
if inpt.Left == true
and inpt_prev.Left ~= true then
b.down = true
end
end
if inpt.Left ~= true then
b.down = false
end
local color = grey
local text = grey
if b.down then
color = white
text = white
end
if b.var and _G[b.var] == true then
text = white
end
if show_buttons then
gui_box(b.x1, b.y1, b.x2, b.y2, color, 0)
local xx = (b.x2 - b.x1 - #b.text*4)/2 + b.x1 + 1
local yy = (b.y2 - b.y1 - 8)/2 + b.y1
gui_text(xx, yy, b.text, text)
end
end
function render()
if sync then
gui_draw_info, gui_draw_info_prev = gui_draw_info_prev, gui_draw_info
end
for k, v in pairs(buttons) do
draw_button(v)
end
for k, v in ipairs(gui_draw_info) do
if k ~= "idx" then
v.f(v)
end
end
gui_draw_info = { idx = 1 }
inpt_prev = mouse_get()
end
if gens then
gui.register(render)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment