Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- PETSCII ROBOTS - OpenComputers port (single file)
- Original PET 4032 game by David Murray (the 8-Bit Guy), 2020-2022.
- Port of the PET assembly source (PETROBOTS12.ASM / BACKGROUND_TASKS.ASM);
- all 14 maps, the tileset, screens and sounds are embedded below.
- Requirements: OpenComputers, GPU + screen able to do at least 40x25
- (Tier 2 or Tier 3), a keyboard. ~512 KB RAM recommended.
- Run: petrobots [green|amber|white] [-q]
- -q = no sound
- Keys (original PET layout, arrow keys also work):
- Arrows / 8 2 4 6 walk W A S D fire up/left/down/right
- < or , cycle weapon > or . cycle item
- Space use item Z search object
- M move object Tab/Home big map
- Shift+M music on/off Esc / P pause (then Y = quit to menu)
- Q (title screen) quit to OpenOS
- Menu: Up/Down + Space/Enter. "CONTROLS" can switch to custom keys.
- ]]
- local component = require("component")
- local computer = require("computer")
- local event = require("event")
- local gpu = component.gpu
- local floor, sbyte, schar, ssub, concat = math.floor, string.byte, string.char, string.sub, table.concat
- local unpack = table.unpack or unpack
- local ucode = (unicode and unicode.char) or (utf8 and utf8.char)
- local ARGS = {...}
- local QUIET = false
- local FG, BG = 0x55FF55, 0x000000
- for _, a in ipairs(ARGS) do
- if a == "-q" then QUIET = true
- elseif a == "amber" then FG = 0xFFB000
- elseif a == "white" then FG = 0xE0E0E0
- elseif a == "green" then FG = 0x55FF55 end
- end
- -- ---------------------------------------------------------------- decoders
- local B64 = {}
- do
- local alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
- for i = 1, 64 do B64[sbyte(alpha, i)] = i - 1 end
- end
- local function bytes_to_string(t, n)
- local parts = {}
- for i = 1, n, 4000 do
- local j = i + 3999
- if j > n then j = n end
- parts[#parts + 1] = schar(unpack(t, i, j))
- end
- return concat(parts)
- end
- local function b64decode(s)
- local out, n = {}, 0
- local acc, bits = 0, 0
- for i = 1, #s do
- local v = B64[sbyte(s, i)]
- if v then
- acc = acc * 64 + v
- bits = bits + 6
- if bits >= 8 then
- bits = bits - 8
- local p = 2 ^ bits
- local b = floor(acc / p)
- acc = acc - b * p
- n = n + 1
- out[n] = b
- end
- end
- end
- return bytes_to_string(out, n)
- end
- -- raw DEFLATE decoder (after zlib's "puff")
- local LBASE = {3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258}
- local LEXT = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}
- local DBASE = {1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577}
- local DEXT = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}
- local CLORD = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}
- local POW2 = {}
- for i = 0, 32 do POW2[i] = 2 ^ i end
- local function huff(lengths, n)
- local count, symbol = {}, {}
- for i = 0, 15 do count[i] = 0 end
- for i = 0, n - 1 do count[lengths[i]] = count[lengths[i]] + 1 end
- local offs = {[1] = 0}
- for i = 1, 14 do offs[i + 1] = offs[i] + count[i] end
- for i = 0, n - 1 do
- local l = lengths[i]
- if l ~= 0 then symbol[offs[l]] = i; offs[l] = offs[l] + 1 end
- end
- return {count = count, symbol = symbol}
- end
- local function inflate(s)
- local pos, bitbuf, bitcnt = 1, 0, 0
- local out, on = {}, 0
- local function getbits(n)
- if n == 0 then return 0 end
- while bitcnt < n do
- bitbuf = bitbuf + (sbyte(s, pos) or 0) * POW2[bitcnt]
- pos = pos + 1
- bitcnt = bitcnt + 8
- end
- local p = POW2[n]
- local v = bitbuf % p
- bitbuf = (bitbuf - v) / p
- bitcnt = bitcnt - n
- return floor(v)
- end
- local function decode(h)
- local code, first, index = 0, 0, 0
- local count, symbol = h.count, h.symbol
- for len = 1, 15 do
- code = code + getbits(1)
- local c = count[len]
- if code - c < first then return symbol[index + (code - first)] end
- index = index + c
- first = (first + c) * 2
- code = code * 2
- end
- error("bad huffman code")
- end
- local fixl, fixd
- repeat
- local last = getbits(1)
- local typ = getbits(2)
- if typ == 0 then
- bitbuf, bitcnt = 0, 0
- local len = sbyte(s, pos) + sbyte(s, pos + 1) * 256
- pos = pos + 4
- for i = 0, len - 1 do on = on + 1; out[on] = sbyte(s, pos + i) end
- pos = pos + len
- else
- local lh, dh
- if typ == 1 then
- if not fixl then
- local l = {}
- for i = 0, 143 do l[i] = 8 end
- for i = 144, 255 do l[i] = 9 end
- for i = 256, 279 do l[i] = 7 end
- for i = 280, 287 do l[i] = 8 end
- fixl = huff(l, 288)
- local d = {}
- for i = 0, 29 do d[i] = 5 end
- fixd = huff(d, 30)
- end
- lh, dh = fixl, fixd
- else
- local nlen = getbits(5) + 257
- local ndist = getbits(5) + 1
- local ncode = getbits(4) + 4
- local lengths = {}
- for i = 0, 18 do lengths[i] = 0 end
- for i = 1, ncode do lengths[CLORD[i]] = getbits(3) end
- local ch = huff(lengths, 19)
- local idx = 0
- local ll = {}
- while idx < nlen + ndist do
- local sym = decode(ch)
- if sym < 16 then
- ll[idx] = sym; idx = idx + 1
- else
- local prev, rep = 0, 0
- if sym == 16 then prev = ll[idx - 1]; rep = 3 + getbits(2)
- elseif sym == 17 then rep = 3 + getbits(3)
- else rep = 11 + getbits(7) end
- for _ = 1, rep do ll[idx] = prev; idx = idx + 1 end
- end
- end
- local l1, l2 = {}, {}
- for i = 0, nlen - 1 do l1[i] = ll[i] end
- for i = 0, ndist - 1 do l2[i] = ll[nlen + i] end
- lh = huff(l1, nlen)
- dh = huff(l2, ndist)
- end
- while true do
- local sym = decode(lh)
- if sym < 256 then
- on = on + 1; out[on] = sym
- elseif sym == 256 then
- break
- else
- sym = sym - 256
- local len = LBASE[sym] + getbits(LEXT[sym])
- local ds = decode(dh) + 1
- local dist = DBASE[ds] + getbits(DEXT[ds])
- for _ = 1, len do on = on + 1; out[on] = out[on - dist] end
- end
- end
- end
- until last == 1
- return bytes_to_string(out, on)
- end
- local function unpack_blob(b64) return inflate(b64decode(b64)) end
- local D_TILES=[=[
- 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
- ]=]
- local D_SCREENS=[=[
- 7ZdPa9swGMZRXbnWYlvBUhZyez+HTmGBsbY00ENPo+wwJjCDsdEvpI/S6zYi2sOOZYcMir/FpFdOE6cmMTTbwrYHZMv2j+f9Y9lxAGD6dqnpO0
- BVNoxVzcyDLNScNmGsyghJCglHJKOm5iptjdHWrnE0pZGEjPRrrjJzZz63TdAcsjhm0SCVo8BVOsjaRmQT8VwWPBVuipxWZ0qpqdLGrJZkl3XM
- pljExeRMTSfHgWsrqS72xfkr5YY25Sr3cTweX7ki3rv9B+ROXp4qNzSWgaU7rsfSFIgbySAjucBLQRg2HJTQGvjGq+ZC1EmbFlxZH5rJRLVp0T
- 93I/HQqHZVxtzf+HZZs4mrfCvniLVwlf3eDKv1t+vPePxwxWPz2eLG6HOkzvWdDa4zu+TM7b1e111o4+19ueTeGKvW4365/uqv/CgbzTC+AIsb
- P/RiUhljmxyuOR9pdVL6scZZt9jdRfMwqc+2+pmF34LTjQbWJ4OR8yv9JGT9iCtVSAdTCpt6D9t0OaSkz/PtHHTTvnP+aXXq4MckzeB/Xx759e
- hI7HN+v6R/VyyPeZERyXhep9LU6675JZSkMtldfp9QFz74FZGSRD3gMciEQp9KETEGBT/gUmDWxx1tL/9GTkQ0JwXjagd+NCV9QQ9Bsow+1S/c
- HyhoRljO8qMn+gkaFXSHfrWWX2PqH1kvv5M7GXfS6R99zvsFFQJD4m/AiA9gyEQCksNA0BU/96pELOZpyoduCUI8yCP/7hR7cj8y/pzCoK82c0
- gd8mHehUtpLDdziBXsKNnC+Q6yokN+SHXID7nt+SG2Lb9oFKUUwlej2OgXSP8ZJbbV65ePBzdwgpIiSoAfPHN/Hjf6YZs7cPv8nP8E
- ]=]
- local D_LEVELS={
- [=[
- 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
- ]=],
- [=[
- 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
- ]=],
- [=[
- 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
- ]=],
- [=[
- 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
- yq/7T8Aw==
- ]=],
- [=[
- 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
- nypsseHqbwak+W+PvKP4b0NX/gs=
- ]=],
- [=[
- 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
- ]=],
- [=[
- 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
- ]=],
- [=[
- rVndbxzVFbex155xNlkbJ4S4BSX2NsYtkYCQpkmgQRhUquQhtA9Ikaq0bwVKZa8SVPoBNJtULTz0qS9F4rGV2n8DpFZYM7rnatdtPe0F1Ej4v+
- g59/PcOzNO4nJ2d+bu3Z353d/5uufemZyamnqABM9TCw9ie2ohzye8zFmZnTXfb9/6zfDmrXdv/1p/bt7Mj1+4eGL11OlzV1Ce+v7m6tefW33x
- arh++Ymjp7/74itfOXLEfD+1/I3ZKxsb/W8/9+jpo/3+2lzv0KHjJEuHD2VZ/+yBhRMPHT0Wrs/mD813lo4tHXvYfF9cmMv65848euzI3ENfXZ
- jPJu4mk5OT9qClN9u5NNub7PVmJ7pzc53O3S7vWJmYnNif0HUP4Od195XGYsZkf703OdN8fXeOyeHD+njp0r0PT6KA/UgZNSDtkJFA3AfuPr5L
- sT9vWZF1AdmIA9Ep6uIwYN98sOx/7ljHZ/f9xz+/ZPksJh/jQ00BFSTy3/H/J3csQ3M3anl8CL1aU3SuQFCPEsI0YLxLDfPGg22I0BZ2oPYK/H
- yB15ivAtv63gpvr/AFnL/S4xqNRsy9Kg9gBzAW0503O/f1vvLy92bMRTNzMAbnJI5rZP9yVBbFCLwpiL+y5PRhDFm+D/nTnz/8IH/m2UyMi7IE
- FxB0Z4avZInoZVGOvANo+4ugYOQ/WMkfvm/8v/y138fTAMYlAWhTGy18/m8r6JllMSrp91HQP8Ct2wLAmxn5r9ANpzc2SbGY/jY2zWtmFntRzb
- UX/XCArtH8iSRiINFCD6V0nqm/jbQGRj4WEP+3vzO2F8b+hI/8s+s36JaPHc+v3zCvHA3TbJvQOyD7ezXTEMrC4dN3KUfYFdTv9G+dWlj90z3/
- +AGdPtTH/L3333ufYAaN+Ni7ZEcixkRMyRDQXP9oGIn+z/JehT4inA/iZ30sDP/u46sMgfN/5+dcfmT4T+uZaobwZQh0khD+aP8yydPe/wx3LU
- ab81c3NhfyHmqBFBH4P5L/YovLrwx/6/+of+94CTxlxaJQUqCdE/173V/M84v5C3TI82vkAKSFx/sx/zp+5v0f+Wt2NfpmZihLhCQ/q/PXsaeZ
- v2RUfu187tTascpt4P+ztwx/5//of43aVzoXlYUUEM2uhr/i/C9bfOvZv/+De30nJ9008w/+L3xCEQHfDon8TwKwabcy7i9i+190/LM1H37Xb7
- xEummxf/B/G8o2psLcQ1IWSndLHv8QRkz+LweB/1p3jQXBZf3Of/hLLm/H/o/6F4KFNDM/6b+U+LOQLghARvOvzv8S428V39z0nZknnnxqJsT/
- I/rF49/lf7K/z2XB/Fr/lJYQU/Bqx+cfYcfs8eNU9/SZbxLNblv+C/k/TM0s+MjnKCNqeF6FRP6/TuWHHDj8lbOr+cpjq/r8rXPnycenf/zqq6
- /Nam3P0OGqC4zg/wLW3WQWzbwBPuQf5fUvtMaw+NmVlviBfOXCqvlgErCKz37yxhs/zZDwqXyIvklOguuHoKrM+r9xaI6vdU95WQCk8e/yDxU/
- ILstKh72yMy6TWwJ//w19+NSiD9finB8glem6hK2LtNv7n8CjWf9GGfUntG/tcHgPAKSmbE/80MK+Mz/s+EwH5IqPP4WZV5X9EELf3I/vHj55I
- l8ONSplvTubED8Tf4l9j03677i8EP+v/OC7hsOmANQ5EuelaP4d7KOgx8sn/xarvNcbIaugemaFn7W1rrIE6Xf77P8jznM5O9hxhyAMp8KaVk2
- 8Vem+Fg+aeeA+U4qiw2dV17+gf4h+P/uZcufeSDx54kmLLw4f0X8farbl5D/db39mQJq+Al/ZYp/wIS7lrflmT1kyccfHDRV+WKU/6gkjQegEv
- 5CrIeLMX6mOwv5g3scjWA7MpGJ/3T6RfsXyoQdp68YvsDfxfhOuJRCfeWsDviz9XYWikZ9rvzyjfxYJLO/sb+ec9rjD2gl5ZdxgKGe5xfouHKh
- 3s6oZESNAU5mdKrcunHXjUvE+KUFt0e3Qo7iP5y+cMug1iN5jEuleK7Gu7iU/gw+xyXwv1w6EVz/oe6RrACoIHJAUh1agjwhe+ZZrL5bj7pupo
- PJK9UYqo8+plt8ApWf0Lj/N2w/sPknUoBfBurUgifdzqMeXbKbBS5Y/L/9HfQQXE3F6y+a+6JtE5XGvwrgeiboIk8MxJZjBmHdDMLg41ccgsYX
- aQBo/6vvPzh8ZeHZTHTwzc4er8Vk2+M/Xv8ffczwnf4L439SJntO6f6HELZ24ZHYLGHNaCoHrf9PSAWVryfi/FvffErqP4tv9jTuU3bhU6+LWv
- WP5i/5sss0FMg6vouDeESNCuA/Nf+HhZ9Z+qV7UKpqv6UehaC0rPyI1rWvrOt0uS7Yf/bGV1j4F9AUgVVd+ywcRBiQCv3CiWKjblKTp0/ppwyJ
- t0zXXyk+OwsFSYja7bImx2lC1/ho/sJtyD1fFEVL/oeoGkYlG16qZiMaVzS2dng9XkQsC+UWYUVZhv2HRnQlaqSUp55opRGfkzf8afPBXFga/i
- H/MzcWkRc227XWJ1rRLbxJ/2VBvJA8bY+pSP9h0hScSyN+rUO1GX7LrXeEUbjee4szUNXgeAxUQGvwJ+BhnBG8W24X3PH1Qojz94gNbOEuA6gl
- ji2+4e2Crqg/GYCm/e+2ZMc9P9Q3dg6OxrlVw3fMNSDtv9gxgKrHnxCNpEUkqWJUo/U1mmT4UpdKIsyA0BD/QnAjJLiReURUN6Q7DjTnssciqH
- +ht+XNH9vzj4hMYEC55gXzlThVJ/ild3Ub/1KwChzi+SdmHAg2pXYWHXVthIrD7D4Xbh9ayumNTVyVSxXqb0j3f/fOafcigX7hcixN8zgBysH1
- G7RA5o+xdP7d2d7eoc82tvR5e6dlPrsPfIR/3gYbmJ3f8oCp26MCAKrGsPNl2L7xzd3Jc2y5XxQ57dUMAGTif8R6h1QAO7axE/YP949vSgUw+6
- 1KFuXSml43xI8BK2hMtPvV/1a036gncH0msIO0rF2M1U/421YB27C9Y5t75N17g0/zmaYnAeL6T0FL/bFfifE9BUo9YB5LJA9h1adf5qPXrT0e
- NdeeOiv7lJAXxYo9WfYlAn5T4V+KP19m/dGDPoZRY8zLcBWth8yMoCA8y+YrJZU86U65JA86o6fzYb7n8j8=
- ]=],
- [=[
- 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
- ]=],
- [=[
- 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
- /yX8fwE=
- ]=],
- [=[
- 5VnNbxNHFHec3c08Myi214WArBKIK8dLYr6bkH5AJVQBTivooaeqaqSqokARUJIUaKmNQ9XCoadeQGoPSFBaqX9JL978Q52vHc/szuw6m6g99K
- 29u56dmd+8N29+7816DKBaHK9WiuPkqDhE6B0UYXy8WCDi+yUmE6US/Vn4obvxsN999Gij33248bDXK8P62sXO2fWVPZNeefrixeXllTMnTl64
- fmHl8xatD2dXOxOzB/f6H7/P2k/eu7f+4fEjtQMffLG8fOXru65bPVxynMWFo8eOLS6eXji90J4v7akdqO7fvUTrOwerpw6dWHx9b+Cz9l5ld+
- AUa0eXjpcqtdrsqUKmjLHvGLsWCm6x5HbGMC51CqVaYbKW1doVQm4KeaQ4VqTw49poiBTEeDLbF2PajCntyQd3OrVSqVZjk8TPRDrkEBNmFCzv
- NomE0YndCCn8O8LBVODwP8Df3HS8kj4Cc3Xn5q2bt9bJ0fX20Z+uvJKDlrOfW8NncOgVJndUUu2P7qzeWf2dHACI/oToOrPQBFaOctnfuXT5L2
- KBaAChDb/HgGZmmxy3B9F15q0mPH7y+Eku/DC8TTqC22E4GAy4BUKL/jAz12RfoD6MgnaAhR0AqG22jh9SfEzbozDceDQYpNofpJQJK/jyysyy
- IMyi+ItL/IJ+bX7BsAbh7gpUXT8Mf/yJ4ocp+NXu2Xff61YgJtT+zCwobq9ZPjCU4n8DNu+huHAHyNSfqOZNKFchCXwxMJSmP0UesLmXC8CC//
- Y7APyr+l+sODI9nyDTuGL+z4AV/BT/1zpEMFwHcRzFVNWbtzyUzj/MCpHv2fXHuqIRPjboj8jMeM6VL69O0JUB2fiS/FL4r+5K8RX8WLEwFXmC
- rn91AxFiGNrFiSryFRFj/XBr8QfZJ5Y8I09evATYRdQf2gVdY1MyBS1k1J/zURiOhN/Lwv/jTwqm6i/cIsLfnmTqj6HR2K/p37u2HXziT0Ku7l
- PwHWVGtfmvJ/wCccpYc708+MSfhNyI/B8XHI8ZNdA1QmbL0Kod6Pf7j/PgU38SEsWdsutPnWMl7SQ+Sww8kjVI29Am51m0yoNP/KlJ1vOEEn8g
- 6hCCJD56+mzq11mkrH/qsstpjJilf/PFyyXy0eIA73Ae6fNPLIRoSJpDJDNQ/R9T+0Ne/cmXLqso7jD+Y/jx+S9HBqJZk4pfTvM/HrItERs3Gg
- 0MSzjG//UY98X4fxfE1/80EQs+PgRwECzPLfxrdvQh/ytxoZcVER23Am+M6B9p/KfwP8T1lwmkRX8gtNVAO8b/qv7ULIbZ0kxM/MqxPt8SPud/
- Xf+R9BptcWDBQyn8D7CfmXsr+I4/Cj7lNpq2ds25rZX/0Shq4dGcb0jFBv1HLHZcnqTzEztfuvypvLedPKRSoNJYnH3G0IkHtLirRUtEuDCPUL
- 92GAXuytG635eMkA+ee/UrnLepxMc58SdJYk22rRXq4VuXQEbLukd2vx85bopwQoiV6fOfUVsrjPMLymRb06qxM7lxjamFsQoIzuTAx9vBx6PS
- qL3HOglvhH8MTJ2K74i1qbBWPnx7w1R8sQGBJtqm/fPigwF/R/U3hw+J3zLi4zz45vzDHD5U/VtJ+5djTK0HtZH1Z9kjoZNkAinxAz6ANkqmit
- PTU9EHUC58BPZXMQK/HcR3K0YOz4ffo6l9n5/Y/kdufaIuMIMPlN3K0WPH19Y4J9Ze2xMlrXn11/DR/J3VFtK6qCfeVJw89SbtByLb8aQV5fK/
- mP1RW9awJz6Lp3k4j8I4Yr92xP4YRsAnvRzmyA0qwDe5+fCxjl+n0+pn4S/BkSUSzieF/nx354+QkA7xHbFlUzZIvAcJm6r/CYDbxO5cf5NWtm
- RSWpfo226hlHzTjo+v8VmPliGmYxBaeTxpuHT5k+QbFtX/UD/xAoIl6DTUcS5D4k2NyF+VrqjNPGLiquA+yn6i16fP0PPnvw0PZNOfjT3AlrWA
- JD4C/jdBfKzawhlqNUem5edfhocF34lIVzNQD76/R2VFvKvBPNllfxMkXn9FlUULGSsQqx8dBnysDR5rT+//TeU78a6Gn0W2GyS64pVFC4l/Ls
- v+vNcp8VavrPq/xNcTb8PLGh3/m7tD/PNp9u/pfQ6vQiJ8NfMVdYJEV2b9l7Psr///4GgZ932tNzHivjFpxfDZt1IeRF7dAmwPitr/b7NNbZut
- z78a6vvmrur6dsE3FSYyXYk/A3PJv1l6QqUHKj6Fn2/nfP1n2gIg61bAVIp58kXi/07g14VJ6kYDmUrrNlP+T+Uf
- ]=],
- [=[
- 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
- ]=],
- [=[
- 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
- a/CG/GSZbCu1mpWk6MH/jw/wFx//Aw==
- ]=],
- [=[
- 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
- fw7nr8f/nvyz8=
- ]=]}
- local D_SFX={[0]={39,11,13,7,8,11,3,7,3,5,6,12,14,2,1,6,8,13,11,1,5,6,7,10,10,3,6,37},{39,11,13,7,8,11,3,7,3,5,6,12,14,2,1,6,8,13,11,1,5,6,7,10,10,3,6,37},{41,9,4,1,37},{39,2,7,12,17,22,27,32,27,22,17,12,7,2,37},{42,23,13,18,35,15,21,9,23,3,37},{39,38,14,38,14,38,14,38,14,38,4,38,4,38,4,38,4,37},{40,2,38,14,38,37},{39,38,14,38,14,38,14,38,14,38,4,38,4,38,4,38,4,37},{39,7,19,6,18,7,19,6,18,7,19,6,18,7,19,37},{39,36,35,34,33,32,31,37},{40,9,21,11,23,13,25,14,2,14,2,14,37},{39,1,38,1,38,1,38,1,37},{39,21,19,17,15,13,21,37},{39,13,12,11,10,11,12,13,37},{40,11,13,14,16,18,19,21,37},{40,14,4,37},{40,17,37},{40,17,37},{44,7,0,9,0,12,0,16,0,0,14,0,0,16,0,17,0,0,16,0,0,17,0,19,0,0,0,0,0,37},{47,13,0,11,0,9,0,8,0,6,0,2,0,0,4,0,0,6,0,0,0,0,0,37}}
- local D_SONGS={[0]={48,1,3,4,6,8,0,6,4,6,38,11,38,9,38,6,4,6,38,11,38,9,38,4,3,4,38,9,38,8,0,0,0,1,3,4,6,8,0,6,4,6,38,11,38,9,38,6,4,6,38,15,38,11,38,9,8,9,0,0,0,8,0,0,0,9,0,20,0,21,0,11,9,8,0,18,0,20,0,9,8,6,38,18,0,20,0,2,4,8,9,8,6,8,0,0,0,9,0,20,0,21,0,11,9,8,0,18,0,20,0,9,8,6,0,18,0,20,0,2,6,13,0,11,9,11,0,0,0,1,3,4,6,8,0,6,4,6,38,11,38,9,38,6,4,6,38,11,38,9,38,4,3,4,38,9,38,8,0,0,0,1,3,4,6,8,0,6,4,6,38,11,38,9,38,6,4,6,38,15,38,11,38,9,8,9,0,0,0,8,0,0,38,8,0,19,0,20,0,22,20,19,0,5,0,7,0,20,19,17,0,5,0,7,0,1,3,7,8,7,5,7,0,0,0,8,0,19,0,20,0,10,8,7,0,17,0,19,0,8,7,5,0,17,0,19,0,3,4,9,0,6,0,8,0,0},{46,3,0,38,3,15,0,0,3,0,0,15,0,1,13,2,14,3,0,38,3,15,0,0,3,0,0,15,0,1,13,2,14,15,38,0,15,38,0,15,38,0,15,0,18,0,15,38,15,38,0,15,38,0,15,38,0,15,0,20,0,15,38,15,38,0,15,38,0,15,38,0,15,0,18,0,15,0,13,0,0,14,0,0,15,48,0,0,38,0,0,46,15,38,0,15,38,0,15,38,0,15,0,18,0,15,0,15,38,0,15,38,0,15,38,0,15,0,20,0,15,38,15,38,0,15,38,0,15,38,0,15,0,18,0,15,0,13,0,0,14,0,0,15,0,0,0,14,0,13,0,12,38,12,0,0,14,0,0,15,48,0,0,0,38,0,0,0,46,12,0,0,14,0,0,17,48,0,0,0,38,0,0,0,46,12,0,0,14,0,0,19,0,0,0,20,0,19,0,17,0,12,0,0,14,0,0,15,48,0,0,0,38,0,0,0,46,12,0,0,14,0,0,17,48,0,0,0,38,0,0,0,46,12,0,0,14,0,0,19,0,0,0,20,0,19,0,17,0,15,0,14,0,13,46,0},{44,2,0,9,0,14,0,9,0,14,0,9,0,2,0,9,0,12,0,9,0,12,0,9,0,2,0,9,0,14,0,9,0,14,0,9,0,2,0,9,0,12,0,9,0,12,0,9,0,47,14,0,0,0,0,44,12,0,0,0,9,0,12,0,0,0,0,0,2,0,9,0,12,0,0,0,9,0,12,0,47,14,0,0,0,0,44,12,0,9,0,38,0,9,0,38,0,0,0,9,0,12,0,16,0,17,0,16,0,12,0,47,14,0,0,0,0,44,12,0,0,0,9,0,12,0,0,0,0,0,2,0,9,0,12,0,0,0,9,0,12,0,47,14,0,0,0,0,44,16,0,0,0,0,0,19,0,0,0,0,0,0,2,0,5,0,7,0,9,0,7,0,11,0,47,16,0,0,0,0,44,14,0,12,0,38,0,12,0,38,0,14,0,38,0,11,0,0,0,0,0,9,0,11,0,16,0,38,0,16,0,18,0,38,0,23,0,38,0,21,0,0,0,0,0,0,0,18,0,0,0,0,0,0,0,20,0,0,0,0,0,0,17,0,0,0,0,0,0,19,0,0,0,0,0,0}}
- local D_INTRO_MUSIC={45,6,18,15,3,15,13,8,18,3,6,8,10,3,18,3,9,45,3,0,1,1,3,38,6,38,0,0,1,38,3,1,0,0,3,0,1,1,3,38,6,38,0,0,1,38,8,6,0,0,3,0,1,1,3,38,6,38,0,0,1,38,3,1,0,0,8,10,8,0,6,0,3,1,3,38,6,38,3,38,0,0,15,0,13,15,38,0,18,0,0,0,13,15,18,0,15,38,15,0,13,15,38,0,18,0,0,0,13,15,20,0,18,0,15,0,13,15,38,0,18,0,0,0,13,15,20,0,15,0,20,22,20,0,18,0,15,13,15,38,18,38,15,38,0,0,15,0,13,15,38,0,18,0,0,0,13,15,18,38,15,38,15,0,13,15,38,0,18,0,0,0,13,15,18,0,20,0,15,0,13,15,38,0,18,0,0,0,13,15,18,0,15,0,20,22,20,0,18,20,18,0,15,18,13,0,15,8,10,11,15,4,1,4,13,4,1,4,15,4,1,4,18,4,1,4,13,4,15,4,18,4,1,4,13,4,1,4,11,10,8,6,8,4,1,4,15,4,1,4,13,4,1,4,11,0}
- local NOTE_FREQ={[0]=0,251,238,224,210,199,188,177,168,158,149,140,133,251,238,224,210,199,188,177,168,158,149,140,133,251,238,224,210,199,188,177,168,158,149,140,133}
- local NOTE_OCT={[0]=0,15,15,15,15,15,15,15,15,15,15,15,15,51,51,51,51,51,51,51,51,51,51,51,51,85,85,85,85,85,85,85,85,85,85,85,85}
- local ART={weapon={{44,32,32,32,32,44},{226,249,239,228,102,102},{32,32,32,32,95,223},{32,32,32,32,32,32}},pistol={{32,32,32,32,32,32},{32,104,98,98,98,32},{32,32,32,95,223,32},{32,32,32,32,32,32}},bomb={{32,32,85,42,32,32},{32,85,102,73,32,32},{32,66,32,72,32,32},{32,74,70,75,32,32}},emp={{32,85,67,67,73,32},{102,223,85,73,233,102},{102,105,74,75,95,102},{32,74,70,70,75,32}},magnet={{77,112,110,112,110,78},{32,66,66,72,72,32},{99,66,74,75,72,99},{78,74,70,70,75,77}},medkit={{32,85,67,67,73,32},{32,160,160,160,160,32},{32,160,235,243,160,32},{32,228,228,228,228,32}}}
- local GAMEOVER={{112,64,64,64,64,64,64,64,64,64,110},{93,7,1,13,5,32,15,22,5,18,93},{109,64,64,64,64,64,64,64,64,64,125}}
- local OUCH={{205,160,160,160,160,206},{160,143,149,131,136,160},{206,160,160,160,160,205}}
- local CP={[0]=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,163,93,8593,8592,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,9473,9824,9475,9473,9473,9620,9473,9475,9475,9491,9495,9499,9625,9586,9585,9627,9628,9679,9601,9829,9615,9487,9587,9675,9827,9621,9830,9547,9617,9475,960,9701,32,9612,9604,9620,9601,9614,9618,9621,9617,9700,9624,9507,9623,9495,9491,9602,9487,9531,9523,9515,9614,9613,9615,9600,9600,9603,9631,9622,9629,9499,9624,9626}
- local CPINV={[32]=9608,[96]=9608,[97]=9616,[98]=9600,[102]=9618,[108]=9627,[123]=9628,[124]=9625,[126]=9631,[127]=9630}
- -- ------------------------------------------------------- screen buffer
- -- scr[i+1] holds the PET screen code for screen offset i (row*40+col)
- local scr, shadow = {}, {}
- for i = 1, 1000 do scr[i] = 32; shadow[i] = -1 end
- local function scr_set(off, code) scr[off + 1] = code end
- local function P(off, code) scr[off + 1] = code end
- local function scr_get(off) return scr[off + 1] end
- local function xor80(c) if c < 128 then return c + 128 end return c - 128 end
- local GS, GSW = {}, {}
- for c = 0, 255 do
- if c < 128 then
- GS[c] = ucode(CP[c]); GSW[c] = false
- else
- local b = c - 128
- if CPINV[b] then GS[c] = ucode(CPINV[b]); GSW[c] = false
- else GS[c] = ucode(CP[b]); GSW[c] = true end
- end
- end
- local curswap = nil
- local function setswap(sw)
- if curswap == sw then return end
- curswap = sw
- if sw then gpu.setBackground(FG); gpu.setForeground(BG)
- else gpu.setBackground(BG); gpu.setForeground(FG) end
- end
- local function flush()
- for y = 0, 24 do
- local base = y * 40
- local x = 0
- while x < 40 do
- local i = base + x + 1
- if scr[i] ~= shadow[i] then
- local sw = GSW[scr[i]]
- local chars, nch = {}, 0
- local xs, last, gap, j = x, x, 0, x
- while j < 40 do
- local ij = base + j + 1
- local c = scr[ij]
- if GSW[c] ~= sw then break end
- if c ~= shadow[ij] then last = j; gap = 0
- else gap = gap + 1; if gap > 2 then break end end
- nch = nch + 1; chars[nch] = GS[c]
- j = j + 1
- end
- local cnt = last - xs + 1
- for k = xs, last do shadow[base + k + 1] = scr[base + k + 1] end
- setswap(sw)
- gpu.set(xs + 1, y + 1, concat(chars, "", 1, cnt))
- x = last + 1
- else
- x = x + 1
- end
- end
- end
- end
- local function invalidate() for i = 1, 1000 do shadow[i] = -1 end end
- local function cls() for i = 1, 1000 do scr[i] = 32 end end
- local SCREENS = unpack_blob(D_SCREENS)
- local function show_screen(n) -- n = 0..3 : intro, game, endgame, custom keys
- local d = SCREENS
- local o = n * 1000
- for i = 1, 1000 do scr[i] = sbyte(d, o + i) end
- end
- -- ------------------------------------------------------- text helpers
- local function tc(ch) -- source char -> PET screen code
- local b = sbyte(ch)
- if b >= 97 and b <= 122 then return b - 96 end
- return b
- end
- local function put_text(off, s) -- write text at screen offset (row*40+col)
- for i = 1, #s do scr_set(off + i - 1, tc(ssub(s, i, i))) end
- end
- -- ------------------------------------------------------- data decode
- local TL, TM, TR, ML, MM, MR, BL, BM, BR = {}, {}, {}, {}, {}, {}, {}, {}, {}
- local TT = {TL, TM, TR, ML, MM, MR, BL, BM, BR}
- local DESTRUCT, ATTR = {}, {}
- local T_WALK, T_HOVER, T_MOVE, T_DESTR, T_SEE, T_PLACE, T_SEARCH = {}, {}, {}, {}, {}, {}, {}
- do
- local d = unpack_blob(D_TILES)
- for t = 0, 255 do
- DESTRUCT[t] = sbyte(d, t + 1)
- local a = sbyte(d, 256 + t + 1)
- ATTR[t] = a
- T_WALK[t] = a % 2 == 1
- T_HOVER[t] = floor(a / 2) % 2 == 1
- T_MOVE[t] = floor(a / 4) % 2 == 1
- T_DESTR[t] = floor(a / 8) % 2 == 1
- T_SEE[t] = floor(a / 16) % 2 == 1
- T_PLACE[t] = floor(a / 32) % 2 == 1
- T_SEARCH[t] = floor(a / 64) % 2 == 1
- for k = 1, 9 do TT[k][t] = sbyte(d, (k + 1) * 256 + t + 1) end
- end
- end
- -- ------------------------------------------------------- game state
- local utype, ux, uy, ua, ub, uc, ud, uhealth = {}, {}, {}, {}, {}, {}, {}, {}
- local utimera, utimerb, utile = {}, {}, {}
- local rows = {}
- local KEYS, AMMO_PISTOL, AMMO_PLASMA = 0, 0, 0
- local INV_BOMBS, INV_EMP, INV_MEDKIT, INV_MAGNET = 0, 0, 0, 0
- local SELW, SELI, SELECT_TIMEOUT = 0, 0, 0
- local BIG_EXP_ACT, MAGNET_ACT, PLASMA_ACT = 0, 0, 0
- local RANDOM, BORDER, SCREEN_SHAKE = 0, 0, 0
- local KEYTIMER, BGTIMER2 = 0, 0
- local WX, WY = 0, 0
- local CURSOR_X, CURSOR_Y, CURSOR_ON = 0, 0, 0
- local REDRAW = 0
- local SEL_MAP, DIFF = 0, 1
- local CONTROL = 0
- local HOURS, MINUTES, SECONDS, CYCLES, CLOCK_ACTIVE = 0, 0, 0, 0, 0
- local EXP_BUF = {}
- local MODE = "none" -- "game" while the game screen is live
- local WORLD = false -- run unit AI each tick
- local QUIT = {}
- local function gettile(x, y)
- if x < 0 or x > 127 or y < 0 or y > 63 then return 0 end
- return sbyte(rows[y], x + 1)
- end
- local function puttile(x, y, t)
- if x < 0 or x > 127 or y < 0 or y > 63 then return end
- local r = rows[y]
- rows[y] = ssub(r, 1, x) .. schar(t) .. ssub(r, x + 2)
- end
- local function load_level(n)
- local L = unpack_blob(D_LEVELS[n + 1])
- local names = {utype, ux, uy, ua, ub, uc, ud, uhealth}
- for k = 1, 8 do
- local arr = names[k]
- for i = 0, 63 do arr[i] = sbyte(L, (k - 1) * 64 + i + 1) end
- end
- for i = 0, 63 do utimera[i] = 0; utimerb[i] = 0; utile[i] = 0 end
- for y = 0, 63 do rows[y] = ssub(L, 513 + y * 128, 640 + y * 128) end
- end
- local function xor8(a, b)
- local res, bit = 0, 1
- for _ = 1, 8 do
- if (a % 2) ~= (b % 2) then res = res + bit end
- a = floor(a / 2); b = floor(b / 2); bit = bit * 2
- end
- return res
- end
- local function random_number() -- 6502 LFSR from the original
- local r = RANDOM
- if r ~= 0 then
- r = r * 2
- if r <= 255 then RANDOM = r; return end
- r = r % 256
- end
- RANDOM = xor8(r, 0x1D)
- end
- -- ------------------------------------------------------- sound
- local SFX_ON = not QUIET
- local MUSIC_ON = false
- local mus = {pat = nil, line = 0, tempo = 7, timer = 0}
- local fx = {pat = nil, line = 0, tempo = 7, timer = 0, id = 255}
- local pending_note = nil
- local function note_freq(n)
- if n < 1 or n > 36 then return nil end
- local N = NOTE_FREQ[n]
- local o = NOTE_OCT[n]
- local shifts = (o == 15 and 8) or (o == 51 and 4) or 2
- local f = 1000000 / (shifts * (N + 2))
- while f > 2000 do f = f / 2 end
- if f < 20 then f = 20 end
- return floor(f)
- end
- local function voice_step(v)
- if v.timer > 0 then v.timer = v.timer - 1; return nil end
- local pat = v.pat
- if not pat then return nil end
- local b = pat[v.line + 1]
- if b == nil then v.line = 0; b = pat[1] end
- if b == 0 then
- v.timer = v.tempo; v.line = v.line + 1
- elseif b == 37 then
- return "end"
- elseif b == 38 then
- pending_note = nil
- v.timer = v.tempo; v.line = v.line + 1
- elseif b >= 39 and b <= 48 then
- v.tempo = b - 38; v.line = v.line + 1
- else
- pending_note = note_freq(b % 64)
- v.timer = v.tempo; v.line = v.line + 1
- end
- return nil
- end
- local function music_tick()
- if not SFX_ON then return end
- if fx.id ~= 255 then
- if voice_step(fx) == "end" then fx.id = 255; fx.pat = nil; pending_note = nil end
- elseif MUSIC_ON then
- if voice_step(mus) == "end" then mus.line = 0 end
- end
- end
- local function play_sound(n)
- if not SFX_ON then return end
- if fx.id ~= 255 and n > fx.id then return end
- fx.pat = D_SFX[n]; fx.line = 0; fx.timer = 0; fx.tempo = 7; fx.id = n
- end
- local function stop_song()
- fx.id = 255; fx.pat = nil; pending_note = nil
- end
- local function start_music(pat)
- mus.pat = pat; mus.line = 0; mus.timer = 0; mus.tempo = 7
- MUSIC_ON = true
- end
- local function emit_sound()
- if pending_note then
- local f = pending_note
- pending_note = nil
- pcall(computer.beep, f, 0.08)
- end
- end
- -- ------------------------------------------------------- tile animation
- local WATER_TIMER, HVAC_STATE, CINEMA_STATE = 0, 0, 0
- local CINEMA = {}
- do
- local msg = "coming soon: space balls 2 - the search for more money, " ..
- "attack of the paperclips: clippy's revenge, it came from planet earth, " ..
- "rocky 5000, all my circuits the movie, conan the librarian, and more! "
- for i = 1, #msg do CINEMA[i - 1] = tc(ssub(msg, i, i)) end
- end
- local CINEMA_LEN = #CINEMA + 1
- local function animate_tiles()
- WATER_TIMER = WATER_TIMER + 1
- if WATER_TIMER < 20 then return end
- WATER_TIMER = 0
- local t
- t = BR[204]; BR[204] = MM[204]; BR[221] = MM[204]; MM[204] = TL[204]; TL[204] = t
- t = BL[204]; BL[204] = MR[204]; BL[221] = MR[204]; MR[204] = TM[204]; TM[204] = t; TM[221] = t
- t = BM[204]; BM[204] = ML[204]; BM[221] = ML[204]; ML[204] = TR[204]; TR[204] = t; TR[221] = t
- -- trash compactor
- t = TR[148]; TR[148] = TM[148]; TM[148] = TL[148]; TL[148] = t
- t = MR[148]; MR[148] = MM[148]; MM[148] = ML[148]; ML[148] = t
- t = BR[148]; BR[148] = BM[148]; BM[148] = BL[148]; BL[148] = t
- -- HVAC fan
- if HVAC_STATE ~= 0 then
- MM[196] = 0xCD; TL[201] = 0xCD; ML[197] = 0xCE; TM[200] = 0xCE
- MR[196] = 0xA0; BM[196] = 0xA0; BL[197] = 0xA0; TR[200] = 0xA0
- HVAC_STATE = 0
- else
- MM[196] = 0xA0; TL[201] = 0xA0; ML[197] = 0xA0; TM[200] = 0xA0
- MR[196] = 0xC2; TR[200] = 0xC2; BM[196] = 0xC0; BL[197] = 0xC0
- HVAC_STATE = 1
- end
- -- cinema screen
- MM[20] = MR[20]; MR[20] = ML[21]; ML[21] = MM[21]; MM[21] = MR[21]; MR[21] = ML[22]
- ML[22] = CINEMA[CINEMA_STATE]
- CINEMA_STATE = CINEMA_STATE + 1
- if CINEMA_STATE == CINEMA_LEN then CINEMA_STATE = 0 end
- -- server light
- if MR[143] == 0xD7 then MR[143] = 0xD1 else MR[143] = 0xD7 end
- REDRAW = 1
- end
- (function()
- -- ------------------------------------------------------- side panel displays
- -- NOTE: the original proximity test for robot attacks (CMP #1) can only be true
- -- for the player's own tile, so robots would never hurt you. The comments say
- -- "robot next to player"; ATTACK_RANGE = 2 gives that. Set to 1 for the
- -- literal behaviour of the shipped code.
- local ATTACK_RANGE = 2
- local function decwrite(off, n)
- n = n % 256
- P(off, 0x30 + floor(n / 100)); P(off + 1, 0x30 + floor(n / 10) % 10); P(off + 2, 0x30 + n % 10)
- end
- local function display_player_health()
- local h = uhealth[0]
- local half = floor(h / 2)
- local y = 0
- while y < half do P(0x3BA + y, 0x66); y = y + 1 end
- if h % 2 == 1 then P(0x3BA + y, 0x5C); y = y + 1 end
- while y < 6 do P(0x3BA + y, 0x20); y = y + 1 end
- end
- local function draw_art(off, art, nrows)
- for r = 1, nrows do
- local row = art[r]
- for i = 1, 6 do P(off + (r - 1) * 40 + i - 1, row[i]) end
- end
- end
- local function blank_area(off)
- for r = 0, 4 do for i = 0, 5 do P(off + r * 40 + i, 32) end end
- end
- local function preselect_weapon()
- if SELW ~= 0 then return end
- if AMMO_PISTOL ~= 0 then SELW = 1
- elseif AMMO_PLASMA ~= 0 then SELW = 2
- else SELW = 0; blank_area(0x4A) end
- end
- local function display_weapon()
- while true do
- preselect_weapon()
- if SELW == 0 then return end
- if SELW == 1 then
- if AMMO_PISTOL == 0 then SELW = 0
- else draw_art(0x4A, ART.pistol, 4); decwrite(0xED, AMMO_PISTOL); return end
- elseif SELW == 2 then
- if AMMO_PLASMA == 0 then SELW = 0
- else draw_art(0x4A, ART.weapon, 4); decwrite(0xED, AMMO_PLASMA); return end
- else SELW = 0 end
- end
- end
- local function preselect_item()
- if SELI ~= 0 then return end
- if INV_BOMBS ~= 0 then SELI = 1
- elseif INV_EMP ~= 0 then SELI = 2
- elseif INV_MEDKIT ~= 0 then SELI = 3
- elseif INV_MAGNET ~= 0 then SELI = 4
- else SELI = 0; blank_area(0x162) end
- end
- local function display_item()
- preselect_item()
- while true do
- local s = SELI
- if s == 0 then return end
- if s == 5 then SELI = 0; return end
- if s == 1 then
- if INV_BOMBS == 0 then SELI = 2 else draw_art(0x162, ART.bomb, 4); decwrite(0x205, INV_BOMBS); return end
- elseif s == 2 then
- if INV_EMP == 0 then SELI = 3 else draw_art(0x162, ART.emp, 4); decwrite(0x205, INV_EMP); return end
- elseif s == 3 then
- if INV_MEDKIT == 0 then SELI = 4 else draw_art(0x162, ART.medkit, 4); decwrite(0x205, INV_MEDKIT); return end
- elseif s == 4 then
- if INV_MAGNET == 0 then SELI = 0; preselect_item(); return display_item()
- else draw_art(0x162, ART.magnet, 4); decwrite(0x205, INV_MAGNET); return end
- else SELI = 0 end
- end
- end
- local function display_keys()
- for i = 0, 5 do P(0x27A + i, 32); P(0x2A2 + i, 32) end
- if KEYS % 2 == 1 then P(0x27A, 0x63); P(0x27B, 0x4D); P(0x2A2, 0x41); P(0x2A3, 0x67) end
- if floor(KEYS / 2) % 2 == 1 then P(0x27C, 0x63); P(0x27D, 0x4D); P(0x2A4, 0x53); P(0x2A5, 0x67) end
- if floor(KEYS / 4) % 2 == 1 then P(0x27E, 0x63); P(0x27F, 0x4D); P(0x2A6, 0x2A); P(0x2A7, 0x67) end
- end
- -- info window (3 lines at the bottom left)
- local function scroll_info()
- for x = 0, 32 do
- scr[0x370 + x + 1] = scr[0x398 + x + 1]
- scr[0x398 + x + 1] = scr[0x3C0 + x + 1]
- scr[0x3C0 + x + 1] = 32
- end
- end
- local function print_info(text)
- scroll_info()
- local x = 0
- for i = 1, #text do
- local ch = ssub(text, i, i)
- if ch == "\n" then x = 0; scroll_info()
- else
- if x < 33 then P(0x3C0 + x, tc(ch)) end
- x = x + 1
- end
- end
- end
- -- ------------------------------------------------------- map window
- local PRE = {}
- local function reverse_tile()
- local o = CURSOR_Y * 120 + CURSOR_X * 3
- for r = 0, 2 do for c = 0, 2 do
- local i = o + r * 40 + c + 1
- if scr[i] then scr[i] = xor80(scr[i]) end
- end end
- end
- local function draw_map_window()
- for i = 0, 76 do PRE[i] = 0 end
- for u = 0, 31 do
- local ok
- if u == 0 then ok = true
- else ok = utype[u] ~= 0 and ux[u] >= WX and ux[u] <= WX + 10 and uy[u] >= WY and uy[u] <= WY + 6 end
- if ok then
- local dx, dy = ux[u] - WX, uy[u] - WY
- if dx >= 0 and dx <= 10 and dy >= 0 and dy <= 6 then
- local idx = dy * 11 + dx
- local t = utile[u]
- if (t == 130 or t == 134) and PRE[idx] ~= 0 then
- else PRE[idx] = t end
- end
- end
- end
- REDRAW = 0
- for ty = 0, 6 do
- for tx = 0, 10 do
- local t = gettile(WX + tx, WY + ty)
- local o = ty * 120 + tx * 3 + 1
- scr[o] = TL[t]; scr[o + 1] = TM[t]; scr[o + 2] = TR[t]
- scr[o + 40] = ML[t]; scr[o + 41] = MM[t]; scr[o + 42] = MR[t]
- scr[o + 80] = BL[t]; scr[o + 81] = BM[t]; scr[o + 82] = BR[t]
- local s = PRE[ty * 11 + tx]
- if s ~= 0 then
- local c
- c = TL[s]; if c ~= 0x3A then scr[o] = c end
- c = TM[s]; if c ~= 0x3A then scr[o + 1] = c end
- c = TR[s]; if c ~= 0x3A then scr[o + 2] = c end
- c = ML[s]; if c ~= 0x3A then scr[o + 40] = c end
- c = MM[s]; if c ~= 0x3A then scr[o + 41] = c end
- c = MR[s]; if c ~= 0x3A then scr[o + 42] = c end
- c = BL[s]; if c ~= 0x3A then scr[o + 80] = c end
- c = BM[s]; if c ~= 0x3A then scr[o + 81] = c end
- c = BR[s]; if c ~= 0x3A then scr[o + 82] = c end
- end
- end
- if CURSOR_ON == 1 and ty == CURSOR_Y then reverse_tile() end
- end
- end
- local function calc_and_redraw()
- WX = ux[0] - 5; WY = uy[0] - 3
- REDRAW = 1
- end
- local function check_window_redraw(u)
- if ux[u] >= WX and ux[u] <= WX + 10 and uy[u] >= WY and uy[u] <= WY + 6 then REDRAW = 1 end
- end
- -- ------------------------------------------------------- unit helpers
- local function check_for_unit(x, y)
- for u = 0, 27 do
- if utype[u] ~= 0 and ux[u] == x and uy[u] == y then return u end
- end
- return 255
- end
- local function check_for_hidden_unit(x, y)
- for u = 48, 63 do
- if utype[u] ~= 0 then
- local ok = false
- if ux[u] == x then ok = true
- elseif ux[u] < x and ux[u] + uc[u] >= x then ok = true end
- if ok then
- ok = false
- if uy[u] == y then ok = true
- elseif uy[u] < y and uy[u] + ud[u] >= y then ok = true end
- if ok then return u end
- end
- end
- end
- return 255
- end
- local function free_weapon_slot()
- for u = 28, 31 do if utype[u] == 0 then return u end end
- return nil
- end
- -- dir: 0 up, 1 down, 2 left, 3 right. mt: 1 walk, 2 hover. returns success
- local DX = {[0] = 0, 0, -1, 1}
- local DY = {[0] = -1, 1, 0, 0}
- local function walk(u, dir, mt)
- local x, y = ux[u], uy[u]
- if dir == 3 and x == 122 then return false end
- if dir == 2 and x == 5 then return false end
- if dir == 1 and y == 60 then return false end
- if dir == 0 and y == 3 then return false end
- local nx, ny = x + DX[dir], y + DY[dir]
- local t = gettile(nx, ny)
- if mt == 1 then if not T_WALK[t] then return false end
- else if not T_HOVER[t] then return false end end
- if check_for_unit(nx, ny) ~= 255 then return false end
- ux[u] = nx; uy[u] = ny
- return true
- end
- local function inflict_damage(t, dmg)
- local h = uhealth[t] - dmg
- if h > 0 then
- uhealth[t] = h
- if t == 0 then display_player_health(); BORDER = 10 end
- return
- end
- uhealth[t] = 0
- if t == 0 then
- utype[0] = 0
- display_player_health()
- BORDER = 10
- else
- if utype[t] ~= 8 then
- utype[t] = 8; utimera[t] = 255; utile[t] = 115
- end
- end
- end
- local function alter_ai(t)
- if utype[t] == 2 or utype[t] == 3 then utype[t] = 4 end
- end
- local function create_player_explosion()
- local s = free_weapon_slot()
- if not s then return end
- utype[s] = 11; utile[s] = 248; utimera[s] = 1
- ux[s] = ux[0]; uy[s] = uy[0]
- end
- local function in_range(u, r)
- local dx = ux[u] - ux[0]; if dx < 0 then dx = -dx end
- local dy = uy[u] - uy[0]; if dy < 0 then dy = -dy end
- return dx < r and dy < r
- end
- local function robot_attack_range(u) return in_range(u, ATTACK_RANGE == 1 and 1 or 2) end
- local function door_proximity(u) return in_range(u, 2) end
- -- ------------------------------------------------------- explosions
- local function bex_center(u)
- local f = check_for_unit(ux[u], uy[u])
- if f ~= 255 then inflict_damage(f, 11) end
- end
- local BDIRS = {{0, -1}, {0, 1}, {1, 0}, {-1, 0}, {1, -1}, {-1, -1}, {1, 1}, {-1, 1}}
- local function big_exp_phase1(u)
- if BIG_EXP_ACT ~= 0 then utimera[u] = 10; return end
- BIG_EXP_ACT = 1; SCREEN_SHAKE = 1
- play_sound(0)
- bex_center(u)
- for d = 1, 8 do
- local x, y = ux[u], uy[u]
- for step = 1, 2 do
- x = x + BDIRS[d][1]; y = y + BDIRS[d][2]
- local t = gettile(x, y)
- if not T_SEE[t] then break end
- EXP_BUF[(d - 1) * 2 + step - 1] = t
- puttile(x, y, 246)
- local f = check_for_unit(x, y)
- if f ~= 255 then inflict_damage(f, 11) end
- end
- end
- utile[u] = 246
- ua[u] = 1
- utimera[u] = 12
- REDRAW = 1
- end
- local function restore_tile(x, y, orig)
- if gettile(x, y) ~= 246 then return end
- if orig == 131 then
- puttile(x, y, 135)
- local s = free_weapon_slot()
- if not s then return end
- utype[s] = 6; utile[s] = 131; ux[s] = x; uy[s] = y
- utimera[s] = 10; ua[s] = 0
- elseif T_DESTR[orig] then puttile(x, y, DESTRUCT[orig])
- else puttile(x, y, orig) end
- end
- local function big_exp_phase2(u)
- local cx, cy = ux[u], uy[u]
- local ct = gettile(cx, cy)
- puttile(cx, cy, 246)
- restore_tile(cx, cy, ct)
- for d = 1, 8 do
- local x, y = cx, cy
- for step = 1, 2 do
- x = x + BDIRS[d][1]; y = y + BDIRS[d][2]
- restore_tile(x, y, EXP_BUF[(d - 1) * 2 + step - 1] or 0)
- end
- end
- REDRAW = 1
- utype[u] = 0; BIG_EXP_ACT = 0; SCREEN_SHAKE = 0
- end
- -- ------------------------------------------------------- AI routines
- local AI = {}
- local function hoverbot_animate(u)
- if utimerb[u] ~= 0 then utimerb[u] = utimerb[u] - 1; return end
- utimerb[u] = 3
- if utile[u] == 98 then utile[u] = 99 else utile[u] = 98 end
- end
- local function droid(u, a, b) -- a,b = the two directions (dir0 when A~=1)
- hoverbot_animate(u)
- utimera[u] = 10
- if ua[u] ~= 1 then
- if not walk(u, a, 2) then ua[u] = 1 end
- else
- if not walk(u, b, 2) then ua[u] = 0 end
- end
- check_window_redraw(u)
- end
- AI[2] = function(u) droid(u, 2, 3) end -- left / right
- AI[3] = function(u) droid(u, 0, 1) end -- up / down
- local function chase(u, mt)
- if ux[u] ~= ux[0] then
- if ux[u] < ux[0] then walk(u, 3, mt) else walk(u, 2, mt) end
- end
- if uy[u] ~= uy[0] then
- if uy[u] < uy[0] then walk(u, 1, mt) else walk(u, 0, mt) end
- end
- end
- AI[4] = function(u) -- hoverbot attack mode
- utimerb[u] = 0
- hoverbot_animate(u)
- utimera[u] = 7
- chase(u, 2)
- if robot_attack_range(u) then
- inflict_damage(0, 1)
- create_player_explosion()
- play_sound(7)
- utimera[u] = 30
- end
- check_window_redraw(u)
- end
- AI[5] = function(u) -- robot on water after EMP
- utile[u] = utile[u] + 1
- if utile[u] == 143 then utile[u] = 140 end
- ua[u] = (ua[u] - 1) % 256
- if ua[u] == 0 then
- utype[u] = 8; utimera[u] = 255; utile[u] = 115
- end
- check_window_redraw(u)
- end
- AI[6] = function(u)
- if ua[u] == 0 then big_exp_phase1(u)
- elseif ua[u] == 1 then big_exp_phase2(u) end
- end
- AI[7] = function(u) -- transporter pad
- if ux[u] == ux[0] and uy[u] == uy[0] then
- if ua[u] ~= 0 then
- print_info("transporter will not activate\nuntil all robots destroyed.")
- play_sound(11)
- utimera[u] = 100
- else
- utype[u] = 23; utimera[u] = 5; utimerb[u] = 0
- end
- return
- end
- if ua[u] == 1 then
- local alldead = true
- for x = 1, 27 do if utype[x] ~= 0 then alldead = false; break end end
- if alldead then ua[u] = 0 end
- utimera[u] = 30
- return
- end
- if utimerb[u] == 1 then utimerb[u] = 0; puttile(ux[u], uy[u], 31)
- else utimerb[u] = 1; puttile(ux[u], uy[u], 30) end
- check_window_redraw(u)
- utimera[u] = 30
- end
- AI[8] = function(u) utype[u] = 0 end
- AI[9] = function(u) -- evilbot
- utimera[u] = 5
- local t = utile[u]
- if t == 100 or t == 101 or t == 102 then utile[u] = t + 1 else utile[u] = 100 end
- if utimerb[u] ~= 0 then
- utimerb[u] = utimerb[u] - 1
- check_window_redraw(u)
- return
- end
- utimerb[u] = 1
- chase(u, 1)
- if robot_attack_range(u) then
- inflict_damage(0, 5)
- create_player_explosion()
- play_sound(7)
- utimera[u] = 15
- end
- check_window_redraw(u)
- end
- -- doors ------------------------------------------------------------
- local function hdoor(u, p1, p2, p3)
- local x, y = ux[u], uy[u]
- puttile(x - 1, y, p1); puttile(x, y, p2); puttile(x + 1, y, p3)
- end
- local function vdoor(u, p1, p2, p3)
- local x, y = ux[u], uy[u]
- puttile(x, y - 1, p1); puttile(x, y, p2); puttile(x, y + 1, p3)
- end
- local function door_draw(u, h1, h2, h3, v1, v2, v3)
- if ua[u] == 1 then vdoor(u, v1, v2, v3) else hdoor(u, h1, h2, h3) end
- end
- AI[10] = function(u)
- local b = ub[u]
- if b == 0 then
- door_draw(u, 88, 89, 86, 70, 74, 78); ub[u] = 1; utimera[u] = 5
- elseif b == 1 then
- door_draw(u, 17, 9, 91, 27, 9, 15); ub[u] = 2; utimera[u] = 30
- elseif b == 2 then
- if door_proximity(u) then utimerb[u] = 30; return end
- if gettile(ux[u], uy[u]) ~= 9 then utimera[u] = 35; return end
- play_sound(14)
- door_draw(u, 88, 89, 86, 70, 74, 78); ub[u] = 3; utimera[u] = 5
- elseif b == 3 then
- door_draw(u, 84, 85, 86, 69, 73, 77); ub[u] = 4; utimera[u] = 5
- elseif b == 4 then
- door_draw(u, 80, 81, 82, 68, 72, 76); ub[u] = 5; utimera[u] = 5
- elseif b == 5 then
- if not door_proximity(u) then utimera[u] = 20; return end
- local lock = uc[u]
- local ok = false
- if lock == 0 then ok = true
- elseif lock == 1 then ok = KEYS % 2 == 1
- elseif lock == 2 then ok = floor(KEYS / 2) % 2 == 1
- elseif lock == 3 then ok = floor(KEYS / 4) % 2 == 1 end
- if not ok then utimera[u] = 20; return end
- play_sound(14)
- door_draw(u, 84, 85, 86, 69, 73, 77); ub[u] = 0; utimera[u] = 5
- else return end
- check_window_redraw(u)
- end
- local elevator_panel -- defined later (needs input loop)
- AI[19] = function(u)
- local b = ub[u]
- if b == 0 then
- hdoor(u, 181, 89, 173); ub[u] = 1; utimera[u] = 5
- elseif b == 1 then
- hdoor(u, 182, 9, 172); ub[u] = 2; utimera[u] = 50
- elseif b == 2 then
- if gettile(ux[u], uy[u]) ~= 9 or check_for_unit(ux[u], uy[u]) ~= 255 then utimera[u] = 35; return end
- play_sound(14)
- hdoor(u, 181, 89, 173); ub[u] = 3; utimera[u] = 5
- elseif b == 3 then
- hdoor(u, 84, 85, 173); ub[u] = 4; utimera[u] = 5
- elseif b == 4 then
- hdoor(u, 80, 81, 174); ub[u] = 5; utimera[u] = 5
- check_window_redraw(u)
- elevator_panel(u)
- return
- elseif b == 5 then
- if not door_proximity(u) then utimera[u] = 20; return end
- play_sound(14)
- hdoor(u, 84, 85, 173); ub[u] = 0; utimera[u] = 5
- else return end
- check_window_redraw(u)
- end
- AI[11] = function(u) -- small explosion
- utimera[u] = 0
- utile[u] = utile[u] + 1
- if utile[u] == 252 then utype[u] = 0 end
- check_window_redraw(u)
- end
- -- weapons fire -----------------------------------------------------
- local function deactivate_weapon(u)
- utype[u] = 0
- if ub[u] == 1 then ub[u] = 0; PLASMA_ACT = 0 end
- end
- local function start_bomb(u, x, y, timer)
- utype[u] = 6; utile[u] = 131; ux[u] = x; uy[u] = y
- utimera[u] = timer; ua[u] = 0
- end
- local function pistol_common(u)
- if ub[u] ~= 0 then
- -- plasma
- ua[u] = (ua[u] - 1) % 256
- local t = gettile(ux[u], uy[u])
- local hit = true
- if t ~= 131 and T_SEE[t] then
- if check_for_unit(ux[u], uy[u]) == 255 then hit = false end
- end
- if not hit then check_window_redraw(u); return end
- utype[u] = 6; utimera[u] = 1; ua[u] = 0; PLASMA_ACT = 0
- check_window_redraw(u)
- return
- end
- ua[u] = (ua[u] - 1) % 256
- local x, y = ux[u], uy[u]
- local t = gettile(x, y)
- if t == 131 then
- puttile(x, y, 135)
- start_bomb(u, x, y, 5)
- return
- end
- if not T_SEE[t] then
- utype[u] = 11; utile[u] = 248
- check_window_redraw(u)
- return
- end
- local f = check_for_unit(x, y)
- if f == 255 then check_window_redraw(u); return end
- utype[u] = 11; utile[u] = 248
- inflict_damage(f, 1)
- alter_ai(f)
- check_window_redraw(u)
- end
- for dir = 0, 3 do
- AI[12 + dir] = function(u)
- if ua[u] == 0 then
- deactivate_weapon(u)
- check_window_redraw(u)
- return
- end
- ux[u] = ux[u] + DX[dir]; uy[u] = uy[u] + DY[dir]
- pistol_common(u)
- end
- end
- AI[16] = function(u) -- trash compactor
- local st = ua[u]
- local function piece(a, b, c, d)
- local x, y = ux[u], uy[u]
- puttile(x, y - 1, a); puttile(x + 1, y - 1, b); puttile(x, y, c); puttile(x + 1, y, d)
- check_window_redraw(u)
- end
- if st == 0 then
- local x, y = ux[u], uy[u]
- local idle = false
- if gettile(x, y) == 148 and gettile(x + 1, y) == 148 then
- utimera[u] = 20
- if check_for_unit(x, y) == 255 then idle = true end
- end
- if idle then return end
- piece(146, 147, 150, 151)
- ua[u] = 1; utimera[u] = 10
- play_sound(14)
- elseif st == 1 then
- piece(152, 153, 156, 157)
- ua[u] = 2; utimera[u] = 50
- local x, y = ux[u], uy[u]
- local f = check_for_unit(x, y)
- if f == 255 then f = check_for_unit(x + 1, y) end
- if f == 255 then return end
- print_info("you're terminated!")
- play_sound(0)
- utype[f] = 0; uhealth[f] = 0
- local s = free_weapon_slot()
- if not s then check_window_redraw(u); return end
- utype[s] = 11; utile[s] = 248; utimera[s] = 0
- ux[s] = x; uy[s] = y
- if f == 0 then BORDER = 10 end
- check_window_redraw(u)
- elseif st == 2 then
- piece(146, 147, 150, 151)
- ua[u] = 3; utimera[u] = 10
- elseif st == 3 then
- piece(144, 145, 148, 148)
- ua[u] = 0; utimera[u] = 20
- play_sound(14)
- end
- end
- -- rollerbots ---------------------------------------------------------
- local function rollerbot_animate(u)
- if utimerb[u] ~= 0 then utimerb[u] = utimerb[u] - 1; return end
- utimerb[u] = 3
- if utile[u] == 164 then utile[u] = 165 else utile[u] = 164 end
- check_window_redraw(u)
- end
- local function rollerbot_shoot(u, dir, ox, oy)
- local s = free_weapon_slot()
- if not s then return end
- utype[s] = 12 + dir
- utile[s] = (dir < 2) and 244 or 245
- ua[s] = 5; ub[s] = 0; utimera[s] = 0
- ux[s] = ox; uy[s] = oy
- play_sound(9)
- end
- local function rollerbot_fire_detect(u)
- local ox, oy = ux[u], uy[u]
- if oy == uy[0] then
- if ox < ux[0] then
- if ux[0] - ox < 6 then rollerbot_shoot(u, 3, ox, oy) end
- else
- if ox - ux[0] < 6 then rollerbot_shoot(u, 2, ox, oy) end
- end
- elseif ox == ux[0] then
- if oy < uy[0] then
- if uy[0] - oy < 4 then rollerbot_shoot(u, 1, ox, oy) end
- else
- if oy - uy[0] < 4 then rollerbot_shoot(u, 0, ox, oy) end
- end
- end
- end
- local function roller(u, a, b)
- utimera[u] = 7
- rollerbot_animate(u)
- if ua[u] ~= 1 then
- if not walk(u, a, 1) then ua[u] = 1 end
- else
- if not walk(u, b, 1) then ua[u] = 0 end
- end
- rollerbot_fire_detect(u)
- check_window_redraw(u)
- end
- AI[17] = function(u) roller(u, 0, 1) end
- AI[18] = function(u) roller(u, 2, 3) end
- -- magnet -------------------------------------------------------------
- AI[20] = function(u)
- utimerb[u] = (utimerb[u] - 1) % 256
- if utimerb[u] == 0 then
- ua[u] = (ua[u] - 1) % 256
- if ua[u] == 0 then utype[u] = 0; MAGNET_ACT = 0; return end
- end
- local f = check_for_unit(ux[u], uy[u])
- if f == 255 then return end
- if f == 0 then
- INV_MAGNET = (INV_MAGNET + 1) % 256
- display_item()
- else
- play_sound(4)
- ud[f] = utype[f]
- utype[f] = 21
- utimerb[f] = 60
- end
- utype[u] = 0; MAGNET_ACT = 0
- end
- AI[21] = function(u) -- magnetized (crazy) robot
- random_number()
- local d = RANDOM % 4
- walk(u, d, 1)
- check_window_redraw(u)
- utimera[u] = 10
- utimerb[u] = (utimerb[u] - 1) % 256
- if utimerb[u] == 0 then utype[u] = ud[u] end
- end
- AI[22] = function(u) -- water raft
- local x, y = ux[u], uy[u]
- local right = ua[u] == 1
- puttile(x, y, 204)
- if x == ux[0] and y == uy[0] then
- local d = right and 1 or -1
- ux[0] = ux[0] + d; ux[u] = ux[u] + d
- puttile(ux[u], uy[u], 242)
- calc_and_redraw()
- else
- ux[u] = ux[u] + (right and 1 or -1)
- puttile(ux[u], uy[u], 242)
- check_window_redraw(u)
- end
- local dest = right and uc[u] or ub[u]
- if ux[u] ~= dest then utimera[u] = 6
- else utimera[u] = 100; ua[u] = right and 0 or 1 end
- end
- AI[23] = function(u) -- dematerialize (transporter in use)
- local tb = utimerb[u]
- utile[0] = 252 + tb % 2 + floor(tb / 8) % 2
- utimerb[u] = tb + 1
- if utimerb[u] ~= 16 then
- utimera[u] = 1; REDRAW = 1
- return
- end
- if ub[u] == 1 then
- utile[0] = 243
- ux[0] = uc[u]; uy[0] = ud[u]
- utype[u] = 7
- calc_and_redraw()
- else
- utype[0] = 2 -- level complete
- utype[u] = 7
- end
- end
- local function background_tasks()
- for u = 1, 63 do
- local t = utype[u]
- if t ~= 0 then
- if utimera[u] ~= 0 then utimera[u] = utimera[u] - 1
- elseif t < 24 then
- local f = AI[t]
- if f then f(u) end
- end
- end
- end
- end
- -- ------------------------------------------------------- time, input, pump
- local lasttime = 0
- local keyq = {}
- local FLASH_STATE = 0
- local SHAKE_PHASE = false
- local LASTKEY = nil
- local PX, PY = 0, 0
- local function keyname(ch, code)
- if code == 200 then return "UP" elseif code == 208 then return "DOWN"
- elseif code == 203 then return "LEFT" elseif code == 205 then return "RIGHT"
- elseif code == 28 or code == 156 then return "ENTER"
- elseif code == 1 then return "ESC"
- elseif code == 199 then return "HOME" elseif code == 15 then return "TAB" end
- if ch and ch >= 32 and ch < 127 then return schar(ch) end
- return nil
- end
- local function handle_event(name, a, b, c)
- if name == "key_down" then
- local k = keyname(b, c)
- if k then keyq[#keyq + 1] = k; if #keyq > 16 then table.remove(keyq, 1) end end
- elseif name == "interrupted" then
- error(QUIT, 0)
- end
- end
- local function getkey()
- if #keyq == 0 then return nil end
- return table.remove(keyq, 1)
- end
- local function irq()
- animate_tiles()
- music_tick()
- if CLOCK_ACTIVE == 1 then
- CYCLES = CYCLES + 1
- if CYCLES >= 60 then
- CYCLES = 0; SECONDS = SECONDS + 1
- if SECONDS == 60 then
- SECONDS = 0; MINUTES = MINUTES + 1
- if MINUTES == 60 then MINUTES = 0; HOURS = HOURS + 1 end
- end
- end
- end
- if BGTIMER2 > 0 then BGTIMER2 = BGTIMER2 - 1 end
- if KEYTIMER > 0 then KEYTIMER = KEYTIMER - 1 end
- if BORDER > 0 then BORDER = BORDER - 1 end
- end
- local function game_tick_extras()
- if SELECT_TIMEOUT > 0 then SELECT_TIMEOUT = SELECT_TIMEOUT - 1 end
- if BORDER == 0 then
- if FLASH_STATE == 1 then
- for i = 0, 5 do P(0x2F2 + i, 32); P(0x31A + i, 32); P(0x342 + i, 32) end
- FLASH_STATE = 0
- end
- elseif FLASH_STATE == 0 then
- for i = 1, 6 do P(0x2F2 + i - 1, OUCH[1][i]); P(0x31A + i - 1, OUCH[2][i]); P(0x342 + i - 1, OUCH[3][i]) end
- FLASH_STATE = 1
- end
- end
- local function pump(wait)
- local now = computer.uptime()
- local n = floor((now - lasttime) * 60)
- if n > 0 then
- if n > 6 then n = 6; lasttime = now else lasttime = lasttime + n / 60 end
- for _ = 1, n do
- irq()
- if MODE == "game" then
- game_tick_extras()
- if WORLD then background_tasks() end
- end
- end
- end
- if MODE == "game" then
- if SCREEN_SHAKE == 1 then
- REDRAW = 1
- SHAKE_PHASE = not SHAKE_PHASE
- else SHAKE_PHASE = false end
- if REDRAW == 1 then
- draw_map_window()
- if SHAKE_PHASE then
- for r = 0, 20 do for c = 0, 31 do scr[r * 40 + c + 1] = scr[r * 40 + c + 2] end end
- end
- end
- end
- emit_sound()
- flush()
- handle_event(event.pull(wait or 0.05))
- end
- local function raw_key() -- wait for a key without advancing the world
- flush()
- while true do
- handle_event(event.pull(0.1))
- local k = getkey()
- if k then lasttime = computer.uptime(); return k end
- end
- end
- local function clear_key_buffer() keyq = {}; KEYTIMER = 20 end
- -- ------------------------------------------------------- controls
- local K = {up = "8", down = "2", left = "4", right = "6", fup = "w", fdown = "s",
- fleft = "a", fright = "d", cw = "<", ci = ">", use = " ", search = "z", move = "m"}
- local KORDER = {"up", "down", "left", "right", "fup", "fdown", "fleft", "fright", "cw", "ci", "use", "search", "move"}
- local KEYS_DEFINED = false
- local KSTD = {}
- for k, v in pairs(K) do KSTD[k] = v end
- local function action_of(n)
- if n == "UP" then return "up" elseif n == "DOWN" then return "down"
- elseif n == "LEFT" then return "left" elseif n == "RIGHT" then return "right" end
- if n == K.cw or n == "," then return "cw" end
- if n == K.ci or n == "." then return "ci" end
- for _, a in ipairs(KORDER) do if K[a] == n then return a end end
- if n == "ESC" or n == "p" or n == "P" then return "pause" end
- if n == "HOME" or n == "TAB" then return "map" end
- if n == "M" then return "music" end
- if n == "C" then return "cheat" end
- return nil
- end
- local function dir_of(a)
- if a == "up" or a == "fup" and false then return 0 end
- if a == "up" then return 0 elseif a == "down" then return 1
- elseif a == "left" then return 2 elseif a == "right" then return 3 end
- return nil
- end
- -- ------------------------------------------------------- player actions
- local function animate_player()
- if utile[0] == 243 then utile[0] = 247 else utile[0] = 243 end
- end
- local function fire(dir)
- if SELW == 0 then return end
- local s
- if SELW == 1 then
- if AMMO_PISTOL == 0 then return end
- s = free_weapon_slot(); if not s then return end
- ub[s] = 0
- utile[s] = (dir < 2) and 244 or 245
- else
- if BIG_EXP_ACT == 1 or PLASMA_ACT == 1 or AMMO_PLASMA == 0 then return end
- s = free_weapon_slot(); if not s then return end
- ub[s] = 1; PLASMA_ACT = 1
- utile[s] = (dir < 2) and 240 or 241
- end
- utype[s] = 12 + dir
- ua[s] = (dir < 2) and 3 or 5
- utimera[s] = 0
- ux[s] = ux[0]; uy[s] = uy[0]
- if SELW == 2 then play_sound(8); AMMO_PLASMA = AMMO_PLASMA - 1
- else play_sound(9); AMMO_PISTOL = AMMO_PISTOL - 1 end
- display_weapon()
- end
- local function cycle_weapon()
- play_sound(12)
- if SELECT_TIMEOUT ~= 0 then return end
- SELECT_TIMEOUT = 3; KEYTIMER = 20
- SELW = SELW + 1
- if SELW ~= 2 then SELW = 0 end
- display_weapon()
- end
- local function cycle_item()
- play_sound(13)
- if SELECT_TIMEOUT ~= 0 then return end
- SELECT_TIMEOUT = 3; KEYTIMER = 20
- SELI = SELI + 1
- if SELI == 5 then SELI = 0 end
- display_item()
- end
- -- returns true when the player picked a direction (CURSOR_* updated)
- local function user_select_object()
- play_sound(16)
- CURSOR_X = 5; CURSOR_Y = 3; CURSOR_ON = 1
- reverse_tile()
- while true do
- pump()
- if utype[0] == 0 then CURSOR_ON = 0; return false end
- local k = getkey()
- if k then
- local a = action_of(k)
- if k == "ESC" then CURSOR_ON = 0; draw_map_window(); return false end
- if a == "right" then CURSOR_X = CURSOR_X + 1; return true
- elseif a == "left" then CURSOR_X = CURSOR_X - 1; return true
- elseif a == "down" then CURSOR_Y = CURSOR_Y + 1; return true
- elseif a == "up" then CURSOR_Y = CURSOR_Y - 1; return true end
- end
- end
- end
- local function cursor_map() return CURSOR_X + WX, CURSOR_Y + WY end
- local function search_object()
- if not user_select_object() then return end
- REDRAW = 1
- local mx, my = cursor_map()
- local t = gettile(mx, my)
- if not T_SEARCH[t] then
- CURSOR_ON = 0
- print_info("nothing found here.")
- return
- end
- if t == 41 or t == 45 or t == 199 then puttile(mx, my, DESTRUCT[t]) end
- print_info("searching")
- for bar = 0, 7 do
- BGTIMER2 = 18
- while BGTIMER2 ~= 0 do pump() end
- P(0x3C9 + bar, 46)
- end
- CURSOR_ON = 0
- draw_map_window()
- local f = check_for_hidden_unit(mx, my)
- if f == 255 then print_info("nothing found here."); return end
- local typ, qty = utype[f], ua[f]
- utype[f] = 0
- play_sound(10)
- if typ == 128 then
- if qty == 0 then KEYS = KEYS % 2 == 1 and KEYS or KEYS + 1
- elseif qty == 1 then KEYS = floor(KEYS / 2) % 2 == 1 and KEYS or KEYS + 2
- else KEYS = floor(KEYS / 4) % 2 == 1 and KEYS or KEYS + 4 end
- print_info("you found a key card!")
- display_keys()
- elseif typ == 129 then
- INV_BOMBS = (INV_BOMBS + qty) % 256
- print_info("you found a timebomb!"); display_item()
- elseif typ == 130 then
- INV_EMP = (INV_EMP + qty) % 256
- print_info("you found an emp device!"); display_item()
- elseif typ == 131 then
- AMMO_PISTOL = AMMO_PISTOL + qty
- if AMMO_PISTOL > 255 then AMMO_PISTOL = 255 end
- print_info("you found a pistol!"); display_weapon()
- elseif typ == 132 then
- AMMO_PLASMA = (AMMO_PLASMA + qty) % 256
- print_info("you found a plasma gun!"); display_weapon()
- elseif typ == 133 then
- INV_MEDKIT = (INV_MEDKIT + qty) % 256
- print_info("you found a medkit!"); display_item()
- elseif typ == 134 then
- INV_MAGNET = (INV_MAGNET + qty) % 256
- print_info("you found a magnet!"); display_item()
- end
- end
- local function move_object()
- if not user_select_object() then return end
- CURSOR_ON = 0
- draw_map_window()
- local mx, my = cursor_map()
- local hidden = check_for_hidden_unit(mx, my)
- local t = gettile(mx, my)
- if not T_MOVE[t] then
- print_info("can't move that!")
- play_sound(11)
- return
- end
- local otile, ox, oy = t, mx, my
- CURSOR_ON = 1
- reverse_tile()
- local got = false
- while not got do
- pump()
- if utype[0] == 0 then CURSOR_ON = 0; return end
- local k = getkey()
- if k then
- local a = action_of(k)
- if k == "ESC" then CURSOR_ON = 0; draw_map_window(); return end
- if a == "right" then CURSOR_X = CURSOR_X + 1; got = true
- elseif a == "left" then CURSOR_X = CURSOR_X - 1; got = true
- elseif a == "down" then CURSOR_Y = CURSOR_Y + 1; got = true
- elseif a == "up" then CURSOR_Y = CURSOR_Y - 1; got = true end
- end
- end
- CURSOR_ON = 0
- draw_map_window()
- local dx, dy = cursor_map()
- local dt = gettile(dx, dy)
- if not T_PLACE[dt] or check_for_unit(dx, dy) ~= 255 then
- print_info("blocked!")
- play_sound(11)
- return
- end
- play_sound(6)
- puttile(dx, dy, otile)
- if dt == 148 then dt = 9 end
- puttile(ox, oy, dt)
- REDRAW = 1
- if hidden ~= 255 then ux[hidden] = dx; uy[hidden] = dy end
- end
- local function bomb_magnet_common1()
- CURSOR_ON = 0
- draw_map_window()
- local mx, my = cursor_map()
- return mx, my, T_WALK[gettile(mx, my)]
- end
- local function blocked_msg() print_info("blocked!"); play_sound(11) end
- local function use_bomb()
- if not user_select_object() then return end
- local mx, my, ok = bomb_magnet_common1()
- if not ok or check_for_unit(mx, my) ~= 255 then blocked_msg(); return end
- local s = free_weapon_slot(); if not s then return end
- utype[s] = 6; utile[s] = 130; ux[s] = mx; uy[s] = my
- utimera[s] = 100; ua[s] = 0
- INV_BOMBS = INV_BOMBS - 1
- display_item()
- REDRAW = 1; SELECT_TIMEOUT = 3
- play_sound(6)
- end
- local function use_magnet()
- if MAGNET_ACT ~= 0 then return end
- if not user_select_object() then return end
- local mx, my, ok = bomb_magnet_common1()
- if not ok then blocked_msg(); return end
- local s = free_weapon_slot(); if not s then return end
- utype[s] = 20; utile[s] = 134; ux[s] = mx; uy[s] = my
- utimera[s] = 1; utimerb[s] = 255; ua[s] = 3
- MAGNET_ACT = 1
- INV_MAGNET = INV_MAGNET - 1
- display_item()
- REDRAW = 1
- play_sound(6)
- end
- local function wait_real(sec)
- local t0 = computer.uptime()
- while computer.uptime() - t0 < sec do
- flush()
- handle_event(event.pull(0.05))
- end
- lasttime = computer.uptime()
- end
- local function use_emp()
- for r = 0, 20 do for c = 0, 32 do
- local i = r * 40 + c + 1
- scr[i] = xor80(scr[i])
- end end
- REDRAW = 0
- play_sound(3)
- INV_EMP = INV_EMP - 1
- display_item()
- flush()
- for u = 1, 27 do
- if utype[u] ~= 0 and ux[u] >= WX and ux[u] <= WX + 10 and uy[u] >= WY and uy[u] <= WY + 6 then
- utimera[u] = 255
- if gettile(ux[u], uy[u]) == 204 then
- utype[u] = 5; utimera[u] = 5; ua[u] = 60; utile[u] = 140
- end
- end
- end
- print_info("emp activated!\nnearby robots are rebooting.")
- SELECT_TIMEOUT = 3
- wait_real(0.15)
- REDRAW = 1
- end
- local function use_medkit()
- if uhealth[0] == 12 then return end
- local need = 12 - uhealth[0]
- if INV_MEDKIT >= need then
- uhealth[0] = 12; INV_MEDKIT = INV_MEDKIT - need
- else
- uhealth[0] = uhealth[0] + INV_MEDKIT; INV_MEDKIT = 0
- end
- display_player_health(); display_item()
- play_sound(2)
- print_info("ahhh, much better!")
- end
- local function use_item()
- if SELECT_TIMEOUT ~= 0 then return end
- if SELI == 1 then use_bomb()
- elseif SELI == 2 then use_emp()
- elseif SELI == 3 then use_medkit()
- elseif SELI == 4 then use_magnet() end
- end
- local function cheater()
- KEYS = 7
- AMMO_PISTOL = 100; AMMO_PLASMA = 100; INV_BOMBS = 100; INV_EMP = 100
- INV_MEDKIT = 100; INV_MAGNET = 100
- SELW = 1; SELI = 1
- display_keys(); display_weapon(); display_item()
- end
- local LEVEL_MUSIC = {0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1}
- local function toggle_music()
- if MUSIC_ON then
- print_info("music off.")
- MUSIC_ON = false; pending_note = nil
- else
- print_info("music on.")
- start_music(D_SONGS[LEVEL_MUSIC[SEL_MAP + 1]])
- end
- end
- -- ------------------------------------------------------- elevator
- function elevator_panel(u)
- if ux[u] ~= ux[0] or uy[u] - 1 ~= uy[0] then return end
- print_info("[ elevator panel ] down\n[ select level ] opens")
- print_info("[ ] door")
- draw_map_window()
- local maxf = ud[u]
- for i = 0, maxf - 1 do P(0x3C6 + i, 0x31 + i) end
- local cur = uc[u]
- local function invert() local o = 0x3C5 + cur; scr[o + 1] = xor80(scr[o + 1]) end
- invert()
- local function find_xy()
- for e = 32, 47 do
- if utype[e] == 19 and uc[e] == cur then
- ux[0] = ux[e]; uy[0] = uy[e] - 1
- WX = ux[e] - 5; WY = uy[e] - 4
- draw_map_window()
- play_sound(17)
- return
- end
- end
- end
- while true do
- local k = raw_key()
- local a = action_of(k)
- if a == "left" then
- if cur ~= 1 then invert(); cur = cur - 1; invert(); find_xy() end
- elseif a == "right" then
- if cur ~= maxf then invert(); cur = cur + 1; invert(); find_xy() end
- elseif a == "down" then
- scroll_info(); scroll_info(); scroll_info()
- clear_key_buffer()
- lasttime = computer.uptime()
- return
- end
- end
- end
- -- ------------------------------------------------------- map view
- local BIGX, BIGY = 16, 0
- local BP_STATUS, BP_ROBOTS = 0, 0
- local PIXELMAP = {[0] = 0x20, 0x6C, 0x7B, 0x62, 0x7C, 0xE1, 0xFF, 0xFE, 0x7E, 0x7F, 0x61, 0xFC, 0xE2, 0xFB, 0xEC, 0xA0}
- local PMIDX = {}
- for i = 0, 15 do PMIDX[PIXELMAP[i]] = i end
- local function pixel_info()
- local off = floor(PY / 2) * 40 + floor(PX / 2)
- local cur = PMIDX[scr[off + 1]] or 0
- local m = (PX % 2 == 0) and 2 or 1
- if PY % 2 == 0 then m = m * 4 end
- return off, cur, m
- end
- local function pixel_plot()
- local off, cur, m = pixel_info()
- if floor(cur / m) % 2 == 0 then cur = cur + m end
- scr[off + 1] = PIXELMAP[cur]
- end
- local function pixel_unplot()
- local off, cur, m = pixel_info()
- if floor(cur / m) % 2 == 1 then cur = cur - m end
- scr[off + 1] = PIXELMAP[cur]
- end
- local function render_map()
- cls()
- for py = 0, 49 do
- for px = 0, 79 do
- if not T_WALK[gettile(px + BIGX, py + BIGY)] then PX = px; PY = py; pixel_plot() end
- end
- end
- end
- local function blink_robots()
- for u = 1, 27 do
- if utype[u] ~= 0 and ux[u] >= BIGX and ux[u] <= BIGX + 79 and uy[u] >= BIGY and uy[u] <= BIGY + 49 then
- PX = ux[u] - BIGX; PY = uy[u] - BIGY
- if BP_STATUS ~= 0 then pixel_unplot() else pixel_plot() end
- end
- end
- BP_STATUS = 1 - BP_STATUS
- end
- local function blink_player()
- BGTIMER2 = 20
- if BP_ROBOTS ~= 0 then blink_robots(); return end
- if ux[0] >= BIGX and ux[0] <= BIGX + 79 and uy[0] >= BIGY and uy[0] <= BIGY + 49 then
- PX = ux[0] - BIGX; PY = uy[0] - BIGY
- if BP_STATUS ~= 0 then pixel_unplot(); BP_STATUS = 0
- else pixel_plot(); BP_STATUS = 1 end
- end
- end
- local function toggle_player_robots()
- play_sound(15)
- if BP_ROBOTS == 0 then
- pixel_unplot(); BP_ROBOTS = 1
- else
- BP_STATUS = 1; blink_robots(); BP_ROBOTS = 0
- end
- end
- local function display_game_frame()
- show_screen(1)
- draw_map_window()
- display_player_health(); display_keys(); display_weapon(); display_item()
- end
- local function display_map()
- play_sound(15)
- local m0, w0 = MODE, WORLD
- MODE = "map"; WORLD = false
- render_map()
- clear_key_buffer()
- while true do
- pump()
- if BGTIMER2 == 0 then blink_player() end
- local k = getkey()
- if k then
- local a = action_of(k)
- if a == "right" then
- play_sound(17); if BIGX ~= 48 then BIGX = BIGX + 16; render_map() end
- elseif a == "left" then
- play_sound(17); if BIGX ~= 0 then BIGX = BIGX - 16; render_map() end
- elseif a == "up" then
- play_sound(17); if BIGY ~= 0 then BIGY = 0; render_map() end
- elseif a == "down" then
- play_sound(17); if BIGY ~= 14 then BIGY = 14; render_map() end
- elseif a == "map" then toggle_player_robots()
- else break end
- end
- end
- play_sound(15)
- MODE = m0; WORLD = w0
- display_game_frame()
- REDRAW = 1
- lasttime = computer.uptime()
- clear_key_buffer()
- end
- -- ------------------------------------------------------- menus & flow
- local MAP_NAMES = {"01-research lab ", "02-headquarters ", "03-the village ", "04-the islands ",
- "05-downtown ", "06-pi university", "07-more islands ", "08-robot hotel ",
- "09-forest moon ", "10-death tower ", "11-bunker ", "12-castle robot ",
- "13-rocket center", "14-pilands "}
- local FACES = {{0x3A, 0x43, 0x49, 0x55, 0x43, 0x3A, 0x49, 0x55}, {0x40, 0x40, 0x6E, 0x70, 0x40, 0x40, 0x49, 0x55},
- {0x3A, 0x4D, 0x3A, 0x3A, 0x4E, 0x3A, 0x4D, 0x4E}}
- local FACE_OFF = {0xDD, 0xDE, 0xDF, 0xE1, 0xE2, 0xE3, 0x107, 0x109}
- local DIFF_WORDS = {[0] = "easy", "normal", "hard"}
- local CONTROL_TEXT = {[0] = "keyboard ", "custom key"}
- local function display_map_name(off)
- put_text(off or 0x16A, MAP_NAMES[SEL_MAP + 1])
- end
- local function change_difficulty()
- local f = FACES[DIFF + 1]
- for i = 1, 8 do P(FACE_OFF[i], f[i]) end
- end
- local function show_controls_text()
- local s = CONTROL_TEXT[CONTROL]
- for i = 1, 10 do P(0xCC + i - 1, tc(ssub(s, i, i)) + 128) end
- end
- local function custom_keys_screen()
- if KEYS_DEFINED then return end
- show_screen(3)
- for i, a in ipairs(KORDER) do
- local k = raw_key()
- K[a] = k
- local off = 0x151 + 40 * (i - 1)
- local nm = string.upper(k)
- for j = 1, 5 do P(off + j - 1, j <= #nm and tc(ssub(string.lower(nm), j, j)) or 32) end
- flush()
- end
- KEYS_DEFINED = true
- end
- local function set_controls()
- if CONTROL == 1 then custom_keys_screen()
- else for k, v in pairs(KSTD) do K[k] = v end end
- end
- local function reset_game_state()
- KEYS = 0; AMMO_PISTOL = 0; AMMO_PLASMA = 0
- INV_BOMBS = 0; INV_EMP = 0; INV_MEDKIT = 0; INV_MAGNET = 0
- SELW = 0; SELI = 0; MAGNET_ACT = 0; PLASMA_ACT = 0; BIG_EXP_ACT = 0
- CYCLES = 0; SECONDS = 0; MINUTES = 0; HOURS = 0
- SCREEN_SHAKE = 0; BORDER = 0; FLASH_STATE = 0; SELECT_TIMEOUT = 0
- WATER_TIMER = 0
- end
- local function set_diff_level()
- if DIFF == 0 then
- for u = 48, 63 do
- if utype[u] ~= 0 and utype[u] ~= 128 then ua[u] = (ua[u] * 2) % 256 end
- end
- elseif DIFF == 2 then
- for u = 0, 27 do
- if utype[u] == 2 or utype[u] == 3 then utype[u] = 4 end
- end
- end
- end
- local function init_game()
- reset_game_state()
- show_screen(1)
- load_level(SEL_MAP)
- set_diff_level()
- animate_player()
- calc_and_redraw()
- draw_map_window()
- display_player_health(); display_keys(); display_weapon()
- utype[0] = 1
- for u = 1, 47 do utimera[u] = u; utimerb[u] = 0 end
- MODE = "game"; WORLD = true
- CLOCK_ACTIVE = 1
- print_info("welcome to pet-robots v1.1!\nby david murray 2022\nmap on tab/home key.")
- KEYTIMER = 30
- lasttime = computer.uptime()
- end
- local function pause_game()
- play_sound(15)
- CLOCK_ACTIVE = 0
- WORLD = false
- scroll_info()
- print_info("game paused.\nexit game (y/n)")
- clear_key_buffer()
- while true do
- pump()
- local k = getkey()
- if k == "ESC" or k == "p" or k == "P" or k == "n" or k == "N" then
- scroll_info(); scroll_info(); scroll_info()
- clear_key_buffer()
- CLOCK_ACTIVE = 1; WORLD = true
- play_sound(15)
- lasttime = computer.uptime()
- return false
- elseif k == "y" or k == "Y" then
- utype[0] = 0
- play_sound(15)
- WORLD = true
- return true
- end
- end
- end
- local function endgame_screen()
- MODE = "end"; WORLD = false
- show_screen(2)
- put_text(0x12E, MAP_NAMES[SEL_MAP + 1])
- decwrite(0x17D, HOURS); decwrite(0x180, MINUTES); decwrite(0x183, SECONDS)
- P(0x17D, 32); P(0x180, 58); P(0x183, 58)
- local n = 0
- for u = 1, 27 do if utype[u] ~= 0 then n = n + 1 end end
- decwrite(0x1CE, n)
- n = 0
- for u = 48, 63 do if utype[u] ~= 0 then n = n + 1 end end
- decwrite(0x21E, n)
- put_text(0x26E, DIFF_WORDS[DIFF])
- stop_song()
- if utype[0] == 0 then
- put_text(0x88, "you lose!"); play_sound(19)
- else
- put_text(0x88, "you win!"); play_sound(18)
- end
- clear_key_buffer()
- while true do
- pump()
- if getkey() then break end
- end
- end
- local function game_over(skip_anim)
- CLOCK_ACTIVE = 0
- MUSIC_ON = false; pending_note = nil
- if not skip_anim then
- if utype[0] == 0 then utile[0] = 111; KEYTIMER = 100 end
- while KEYTIMER ~= 0 do pump() end
- for i = 0, 10 do
- P(0x173 + i, GAMEOVER[1][i + 1]); P(0x19B + i, GAMEOVER[2][i + 1]); P(0x1C3 + i, GAMEOVER[3][i + 1])
- end
- KEYTIMER = 100
- while KEYTIMER ~= 0 do pump() end
- keyq = {}
- while true do
- pump()
- if getkey() then break end
- end
- end
- keyq = {}
- MUSIC_ON = false
- endgame_screen()
- end
- local function game_loop()
- while true do
- pump()
- if utype[0] ~= 1 then return game_over(false) end
- local k = getkey()
- if k and (k ~= LASTKEY or KEYTIMER == 0) then
- LASTKEY = k
- local a = action_of(k)
- if a == "up" or a == "down" or a == "left" or a == "right" then
- local d = dir_of(a)
- if walk(0, d, 1) then
- animate_player()
- calc_and_redraw()
- end
- KEYTIMER = 6
- elseif a == "fup" then fire(0); KEYTIMER = 20
- elseif a == "fdown" then fire(1); KEYTIMER = 20
- elseif a == "fleft" then fire(2); KEYTIMER = 20
- elseif a == "fright" then fire(3); KEYTIMER = 20
- elseif a == "cw" then cycle_weapon(); clear_key_buffer()
- elseif a == "ci" then cycle_item(); clear_key_buffer()
- elseif a == "move" then move_object(); clear_key_buffer()
- elseif a == "search" then search_object(); clear_key_buffer()
- elseif a == "use" then use_item(); clear_key_buffer()
- elseif a == "pause" then
- if pause_game() then return game_over(true) end
- elseif a == "cheat" then cheater()
- elseif a == "music" then toggle_music(); clear_key_buffer()
- elseif a == "map" then display_map() end
- end
- end
- end
- local function intro_screen()
- MODE = "menu"; WORLD = false
- show_screen(0)
- display_map_name()
- change_difficulty()
- show_controls_text()
- start_music(D_INTRO_MUSIC)
- local menuy = 0
- local function rev(y)
- for i = 0, 9 do local o = 0x54 + 40 * y + i; scr[o + 1] = xor80(scr[o + 1]) end
- end
- rev(menuy)
- clear_key_buffer()
- while true do
- pump()
- local k = getkey()
- if k then
- local a = action_of(k)
- if a == "down" and menuy < 3 then
- rev(menuy); menuy = menuy + 1; rev(menuy); play_sound(15)
- elseif a == "up" and menuy > 0 then
- rev(menuy); menuy = menuy - 1; rev(menuy); play_sound(15)
- elseif k == " " or k == "ENTER" then
- if k == "ENTER" then play_sound(15) end
- if menuy == 0 then
- set_controls()
- MUSIC_ON = false; stop_song(); pending_note = nil
- return true
- elseif menuy == 1 then
- play_sound(15)
- SEL_MAP = (SEL_MAP + 1) % 14
- display_map_name()
- elseif menuy == 2 then
- DIFF = (DIFF + 1) % 3
- change_difficulty()
- play_sound(15)
- else
- KEYS_DEFINED = false
- CONTROL = (CONTROL + 1) % 2
- show_controls_text()
- play_sound(15)
- end
- elseif k == "q" or k == "Q" then
- return false
- end
- end
- end
- end
- -- ------------------------------------------------------- main
- local function main()
- local mw, mh = gpu.maxResolution()
- if mw < 40 or mh < 25 then
- error("PETSCII Robots needs a screen/GPU that supports 40x25 (Tier 2 or better).", 0)
- end
- local ow, oh = gpu.getResolution()
- local ofg, obg = gpu.getForeground(), gpu.getBackground()
- local function restore()
- pcall(gpu.setResolution, ow, oh)
- pcall(gpu.setBackground, obg)
- pcall(gpu.setForeground, ofg)
- pcall(gpu.fill, 1, 1, ow, oh, " ")
- end
- gpu.setResolution(40, 25)
- curswap = nil
- setswap(false)
- gpu.fill(1, 1, 40, 25, " ")
- invalidate()
- lasttime = computer.uptime()
- local ok, err = pcall(function()
- while intro_screen() do
- init_game()
- game_loop()
- end
- end)
- restore()
- if not ok and err ~= QUIT then error(err, 0) end
- end
- main()
- end)()
Add Comment
Please, Sign In to add comment