-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.lua
341 lines (302 loc) · 9.75 KB
/
main.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
-- raytracer, maybe? --
local w, h = term.getSize(2)
local texWidth, texHeight = 64, 64
local SCALE = jit and 1 or 4
w, h = math.floor(w/SCALE), math.floor(h/SCALE)
local world = {}
local textures = {}
do
local _world = {[0]={}}
local wn, rn = 0, 0
for line in io.lines(shell.dir().."/world.txt") do
if line == "" then
wn = wn + 1
_world[wn] = {}
rn = 0
else
_world[wn][rn] = {}
local cn = 0
for c in line:gmatch(".") do
_world[wn][rn][cn] = tonumber("0x"..c) or 0
cn = cn + 1
end
rn = rn + 1
end
end
for i=#_world, 0, -1 do
world[#_world - i] = _world[i]
end
end
local lastSetPal = 0
local function loadTexture(id, file)
textures[id] = {}
local tex = textures[id]
local n = 0
local handle = assert(io.open(shell.dir().."/textures/"..file))
local palConv = {}
local palLen = handle:read(1):byte()
local r = 0
local eq = 0
while r < palLen do
r = r + 4
local colID = handle:read(1):byte()
local rgb = string.unpack("<I3", handle:read(3))
for i=0, lastSetPal, 1 do
local mr, mg, mb = term.getPaletteColor(i)
mr, mg, mb = mr * 255, mg * 255, mb * 255
local r, g, b = bit32.band(rgb, 0xff0000), bit32.band(rgb, 0x00ff00),
bit32.band(rgb, 0x0000ff)
if math.floor(r/16) == math.floor(mr/16) and
math.floor(b/16) == math.floor(mb/16) and
math.floor(g/16) == math.floor(mg/16) then
palConv[colID] = i
eq = eq + 1
break
end
end
if not palConv[colID] then
lastSetPal = lastSetPal + 2
assert(lastSetPal < 256, "too many texture colors!")
term.setPaletteColor(lastSetPal - 1,
bit32.band(bit32.rshift(rgb, 1), 8355711))
term.setPaletteColor(lastSetPal, rgb)
palConv[colID] = lastSetPal
end
end
repeat
local byte = handle:read(1)
if byte then
tex[n] = palConv[string.byte(byte)]
n = n + 1
end
until not byte
handle:close()
end
-- x = forward/backward, y=left/right, z=up/down
local posX, posY, posZ = 5.3, 5, 3
local dirX, dirY, dirZ = 1, 0, 0
local planeX, planeY, planeZ = 0, 0.66, 0.4
local function castRay(x,y,dx,dy,dz,drawBuf)
local mapX = math.floor(posX)-- + 0.5)
local mapY = math.floor(posY)-- + 0.5)
local mapZ = math.floor(posZ)-- + 0.5)
local cameraX = 2 * x / math.floor(w) - 1
local cameraZ = 2 * y / math.floor(h) - 1
local rayDirX = dx + planeX * cameraX
local rayDirY = dy + planeY * cameraX
local rayDirZ = dz + planeZ * cameraZ
local sideDistX, sideDistY, sideDistZ
local deltaDistX = rayDirX == 0 and 1e30 or math.abs(1 / rayDirX)
local deltaDistY = rayDirY == 0 and 1e30 or math.abs(1 / rayDirY)
local deltaDistZ = rayDirZ == 0 and 1e30 or math.abs(1 / rayDirZ)
local perpWallDist
local stepX, stepY, stepZ
local hit = false
local side
if rayDirX < 0 then
stepX = -1
sideDistX = (posX - mapX) * deltaDistX
else
stepX = 1
sideDistX = (mapX + 1 - posX) * deltaDistX
end
if rayDirY < 0 then
stepY = -1
sideDistY = (posY - mapY) * deltaDistY
else
stepY = 1
sideDistY = (mapY + 1 - posY) * deltaDistY
end
if rayDirZ < 0 then
stepZ = -1
sideDistZ = (posZ - mapZ) * deltaDistZ
else
stepZ = 1
sideDistZ = (mapZ + 1 - posZ) * deltaDistZ
end
while not hit do
if sideDistX < sideDistY then
if sideDistX < sideDistZ then
sideDistX = sideDistX + deltaDistX
mapX = mapX + stepX
side = 0
else
sideDistZ = sideDistZ + deltaDistZ
mapZ = mapZ + stepZ
side = 2
end
elseif sideDistY < sideDistZ then
sideDistY = sideDistY + deltaDistY
mapY = mapY + stepY
side = 1
else
sideDistZ = sideDistZ + deltaDistZ
mapZ = mapZ + stepZ
side = 2
end
if not (world[mapZ] and world[mapZ][mapY] and world[mapZ][mapY][mapX]) then
hit = 0xf
elseif world[mapZ][mapY][mapX] ~= 0 then
hit = world[mapZ][mapY][mapX]
end
end
if side == 0 then perpWallDist = (sideDistX - deltaDistX)
elseif side == 1 then perpWallDist = (sideDistY - deltaDistY)
else perpWallDist = (sideDistZ - deltaDistZ) end
if drawBuf then
local color
local tex = textures[hit]
if (not tex) or #tex < texWidth*texHeight-2 then
color = hit + side
if hit == 0xf then color = 0xf end
if color > 0xf then color = color - 0xf end
else
local wallX, wallY
if side == 0 then wallX = posY + perpWallDist * rayDirY
else wallX = posX + perpWallDist * rayDirX end
wallY = posZ + perpWallDist * rayDirZ
wallX = wallX - math.floor(wallX)
local texX = math.floor(wallX * texWidth)
local texY = math.floor(wallY * texHeight)
if side == 0 and rayDirX > 0 then texX = texWidth - texX - 1 end
if side == 1 and rayDirY < 0 then texX = texWidth - texX - 1 end
if side == 2 and rayDirZ > 0 then texY = texHeight - texY - 1 end
if math.abs(texX) >= texWidth then texX = texX % texWidth end
if math.abs(texY) >= texHeight then texY = texY % texHeight end
local texPos = texX + (texY * texHeight)
if side == 2 then color = 2
else color = tex[math.floor(texPos)] or 0 end
end
for i=1, SCALE, 1 do
drawBuf[y*SCALE+i-1] = drawBuf[y*SCALE+i-1] .. string.char(color):rep(SCALE)
end
end
return perpWallDist, hit
end
local pressed = {}
local moveZ = 0
term.setGraphicsMode(2)
loadTexture(1, "bluestone.tex")
loadTexture(2, "wood.tex")
loadTexture(3, "eagle.tex")
loadTexture(4, "purplestone.tex")
loadTexture(5, "redbrick.tex")
loadTexture(6, "greystone.tex")
loadTexture(7, "colorstone.tex")
local drawBuf = {}
local oldtime, time
local lastTimerID
local ftavg = 0
while true do
for i=0, h*SCALE+1, 1 do drawBuf[i] = "" end
for y = 0, h-1, 1 do
for x = 0, w-1, 1 do
castRay(x, y, dirX, dirY, dirZ, drawBuf)
end
end
oldTime = time or os.epoch("utc")
time = os.epoch("utc")
local frametime = (time - oldTime) / 1000
moveSpeed = frametime * 7
rotSpeed = frametime * 3
ftavg = (ftavg + frametime) / (ftavg == 0 and 1 or 2)
term.drawPixels(0, 0, drawBuf)
--os.sleep(0.01)
if not lastTimerID then
lastTimerID = os.startTimer(0)
end
local sig, code, rep = os.pullEventRaw()
if sig == "terminate" then break end
if sig == "timer" and code == lastTimerID then
lastTimerID = nil
elseif sig == "key" and not rep then
pressed[code] = true
elseif sig == "key_up" then
pressed[code] = false
end
local distZ, tile = castRay(math.floor(w*0.5), math.floor(h*0.5), 0, 0, 1)
local pdistZ, _tile = castRay(math.floor(w*0.5), math.floor(h*0.5), 0, 0, -1)
local oldMoveZ = moveZ
if distZ <= 1 and moveZ < 0 and tile ~= 0xf then
if pressed[keys.space] then
moveZ = 0.13
else
moveZ = 0
end
elseif pdistZ <= 1 and moveZ > 0 and _tile ~= 0xf then
moveZ = 0
elseif distZ > 1 then
moveZ = math.max(-0.1, moveZ - 0.0075)
elseif pressed[keys.space] then
moveZ = 0.13
end
posZ = posZ - moveZ
if pressed[keys.w] then
local nposX = posX + dirX * moveSpeed
local nposY = posY + dirY * moveSpeed
local dist = math.min(
castRay(math.floor(w * 0.5), math.floor(h * 0.5), dirX, dirY, 0),
castRay(math.floor(w * 0.75), math.floor(h * 0.5), dirX, dirY, 0),
castRay(math.floor(w * 0.25), math.floor(h * 0.5), dirX, dirY, 0))
if dist > 0.8 then
posX, posY = nposX, nposY
end
end
if pressed[keys.s] then
local nposX = posX - dirX * moveSpeed
local nposY = posY - dirY * moveSpeed
local dist = math.min(
castRay(math.floor(w * 0.5), math.floor(h * 0.5), -dirX, -dirY, 0),
castRay(math.floor(w * 0.75), math.floor(h * 0.5), -dirX, -dirY, 0),
castRay(math.floor(w * 0.25), math.floor(h * 0.5), -dirX, -dirY, 0))
if dist > 0.8 then
posX, posY = nposX, nposY
end
end
--[[
if pressed[keys.a] then
local _dirX = dirX * math.cos(-90) - dirY * math.sin(-90)
local _dirY = dirX * math.sin(-90) + dirY * math.cos(-90)
local nposX = posX + _dirX * moveSpeed
local nposY = posY + _dirY * moveSpeed
local dist = math.min(
castRay(math.floor(w * 0.5), math.floor(h * 0.5), -_dirX, _dirY, 0),
castRay(math.floor(w * 0.75), math.floor(h * 0.5), -_dirX, _dirY, 0),
castRay(math.floor(w * 0.25), math.floor(h * 0.5), -_dirX, _dirY, 0))
if dist >= 0.8 then
posX, posY = nposX, nposY
end
end
if pressed[keys.d] then
local _dirX = dirX * math.cos(90) - dirY * math.sin(90)
local _dirY = dirX * math.sin(90) + dirY * math.cos(90)
local nposX = posX - _dirX * moveSpeed
local nposY = posY - _dirY * moveSpeed
local dist = math.min(
castRay(math.floor(w * 0.5), math.floor(h * 0.5), -_dirX, -_dirY, 0),
castRay(math.floor(w * 0.75), math.floor(h * 0.5), -_dirX, -_dirY, 0),
castRay(math.floor(w * 0.25), math.floor(h * 0.5), -_dirX, -_dirY, 0))
if dist >= 0.8 then
posX, posY = nposX, nposY
end
end
--]]
if pressed[keys.left] then
local oldDirX = dirX
dirX = dirX * math.cos(-rotSpeed) - dirY * math.sin(-rotSpeed)
dirY = oldDirX * math.sin(-rotSpeed) + dirY * math.cos(-rotSpeed)
local oldPlaneX = planeX
planeX = planeX * math.cos(-rotSpeed) - planeY * math.sin(-rotSpeed)
planeY = oldPlaneX * math.sin(-rotSpeed) + planeY * math.cos(-rotSpeed)
end
if pressed[keys.right] then
local oldDirX = dirX
dirX = dirX * math.cos(rotSpeed) - dirY * math.sin(rotSpeed)
dirY = oldDirX * math.sin(rotSpeed) + dirY * math.cos(rotSpeed)
local oldPlaneX = planeX
planeX = planeX * math.cos(rotSpeed) - planeY * math.sin(rotSpeed)
planeY = oldPlaneX * math.sin(rotSpeed) + planeY * math.cos(rotSpeed)
end
end
term.setGraphicsMode(0)
print(string.format("Average frametime: %.2fms\nAverage FPS: %.2f", ftavg*1000, 1/ftavg))