User File #61910135187593856

Upload All User Files

#61910135187593856 -

doom_troopers.lua
325 downloads
Uploaded 3/11/2020 2:57 AM by EZGames69 (see all 153)
-- Doom Troopers script for gens/bizhawk for TAS
-- by r57shell
local scrollx = 0
local scrolly = 0
local level = 0
local level_prev = -1
local wblocks = 0
local hblocks = 0
local blocks = nil
local cdata = nil
local invmap = {}
local ru8, ru16, ru32, rs8, rs16, rs32

colors = {
[1]  = 0xFFFFFF99, -- wall
[2]  = 0xFFFFFF30, -- left
[3]  = 0xFFFFFF30, -- right
[6]  = 0x0000FF99, -- ceil
[10] = 0x00FF0099, -- exit
[15] = 0x99990099, -- player respawn
[16] = 0xFF000099, -- pit / lava
[17] = 0x00FF0099, -- mercury 2 secret room 1 exit
[18] = 0x00FF0099, -- mercury 2 secret room 1 teleport
[19] = 0x00FF0099, -- mercury 2 secret room 2 teleport
[23] = 0x00FF0099, -- mercury 1 secret room exit
[24] = 0x00FF0099, -- mercury 1 secret room teleport
[57] = 0xFF000099, -- nero spikes
[65] = 0x00FF0099, -- nero secret level
[66] = 0x00FF0099, -- nero secret level exit
}

local ground_color = 0x00FFFF99
local text_color = 0xFFFFFFFF
local obj_text_color = 0x00FF00FF
local obj_color = 0x00FF00FF
local wind_color = 0x0000FF99
local red = 0xFF0000FF

local cached = true
local bad_level = {
[3] = true, -- story
[4] = true, -- character selection
[5] = true, -- character selection 2
[10] = true, -- title
[12] = true, -- planet 1 screen
[19] = true, -- planet 2 screen
[26] = true, -- planet 3 screen
[33] = true, -- planet 4 screen
[40] = true, -- victory
[41] = true, -- credits
[47] = true, -- rights
[52] = true, -- sega
[53] = true, -- RE
[54] = true, -- AE
}

local gui_text, gui_box, gui_line, gui_pixel

if gens then
	ru8 = memory.readbyteunsigned
	ru16 = memory.readwordunsigned
	ru32 = memory.readlongunsigned
	rs8 = memory.readbytesigned
	rs16 = memory.readwordsigned
	rs32 = memory.readlongsigned

	gui_text, gui_line, gui_pixel = gui.text, gui.line, gui.setpixel

	local gb = gui.box
	function gui_box(x1, y1, x2, y2, c, o)
		gb(x1, y1, x2, y2, o, c)
	end
else
	ru8 = memory.read_u8
	ru16 = memory.read_u16_be
	ru32 = memory.read_u32_be
	rs8 = memory.read_s8
	rs16 = memory.read_s16_be
	rs32 = memory.read_s32_be

	gui_text = gui.pixelText
	gui_box = gui.drawBox
	gui_line = gui.drawLine
	gui_pixel = gui.drawPixel

	function cconvert(c)
		if type(c) == "string" then
			c = tonumber(c,16)
		end
		return bit.band(c, 0xFFFFFF00) / 0x100 + bit.band(c, 0xFF) * 0x1000000
	end

	for i = 0, 0xFF do
		if colors[i] then
			colors[i] = cconvert(colors[i])
		end
	end

	ground_color = cconvert(ground_color)
	text_color = cconvert(text_color)
	obj_text_color = cconvert(obj_text_color)
	obj_color = cconvert(obj_color)
	wind_color = cconvert(wind_color)
	red = cconvert(red)
end

local max, min, floor = math.max, math.min, math.floor

gui_pixel_, gui_box_, gui_line_ = gui_pixel, gui_box, gui_line

local func_chunk =
[[local gui_box, gui_line, gui_pixel = gui_box_, gui_line_, gui_pixel_
return function (x,y)
]]

