-
Notifications
You must be signed in to change notification settings - Fork 383
/
Gargoyles.lua
521 lines (491 loc) · 15.7 KB
/
Gargoyles.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
-- Gargoyles, Genesis (BizHawk)
-- feos, 2015-2017
--== Shortcuts ==--
local rb = memory.read_u8
local rw = memory.read_u16_be
local rws = memory.read_s16_be
local r24 = memory.read_u24_be
local rl = memory.read_u32_be
local box = gui.drawBox
local text = gui.text
local ptext = gui.pixelText
local line = gui.drawLine
--== RAM addresses ==--
local levnum = 0xff00ba
local LevelFlr = 0xff00c0
local LevelCon = 0xff00c4
local mapline_tab = 0xff0244
local GlobalBase = 0xff1c76
local GolBase = 0xff2c76
local MapA_Buff = 0xff4af0
--== Camera Hack ==--
local camhack = false
local div = 1 -- scale
local size = 16/div -- block size
--== Block cache ==--
local col = 0 -- block color
local opout = 0x33000000 -- outer opacity
local opin = 0x66000000 -- inner opacity
local op = 0xff000000
local cache = {}
--== Other stuff ==--
local MsgCutoff = 20
local MsgTime = 1
local MsgStep = 14
local MsgOffs = 2
local MsgTable = {}
local XposLast = 0
local YposLast = 0
local room = 0
local workinglast = 0
local wSize = client.getwindowsize()
local lagcount = emu.lagcount()
gui.defaultTextBackground(0xff000000)
--== Object types ==--
local types = {
"Goliath","Orb","Health1PLR","Health2PLR","Health1NME","Health2NME","Numeral",
"BigExplode","SmallExplode","GlassDebris","MetalDebris","WoodDebris",
"WallDebris","SignPiece","SteamVent","BreakWall","SkyLight","BreakLight",
"ThrowCrate","BreakEdgeLeft","BreakEdgeRight","Spark","Spark2","Sparks",
"Sparks2","Fireball","HomingProj1","HorzProj1","VertProj1","DirProj1",
"DirProj2","DropMine","Scratch","Icon","RaptorBot","SniperBot","SpiderBot",
"WaspBot","Xanatos","PlasmaBot","RabidHH","MorningStar","Archer","Arrow",
"Valkyrie","Axe","WeaponExp","Couldron","SpittingCouldron","FireballHead",
"FireballTrail","BigFireballHead","BigFireballTrail","Oil","OilGenerator",
"Claw","Stump","StumpBubble","StumpFire","ClawStump","StumpFireGen","Vent",
"VentSparks","Chain","FlameLick","Floor","MutVikBody","MutVikHead",
"MutVikHammer","EyeOfOdin","EyeOfOdinTrail","L1BreakWall","Catapult",
"L1BreakFloor","Gate","GateCrusher","Weight","WeightCrusher","WallFire",
"Balista","BalistaLog","PasteWall","FlameBoulder","CastlePiece",
"MutantSpiderBot","MutSpiLegs","MutSpiHead","MutSpiHeadFlame","MutSpiProj",
"MutSpiElecV","MutSpiElecH","PlasmaBall","PlasmaBallTail","PlasmaDeadHead",
"VertFlame","WallFlame","FloorFlame","OPPlatform","OPLink","OPOrb",
"Furnace","RobotGenerator","RockGenerator","BigRock","MediumRock",
"SmallRock","BigCouldronGen","BigCouldron","Trough","TroughGen","Energizer",
"Demona","TrajectoryProj","WallPaste","EdgePaste","Tentacle","Infuser",
"BigGuns","BigGunsProj","HighSignPole","HighSign","LowLight","L5Skylight",
"L5Wall","ElecGenerator","Electricity","WaspGenerator","TunnelEdge",
"ForegroundPost","Sorcerer","LightningTop","LightningBot","MDemonaWallFire",
"MDemonaFloorFire","EyeRooftopUp","EyeRooftopDn","EyeRaptor"
}
local function RoomTime()
local start11 = 894--767
local start12 = 2294
local start13 = 5468 -- 4254 -- 4101
local startl4 = 5506
local startl5 = 7117
local startl6 = 8412
local startl7 = 17117
local timer = emu.framecount()
if timer < start11 then room = timer
elseif timer < start12 then room = timer - start11
elseif timer < start13 then room = timer - start12
elseif timer < startl4 then room = timer - start13
elseif timer < startl5 then room = timer - startl4
elseif timer < startl6 then room = timer - startl5
elseif timer < startl7 then room = timer - startl6
end
text(2, 2, string.format("cx:%5d\ncy:%5d\nroom:%d", camx, camy, room), "white", "bottomright")
end
local function HUD()
--if working > 0 then return end
local rndcol = "white"
if rndlast ~= rnd1 then rndcol = "red" end
text(0, 2, string.format("RNG:%08X %04X", rnd1, rnd2), rndcol, "bottomleft")
text(2, 0, string.format(
"x: %4d\ny: %4d\ndx: %3d\ndy: %3d\nhp: %3d\nrun:%3d\ninv:%3d",
Xpos, Ypos, Xspd, Yspd, health, run, inv),
"white", "topright")
end
local function CamhackHUD()
if working == 0 then
-- screen edge
box((backx-camx- 1)/div,
(backy-camy- 1)/div,
(backx-camx+320)/div,
(backy-camy+224)/div,
0xff0000ff, 0)
-- map edge
box( 0-camx/div+size,
0-camy/div+size,
mapw/div-camx/div,
maph/div-camy/div,
0xff0000ff, 0)
end
if camhack or div > 1 then
text(0, 0, string.format("div:%d", div), "white", "topleft")
end
end
local function PosToIndex(x, y)
return (x // 16)+(y // 16)*xblocks
end
local function IndexToPos(i)
return { x=(i%xblocks)*16, y=(i // xblocks)*16 }
end
local function InBounds(x, minimum, maximum)
if x >= minimum and x <= maximum
then return true
else return false
end
end
local function GetBlock(x, y)
if working > 0 then return nil end
local final = { contour={}, block=0 }
if x > 0 and x < mapw
and y > 0 and y < maph then
local pixels = 0
local x1 = x/div-camx/div
local x2 = x1+size-1
local y1 = y/div-camy/div
local y2 = y1+size-1
local d4 = rw(mapline_tab+(y >> 4)*2)
local a1 = r24(LevelFlr+1)
local d1 = rw(MapA_Buff+d4+(x >> 4)*2) >> 1
final.block = rb(a1+d1+2)
d1 = rw(a1+d1)
a1 = r24(LevelCon+1)+d1
if rb(a1) > 0 or rb(a1+8) > 0 then
for pixel=0, 15 do
final.contour[pixel] = rb(a1+pixel)
end
else
final.contour = nil
end
else
return nil
end
return final
end
local DrawBlock = {
[0x80] = function(x1, y1, x2, y2) -- WALL
col = 0x00ffffff -- white
line(x1, y1, x1, y2, col+op) -- left
line(x2, y1, x2, y2, col+op) -- right
end,
[0x81] = function(x1, y1, x2, y2) -- CEILING
col = 0x00ffffff -- white
line(x1, y2, x2, y2, col+op) -- bottom
end,
[0x82] = function(x1, y1, x2, y2) -- CLIMB_U
col = 0x0000ffff -- cyan
line(x1, y2, x2, y2, col+op) -- bottom
end,
[0x83] = function(x1, y1, x2, y2) -- CLIMB_R
col = 0x0000ffff -- cyan
line(x1, y1, x1, y2, col+op) -- left
end,
[0x84] = function(x1, y1, x2, y2) -- CLIMB_L
col = 0x0000ffff -- cyan
line(x2, y1, x2, y2, col+op) -- right
end,
[0x85] = function(x1, y1, x2, y2) -- CLIMB_LR
col = 0x0000ffff -- cyan
line(x1, y1, x1, y2, col+op) -- left
line(x2, y1, x2, y2, col+op) -- right
end,
[0x86] = function(x1, y1, x2, y2) -- CLIMB_R_STAND_R
col = 0x00ffffff -- white
line(x1, y1, x2, y1, col+op) -- top
col = 0x0000ffff -- cyan
line(x1, y1, x1, y2, col+op) -- left
end,
[0x87] = function(x1, y1, x2, y2) -- CLIMB_L_STAND_L
col = 0x00ffffff -- white
line(x1, y1, x2, y1, col+op) -- top
col = 0x0000ffff -- cyan
line(x2, y1, x2, y2, col+op) -- right
end,
[0x88] = function(x1, y1, x2, y2) -- CLIMB_LR_STAND_LR
col = 0x00ffffff -- white
line(x1, y1, x2, y1, col+op) -- top
col = 0x00ff00ff -- cyan
line(x1, y1, x1, y2, col+op) -- left
col = 0x0000ffff -- cyan
line(x2, y1, x2, y2, col+op) -- right
end,
[0x70] = function(x1, y1, x2, y2) -- GRAB_SWING
col = 0x0000ff00 -- green
box(x1, y1, x2, y2, col, col+opout)
end,
[0x7f] = function(x1, y1, x2, y2) -- EXIT
col = 0x00ffff00 -- yellow
end,
[0xd0] = function(x1, y1, x2, y2) -- SPIKES
col = 0x00ff0000 -- red
box(x1, y1, x2, y2, col, col+opout)
end,
[0xd1] = function(x1, y1, x2, y2) -- SPIKES
col = 0x00ff0000 -- red
box(x1, y1, x2, y2, col, col+opout)
end
}
local function DrawBlockDefault(x1, y1, x2, y2) -- LEVEL_SPECIFIC
col = 0x00ff8800 -- orange
box(x1, y1, x2, y2, col+opin, col+opout)
end
local function DrawBG(unit, x, y)
local val= 0
local x1 = x/div-camx/div-(camx%16)/div
local x2 = x1+size-1
local y1 = y/div-camy/div-(camy%16)/div
local y2 = y1+size-1
if unit.contour ~= nil then
box(x1, y1, x2, y2, 0x5500ff00, 0x5500ff00)
for pixel=0, 15 do
val = unit.contour[pixel]
--[ [--
if val > 0 then
gui.drawPixel(
x1+pixel/div,
y1+val/div-1/div,
0xffffff00)
end
--]]--
end
end
if unit.block > 0 then
local Fn = DrawBlock[unit.block] or DrawBlockDefault
Fn(x1, y1, x2, y2)
box(x1, y1, x2, y2, col+opin, col+opout)
end
end
local function Background()
if working > 0 then
cache = {}
return
end
if camhack then
camx = Xpos-320/2*div
camy = Ypos-224/2*div
box(0, 0, 320, 240, 0, 0x66000000)
end
local border = 0
local offset = 32
local basex = camx+border
local basey = camy+border
local basei = PosToIndex(basex-offset, basey-offset)
local boundx = 320*div-border
local boundy = 224*div-border
local xblockstockeck = ((camx+boundx+offset)-(basex-offset))/size/div
local yblockstockeck = ((camy+boundy+offset)-(basey-offset))/size/div
for yblock = 0, yblockstockeck do
for xblock = 0, xblockstockeck do
local i = yblock*xblocks+xblock+basei
local x = basex+xblock*size*div
local y = basey+yblock*size*div
if InBounds(x, basex-offset, camx+boundx+offset) then
local unit = cache[i]
if unit == nil or workinglast > 0 then
if InBounds(x, basex, camx+boundx)
and InBounds(y, basey, camy+boundy)
then cache[i] = GetBlock(x, y)
end
else
if not InBounds(x, basex, camx+boundx)
and not InBounds(y, basey, camy+boundy)
then cache[i] = nil
end
end
if unit ~= nil then
DrawBG(unit, x, y)
end
elseif cache[i] ~= nil
then cache[i] = nil
end
end
end
CamhackHUD()
end
local function Clamp(v, vmin, vmax)
if v < vmin then v = vmin end
if v > vmax then v = vmax end
return v
end
local function Objects()
if working > 0 then return end
for i=0, 63 do
local base = GlobalBase+i*128
local flag2 = rb(base+0x49) & 0x10 -- active
if flag2 == 0x10 then
local xpos = rw (base+0x00)
local ypos = rw (base+0x02)
local state = rw (base+0x0c)
local dmg = rb (base+0x10)
local id = rw (base+0x40)
local hp = rw (base+0x50)
local cRAM = r24(base+0x75) -- pointer to 4 collision boxes per object
local xscr = (xpos-camx)/div
local yscr = (ypos-camy)/div
local num = id/6
local name = types[num]
local col = 0 -- collision color
for boxx=0, 4 do
local x0 = rw (cRAM+boxx*8)
local x1 = (rws(cRAM+boxx*8+0)-camx)/div
local y1 = (rws(cRAM+boxx*8+2)-camy)/div
local x2 = (rws(cRAM+boxx*8+4)-camx)/div
local y2 = (rws(cRAM+boxx*8+6)-camy)/div
if boxx == 0 then
col = 0xff00ff00 -- body
-- archer hp doesn't matter
if id == 282 or id == 258 then hp = 1 end
if hp > 0 and id > 0 and x0 ~= 0x8888 then
local xx = Clamp(xscr, 0, 318-string.len(name)*4)
local yy = Clamp(yscr, 0, 214)
ptext(xx, yy+2, string.format("%d", hp), col)
end
elseif boxx == 1 then
col = 0xffffff00 -- floor
elseif boxx == 2 then
if dmg > 0
then col = 0xffff0000 -- projectile
else col = 0xff8800ff -- item
end
if dmg > 0 then
text(x1*wSize+2, y2*wSize+1,
string.format("%d", dmg), col, 0x88000000)
end
else
col = 0xffffffff -- other
end
if x1 ~= 0x8888
and x1 <= 320 and x2 >= 0
and y1 <= 224 and y2 >= 0 then
box(x1, y1, x2, y2, col, 0)
end
end
end
end
end
local function PostRndRoll()
for i = 1,#MsgTable do
if MsgTable[i] and MsgTable[i].index == i then
local base = MsgTable[i].base
local xpos = rw(base+0x00)
local ypos = rw(base+0x02)
local id = rw(base+0x40)
local x = (xpos-camx)/div
local y = (ypos-camy)/div
local num = id/6
local ymsg = 0
local yoffs = ((i-1) // MsgCutoff)*14
local name = types[num]
local color = 0xffffff00
if base == GolBase then
name = "Goliath"
elseif not name then
name = string.format("%X", base)
color = 0xff00ffff
end
if y < 224/2 then
yoffs = -yoffs
ymsg = 210
end
x = Clamp(x, 2, 320-string.len(name)*4)
y = Clamp(y, 20, 214)
line ((i-1)%MsgCutoff*MsgStep+3 +MsgOffs, ymsg+yoffs+4, x, y, color-0x88000000)
ptext((i-1)%MsgCutoff*MsgStep*wSize+MsgOffs, ymsg+yoffs, i, color)
MsgTable[i].timer = MsgTable[i].timer-1
if MsgTable[i].timer <= 0 then
MsgTable[i] = nil
end
end
end
end
local function PlayerBoxes()
if working > 0 then return end
local xx = (Xpos-camx)/div
local yy = (Ypos-camy)/div
local col = 0xff00ffff
local swcol = col -- usual detection
if Yspd > 0 then -- gimme swings to grab!
swcol = 0xff00ff00
elseif Yspd == 0 then -- can tell that too
swcol = 0xffffffff
end
if facing == 2
then box(xx-0xf /div-2, yy-0x2c/div-1, xx-0xf/div+0, yy-0x2c/div+1, swcol, 0) -- lefttop
else box(xx+0xf /div , yy-0x2c/div-1, xx+0xf/div+2, yy-0x2c/div+1, swcol, 0) -- rightttop
end
box(xx -1, yy-0x2c/div-1, xx +1, yy-0x2c/div+1, col, 0) -- top
box(xx-0xf /div-2, yy-0x1f/div-1, xx-0xf /div+0, yy-0x1f/div+1, col, 0) -- left
box(xx+0x10/div-1, yy-0x1f/div-1, xx+0x10/div+1, yy-0x1f/div+1, col, 0) -- right
-- box(xx -1, yy-0x1f/div-1, xx +1, yy-0x1f/div+1, col, 0) -- center
box(xx -1, yy-0x0f/div-1, xx +1, yy-0x0f/div+1, col, 0) -- bottom
box(xx -1, yy -1, xx +1, yy +1,0xffffff00, 0) -- feet
-- box(xx -1, yy+0x10/div-1, xx +1, yy+0x10/div+1, col, 0) -- ground
end
local function Input()
local i, u, d, l, r, a, b, c, s
if movie.isloaded()
then i = movie.getinput(emu.framecount()-1)
else i = joypad.getimmediate()
end
if i["P1 Up" ] then u = "U" else u = " " end
if i["P1 Down" ] then d = "D" else d = " " end
if i["P1 Left" ] then l = "L" else l = " " end
if i["P1 Right"] then r = "R" else r = " " end
if i["P1 A" ] then a = "A" else a = " " end
if i["P1 B" ] then b = "B" else b = " " end
if i["P1 C" ] then c = "C" else c = " " end
if i["P1 Start"] then s = "S" else s = " " end
text(1, 10, u..d..l..r..a..b..c..s, "yellow")
end
event.onframeend(function()
emu.setislagged(rb(0xfff6d4) == 0)
if rb(0xfff6d4) == 0 then
lagcount = lagcount+1
framecol = "red"
else
framecol = "white"
end
emu.setlagcount(lagcount)
wSize = client.getwindowsize()
rndlast = rnd1
workinglast = working
XposLast = Xpos
YposLast = Ypos
end)
event.onmemoryexecute(function()
local a0 = emu.getregister("M68K A0") & 0xffffff
if a0 ~= 0xff4044 then
for i = 1, 200 do
if MsgTable[i] == nil then
MsgTable[i] = { index = i, timer = MsgTime, base = a0 }
break
end
end
end
end, 0x257A, "RNGseed")
local function main()
rnd1 = rl (0xff001c)
rnd2 = rw (0xff0020)
working = rb (0xff0073)
xblocks = rw (0xff00d4)
mapw = rw (0xff00d4)*8
maph = rw (0xff00d6)*8
Xpos = rws(0xff0106)
Ypos = rws(0xff0108)
camx = rws(0xff010c)+16
camy = rws(0xff010e)+16
run = rb (0xff1699)
inv = rw (0xff16d2)
health = rws(0xff2cc6)
backx = camx
backy = camy
Xspd = Xpos-XposLast
Yspd = Ypos-YposLast
facing = rb(GolBase+0x48) & 2 -- object flag 1
if working > 0 then MsgTable = {} end
Background()
PlayerBoxes()
Objects()
PostRndRoll()
HUD()
RoomTime()
end
while true do
main()
emu.frameadvance()
gui.clearGraphics()
end