-- splits tile into axis aligned rectangles of same color
-- w - width, h - height, data - actual tile colors. nil ignored.
-- returns list of rectangles.
-- all rectangles defined by: type, x1, y1, x2, y2, color
-- type is rectangle color, type should be ignored
-- x1, y1 - minimum x, y inclusive
-- x2, y2 - maximum x, y inclusive
function tile_split_simple(data, w, h)
	local arr = {}
	for x = 0, w-1 do
		for y = 0, h-1 do
			local v = data[y*w+x]
			if v then
				local t,x1,y1,x2,y2,z = unpack(arr[#arr] or {})
				if z == v
				and t < 2
				and x2 == x
				and y2 == y-1 then
					arr[#arr][1] = 1
					arr[#arr][5] = y
					local tt,xx1,yy1,xx2,yy2,zz = unpack(arr[#arr-1] or {})
					if (tt == 1 or tt == 2)
					and zz == z
					and yy1 == y1
					and yy2 == y
					and xx2 == x-1 then
						arr[#arr] = nil
						arr[#arr][1] = 2
						arr[#arr][4] = x
					end
				else
					arr[#arr+1] = {0,x,y,x,y,v}
				end
			end
		end
	end
	return arr
end

-- flips tile data and splitter list vertically
function tile_splitter_vflip(data, w, h, list)
	n = {}
	for y = 0, h-1 do
		for x = 0, w-1 do
			n[(h-1-y)*w+x] = data[y*w+x]
		end
	end
	for i = 1, #list do
		local a = list[i]
		a[3], a[5] = h-1-a[5], h-1-a[3]
	end
	return n
end

-- flips tile data and splitter list diagonally
function tile_splitter_dflip(data, w, h, list)
	n = {}
	for y = 0, h-1 do
		for x = 0, w-1 do
			n[x*h+y] = data[y*w+x]
		end
	end
	for i = 1, #list do
		local a = list[i]
		a[2], a[3], a[4], a[5] = a[3], a[2], a[5], a[4]
	end
	return n
end

-- adds pixel to the tile
function tile_splitter_add(self, x, y, color)
	if color then
		table.insert(self, x)
		table.insert(self, y)
		table.insert(self, color)
	end
end

function tile_splitter_split(self)
	if #self < 3 then return {} end

	-- find bound box
	local minx, maxx, miny, maxy = self[1], self[1], self[2], self[2]
	for i = 4, #self, 3 do
		local x, y = unpack(self, i, i+1)
		minx = min(minx, x)
		maxx = max(maxx, x)
		miny = min(miny, y)
		maxy = max(maxy, y)
	end

	-- crop bounding box
	maxx = maxx + 1
	maxy = maxy + 1
	local w, h = maxx-minx, maxy-miny
	data = {}
	for i = 1, #self, 3 do
		local x, y, color = unpack(self, i, i+2)
		data[(y-miny)*w+(x-minx)] = color
	end

	-- try all scan orders and pick the best
	local best = nil
	for i = 1, 4 do
		local list = tile_split_simple(data, w, h)
		if best == nil or #list < #best then
			best = list
		end
		data = tile_splitter_vflip(data, w, h, best)

		list = tile_split_simple(data, w, h)
		if best == nil or #list < #best then
			best = list
		end
		data = tile_splitter_dflip(data, w, h, best)
		w, h = h, w -- swap w, h because we flipped it diagonaly
	end

	-- shift it back to original position
	for i = 1, #best do
		best[i][2] = best[i][2] + minx
		best[i][3] = best[i][3] + miny
		best[i][4] = best[i][4] + minx
		best[i][5] = best[i][5] + miny
	end
	return best
end

tile_splitter_metatable = {
	__index = {
		add = tile_splitter_add,
		split = tile_splitter_split,
	},
}

function tile_splitter()
	local self = {}
	setmetatable(self, tile_splitter_metatable)
	return self
end

-- converts list returned by splitter into function code.
function tile_generator(arr)
	f = ""
	for i = 1, #arr do
		local t,x1,y1,x2,y2,z = unpack(arr[i])
		if x1 == x2 and y1 == y2 then
			f = f.."gui_pixel(x+"..x2..",y+"..y2..","..z..")\n"
		elseif x1 == x2 or y1 == y2 then
			f = f.."gui_line(x+"..x1..",y+"..y1..
				",x+"..x2..",y+"..y2..","..z..")\n"
		else
			f = f.."gui_box(x+"..x1..",y+"..y1..
				",x+"..x2..",y+"..y2..","..z..","..z..")\n"
		end
	end
	return f
end

function climb(s)
	local ts = tile_splitter()
	for x = 0, 15 do
		for y = 0, 15 do
			if math.abs(x - s) + math.abs(y) < 10
			or x == 0 or x == 15 or y == 0 or y == 15 then
				ts:add(x, y, colors[1])
			end
		end
	end
	local arr = ts:split()
	local f = tile_generator(arr)
	f = func_chunk..f.."end"
	return loadstring(f)()
end

function climb_wall(s)
	local ts = tile_splitter()
	for x = 0, 15 do
		for y = 0, 15 do
			if math.abs(x - s) < 1
			or y - floor(y / 3) * 3 == 0 and math.abs(x - s) < 3 then
				ts:add(x, y, colors[1])
			end
		end
	end
	local arr = ts:split()
	local f = tile_generator(arr)
	f = func_chunk..f.."end"
	return loadstring(f)()
end

function flowf(dx, dy)
	local len = math.sqrt(dx * dx + dy * dy)
	local nx, ny = dx / len, dy / len
	dx = dx * 1
	dy = dy * 1
	dx = dx + 8
	dy = dy + 8
	local al = 2
	local dx1, dy1 = dx - (nx - ny) * al, dy - (ny + nx) * al
	local dx2, dy2 = dx - (nx + ny) * al, dy - (ny - nx) * al
	local c = wind_color
	return function(x, y)
		gui_line(x + 8, y + 8, x + dx, y + dy, c)
		gui_line(x + dx, y + dy, x + dx1, y + dy1, c)
		gui_line(x + dx, y + dy, x + dx2, y + dy2, c)
		gui_box(x, y, x + 15, y + 15, c, 0)
	end
end

local climb_wall_r = climb_wall(0)
local climb_wall_l = climb_wall(15)
local callbacks = {
[4] = function (x, y) end,
[5] = function (x, y) end,
[7] = function(x, y) climb_wall_r(x, y) climb_wall_l(x, y) end,
[11] = climb(0), -- climb right
[12] = climb(15), -- climb left
[13] = climb_wall_r, -- climb wall right
[14] = climb_wall_l, -- climb wall left
[0x19] = flowf(-1, 0),
[0x1A] = flowf(-2, 0),
[0x1B] = flowf(-6, 0),
[0x1D] = flowf(1, 0),
[0x1E] = flowf(2, 0),
[0x24] = flowf(0, -14),
[0x25] = flowf(1, -1),
[0x26] = flowf(2, -6),
[0x27] = flowf(6, -10),
[0x28] = flowf(8, -13),
[0x29] = flowf(-1, -1),
[0x2A] = flowf(-2, -6),
[0x2B] = flowf(-6, -10),
[0x2C] = flowf(-8, -13),
[0x30] = flowf(0, 13),
[0x33] = flowf(6, 10),
[0x34] = flowf(8, 13),
[0x35] = flowf(-1, 1),
[0x36] = flowf(-2, 6),
}

for i = 0, 0xFF do
	if colors[i] and not callbacks[i] then
		local c = colors[i]
		callbacks[i] = function (x, y) gui_box(x, y, x + 15, y + 15, c, c) end
	end
end

function loadlevel()
	wblocks = floor(ru16(0xFF00C0) / 16)
	hblocks = floor(ru16(0xFF00C2) / 16)
	if wblocks == 0 or hblocks == 0 then
		level_prev = -1
		return
	end
	level_prev = level
	--local offs = 0x54FEC + level * 2
	--offs = offs + ru16(offs)
	cdata = {}
	blocks = {}
	invmap = {}
	local BC = ru32(0xFF00BC)
	local B4 = ru32(0xFF00B4)
	local B8 = ru32(0xFF00B8)
	for y = 0, hblocks - 1 do
		for x = 0, wblocks - 1 do
			local yy = ru16(BC + y * 2) + x * 2
			local aa = yy - floor(yy / 0x10000) * 0x10000 + floor(BC / 0x10000) * 0x10000
			local a = B4 + floor(ru16(aa) / 2)
			local t = ru8(a + 2)
			invmap[aa] = {x * 16, y * 16, t}
			local dd = ru16(a)
			local b8o = ru16(0xFF00C4 + floor(dd / 4))
			local b8d = B8 + b8o
			if cached then
				blocks[y * wblocks + x + 1] = {t, b8d}
			end
			if cdata[b8d] == nil then
				local ts = tile_splitter()
				for xxx = 0, 15 do
					local v = ru8(b8d + xxx)
					if v > 0 then
						ts:add(xxx, v - 1, ground_color)
					end
				end
				local arr = ts:split()
				local f = tile_generator(arr)
				f = func_chunk..f.."end"
				cdata[b8d] = loadstring(f)()
			end
		end
	end
end

function drawcell(x, y, t)
	local c =  callbacks[t]
	if c then
		c(x - scrollx, y - scrolly)
	else
		gui_text(x + 6 - scrollx, y + 5 - scrolly, string.format("%X", t), text_color)
	end
end

function drawcell_high(x, y, t)
	local c = callbacks[t]
	if c then
		c(x - scrollx, y - scrolly)
		c(x - scrollx, y - scrolly)
		c(x - scrollx, y - scrolly)
	else
		gui_text(x + 6 - scrollx, y + 5 - scrolly, string.format("%X", t), text_color)
	end
end

function drawcollision()
	if level_prev ~= level then
		loadlevel()
		if level_prev ~= level then
			return
		end
	end
	local BC = ru32(0xFF00BC)
	local BCC = floor(BC / 0x10000) * 0x10000
	local B4 = ru32(0xFF00B4)
	local B8 = ru32(0xFF00B8)
	local left, right, top, bottom = scrollx, scrollx + 320, scrolly, scrolly + 224
	if cached then
		for y_ = max(0,floor(top/16)), min(hblocks-1,floor((bottom-1)/16)) do
			local y = y_ * 16
			for x_ = max(0,floor(left/16)), min(wblocks-1,floor((right-1)/16)) do
				local tt = blocks[y_ * wblocks + x_ + 1]
				if tt then
					local x = x_ * 16
					local t, b = unpack(tt)
					if t > 0 then
						drawcell(x, y, t)
					end
					local f = cdata[b]
					if f then
						f(x - scrollx, y - scrolly)
					end
				end
			end
		end
	else
		for y_ = max(0,floor(top/16)), min(hblocks-1,floor((bottom-1)/16)) do
			local y = y_ * 16
			local y1 = ru16(BC + y_ * 2)
			for x_ = max(0,floor(left/16)), min(wblocks-1,floor((right-1)/16)) do
				local x = x_*16
				local yy = y1 + x_ * 2
				local aa = yy - floor(yy / 0x10000) * 0x10000 + BCC
				local a = B4 + floor(ru16(aa) / 2)
				local t = ru8(a + 2)
				local idx = ru16(a)
				local b8o = ru16(0xFF00C4 + floor(idx / 4))
				local b8d = B8 + b8o
				local f = cdata[b8d]
				if t > 0 then
					drawcell(x, y, t)
				end
				if f then
					f(x - scrollx, y - scrolly)
				else
					for i = 0, 15 do
						local v = ru8(b8d + i)
						if v > 0 then
							gui_pixel(x - scrollx + i, y - scrolly + v - 1, ground_color)
						end
					end
				end
			end
		end
	end
end

function mercury_boss(dx, dy, ax, ay, tx, ty)
	if dx < 0 then
		if -dx < 2 then
		elseif -dx < 20 then
			ax, tx = 4, 0x20
		else
			ax, tx = 0x28, 0x400
		end
	else
		if dx < 2 then
		elseif dx < 20 then
			ax, tx = 4, -0x20
		elseif dx < 40 then
			ax, tx = 15, -0x200
		else
			ax, tx = 0x28, -0x400
		end
	end
	if dy < 0 then
		if -dy < 3 then
		elseif -dy < 20 then
			ay, ty = 4, 0x20
		elseif -dy < 40 then
			ay, ty = 15, 0x200
		else
			ay, ty = 0x28, 0x400
		end
	else
		if dy < 3 then
		elseif dy < 20 then
			ay, ty = 4, -0x20
		elseif dy < 40 then
			ay, ty = 15, -0x200
		else
			ay, ty = 0x28, -0x400
		end
	end
	return ax, ay, tx, ty
end

function mercury_drone(x, y, ax, ay, tx, ty, s)
	local dx, dy
	if x > 0 then
		if y > 0 then
			dx = x + s
			dy = y - s
		else
			dx = x - s
			dy = y - s
		end
	else
		if y > 0 then
			dx = x + s
			dy = y + s
		else
			dx = x - s
			dy = y + s
		end
	end
	if dx <= 0 then
		tx = 0x500
	else
		tx = -0x500
	end
	if dy <= 0 then
		ty = 0x500
	else
		ty = -0x500
	end
	return ax, ay, tx, ty
end

function traj(n, f, rx, ry, xx, yy, ax, ay, sx, sy, tx, ty, ss)
	for i = 1, n do
		ax, ay, tx, ty = f(floor(rx / 256) - xx, floor(ry / 256) - yy, ax, ay, tx, ty, ss)
		if sx == tx then
		elseif sx < tx then
			sx = sx + ax
			if sx > tx then sx = tx end
		elseif sx > tx then
			sx = sx - ax
			if sx < tx then sx = tx end
		end
		if sy == ty then
		elseif sy < ty then
			sy = sy + ay
			if sy > ty then sy = ty end
		elseif sy > ty then
			sy = sy - ay
			if sy < ty then sy = ty end
		end
		gui_line(rx / 256, ry / 256, (rx + sx) / 256, (ry + sy) / 256, red)
		rx = rx + sx
		ry = ry + sy
	end
end

local function rpos(addr)
	local r = ru32(addr)
	r = r - floor(r / 0x1000000) * 0x1000000
	if r > 0x800000 then
		return r - 0x1000000
	else
		return r
	end
end

local mercury_boss_attacks = {'F', 'F', 'D', 'S', 'd'}

function drawobjs()
	for i = 0, 100 do
		local base = ru16(0xFF2BBA + i*2)
		if base == 0 then
			break
		end
		base = base + 0xFF0000
		local x = ru16(base + 0x32) - scrollx
		local y = ru16(base + 0x34) - scrolly
		local hp = rs16(base + 0x32 + 0x4A)
		local proc = ru32(base + 0x8C)
		if proc == 0x64028 or proc == 0x63FDE then
			hp = rs16(base + 0x7C)
			gui_text(85, 8, string.format("boss atk:%3d mv:%3d aim:%2d", rs16(base + 0xA4), rs16(base + 0xA0), rs16(base + 0xA8)), obj_text_color)
			local atk = ru8(base + 0xA6)
			local D, S, F = ' ', ' ', ' '
			if atk - floor(atk / 8) * 8 >= 4 then D = 'D' end
			if atk - floor(atk / 4) * 4 >= 2 then S = 'S' end
			if atk - floor(atk / 2) * 2 >= 1 then F = 'F' end
			local pt = ru16(base + 0x7A) -- player target
			local na = ru16(base + 0xA2) -- next attack
			if na >= 3 then
				na = 3
				if pt ~= 0 and rs16(0xFF0000 + pt + 0x34) <= rs16(base + 0x34) then
					na = 4	-- staff failed
				end
			end
			gui_text(90, 16, string.format("hp:%d %s %s", hp, mercury_boss_attacks[na + 1], D..S..F), obj_text_color)
			local xx = rs16(base + 0xAA) - scrollx
			local yy = rs16(base + 0xAC) - scrolly
			local tsx = rs16(base + 0x5A) + rs16(base + 0x5C)
			local tsy = rs16(base + 0x64) + rs16(base + 0x66)
			local sx = rs16(base + 0x46)
			local sy = rs16(base + 0x48)
			local ax = rs16(base + 0x60)
			local ay = rs16(base + 0x62)
			local rx = rpos(base + 0x4E) - scrollx * 256
			local ry = rpos(base + 0x52) - scrolly * 256
			traj(rs16(base + 0xA0), mercury_boss, rx, ry, xx, yy, ax, ay, sx, sy, tsx, tsy)
			if pt ~= 0 then
				local px = rs16(0xFF0000 + pt + 0x32) - scrollx
				local py = rs16(0xFF0000 + pt + 0x34) - scrolly
				gui_box(px - 2, py - 2, px + 2, py + 2, red, 0)
			end
			gui_box(xx - 1, yy - 1, xx + 1, yy + 1, red, red)
			gui_line(x, y, xx, yy, red)
		end
		if proc == 0x64820 or proc == 0x647E0 then
			gui_text(140, 16, string.format("b:%3d mv:%2d", rs16(base + 0xAA), rs16(base + 0xAC)), obj_text_color)
			local xx = ru16(base + 0xA6) - scrollx
			local yy = ru16(base + 0xA8) - scrolly
			local r = ru16(base + 0xA4)
			local tsx = rs16(base + 0x5A) + rs16(base + 0x5C)
			local tsy = rs16(base + 0x64) + rs16(base + 0x66)
			local sx = rs16(base + 0x46)
			local sy = rs16(base + 0x48)
			local ax = rs16(base + 0x60)
			local ay = rs16(base + 0x62)
			local rx = rpos(base + 0x4E) - scrollx * 256
			local ry = rpos(base + 0x52) - scrolly * 256
			traj(rs16(base + 0xAC), mercury_drone, rx, ry, xx, yy, ax, ay, sx, sy, tsx, tsy, r)
			if r == 0 then r = 1 end
			gui_box(xx - r, yy - r, xx + r, yy + r, red, 0)
			gui_line(x, y, xx, yy, red)
		end
		if x + 16 >= 0 and x < 320
		and y + 16 >= 0 and y < 224 then
			gui_box(x - 1, y - 1, x + 1, y + 1, obj_color)
			if hp > 0 then
				gui_text(x - 1, y - 10, string.format("%d", hp), obj_text_color)
			end
			local z = ru8(base + 0x40)
			if ru32(base + 0x68) == 0x51412
			and z - floor(z / 128) * 128 >= 64 then
				local width = ru8(base + 0x44)
				local height = ru8(base + 0x45)
				if height > 0x80 then height = height - 0x100 end
				gui_box(x - width, y + height, x + width, y, obj_color, 0)
			end
		end
	end
end

function speed(v)
	local vv
	local r = ' '
	if v < 0 then
		r = '-'
		v = -v
	end
	vv = floor(v / 256)
	return r..string.format("%d.%02d", vv, floor((v - vv * 256) * 100 / 256))
end

function player_text(player, posx, posy)
	local xpos = ru16(player + 0x32 + 0x0)
	local ypos = ru16(player + 0x32 + 0x2)
	local xspeed = rs16(player + 0x46)
	local yspeed = rs16(player + 0x48)
	local hp = rs16(player + 0x32 + 0x4A)
	local wt, wb
	if posx < 100 then
		wt = ru8(0xFF0048 + 0x23)
		wb = ru16(0xFF0048 + 0xE)
	else
		wt = ru8(0xFF006E + 0x23)
		wb = ru16(0xFF006E + 0xE)
	end
	local text = string.format("x:%6d y:%6d sx:%s sy:%s", xpos, ypos, speed(xspeed), speed(yspeed))
	gui_text(posx, posy, text)
	text = string.format("hp: %d w:%d b:%d", hp, wt, wb)
	gui_text(posx, posy + 8, text)
end

function render()
	level = ru16(0xFF002C)
	if bad_level[level] then
		level_prev = -1
		return
	end

	local player1 = 0xFF0642
	local player2 = ru16(0xFF0078) --0xFF06F0
	player_text(player1, 20, 0)
	if player2 > 0 then
		player_text(0xFF0000+player2, 195, 0)
	end

	scrollx = rs16(0xFF2DEE)
	scrolly = rs16(0xFF2DF0)
	drawcollision()
	drawobjs()
end

function cell_check(a)
	if invmap[a] then
		local x, y, t  = unpack(invmap[a])
		if t > 0 then
			drawcell_high(x, y, t)
		else
			--gui_box(x - scrollx, y - scrolly, x + 15 - scrollx, y + 15 - scrolly, obj_color)
		end
	end
end

if gens then
	memory.registerexec(0x5132C,function ()
		cell_check(memory.getregister("a1"))
	end)

	memory.registerexec(0x509C0,function ()
		cell_check(memory.getregister("a1"))
	end)
	gui.register(render)
else
	event.onmemoryexecute(function ()
		cell_check(emu.getregister("M68K A1"))
	end, 0x5132C)

	event.onmemoryexecute(function ()
		cell_check(emu.getregister("M68K A1"))
	end, 0x509C0)
	event.onframeend(render)
end