sudo-noclip 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
main.py
ADDED
|
@@ -0,0 +1,1433 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import cast
|
|
3
|
+
import time
|
|
4
|
+
import os
|
|
5
|
+
import random
|
|
6
|
+
import shutil
|
|
7
|
+
import math
|
|
8
|
+
import sys
|
|
9
|
+
from numba import njit, prange
|
|
10
|
+
import numpy as np
|
|
11
|
+
from functools import lru_cache
|
|
12
|
+
|
|
13
|
+
class Terminal:
|
|
14
|
+
def __init__(self):
|
|
15
|
+
self.plat = sys.platform
|
|
16
|
+
self.fd = None
|
|
17
|
+
self.orig = None
|
|
18
|
+
|
|
19
|
+
if self.plat != "win32":
|
|
20
|
+
import termios
|
|
21
|
+
import tty
|
|
22
|
+
|
|
23
|
+
self.fd = sys.stdin.fileno()
|
|
24
|
+
self.orig = termios.tcgetattr(self.fd)
|
|
25
|
+
tty.setcbreak(self.fd)
|
|
26
|
+
|
|
27
|
+
def hide(self):
|
|
28
|
+
print("\033[?25l", end="", flush=True)
|
|
29
|
+
|
|
30
|
+
def show(self):
|
|
31
|
+
print("\033[?25h", end="", flush=True)
|
|
32
|
+
self.restore()
|
|
33
|
+
|
|
34
|
+
def restore(self):
|
|
35
|
+
if self.plat != "win32" and self.fd is not None and self.orig is not None:
|
|
36
|
+
import termios
|
|
37
|
+
try:
|
|
38
|
+
termios.tcsetattr(self.fd, termios.TCSADRAIN, self.orig)
|
|
39
|
+
except Exception:
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
def get_char(self) -> str:
|
|
43
|
+
if sys.platform == "win32":
|
|
44
|
+
import msvcrt
|
|
45
|
+
if msvcrt.kbhit():
|
|
46
|
+
ch = msvcrt.getch()
|
|
47
|
+
while msvcrt.kbhit():
|
|
48
|
+
msvcrt.getch()
|
|
49
|
+
return ch.decode("utf-8", errors="ignore")
|
|
50
|
+
return ""
|
|
51
|
+
else:
|
|
52
|
+
import select
|
|
53
|
+
import termios
|
|
54
|
+
|
|
55
|
+
if self.fd is not None and select.select([self.fd], [], [], 0)[0]:
|
|
56
|
+
ch = os.read(self.fd, 1).decode("utf-8", errors="ignore")
|
|
57
|
+
termios.tcflush(self.fd, termios.TCIFLUSH)
|
|
58
|
+
return ch
|
|
59
|
+
return ""
|
|
60
|
+
|
|
61
|
+
class Matrix():
|
|
62
|
+
def __init__(self, raw: list[list[float]], angle: float | None = None):
|
|
63
|
+
if angle is not None:
|
|
64
|
+
self.raw = [[math.cos(angle), -math.sin(angle)],
|
|
65
|
+
[math.sin(angle), math.cos(angle)]]
|
|
66
|
+
else:
|
|
67
|
+
self.raw = raw
|
|
68
|
+
|
|
69
|
+
def __matmul__(self, other: Matrix | Vec): # type: ignore
|
|
70
|
+
if isinstance(other, Vec):
|
|
71
|
+
out = (self.raw[0][0] * other.raw[0] + self.raw[0][1] * other.raw[1], self.raw[1][0] * other.raw[0] + self.raw[1][1] * other.raw[1])
|
|
72
|
+
return Vec(out)
|
|
73
|
+
else:
|
|
74
|
+
out = [[self.raw[0][0] * other.raw[0][0] + self.raw[0][1] * other.raw[1][0], self.raw[0][0] * other.raw[0][1] + self.raw[0][1] * other.raw[1][1]],
|
|
75
|
+
[self.raw[1][0] * other.raw[0][0] + self.raw[1][1] * other.raw[1][0], self.raw[1][0] * other.raw[0][1] + self.raw[1][1] * other.raw[1][1]]]
|
|
76
|
+
return Matrix(out)
|
|
77
|
+
|
|
78
|
+
class Vec():
|
|
79
|
+
def __init__(self, raw: tuple[float, float]):
|
|
80
|
+
self.raw = raw
|
|
81
|
+
|
|
82
|
+
def __mul__(self, other: float):
|
|
83
|
+
return Vec((self.raw[0] * other, self.raw[1] * other))
|
|
84
|
+
|
|
85
|
+
def __add__(self, other):
|
|
86
|
+
return Vec((self.raw[0] + other.raw[0], self.raw[1] + other.raw[1]))
|
|
87
|
+
def __sub__(self, other):
|
|
88
|
+
return Vec((self.raw[0] - other.raw[0], self.raw[1] - other.raw[1]))
|
|
89
|
+
|
|
90
|
+
def col(x: str, r: float, g: float, b: float, block: bool = False) -> str:
|
|
91
|
+
return f"\033[{48 if block else 38};2;{min(255, round(r))};{min(255, round(g))};{min(255, round(b))}m{x}\033[0m"
|
|
92
|
+
|
|
93
|
+
def clear():
|
|
94
|
+
os.system("clear" if sys.platform!="win32" else "cls")
|
|
95
|
+
|
|
96
|
+
def guide(term: Terminal):
|
|
97
|
+
clear()
|
|
98
|
+
|
|
99
|
+
out = col("[GUIDE]", 255, 0, 0)
|
|
100
|
+
out+="\n\n"+col("[1]", 255, 255, 0)+"\tFear\n"+col("[2]", 255, 255, 0)+"\tStability\n"+col("[3]", 255, 255, 0)+"\tCorrupted Fragments\n"+col("[4]", 255, 255, 0)+"\tPockets\n"+col("[5]", 255, 255, 0)+"\tRifts\n"+col("[6]", 255, 255, 0)+"\tTrapped Rifts\n\n"+col("[7]", 255, 255, 0)+"\tBack\n"+"\n\nSelect an option (1-7)"
|
|
101
|
+
|
|
102
|
+
for ch in out:
|
|
103
|
+
print(ch, end="", flush=True)
|
|
104
|
+
time.sleep(0.005)
|
|
105
|
+
|
|
106
|
+
while True:
|
|
107
|
+
try:
|
|
108
|
+
opt = min(max(int(term.get_char()), 1), 7)
|
|
109
|
+
clear()
|
|
110
|
+
match opt:
|
|
111
|
+
case 1:
|
|
112
|
+
out = col("[FEAR]", 255, 0, 0)
|
|
113
|
+
out+="\n\nFear is a metric influenced by anomalies and entities.\nIt can be used to navigate safely.\nAs a rule of thumb: if your Fear spikes whilst exploring in a direction, "+col("turn back immediately.", 255, 0, 0)
|
|
114
|
+
case 2:
|
|
115
|
+
out = col("[STABILITY]", 255, 0, 0)
|
|
116
|
+
out+="\n\nThe Dimension Stability indicates the structural integrity of your current dimension.\nIf your presence drives the dimension into instability, the "+col("?????????", 255, 0, 0)+" will rip, causing a Rift to form and the dimension to corrupt.\n."
|
|
117
|
+
out+="An unstable dimension may show signs of corruption prior to tearing, repairing itself once relieved of human influence."
|
|
118
|
+
case 3:
|
|
119
|
+
out = col("[CORRUPTED FRAGMENTS]", 255, 0, 0)
|
|
120
|
+
out+="\n\nCorrupted Fragments are notes left behind by past visitors.\nDue to their time spent in the "+col("?????????", 255, 0, 0)+", these notes are often fragmented and corrupted, hence the name."
|
|
121
|
+
out+="\nFinding and recovering these yields information about the past experiences of others, which may be useful."
|
|
122
|
+
case 4:
|
|
123
|
+
out = col("[POCKETS]", 255, 0, 0)
|
|
124
|
+
out+="\n\nPockets are spatial anomalies containing items left behind by previous visitors.\nAppearing as jagged tears in the world's geometry, they mimic the colour of adjacent walls yet possess a visibly distorted and unnatural form."
|
|
125
|
+
out+="\nThey react to the physical proximity of a human, opening automatically when approached."
|
|
126
|
+
case 5:
|
|
127
|
+
out = col("[RIFTS]", 255, 0, 0)
|
|
128
|
+
out+="\n\nRifts are physical tears in the "+col("?????????", 255, 0, 0)+", caused when a Dimension becomes unstable due to human presence.\nUnlike Pockets, Rifts take on a blinding white colour and serve as doorways to other dimensions."
|
|
129
|
+
out+="\nIn the past, Rifts required decades to form due to the immense stability of the "+col("?????????", 255, 0, 0)+".\nHowever, as of late, Rifts have begun occurring alarmingly often; the cause remains unknown."
|
|
130
|
+
case 6:
|
|
131
|
+
out = col("[TRAPPED RIFTS]", 255, 0, 0)
|
|
132
|
+
out+="\n\nTrapped Rifts appear identical to Rifts when viewed from afar, but drastically change colour when approached.\nThese form when an organic anomaly is forced to tear open a dimension, typically by a higher power."
|
|
133
|
+
out+="\nAlthough rare, one would still advise caution towards Rifts in the event that they are traps."
|
|
134
|
+
case 7:
|
|
135
|
+
clear()
|
|
136
|
+
return
|
|
137
|
+
out+="\n\n\nPress "+col("[B]", 255, 255, 0)+" to return to the Main Menu."
|
|
138
|
+
for ch in out:
|
|
139
|
+
print(ch, end="", flush=True)
|
|
140
|
+
time.sleep(0.005)
|
|
141
|
+
|
|
142
|
+
while term.get_char().lower()!="b":
|
|
143
|
+
time.sleep(0.05)
|
|
144
|
+
clear()
|
|
145
|
+
return
|
|
146
|
+
except:
|
|
147
|
+
pass
|
|
148
|
+
|
|
149
|
+
clear()
|
|
150
|
+
|
|
151
|
+
def help(term: Terminal):
|
|
152
|
+
clear()
|
|
153
|
+
|
|
154
|
+
out = col("[HELP]", 255, 0, 0)
|
|
155
|
+
out+="\n\nControls:\nUse the "+col("[WASD]", 0, 255, 0)+" keys to move around,\n"+col("[O]", 0, 255, 0)+" to turn to the left,\nand "+col("[P]", 0, 255, 0)+" to turn to the right.\nTravelling through water will cause you to move slower."
|
|
156
|
+
out+="\n\nStatus Bar:\nThe Status Bar at the bottom of the display tracks vital information.\nIt displays core controls, technical info, "+col("[Fear]", 0, 255, 0)+" Level, and Dimension "+col("[Stability]", 0, 255, 0)+"."
|
|
157
|
+
out+="\n\nObjective:\nYou have noclipped into the "+col("?????????", 255, 0, 0)+" and must find a way out.\nSurvive the "+col("??????", 255, 0, 0)+", recover information, and find Rifts to navigate your way out."
|
|
158
|
+
out+="\n\n\nPress "+col("[B]", 255, 255, 0)+" to return to the Main Menu."
|
|
159
|
+
|
|
160
|
+
for ch in out:
|
|
161
|
+
print(ch, end="", flush=True)
|
|
162
|
+
time.sleep(0.005)
|
|
163
|
+
|
|
164
|
+
while term.get_char().lower()!="b":
|
|
165
|
+
time.sleep(0.05)
|
|
166
|
+
clear()
|
|
167
|
+
|
|
168
|
+
sett = {"raycast": 8, "quant": 1/128}
|
|
169
|
+
|
|
170
|
+
def settings(term: Terminal):
|
|
171
|
+
clear()
|
|
172
|
+
|
|
173
|
+
out = col("[SETTINGS]", 255, 0, 0)
|
|
174
|
+
out+="\n\nThis is a very CPU-intensive game. Adjust your settings accordingly.\n\n"+col("[1]", 255, 255, 0)+"\tRaycasting\n"+col("[2]", 255, 255, 0)+"\tColour Quantisation\n\n"+col("[3]", 255, 255, 0)+"\tBack"
|
|
175
|
+
|
|
176
|
+
for ch in out:
|
|
177
|
+
print(ch, end="", flush=True)
|
|
178
|
+
time.sleep(0.005)
|
|
179
|
+
|
|
180
|
+
while True:
|
|
181
|
+
try:
|
|
182
|
+
opt = min(max(int(term.get_char()), 1), 3)
|
|
183
|
+
clear()
|
|
184
|
+
key = "raycast"
|
|
185
|
+
values = []
|
|
186
|
+
match opt:
|
|
187
|
+
case 1:
|
|
188
|
+
out = col("[RAYCASTING]", 255, 0, 0)
|
|
189
|
+
out+="\n\nRaycasting is how this game renders scenes.\nThis setting controls how precise the raycasting is.\nA lower setting increases performance at the cost of aliasing.\n\n"
|
|
190
|
+
out+=col("[1]", 255, 255, 0)+"\t4 (Fastest)\n"+col("[2]", 255, 255, 0)+"\t8\n"+col("[3]", 255, 255, 0)+"\t16\n"+col("[4]", 255, 255, 0)+"\t32 (Highest Quality)\n\n"+col("[5]", 255, 255, 0)+"\tBack"
|
|
191
|
+
key = "raycast"
|
|
192
|
+
values = [4, 8, 16, 32]
|
|
193
|
+
case 2:
|
|
194
|
+
out = col("[COLOUR QUANTISATION]", 255, 0, 0)
|
|
195
|
+
out+="\n\nThis game uses ANSI escape codes to colour individual characters for pixels.\nANSI colours are expensive on CPU (irrelevant if you are on a GPU accelerated terminal),\nso near-identical colours are merged to increase performance.\n"
|
|
196
|
+
out+="Increasing this setting increases the total amount of colour shades\nthat can be displayed, at the cost of performance.\n\n"
|
|
197
|
+
out+=col("[1]", 255, 255, 0)+"\t32 Shades\n"+col("[2]", 255, 255, 0)+"\t64 Shades\n"+col("[3]", 255, 255, 0)+"\t128 Shades\n"+col("[4]", 255, 255, 0)+"\t256 Shades\n\n"+col("[5]", 255, 255, 0)+"\tBack"
|
|
198
|
+
key = "quant"
|
|
199
|
+
values = [1/32, 1/64, 1/128, 1/256]
|
|
200
|
+
case 3:
|
|
201
|
+
clear()
|
|
202
|
+
return
|
|
203
|
+
for ch in out:
|
|
204
|
+
print(ch, end="", flush=True)
|
|
205
|
+
time.sleep(0.005)
|
|
206
|
+
|
|
207
|
+
while True:
|
|
208
|
+
char = term.get_char()
|
|
209
|
+
match char.lower():
|
|
210
|
+
case "1":
|
|
211
|
+
sett[key] = values[0]
|
|
212
|
+
break
|
|
213
|
+
case "2":
|
|
214
|
+
sett[key] = values[1]
|
|
215
|
+
break
|
|
216
|
+
case "3":
|
|
217
|
+
sett[key] = values[2]
|
|
218
|
+
break
|
|
219
|
+
case "4":
|
|
220
|
+
sett[key] = values[3]
|
|
221
|
+
break
|
|
222
|
+
case "5":
|
|
223
|
+
break
|
|
224
|
+
clear()
|
|
225
|
+
return
|
|
226
|
+
except:
|
|
227
|
+
pass
|
|
228
|
+
|
|
229
|
+
def credits(term: Terminal, rand: list[str]):
|
|
230
|
+
clear()
|
|
231
|
+
|
|
232
|
+
while term.get_char().lower()!="b":
|
|
233
|
+
print("\033[1;1H")
|
|
234
|
+
out = "[CREDITS]"
|
|
235
|
+
out+="\n\n\n"+col("Made by:\t", 40, 0, 255)+col(random.choice(rand)+random.choice(rand)+random.choice(rand)+random.choice(rand), 135, 0, 255)+col("[STEFAN LUPASCU]", 40, 0, 255)+col(random.choice(rand)+random.choice(rand)+random.choice(rand)+random.choice(rand), 135, 0, 255)
|
|
236
|
+
out+="\n\n\nPress "+col("[B]", 255, 255, 0)+" to return to the Main Menu."
|
|
237
|
+
|
|
238
|
+
print(out, end="", flush=True)
|
|
239
|
+
|
|
240
|
+
time.sleep(0.05)
|
|
241
|
+
clear()
|
|
242
|
+
|
|
243
|
+
def menu(rand: list[str], term: Terminal):
|
|
244
|
+
clear()
|
|
245
|
+
print()
|
|
246
|
+
|
|
247
|
+
while True:
|
|
248
|
+
print("\033[1;1H", end="")
|
|
249
|
+
out = col(f"{random.choice(rand)}{random.choice(rand)}{random.choice(rand)}[SUDO NOCLIP]{random.choice(rand)}{random.choice(rand)}{random.choice(rand)}", 255, 0, 0)
|
|
250
|
+
out+="\n\n"+col("[1]", 255, 255, 0)+"\tPlay\n"+col("[2]", 255, 255, 0)+"\tGuide\n"+col("[3]", 255, 255, 0)+"\tHelp\n"+col("[4]", 255, 255, 0)+"\tSettings\n"+col("[5]", 255, 255, 0)+"\tCredits\n\n"+col("[6]", 255, 255, 0)+"\tQuit"+"\n\n\nSelect an option (1-6)"
|
|
251
|
+
print(out, end="", flush=True)
|
|
252
|
+
|
|
253
|
+
try:
|
|
254
|
+
opt = min(max(int(term.get_char()), 1), 6)
|
|
255
|
+
match opt:
|
|
256
|
+
case 1:
|
|
257
|
+
clear()
|
|
258
|
+
return
|
|
259
|
+
case 2:
|
|
260
|
+
guide(term)
|
|
261
|
+
case 3:
|
|
262
|
+
help(term)
|
|
263
|
+
case 4:
|
|
264
|
+
settings(term)
|
|
265
|
+
case 5:
|
|
266
|
+
credits(term, rand)
|
|
267
|
+
case 6:
|
|
268
|
+
term.show()
|
|
269
|
+
sys.exit()
|
|
270
|
+
except Exception:
|
|
271
|
+
pass
|
|
272
|
+
|
|
273
|
+
time.sleep(0.05)
|
|
274
|
+
|
|
275
|
+
def read_note(num: str, lines: list[str]):
|
|
276
|
+
clear()
|
|
277
|
+
print("\033[1;1H")
|
|
278
|
+
print()
|
|
279
|
+
time.sleep(2)
|
|
280
|
+
|
|
281
|
+
for ch in f"[RECOVERING CORRUPTED FRAGMENT: ANONYMOUS_USER_0-X{num}]":
|
|
282
|
+
print("\033[3m"+ch+"\033[0m", end="", flush=True)
|
|
283
|
+
if ch!=" ": time.sleep(0.2)
|
|
284
|
+
time.sleep(0.5)
|
|
285
|
+
|
|
286
|
+
print()
|
|
287
|
+
print()
|
|
288
|
+
for line in lines:
|
|
289
|
+
for ch in line:
|
|
290
|
+
print(col(ch, 255, 255, 0), end="", flush=True)
|
|
291
|
+
if ch!=" ": time.sleep(0.2)
|
|
292
|
+
time.sleep(0.5)
|
|
293
|
+
print()
|
|
294
|
+
print()
|
|
295
|
+
time.sleep(4)
|
|
296
|
+
|
|
297
|
+
def load(width: int, heigth: int, term: Terminal):
|
|
298
|
+
rand = [chr(x) for x in range(33, 127)]
|
|
299
|
+
menu(rand, term)
|
|
300
|
+
|
|
301
|
+
def skip():
|
|
302
|
+
if term.get_char()=="x":
|
|
303
|
+
print()
|
|
304
|
+
return 1/0
|
|
305
|
+
|
|
306
|
+
print()
|
|
307
|
+
print("Press x to skip intro")
|
|
308
|
+
print()
|
|
309
|
+
opt = [" ", ". ", ".. ", "..."]
|
|
310
|
+
for _ in range(4):
|
|
311
|
+
skip()
|
|
312
|
+
for suff in opt:
|
|
313
|
+
print("\rLoading"+suff, end="")
|
|
314
|
+
time.sleep(0.25)
|
|
315
|
+
|
|
316
|
+
gl = 72
|
|
317
|
+
for i in range(gl):
|
|
318
|
+
skip()
|
|
319
|
+
out = ""
|
|
320
|
+
for ch in "Loading"+opt[i%4]:
|
|
321
|
+
chan = (i/(gl-1))**3
|
|
322
|
+
if random.random()>chan:
|
|
323
|
+
out+=ch
|
|
324
|
+
else:
|
|
325
|
+
out+=col(random.choice(rand), 255, 0, 0)
|
|
326
|
+
|
|
327
|
+
print("\r\033[K"+out+"\t"+out+"\t"+out, end="", flush=True)
|
|
328
|
+
time.sleep(0.05+((1-(i/(gl-1))) * 0.15))
|
|
329
|
+
|
|
330
|
+
for _ in range(16):
|
|
331
|
+
skip()
|
|
332
|
+
out = ""
|
|
333
|
+
for _ in range(width):
|
|
334
|
+
out+=random.choice(rand)
|
|
335
|
+
print("\r\033[K"+col(out, 255, 0, 0), end="", flush=True)
|
|
336
|
+
time.sleep(0.04)
|
|
337
|
+
|
|
338
|
+
for _ in range(16):
|
|
339
|
+
skip()
|
|
340
|
+
out = ""
|
|
341
|
+
for _ in range(heigth):
|
|
342
|
+
for _ in range(width):
|
|
343
|
+
out+=random.choice(rand)
|
|
344
|
+
out+="\n"
|
|
345
|
+
out = out[:-2]
|
|
346
|
+
print("\r\033[K"+col(out, 255, 0, 0), end="", flush=True)
|
|
347
|
+
time.sleep(0.04)
|
|
348
|
+
skip()
|
|
349
|
+
|
|
350
|
+
clear()
|
|
351
|
+
time.sleep(1.5)
|
|
352
|
+
|
|
353
|
+
print()
|
|
354
|
+
for ch in "Greetings, Traveller.":
|
|
355
|
+
skip()
|
|
356
|
+
print(col(ch, 0, 180, 200), end="", flush=True)
|
|
357
|
+
if ch!=" ": time.sleep(0.2)
|
|
358
|
+
print("\n")
|
|
359
|
+
for ch in "Trust your heart":
|
|
360
|
+
skip()
|
|
361
|
+
print(col(ch, 0, 180, 200), end="", flush=True)
|
|
362
|
+
if ch!=" ": time.sleep(0.2)
|
|
363
|
+
print(col(".", 255, 0, 0), end="", flush=True)
|
|
364
|
+
print()
|
|
365
|
+
for ch in "Stay in the light":
|
|
366
|
+
skip()
|
|
367
|
+
print(col(ch, 0, 180, 200), end="", flush=True)
|
|
368
|
+
if ch!=" ": time.sleep(0.2)
|
|
369
|
+
print(col(".", 255, 0, 0), end="", flush=True)
|
|
370
|
+
print()
|
|
371
|
+
for ch in "Do not close your eyes":
|
|
372
|
+
skip()
|
|
373
|
+
print(col(ch, 0, 180, 200), end="", flush=True)
|
|
374
|
+
if ch!=" ": time.sleep(0.2)
|
|
375
|
+
print(col(".", 255, 0, 0), end="", flush=True)
|
|
376
|
+
print()
|
|
377
|
+
for ch in "Do not let your light go out":
|
|
378
|
+
skip()
|
|
379
|
+
print(col(ch, 0, 180, 200), end="", flush=True)
|
|
380
|
+
if ch!=" ": time.sleep(0.2)
|
|
381
|
+
for ch in "...":
|
|
382
|
+
print(col(ch, 255, 0, 0), end="", flush=True)
|
|
383
|
+
time.sleep(1.5)
|
|
384
|
+
print()
|
|
385
|
+
|
|
386
|
+
read_note("0000", ["My heart sank at the truth,", "That darkness was no fleeting guest.", "My eyes pleaded for an escape,", "But the light offered me none."])
|
|
387
|
+
|
|
388
|
+
clear()
|
|
389
|
+
|
|
390
|
+
def gen_maze(nx: int, ny: int, scale: int, loop: float) -> tuple[np.ndarray, tuple[float, float], np.ndarray, np.ndarray, np.ndarray]:
|
|
391
|
+
def is_valid(x: list) -> bool:
|
|
392
|
+
return x[0]>0 and x[0]*2<nx and x[1]>0 and x[1]*2 <ny
|
|
393
|
+
|
|
394
|
+
out = []
|
|
395
|
+
mask = []
|
|
396
|
+
lights = []
|
|
397
|
+
entity = [9999, 9999]
|
|
398
|
+
for _ in range(ny):
|
|
399
|
+
row = []
|
|
400
|
+
for _ in range(nx):
|
|
401
|
+
row.append(1)
|
|
402
|
+
out.append(row)
|
|
403
|
+
for _ in range(ny//2):
|
|
404
|
+
row = []
|
|
405
|
+
for _ in range(nx//2):
|
|
406
|
+
row.append(0)
|
|
407
|
+
mask.append(row)
|
|
408
|
+
|
|
409
|
+
start = [nx//4, ny//4]
|
|
410
|
+
stack = [start]
|
|
411
|
+
mask[start[1]][start[0]] = 1
|
|
412
|
+
|
|
413
|
+
dirs = [[0,1],[0,-1],[1,0],[-1,0]]
|
|
414
|
+
while stack:
|
|
415
|
+
node = stack[-1]
|
|
416
|
+
|
|
417
|
+
options = []
|
|
418
|
+
for d in dirs:
|
|
419
|
+
o = [node[0]+d[0], node[1]+d[1]]
|
|
420
|
+
if is_valid(o) and (mask[o[1]][o[0]] == 0 or (random.random()<loop)):
|
|
421
|
+
options.append(o)
|
|
422
|
+
if not options:
|
|
423
|
+
stack.pop()
|
|
424
|
+
continue
|
|
425
|
+
|
|
426
|
+
n = random.choice(options)
|
|
427
|
+
mask[n[1]][n[0]] = 1
|
|
428
|
+
|
|
429
|
+
cx, cy = node[0]*2, node[1]*2
|
|
430
|
+
tx, ty = n[0]*2, n[1]*2
|
|
431
|
+
|
|
432
|
+
fx, fy = cx+((tx-cx)//2), cy+((ty-cy)//2)
|
|
433
|
+
out[cy][cx] = 0
|
|
434
|
+
out[ty][tx] = 0
|
|
435
|
+
out[fy][fx] = 0
|
|
436
|
+
|
|
437
|
+
shift = (scale-1) /2
|
|
438
|
+
lights.append((fx*scale + shift, fy*scale + shift))
|
|
439
|
+
|
|
440
|
+
entity[0], entity[1] = (cx, cy) if cy<entity[1] else (entity[0], entity[1])
|
|
441
|
+
stack.append(n)
|
|
442
|
+
|
|
443
|
+
expanded = []
|
|
444
|
+
for row in out:
|
|
445
|
+
expanded_row = [cell for cell in row for _ in range(scale)]
|
|
446
|
+
expanded.extend([expanded_row.copy() for _ in range(scale)])
|
|
447
|
+
|
|
448
|
+
clear()
|
|
449
|
+
return (np.array(expanded), (entity[0] * scale, entity[1] * scale), np.array(out), np.array(lights), np.zeros((nx*ny*scale*scale, 3), dtype=np.int32))
|
|
450
|
+
|
|
451
|
+
@njit(fastmath=True, cache=True, boundscheck=False)
|
|
452
|
+
def pathfind(grid: np.ndarray, target: tuple[float, float], origin: tuple[float, float], stack: np.ndarray, width: int, height: int) -> tuple[int, int]:
|
|
453
|
+
out = np.full((height, width), -1, dtype=np.int32)
|
|
454
|
+
|
|
455
|
+
tx, ty = round(target[0]), round(target[1])
|
|
456
|
+
ox, oy = round(origin[0]), round(origin[1])
|
|
457
|
+
|
|
458
|
+
if (ox, oy) == (tx, ty):
|
|
459
|
+
return (ox, oy)
|
|
460
|
+
|
|
461
|
+
out[oy][ox] = 0
|
|
462
|
+
|
|
463
|
+
stack_top = 0
|
|
464
|
+
stack[0] = (ox, oy, 3)
|
|
465
|
+
|
|
466
|
+
dirs = ((0, 1), (0, -1), (1, 0), (-1, 0))
|
|
467
|
+
while len(stack)>0:
|
|
468
|
+
cx, cy, dist = stack[stack_top]
|
|
469
|
+
stack_top-=1
|
|
470
|
+
dist+=1
|
|
471
|
+
|
|
472
|
+
for (dx, dy) in dirs:
|
|
473
|
+
nx, ny = cx + dx, cy + dy
|
|
474
|
+
if out[ny][nx]==-1:
|
|
475
|
+
|
|
476
|
+
if grid[ny][nx] == 0 or (nx == tx and ny == ty):
|
|
477
|
+
out[ny][nx] = dist
|
|
478
|
+
|
|
479
|
+
stack[stack_top+1] = nx, ny, dist
|
|
480
|
+
stack_top+=1
|
|
481
|
+
|
|
482
|
+
if nx == tx and ny == ty:
|
|
483
|
+
return (cx, cy)
|
|
484
|
+
return (-1, -1)
|
|
485
|
+
|
|
486
|
+
@njit(fastmath=True, cache=True, boundscheck=False)
|
|
487
|
+
def raycast(dir: tuple[float, float], grid: np.ndarray, origin: tuple[float, float], max_dist: float, mini: int, l: int, level: int, mask: np.ndarray, scale: int) -> tuple[bool, float]:
|
|
488
|
+
steps = 1
|
|
489
|
+
while True:
|
|
490
|
+
if steps>= max_dist*mini:
|
|
491
|
+
break
|
|
492
|
+
|
|
493
|
+
sdivm = steps/mini
|
|
494
|
+
px, py = origin[0] + (dir[0] * (sdivm)), origin[1] + (dir[1] * (sdivm))
|
|
495
|
+
x, y = round(px), round(py)
|
|
496
|
+
|
|
497
|
+
if px < 0 or py < 0 or px >= l or py >= l:
|
|
498
|
+
return (False, 1.0)
|
|
499
|
+
|
|
500
|
+
if level!=4:
|
|
501
|
+
cx, cy = int(px // scale), int(py // scale)
|
|
502
|
+
sx, sy = cx*scale, cy*scale
|
|
503
|
+
if mask[cy][cx] == 0:
|
|
504
|
+
dy = (((sy+scale - py) if dir[1]>0 else (py-sy)) / abs(dir[1])) if dir[1]!=0 else 99999.0
|
|
505
|
+
dx = (((sx+scale - px) if dir[0]>0 else (px-sx)) / abs(dir[0])) if dir[0]!=0 else 99999.0
|
|
506
|
+
|
|
507
|
+
steps += round(min(dy, dx)*mini) + 1
|
|
508
|
+
continue
|
|
509
|
+
|
|
510
|
+
match grid[y][x]:
|
|
511
|
+
case 1:
|
|
512
|
+
return (True, (steps)/(max_dist*mini))
|
|
513
|
+
case 2:
|
|
514
|
+
if level==4:
|
|
515
|
+
return (True, -10.0)
|
|
516
|
+
else:
|
|
517
|
+
return (True, -9999.0)
|
|
518
|
+
case 3:
|
|
519
|
+
|
|
520
|
+
return (True, 0.0)
|
|
521
|
+
steps+=1
|
|
522
|
+
|
|
523
|
+
return (False, 1.0)
|
|
524
|
+
|
|
525
|
+
@njit(fastmath=True, cache=True, boundscheck=False, parallel=True)
|
|
526
|
+
def ray_trace(grid: np.ndarray, mask: np.ndarray, scale:int, lights: np.ndarray, light_str: float, light_max: float, kernel: int) -> np.ndarray:
|
|
527
|
+
rt = grid.astype(np.float32)
|
|
528
|
+
|
|
529
|
+
l = len(rt)
|
|
530
|
+
for y in prange(l):
|
|
531
|
+
for x in range(len(rt[0])):
|
|
532
|
+
if grid[y][x]==1:
|
|
533
|
+
rt[y][x] = 0.0
|
|
534
|
+
continue
|
|
535
|
+
|
|
536
|
+
shade = 0.0
|
|
537
|
+
for (lx, ly) in lights:
|
|
538
|
+
dx, dy = lx-x, ly-y
|
|
539
|
+
contr, hit = trace_light(dx, dy, x, y, light_max, l, grid, mask, scale)
|
|
540
|
+
shade+=(light_str/contr) if hit else 0.0
|
|
541
|
+
|
|
542
|
+
rt[y][x] = min(shade, 1.0)
|
|
543
|
+
|
|
544
|
+
print()
|
|
545
|
+
out = np.zeros((len(rt), len(rt[0])), dtype=np.float32)
|
|
546
|
+
l0, l1 = len(out), len(out[0])
|
|
547
|
+
|
|
548
|
+
for py in prange(l0):
|
|
549
|
+
for px in range(l1):
|
|
550
|
+
out[py][px] = sample_light(rt, (px, py), kernel, (l0, l1))
|
|
551
|
+
|
|
552
|
+
return out
|
|
553
|
+
|
|
554
|
+
@njit(fastmath=True, cache=True, boundscheck=False)
|
|
555
|
+
def trace_light(dx: float, dy: float, x: float, y: float, light_max: float, l: int, grid: np.ndarray, mask: np.ndarray, scale: int) -> tuple[float, bool]:
|
|
556
|
+
|
|
557
|
+
mag = math.sqrt(dx**2 + dy**2) + 1e-6
|
|
558
|
+
nx, ny = dx/mag, dy/mag
|
|
559
|
+
|
|
560
|
+
if mag<=light_max:
|
|
561
|
+
hit, _ = raycast((nx, ny), grid, (x, y), mag+0.001, 1, l, 0, mask, scale)
|
|
562
|
+
if not hit:
|
|
563
|
+
return (1 + mag**2, True)
|
|
564
|
+
|
|
565
|
+
return (0.0, False)
|
|
566
|
+
|
|
567
|
+
@njit(fastmath=True, cache=True, boundscheck=False)
|
|
568
|
+
def sample_light(lightmap: np.ndarray, pos: tuple, kernel: int, dim: tuple[int, int]) -> float:
|
|
569
|
+
l = dim[0]
|
|
570
|
+
l1 = dim[1]
|
|
571
|
+
|
|
572
|
+
shade = 0.0
|
|
573
|
+
n = 0
|
|
574
|
+
|
|
575
|
+
for dy in range(-kernel, kernel+1):
|
|
576
|
+
for dx in range(-kernel, kernel+1):
|
|
577
|
+
x, y = round(pos[0]+dx), round(pos[1]+dy)
|
|
578
|
+
if 0<=y<l and 0<=x<l1:
|
|
579
|
+
s = float(lightmap[y][x])
|
|
580
|
+
if s>0.0:
|
|
581
|
+
n+=1
|
|
582
|
+
shade+= s
|
|
583
|
+
|
|
584
|
+
return 0.0 if n==0 else shade/n
|
|
585
|
+
|
|
586
|
+
@njit(fastmath=True, cache=True, boundscheck=False)
|
|
587
|
+
def raster(
|
|
588
|
+
ct: float,
|
|
589
|
+
columns: np.ndarray,
|
|
590
|
+
floor_col: tuple[int, int, int],
|
|
591
|
+
roof_col: tuple[int, int, int],
|
|
592
|
+
half: float,
|
|
593
|
+
wall_proj: float,
|
|
594
|
+
player: tuple[float, float],
|
|
595
|
+
grid_n: int,
|
|
596
|
+
lm: np.ndarray | None,
|
|
597
|
+
gamma: float,
|
|
598
|
+
quantise: float,
|
|
599
|
+
in_water: bool,
|
|
600
|
+
wet_wall: float,
|
|
601
|
+
ao: float,
|
|
602
|
+
water_shine: float,
|
|
603
|
+
wave_shade: float,
|
|
604
|
+
wave_freq: float,
|
|
605
|
+
wave_speed: float,
|
|
606
|
+
move_ema: float,
|
|
607
|
+
global_shade: float,
|
|
608
|
+
grad: float,
|
|
609
|
+
js: bool,
|
|
610
|
+
width: int,
|
|
611
|
+
height: int,
|
|
612
|
+
render_dist: int,
|
|
613
|
+
ambient: float,
|
|
614
|
+
) -> np.ndarray:
|
|
615
|
+
out = np.zeros((height, width, 3), dtype=np.int32)
|
|
616
|
+
|
|
617
|
+
for y in range(height):
|
|
618
|
+
refl_fade = (1.0 - (y-half)/((height-1) -half)) ** 2
|
|
619
|
+
|
|
620
|
+
dy = max(abs(y-half), 0.5)
|
|
621
|
+
d = max(wall_proj * height/(2*dy), 0.25) /render_dist
|
|
622
|
+
|
|
623
|
+
base_eff = (max(0.0, 1-d)**2.0) * global_shade * grad
|
|
624
|
+
|
|
625
|
+
is_roof = y < half
|
|
626
|
+
base = roof_col if is_roof else floor_col
|
|
627
|
+
wave = 1.0
|
|
628
|
+
if (not is_roof) and in_water:
|
|
629
|
+
wave -= wave_shade * (1-refl_fade) * move_ema *(math.sin(y*wave_freq+ct*wave_speed) ** 2)
|
|
630
|
+
base_floor = (round(base[0]*wave), round(base[1]*wave), round(base[2]*wave))
|
|
631
|
+
|
|
632
|
+
for px, (hit, top, bottom, wall0, wall1, wall2, ray_dir0, ray_dir1, ray_dist) in enumerate(columns):
|
|
633
|
+
top = int(top)
|
|
634
|
+
bottom = int(bottom)
|
|
635
|
+
|
|
636
|
+
if js:
|
|
637
|
+
if wall0==0.0 and wall1==0.0 and wall2==0.0:
|
|
638
|
+
if y==height//2 and ((px==width//2 +1) or (px==(width//2 -1))):
|
|
639
|
+
out[y][px] = (255, 255, 255)
|
|
640
|
+
continue
|
|
641
|
+
|
|
642
|
+
floor_dist = max(0.0, d * ray_dist - 2.0)
|
|
643
|
+
hx = round(player[0] + ray_dir0 * floor_dist)
|
|
644
|
+
hy = round(player[1] + ray_dir1 * floor_dist)
|
|
645
|
+
|
|
646
|
+
if lm is not None and 0<=hy<grid_n and 0<=hx<grid_n:
|
|
647
|
+
light = float(lm[hy][hx] * (1-d))
|
|
648
|
+
else:
|
|
649
|
+
light = 1.0
|
|
650
|
+
|
|
651
|
+
eff = round((base_eff * max(ambient, light) *gamma) / quantise) * quantise
|
|
652
|
+
|
|
653
|
+
floor = (round(base_floor[0]*eff), round(base_floor[1]*eff), round(base_floor[2]*eff))
|
|
654
|
+
|
|
655
|
+
if (hit==1.0) and top <= y <= bottom:
|
|
656
|
+
if (not (top <= y-1 <= bottom)) or (not (top <= y+1 <= bottom)):
|
|
657
|
+
if in_water and y>half:
|
|
658
|
+
colour = (wall0*wet_wall + base_floor[0]*(1-wet_wall)*eff, wall1*wet_wall + base_floor[1]*(1-wet_wall)*eff, wall2*wet_wall + base_floor[2]*(1-wet_wall)*eff)
|
|
659
|
+
else:
|
|
660
|
+
colour = (wall0*ao, wall1*ao, wall2*ao)
|
|
661
|
+
else:
|
|
662
|
+
colour = (wall0, wall1, wall2)
|
|
663
|
+
else:
|
|
664
|
+
if ((top <= y-1 <= bottom) or (top <= y+1 <= bottom)) and not in_water:
|
|
665
|
+
colour = (base[0]*eff*ao, base[1]*eff*ao, base[2]*eff*ao)
|
|
666
|
+
else:
|
|
667
|
+
h = bottom
|
|
668
|
+
ref = round(h-(y-h))
|
|
669
|
+
if in_water and (ref>=0) and (not is_roof) and (ref<y):
|
|
670
|
+
sr, sg, sb = out[ref][px]
|
|
671
|
+
|
|
672
|
+
shine = water_shine * refl_fade
|
|
673
|
+
colour = (sr*shine + floor[0]*(1-shine), sg*shine + floor[1]*(1-shine), sb*shine + floor[2]*(1-shine))
|
|
674
|
+
else:
|
|
675
|
+
colour = floor
|
|
676
|
+
|
|
677
|
+
out[y][px][0] = min(255, round(colour[0]))
|
|
678
|
+
out[y][px][1] = min(255, round(colour[1]))
|
|
679
|
+
out[y][px][2] = min(255, round(colour[2]))
|
|
680
|
+
|
|
681
|
+
return out
|
|
682
|
+
|
|
683
|
+
@lru_cache(maxsize=2**15)
|
|
684
|
+
def get_ansi(r: int, g: int, b: int, glyph: str) -> str:
|
|
685
|
+
return f"\033[38;2;{r};{g};{b}m{glyph}"
|
|
686
|
+
|
|
687
|
+
class Theme():
|
|
688
|
+
def __init__(self, wall: tuple[int, int, int], floor: tuple[int, int, int], roof: tuple[int, int, int]):
|
|
689
|
+
self.wall = wall
|
|
690
|
+
self.floor = floor
|
|
691
|
+
self.roof = roof
|
|
692
|
+
def update(self, wall: tuple[int, int, int], floor: tuple[int, int, int], roof: tuple[int, int, int]):
|
|
693
|
+
self.wall = wall
|
|
694
|
+
self.floor = floor
|
|
695
|
+
self.roof = roof
|
|
696
|
+
|
|
697
|
+
class State():
|
|
698
|
+
def __init__(self, grid: int):
|
|
699
|
+
width, height = shutil.get_terminal_size(fallback=(80, 24))
|
|
700
|
+
self.width = width
|
|
701
|
+
self.height = height
|
|
702
|
+
|
|
703
|
+
self.term = Terminal()
|
|
704
|
+
self.term.hide()
|
|
705
|
+
|
|
706
|
+
try:
|
|
707
|
+
load(width, height, self.term)
|
|
708
|
+
except Exception:
|
|
709
|
+
pass
|
|
710
|
+
|
|
711
|
+
clear()
|
|
712
|
+
|
|
713
|
+
self.in_grid = grid
|
|
714
|
+
self.rand = [chr(x) for x in range(33, 127)]
|
|
715
|
+
|
|
716
|
+
self.scale = 8
|
|
717
|
+
self.grid, entity, self.mask, self.lights, self.stack = gen_maze(grid, grid, self.scale, 0.1)
|
|
718
|
+
self.grid_n = len(self.grid)
|
|
719
|
+
|
|
720
|
+
self.player = Vec((self.scale * (grid // 2) + 1, self.scale * (grid // 2) + 1))
|
|
721
|
+
self.view = Matrix([], angle=0)
|
|
722
|
+
self.fov = math.radians(90)
|
|
723
|
+
self.left = Matrix([], angle=math.radians(-15))
|
|
724
|
+
self.right = Matrix([], angle=math.radians(15))
|
|
725
|
+
|
|
726
|
+
self.walk_speed = 0.7
|
|
727
|
+
self.water_speed = 0.45
|
|
728
|
+
self.in_water = False
|
|
729
|
+
|
|
730
|
+
self.render_dist = 32
|
|
731
|
+
self.sub_steps = int(sett["raycast"])
|
|
732
|
+
self.global_shade = 0.8
|
|
733
|
+
self.eff_exp = 1.0
|
|
734
|
+
self.wall_proj = 5.0
|
|
735
|
+
|
|
736
|
+
self.entity = Vec(entity)
|
|
737
|
+
self.entity_step = 0.07
|
|
738
|
+
|
|
739
|
+
self.theme = Theme((228, 230, 168), (156, 147, 88), (207, 204, 162))
|
|
740
|
+
self.rand = [chr(x) for x in range(33, 127)]
|
|
741
|
+
self.quantise = float(sett["quant"])
|
|
742
|
+
|
|
743
|
+
self.horizon_fac = 0.5
|
|
744
|
+
self.wobble = 0.05
|
|
745
|
+
self.move_ema = 1.0
|
|
746
|
+
self.ema_fac = 0.8
|
|
747
|
+
self.wob_fac = 16.0
|
|
748
|
+
self.wob_ema = 0.0
|
|
749
|
+
self.wob_ema_fac = 0.6
|
|
750
|
+
|
|
751
|
+
self.ao = 0.92
|
|
752
|
+
self.wet_wall = 0.75
|
|
753
|
+
self.water_shine = 0.7
|
|
754
|
+
|
|
755
|
+
self.light_strength = 6.0
|
|
756
|
+
self.light_off = 0.01
|
|
757
|
+
self.ambient = 0.1
|
|
758
|
+
self.light_max = math.sqrt((self.light_strength/self.light_off) - 1)
|
|
759
|
+
self.blur_kernel = 2
|
|
760
|
+
|
|
761
|
+
print("Lighting the map", flush=True)
|
|
762
|
+
print()
|
|
763
|
+
print("Ray Tracing...", flush=True)
|
|
764
|
+
self.lm = ray_trace(self.grid, self.mask, self.scale, self.lights, self.light_strength, self.light_max, self.blur_kernel)
|
|
765
|
+
|
|
766
|
+
self.wave_shade = 0.85
|
|
767
|
+
self.wave_speed = 2.75
|
|
768
|
+
self.wave_freq = 3.55
|
|
769
|
+
|
|
770
|
+
self.js_every = 30.0
|
|
771
|
+
self.js_jitter = 5.0
|
|
772
|
+
self.next_js = time.time()+(random.random()*self.js_jitter*2 + (self.js_every-self.js_jitter))
|
|
773
|
+
self.since_js = -1.0
|
|
774
|
+
self.js_time = 0.2
|
|
775
|
+
self.js = False
|
|
776
|
+
|
|
777
|
+
self.glyphs = ["█"]
|
|
778
|
+
self.death_glyphs = ["▓", "░"]
|
|
779
|
+
self.mac = False
|
|
780
|
+
|
|
781
|
+
self.escape = False
|
|
782
|
+
self.level = 1
|
|
783
|
+
self.rift = Vec((-999, -999))
|
|
784
|
+
self.flash = False
|
|
785
|
+
|
|
786
|
+
self.angles = [Vec((math.cos(((i/(self.width- 1)-0.5)) * self.fov), math.sin(((i / (self.width-1)- 0.5)) * self.fov)))for i in range(self.width)]
|
|
787
|
+
|
|
788
|
+
self.fps = 60
|
|
789
|
+
self.rift_time = 220
|
|
790
|
+
self.since_rift = time.time()
|
|
791
|
+
|
|
792
|
+
self.DEV = False
|
|
793
|
+
|
|
794
|
+
self.pocket_every = 40
|
|
795
|
+
self.pocket = Vec((-999, -999))
|
|
796
|
+
self.since_pocket = time.time()
|
|
797
|
+
self.pocket_n = -1
|
|
798
|
+
self.pockets_l1 = [
|
|
799
|
+
[
|
|
800
|
+
"I swear I've been here before.",
|
|
801
|
+
"Have I been going round in circles? No, not possible.",
|
|
802
|
+
"Surely not?"
|
|
803
|
+
],
|
|
804
|
+
[
|
|
805
|
+
"I measured this corridor.",
|
|
806
|
+
"Forty paces long an hour ago. Now it's sixty.",
|
|
807
|
+
"Stop looking at the corners."
|
|
808
|
+
],
|
|
809
|
+
[
|
|
810
|
+
"Don't look at the lights.",
|
|
811
|
+
"They ain't wired to no power.",
|
|
812
|
+
"They buzz and pulse like open eyes.",
|
|
813
|
+
"When the eyes die out, run."
|
|
814
|
+
],
|
|
815
|
+
[
|
|
816
|
+
"One step, two step, three step, four.",
|
|
817
|
+
"Five step, six step, seven...?",
|
|
818
|
+
"They're not mine.",
|
|
819
|
+
"THEY'RE NOT..."
|
|
820
|
+
],
|
|
821
|
+
[
|
|
822
|
+
"If Fair is Foul,",
|
|
823
|
+
"And Foul is Fair",
|
|
824
|
+
"Why are the walls filthy,",
|
|
825
|
+
"As well as the air?"
|
|
826
|
+
],
|
|
827
|
+
]
|
|
828
|
+
self.p_nums_1 = ["1A4F", "28B3", "3F7C", "48A1", "5D2E"]
|
|
829
|
+
self.p_nums_2 = ["8142", "9C55", "A38F", "B1C6", "C409"]
|
|
830
|
+
self.p_nums_3 = ["E831", "F10A", "F5C4", "FA88", "FFFF"]
|
|
831
|
+
self.p_notes = 5
|
|
832
|
+
self.pockets_l2 = [
|
|
833
|
+
[
|
|
834
|
+
"Dutch? Is this Tahiti?"
|
|
835
|
+
],
|
|
836
|
+
[
|
|
837
|
+
"You Sir, are a Fish."
|
|
838
|
+
],
|
|
839
|
+
[
|
|
840
|
+
"Silver for monsters. Steel for humans. Water for...",
|
|
841
|
+
"What do I draw for shadows?"
|
|
842
|
+
],
|
|
843
|
+
[
|
|
844
|
+
"Geppetto, where art thou?",
|
|
845
|
+
"Is that you?",
|
|
846
|
+
"Or is that me?",
|
|
847
|
+
],
|
|
848
|
+
[
|
|
849
|
+
"GET OUT OF MY SWAMP!"
|
|
850
|
+
],
|
|
851
|
+
]
|
|
852
|
+
self.pockets_l3 = [
|
|
853
|
+
[
|
|
854
|
+
"i am sane i am sane i am sane i am sane",
|
|
855
|
+
"i am sane i am sane i am sane i am sane",
|
|
856
|
+
"i am sane i am sane i am sane"
|
|
857
|
+
],
|
|
858
|
+
[
|
|
859
|
+
"Always wait thirty minutes after swimming before eating.",
|
|
860
|
+
"What's the time Mister Wolf?"
|
|
861
|
+
],
|
|
862
|
+
[
|
|
863
|
+
"Look! Look at the funny one hiding behind the pillar!"
|
|
864
|
+
],
|
|
865
|
+
[
|
|
866
|
+
"Wake up Samurai,",
|
|
867
|
+
"We have a city to poke.",
|
|
868
|
+
"Wait, come back!"
|
|
869
|
+
],
|
|
870
|
+
[
|
|
871
|
+
"If he be Mister Hyde,", "then I shall be Mister Seek!"
|
|
872
|
+
],
|
|
873
|
+
]
|
|
874
|
+
|
|
875
|
+
def lose(self):
|
|
876
|
+
print("\033[1;1H")
|
|
877
|
+
out = ""
|
|
878
|
+
for _ in range(self.height):
|
|
879
|
+
for _ in range(self.width):
|
|
880
|
+
out+=random.choice(self.rand)
|
|
881
|
+
out+="\n"
|
|
882
|
+
out = out[:-2]
|
|
883
|
+
print(col(out, 255, 0, 0), end="", flush=True)
|
|
884
|
+
time.sleep(3)
|
|
885
|
+
|
|
886
|
+
print("\033[999;999H")
|
|
887
|
+
print("\n\n")
|
|
888
|
+
time.sleep(2)
|
|
889
|
+
for ch in "Your heart falls silent":
|
|
890
|
+
print(ch, end="", flush=True)
|
|
891
|
+
if ch!=" ": time.sleep(0.1)
|
|
892
|
+
print(col(".", 255, 0, 0), end="", flush=True)
|
|
893
|
+
time.sleep(0.25)
|
|
894
|
+
|
|
895
|
+
print()
|
|
896
|
+
for ch in "You step into the darkness":
|
|
897
|
+
print(ch, end="", flush=True)
|
|
898
|
+
if ch!=" ": time.sleep(0.1)
|
|
899
|
+
print(col(".", 255, 0, 0), end="", flush=True)
|
|
900
|
+
time.sleep(0.25)
|
|
901
|
+
|
|
902
|
+
print()
|
|
903
|
+
for ch in "You close your eyes":
|
|
904
|
+
print(ch, end="", flush=True)
|
|
905
|
+
if ch!=" ": time.sleep(0.1)
|
|
906
|
+
print(col(".", 255, 0, 0), end="", flush=True)
|
|
907
|
+
time.sleep(0.25)
|
|
908
|
+
|
|
909
|
+
print()
|
|
910
|
+
for ch in "Your light flickers":
|
|
911
|
+
print(ch, end="", flush=True)
|
|
912
|
+
if ch!=" ": time.sleep(0.1)
|
|
913
|
+
for ch in " ... ":
|
|
914
|
+
print(col(ch, 255, 0, 0), end="", flush=True)
|
|
915
|
+
time.sleep(0.1)
|
|
916
|
+
|
|
917
|
+
for ch in "then fades":
|
|
918
|
+
print(ch, end="", flush=True)
|
|
919
|
+
if ch!=" ": time.sleep(0.1)
|
|
920
|
+
print(col(".", 255, 0, 0), flush=True)
|
|
921
|
+
time.sleep(0.5)
|
|
922
|
+
|
|
923
|
+
print()
|
|
924
|
+
for ch in "Until we meet again, fellow Traveller":
|
|
925
|
+
print(ch, end="", flush=True)
|
|
926
|
+
if ch!=" ": time.sleep(0.2)
|
|
927
|
+
for ch in "...":
|
|
928
|
+
print(col(ch, 255, 0, 0), end="", flush=True)
|
|
929
|
+
time.sleep(0.5)
|
|
930
|
+
print()
|
|
931
|
+
print()
|
|
932
|
+
|
|
933
|
+
sys.tracebacklimit = 0
|
|
934
|
+
raise ConnectionError("\n[Exit Code: 404 - Connection Lost: Gateway timed out. Host is no longer responding.]")
|
|
935
|
+
|
|
936
|
+
def win(self):
|
|
937
|
+
clear()
|
|
938
|
+
print("\033[1;1H")
|
|
939
|
+
print()
|
|
940
|
+
time.sleep(5)
|
|
941
|
+
|
|
942
|
+
for ch in "Our hearts knew you would come":
|
|
943
|
+
print(col(ch, 0, 180, 200), end="", flush=True)
|
|
944
|
+
if ch!=" ": time.sleep(0.2)
|
|
945
|
+
print(col(".", 255, 0, 0), flush=True)
|
|
946
|
+
time.sleep(0.5)
|
|
947
|
+
|
|
948
|
+
for ch in "The darkness is too vast alone":
|
|
949
|
+
print(col(ch, 0, 180, 200), end="", flush=True)
|
|
950
|
+
if ch!=" ": time.sleep(0.2)
|
|
951
|
+
print(col(".", 255, 0, 0), flush=True)
|
|
952
|
+
time.sleep(0.5)
|
|
953
|
+
|
|
954
|
+
for ch in "Our eyes watched you reach for the light":
|
|
955
|
+
print(col(ch, 0, 180, 200), end="", flush=True)
|
|
956
|
+
if ch!=" ": time.sleep(0.2)
|
|
957
|
+
print(col(".", 255, 0, 0), flush=True)
|
|
958
|
+
time.sleep(0.5)
|
|
959
|
+
|
|
960
|
+
for ch in "So now we can share yours":
|
|
961
|
+
print(col(ch, 0, 180, 200), end="", flush=True)
|
|
962
|
+
if ch!=" ": time.sleep(0.2)
|
|
963
|
+
for ch in ".":
|
|
964
|
+
print(col(ch, 255, 0, 0), end="", flush=True)
|
|
965
|
+
time.sleep(0.5)
|
|
966
|
+
|
|
967
|
+
time.sleep(1)
|
|
968
|
+
print()
|
|
969
|
+
print()
|
|
970
|
+
print()
|
|
971
|
+
|
|
972
|
+
for ch in "Your heart stops its struggle.":
|
|
973
|
+
print(col(ch, 255, 0, 0), end="", flush=True)
|
|
974
|
+
if ch!=" ": time.sleep(0.2)
|
|
975
|
+
time.sleep(0.5)
|
|
976
|
+
|
|
977
|
+
print()
|
|
978
|
+
for ch in "You join the darkness.":
|
|
979
|
+
print(col(ch, 255, 0, 0), end="", flush=True)
|
|
980
|
+
if ch!=" ": time.sleep(0.2)
|
|
981
|
+
time.sleep(0.5)
|
|
982
|
+
|
|
983
|
+
print()
|
|
984
|
+
for ch in "You let your eyes rest.":
|
|
985
|
+
print(col(ch, 255, 0, 0), end="", flush=True)
|
|
986
|
+
if ch!=" ": time.sleep(0.2)
|
|
987
|
+
time.sleep(0.5)
|
|
988
|
+
|
|
989
|
+
print()
|
|
990
|
+
for ch in "Your light flickers ":
|
|
991
|
+
print(col(ch, 255, 0, 0), end="", flush=True)
|
|
992
|
+
if ch!=" ": time.sleep(0.2)
|
|
993
|
+
for ch in "...":
|
|
994
|
+
print(col(ch, 255, 0, 0), end="", flush=True)
|
|
995
|
+
time.sleep(1.0)
|
|
996
|
+
|
|
997
|
+
for ch in " then spreads":
|
|
998
|
+
print(col(ch, 255, 0, 0), end="", flush=True)
|
|
999
|
+
if ch!=" ": time.sleep(0.2)
|
|
1000
|
+
time.sleep(0.5)
|
|
1001
|
+
for ch in "...":
|
|
1002
|
+
print(col(ch, 255, 0, 0), end="", flush=True)
|
|
1003
|
+
time.sleep(1.0)
|
|
1004
|
+
time.sleep(1.0)
|
|
1005
|
+
print()
|
|
1006
|
+
print()
|
|
1007
|
+
sys.tracebacklimit = 0
|
|
1008
|
+
raise ConnectionError("\n[Exit Code: 403 - Access Denied]")
|
|
1009
|
+
|
|
1010
|
+
def resize(self):
|
|
1011
|
+
width, height = shutil.get_terminal_size(fallback=(80, 24))
|
|
1012
|
+
if width!=self.width or height!=self.height:
|
|
1013
|
+
self.height = height
|
|
1014
|
+
self.width = width
|
|
1015
|
+
|
|
1016
|
+
self.angles = [Vec((math.cos(((i/(self.width- 1)-0.5)) * self.fov), math.sin(((i / (self.width-1)- 0.5)) * self.fov)))for i in range(self.width)]
|
|
1017
|
+
|
|
1018
|
+
def is_valid(self, pos: Vec)-> bool:
|
|
1019
|
+
return self.grid[round(pos.raw[1])][round(pos.raw[0])] == 0
|
|
1020
|
+
|
|
1021
|
+
def update(self):
|
|
1022
|
+
move = False
|
|
1023
|
+
speed = self.water_speed if self.in_water else self.walk_speed
|
|
1024
|
+
ch = self.term.get_char()
|
|
1025
|
+
|
|
1026
|
+
if ch == "x":
|
|
1027
|
+
self.lose()
|
|
1028
|
+
if ch == "w":
|
|
1029
|
+
new = self.player + (self.view @ Vec((speed, 0)))
|
|
1030
|
+
if self.is_valid(new):
|
|
1031
|
+
self.player = new
|
|
1032
|
+
self.move_ema = self.move_ema * self.ema_fac + (1-self.ema_fac)
|
|
1033
|
+
move = True
|
|
1034
|
+
elif ch == "a":
|
|
1035
|
+
new = self.player + (self.view @ Vec((0, -speed)))
|
|
1036
|
+
if self.is_valid(new):
|
|
1037
|
+
self.player = new
|
|
1038
|
+
self.move_ema = self.move_ema * self.ema_fac + (1-self.ema_fac)
|
|
1039
|
+
move = True
|
|
1040
|
+
elif ch == "s":
|
|
1041
|
+
new = self.player + (self.view @ Vec((-speed, 0)))
|
|
1042
|
+
if self.is_valid(new):
|
|
1043
|
+
self.player = new
|
|
1044
|
+
self.move_ema = self.move_ema * self.ema_fac + (1-self.ema_fac)
|
|
1045
|
+
move = True
|
|
1046
|
+
elif ch == "d":
|
|
1047
|
+
new = self.player + (self.view @ Vec((0, speed)))
|
|
1048
|
+
if self.is_valid(new):
|
|
1049
|
+
self.player = new
|
|
1050
|
+
self.move_ema = self.move_ema * self.ema_fac + (1-self.ema_fac)
|
|
1051
|
+
move = True
|
|
1052
|
+
if not move:
|
|
1053
|
+
self.move_ema*=self.ema_fac
|
|
1054
|
+
if self.move_ema < 0.05:
|
|
1055
|
+
self.move_ema = 0.0
|
|
1056
|
+
if ch == "o":
|
|
1057
|
+
self.view = cast(Matrix, self.view @ self.left)
|
|
1058
|
+
elif ch == "p":
|
|
1059
|
+
self.view = cast(Matrix, self.view @ self.right)
|
|
1060
|
+
elif ch == "1":
|
|
1061
|
+
self.quantise = 1/32
|
|
1062
|
+
self.sub_steps = 4
|
|
1063
|
+
elif ch == "2":
|
|
1064
|
+
self.quantise = 1/64
|
|
1065
|
+
self.sub_steps = 8
|
|
1066
|
+
elif ch == "3":
|
|
1067
|
+
self.quantise = 1/128
|
|
1068
|
+
self.sub_steps = 16
|
|
1069
|
+
elif ch == "4":
|
|
1070
|
+
self.quantise = 1/256
|
|
1071
|
+
self.sub_steps = 32
|
|
1072
|
+
|
|
1073
|
+
elif ch == "t":
|
|
1074
|
+
if self.mac:
|
|
1075
|
+
self.mac = False
|
|
1076
|
+
#self.glyphs = ["▉", "█"]
|
|
1077
|
+
self.glyphs = ["█"]
|
|
1078
|
+
else:
|
|
1079
|
+
self.mac = True
|
|
1080
|
+
self.glyphs = ["▓", "█"]
|
|
1081
|
+
|
|
1082
|
+
line = self.player-self.pocket
|
|
1083
|
+
if (math.sqrt(line.raw[0]**2 + line.raw[1]**2))<=2 and self.level!=4:
|
|
1084
|
+
s = time.time()
|
|
1085
|
+
match self.level:
|
|
1086
|
+
case 1:
|
|
1087
|
+
note = self.pockets_l1[self.pocket_n]
|
|
1088
|
+
num = self.p_nums_1[self.pocket_n]
|
|
1089
|
+
case 2:
|
|
1090
|
+
note = self.pockets_l2[self.pocket_n]
|
|
1091
|
+
num = self.p_nums_2[self.pocket_n]
|
|
1092
|
+
case 3:
|
|
1093
|
+
note = self.pockets_l3[self.pocket_n]
|
|
1094
|
+
num = self.p_nums_3[self.pocket_n]
|
|
1095
|
+
case _:
|
|
1096
|
+
note = ["How did you even get this??"]
|
|
1097
|
+
num = "????"
|
|
1098
|
+
|
|
1099
|
+
if not self.DEV: read_note(num, note)
|
|
1100
|
+
self.since_rift += (time.time()-s)
|
|
1101
|
+
self.since_pocket += (time.time()-s)
|
|
1102
|
+
|
|
1103
|
+
self.grid[round(self.pocket.raw[1])][round(self.pocket.raw[0])] = 0
|
|
1104
|
+
self.pocket = Vec((-9999, -9999))
|
|
1105
|
+
|
|
1106
|
+
line = self.player-self.rift
|
|
1107
|
+
if (math.sqrt(line.raw[0]**2 + line.raw[1]**2))<=3 and self.level!=4:
|
|
1108
|
+
self.level+=1
|
|
1109
|
+
self.escape = False
|
|
1110
|
+
self.entity.raw = (9999, 99999)
|
|
1111
|
+
self.grid[round(self.rift.raw[1])][round(self.rift.raw[0])] = 1
|
|
1112
|
+
self.rift.raw = (9999, 9999)
|
|
1113
|
+
|
|
1114
|
+
match self.level:
|
|
1115
|
+
case 2:
|
|
1116
|
+
if not self.DEV: read_note("6E90", ["My heart grew sick of the rotting yellow.", "The darkness chased me down the halls.", "My eyes embraced the quiet blue.", "But the unforgiving light chased me still..."])
|
|
1117
|
+
|
|
1118
|
+
self.theme.update((232, 232, 230), (66, 248, 220), (230, 245, 240))
|
|
1119
|
+
self.render_dist = 128
|
|
1120
|
+
self.horizon_fac = 0.4
|
|
1121
|
+
self.wob_fac = 5.0
|
|
1122
|
+
self.wall_proj = 10.0
|
|
1123
|
+
|
|
1124
|
+
self.global_shade = 0.9
|
|
1125
|
+
self.ao = 0.95
|
|
1126
|
+
self.light_strength = 8.0
|
|
1127
|
+
self.light_max = math.sqrt((self.light_strength/self.light_off) - 1)
|
|
1128
|
+
|
|
1129
|
+
self.scale = 16
|
|
1130
|
+
self.grid, entity, self.mask, self.lights, self.stack = gen_maze(self.in_grid, self.in_grid, self.scale, 0.15)
|
|
1131
|
+
self.entity = Vec(entity)
|
|
1132
|
+
self.grid_n = len(self.grid)
|
|
1133
|
+
self.temp = [[-1 for _ in range(self.grid_n)] for _ in range(self.grid_n)]
|
|
1134
|
+
|
|
1135
|
+
print("Lighting the map", flush=True)
|
|
1136
|
+
print()
|
|
1137
|
+
print("Ray Tracing...", flush=True)
|
|
1138
|
+
self.lm = ray_trace(self.grid, self.mask, self.scale, self.lights, self.light_strength, self.light_max, self.blur_kernel)
|
|
1139
|
+
|
|
1140
|
+
self.in_water = True
|
|
1141
|
+
self.entity_step = 0.15
|
|
1142
|
+
self.player = Vec((self.scale * (self.in_grid // 2) + 1, self.scale * (self.in_grid // 2) + 1))
|
|
1143
|
+
self.pocket_n=-1
|
|
1144
|
+
|
|
1145
|
+
clear()
|
|
1146
|
+
case 3:
|
|
1147
|
+
if not self.DEV: read_note("D27E", ["My heart had drowned in the pale blue,", "Where clinical white rivalled the dark.", "My eyes had found comfort in the deep,", "Where my fading light fought on..."])
|
|
1148
|
+
|
|
1149
|
+
self.theme.update((85, 105, 100), (25, 55, 65), (60, 75, 70))
|
|
1150
|
+
self.render_dist = 64
|
|
1151
|
+
|
|
1152
|
+
self.light_strength = 3.0
|
|
1153
|
+
self.global_shade = 0.8
|
|
1154
|
+
self.blur_kernel = 1
|
|
1155
|
+
self.entity_step = 0.25
|
|
1156
|
+
self.light_max = math.sqrt((self.light_strength/self.light_off) - 1)
|
|
1157
|
+
|
|
1158
|
+
self.wall_proj = 6.0
|
|
1159
|
+
|
|
1160
|
+
self.ao = 0.7
|
|
1161
|
+
|
|
1162
|
+
self.scale = 8
|
|
1163
|
+
self.grid, entity, self.mask, self.lights, self.stack = gen_maze(self.in_grid, self.in_grid, self.scale, 0.15)
|
|
1164
|
+
self.entity = Vec(entity)
|
|
1165
|
+
self.grid_n = len(self.grid)
|
|
1166
|
+
self.temp = [[-1 for _ in range(self.grid_n)] for _ in range(self.grid_n)]
|
|
1167
|
+
|
|
1168
|
+
|
|
1169
|
+
print("Lighting the map", flush=True)
|
|
1170
|
+
print()
|
|
1171
|
+
print("Ray Tracing...", flush=True)
|
|
1172
|
+
self.lm = ray_trace(self.grid, self.mask, self.scale, self.lights, self.light_strength, self.light_max, self.blur_kernel)
|
|
1173
|
+
|
|
1174
|
+
self.player = Vec((self.scale * (self.in_grid // 2) + 1, self.scale * (self.in_grid // 2) + 1))
|
|
1175
|
+
self.pocket_n=-1
|
|
1176
|
+
case 4:
|
|
1177
|
+
clear()
|
|
1178
|
+
print("\033[1;1H")
|
|
1179
|
+
print()
|
|
1180
|
+
time.sleep(2)
|
|
1181
|
+
|
|
1182
|
+
for ch in "[Decoding Incoming Message]":
|
|
1183
|
+
print("\033[3m"+ch, end="", flush=True)
|
|
1184
|
+
time.sleep(0.2)
|
|
1185
|
+
|
|
1186
|
+
time.sleep(2.0)
|
|
1187
|
+
print()
|
|
1188
|
+
print()
|
|
1189
|
+
for ch in f"RUN":
|
|
1190
|
+
print("\033[3m\033[1m"+col(ch, 255, 0, 0), end="", flush=True)
|
|
1191
|
+
time.sleep(2.0)
|
|
1192
|
+
time.sleep(2.0)
|
|
1193
|
+
|
|
1194
|
+
self.theme.update((255, 10, 20), (70, 22, 26), (220, 20, 40))
|
|
1195
|
+
self.render_dist = 96
|
|
1196
|
+
self.horizon_fac = 0.5
|
|
1197
|
+
self.wall_proj = 5.0
|
|
1198
|
+
self.wob_fac = 16.0
|
|
1199
|
+
self.ao = 0.1
|
|
1200
|
+
self.lm = None
|
|
1201
|
+
|
|
1202
|
+
self.fov = math.radians(120)
|
|
1203
|
+
self.angles = [Vec((math.cos(((i/(self.width- 1)-0.5)) * self.fov), math.sin(((i / (self.width-1)- 0.5)) * self.fov)))for i in range(self.width)]
|
|
1204
|
+
|
|
1205
|
+
self.global_shade = 0.6
|
|
1206
|
+
self.eff_exp = 2.0
|
|
1207
|
+
|
|
1208
|
+
self.walk_speed = 0.4
|
|
1209
|
+
self.entity_step = 0.3
|
|
1210
|
+
|
|
1211
|
+
self.scale = 1
|
|
1212
|
+
self.grid = np.array([
|
|
1213
|
+
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
|
|
1214
|
+
[1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1],
|
|
1215
|
+
[1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1],
|
|
1216
|
+
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
|
|
1217
|
+
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2],
|
|
1218
|
+
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
|
|
1219
|
+
[1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1],
|
|
1220
|
+
[1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1],
|
|
1221
|
+
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
|
|
1222
|
+
])
|
|
1223
|
+
self.view = Matrix([], angle=0)
|
|
1224
|
+
|
|
1225
|
+
self.grid_n = len(self.grid[0])
|
|
1226
|
+
self.temp = [[-1 for _ in range(self.grid_n)] for _ in range(len(self.grid))]
|
|
1227
|
+
|
|
1228
|
+
self.in_water = False
|
|
1229
|
+
self.player = Vec((self.grid_n//2, len(self.grid)//2))
|
|
1230
|
+
self.entity = Vec((2, len(self.grid)//2))
|
|
1231
|
+
self.rift = Vec((self.grid_n-1, len(self.grid)//2))
|
|
1232
|
+
|
|
1233
|
+
self.flash = True
|
|
1234
|
+
|
|
1235
|
+
clear()
|
|
1236
|
+
|
|
1237
|
+
self.since_rift = time.time()
|
|
1238
|
+
self.since_pocket = time.time()
|
|
1239
|
+
|
|
1240
|
+
line = self.player-self.entity
|
|
1241
|
+
if (math.sqrt(line.raw[0]**2 + line.raw[1]**2))<=2:
|
|
1242
|
+
if self.level==4:
|
|
1243
|
+
self.win()
|
|
1244
|
+
else:
|
|
1245
|
+
clear()
|
|
1246
|
+
self.lose()
|
|
1247
|
+
|
|
1248
|
+
def render(self):
|
|
1249
|
+
horiz = ""
|
|
1250
|
+
dist = []
|
|
1251
|
+
|
|
1252
|
+
line = self.player-self.entity
|
|
1253
|
+
grad = (min((math.sqrt(line.raw[0]**self.eff_exp + line.raw[1]**2))/(self.grid_n/2), 1))**2.0
|
|
1254
|
+
|
|
1255
|
+
gamma = 1.0
|
|
1256
|
+
|
|
1257
|
+
nx, ny = pathfind(self.grid, self.entity.raw, self.player.raw, self.stack, self.grid.shape[1], self.grid.shape[0])
|
|
1258
|
+
if nx >= 0 and ny >= 0:
|
|
1259
|
+
nv = Vec((nx, ny))
|
|
1260
|
+
self.entity -= ((self.entity - nv) * self.entity_step)
|
|
1261
|
+
|
|
1262
|
+
s = time.perf_counter()
|
|
1263
|
+
for px in range(self.width):
|
|
1264
|
+
rot = self.view @ self.angles[px]
|
|
1265
|
+
res = raycast(cast(Vec, rot).raw, self.grid, self.player.raw, self.render_dist, self.sub_steps, self.grid_n, self.level, self.mask, self.scale)
|
|
1266
|
+
horiz += "1" if res[0] else "0"
|
|
1267
|
+
dist.append(res[1])
|
|
1268
|
+
rend = time.perf_counter()-s
|
|
1269
|
+
|
|
1270
|
+
columns = np.zeros((self.width, 9))
|
|
1271
|
+
|
|
1272
|
+
half = self.horizon_fac
|
|
1273
|
+
self.wob_ema = self.wob_ema * self.wob_ema_fac + self.wobble * math.sin((time.time() * self.wob_fac) % (2 * math.pi)) * self.move_ema * (1-self.wob_ema_fac)
|
|
1274
|
+
half+=self.wob_ema
|
|
1275
|
+
|
|
1276
|
+
half*=self.height
|
|
1277
|
+
|
|
1278
|
+
s = time.perf_counter()
|
|
1279
|
+
|
|
1280
|
+
if self.flash:
|
|
1281
|
+
gamma = 2.0
|
|
1282
|
+
self.flash = False
|
|
1283
|
+
|
|
1284
|
+
if (time.time()-self.since_pocket) > self.pocket_every and (self.pocket_n + 1 < self.p_notes) and self.level!=4:
|
|
1285
|
+
if horiz[self.width//2] == "1":
|
|
1286
|
+
target = self.player + (cast(Vec, (self.view @ Vec((1, 0)))) * (max(dist[self.width//2], 1/self.render_dist)*self.render_dist))
|
|
1287
|
+
else:
|
|
1288
|
+
target = self.player + (cast(Vec, self.view @ Vec((1, 0))) * (0.2*self.render_dist))
|
|
1289
|
+
|
|
1290
|
+
self.grid[round(target.raw[1])][round(target.raw[0])] = 3
|
|
1291
|
+
self.mask[int(target.raw[1]//self.scale)][int(target.raw[0]//self.scale)] = 1
|
|
1292
|
+
|
|
1293
|
+
if self.pocket.raw[0]>0 and self.pocket.raw[1]>0:
|
|
1294
|
+
self.grid[round(self.pocket.raw[1])][round(self.pocket.raw[0])] = 0
|
|
1295
|
+
|
|
1296
|
+
self.pocket = target
|
|
1297
|
+
self.pocket_n+=1
|
|
1298
|
+
self.since_pocket = time.time()
|
|
1299
|
+
print("\a", end="", flush=True)
|
|
1300
|
+
|
|
1301
|
+
if (time.time()-(self.since_rift))>=self.rift_time and not self.escape and not (self.level==4):
|
|
1302
|
+
if horiz[self.width//2] == "1":
|
|
1303
|
+
target = self.player + ((self.view @ Vec((1, 0))) * (dist[self.width//2]*self.render_dist))
|
|
1304
|
+
|
|
1305
|
+
if self.grid[round(target.raw[1])][round(target.raw[0])] == 1:
|
|
1306
|
+
self.grid[round(target.raw[1])][round(target.raw[0])] = 2
|
|
1307
|
+
self.mask[int(target.raw[1]//self.scale)][int(target.raw[0]//self.scale)] = 1
|
|
1308
|
+
self.rift = target
|
|
1309
|
+
|
|
1310
|
+
self.entity_step/=1.5
|
|
1311
|
+
self.escape = True
|
|
1312
|
+
|
|
1313
|
+
gamma = 10.0
|
|
1314
|
+
self.flash = True
|
|
1315
|
+
print("\a", end="", flush=True)
|
|
1316
|
+
|
|
1317
|
+
for px, (hit, p_dist) in enumerate(zip(horiz, dist)):
|
|
1318
|
+
if hit == "1" and p_dist >= 0:
|
|
1319
|
+
p_dist *= self.angles[px].raw[0]
|
|
1320
|
+
|
|
1321
|
+
wh = int((self.wall_proj * self.height) / max(p_dist * self.render_dist, 0.25))
|
|
1322
|
+
top = half - (wh // 2)
|
|
1323
|
+
bottom = half + (wh // 2)
|
|
1324
|
+
|
|
1325
|
+
ray_dir = cast(Vec, self.view @ self.angles[px])
|
|
1326
|
+
ray_depth = max(0.0, (dist[px] * self.render_dist) - 1.0)
|
|
1327
|
+
hit_pos = self.player + ray_dir * ray_depth
|
|
1328
|
+
|
|
1329
|
+
x, y = round(hit_pos.raw[0]), round(hit_pos.raw[1])
|
|
1330
|
+
if self.lm is not None and 0<=y<self.grid_n and 0<=x<self.grid_n:
|
|
1331
|
+
light = self.lm[y][x] * (1-p_dist)
|
|
1332
|
+
else:
|
|
1333
|
+
light = 1.0
|
|
1334
|
+
|
|
1335
|
+
eff = (max(0.0, 1-p_dist)**2.0) * self.global_shade * grad * max(self.ambient, light)
|
|
1336
|
+
eff = round(eff / self.quantise)*self.quantise
|
|
1337
|
+
|
|
1338
|
+
wall = (min(round(self.theme.wall[0] * eff), 255), min(round(self.theme.wall[1] * eff), 255), min(round(self.theme.wall[2] * eff), 255))
|
|
1339
|
+
|
|
1340
|
+
ray_dist = self.render_dist/max(self.angles[px].raw[0], 1e-5)
|
|
1341
|
+
columns[px] = ((float(hit == "1"), top, bottom, wall[0], wall[1], wall[2], ray_dir.raw[0], ray_dir.raw[1], ray_dist))
|
|
1342
|
+
|
|
1343
|
+
if self.since_js<0 and not self.js:
|
|
1344
|
+
center = self.width // 2
|
|
1345
|
+
if time.time() >= self.next_js and self.level != 4:
|
|
1346
|
+
if horiz[center - 1] == "0" and horiz[center + 1] == "0":
|
|
1347
|
+
self.since_js = time.time()
|
|
1348
|
+
self.js = True
|
|
1349
|
+
if self.js:
|
|
1350
|
+
if (time.time()-self.since_js)>(self.js_time + 0.3*(1-grad)):
|
|
1351
|
+
self.js = False
|
|
1352
|
+
self.since_js = -1.0
|
|
1353
|
+
self.next_js = time.time()+random.random()*self.js_jitter*2 + (self.js_every-self.js_jitter)
|
|
1354
|
+
|
|
1355
|
+
pool = self.glyphs if grad>=0.25 else self.death_glyphs
|
|
1356
|
+
get_glpyh = (lambda _: random.choice(pool)) if len(self.glyphs)>1 else (lambda _: self.glyphs[0])
|
|
1357
|
+
|
|
1358
|
+
inst = max(0.0, (1-(time.time()-self.since_rift)/self.rift_time), 0.0)
|
|
1359
|
+
|
|
1360
|
+
ct = time.time()
|
|
1361
|
+
|
|
1362
|
+
out = raster(ct, columns, self.theme.floor, self.theme.roof, half, self.wall_proj, self.player.raw, self.grid_n, self.lm, gamma, self.quantise, self.in_water, self.wet_wall, self.ao, self.water_shine, self.wave_shade, self.wave_freq, self.wave_speed, self.move_ema, self.global_shade, grad, self.js, self.width, self.height, self.render_dist, self.ambient)
|
|
1363
|
+
|
|
1364
|
+
tb = 0.0
|
|
1365
|
+
|
|
1366
|
+
screen = []
|
|
1367
|
+
flat = out.reshape(-1, 3)
|
|
1368
|
+
prev = np.empty_like(flat)
|
|
1369
|
+
|
|
1370
|
+
prev[0] = [-1,-1,-1]
|
|
1371
|
+
prev[1:] = flat[:-1]
|
|
1372
|
+
change = np.any(flat != prev, axis=1)
|
|
1373
|
+
change[0] = True
|
|
1374
|
+
|
|
1375
|
+
flat = flat.reshape(self.height, self.width, 3)
|
|
1376
|
+
change = change.reshape(self.height, self.width)
|
|
1377
|
+
|
|
1378
|
+
for iy in range(self.height):
|
|
1379
|
+
row = flat[iy]
|
|
1380
|
+
chg_row = change[iy]
|
|
1381
|
+
|
|
1382
|
+
screen.append("".join(get_ansi(r, g, b, get_glpyh(0)) if chg else get_glpyh(0) for (r, g, b), chg in zip(row, chg_row)))
|
|
1383
|
+
|
|
1384
|
+
buffer = "\n".join(screen)
|
|
1385
|
+
|
|
1386
|
+
shade = time.perf_counter()-s
|
|
1387
|
+
|
|
1388
|
+
print("\033[H" + col(buffer, 0, 0, 0, block=True), end="", flush=True)
|
|
1389
|
+
|
|
1390
|
+
f_col = f"Fear: {round((1-grad)*100)}%"
|
|
1391
|
+
s_col = f"Stability: {round(inst*100)- (0 if self.level!=4 else 150)}%"
|
|
1392
|
+
msg = f"WASD to walk, O/P to turn, 1/2/3/4 to select graphics quality, T to toggle Pixel Preset (current: {'Block Grain' if self.mac else 'No Grain'}), and X to accept defeat | FPS: {self.fps} | {rend*1000.0:.2f}ms Render time | {shade*1000.0:.2f}ms Shade time | {f_col} | {s_col} | "
|
|
1393
|
+
print(f"\r\033[{self.height-1};1H{''.join(['_' for _ in range(len(msg)-1)])} ", end="", flush=True)
|
|
1394
|
+
|
|
1395
|
+
msg = msg.replace(f_col, col(f"Fear: {round((1-grad)*100)}%", 255 if (1-grad>=0.3) else 0, 255 if (1-grad<0.6) else 0, 0))
|
|
1396
|
+
msg = msg.replace(s_col, col(f"Stability: {round(inst*100)- (0 if self.level!=4 else 100)}%", 255 if (inst<0.7 or self.level==4) else 0, 0 if (inst<0.3 or self.level==4) else 255, 0))
|
|
1397
|
+
print(f"\r\033[{self.height};1H{msg}", end="", flush=True)
|
|
1398
|
+
|
|
1399
|
+
def main():
|
|
1400
|
+
state = State(24)
|
|
1401
|
+
target = 1 / 30.0
|
|
1402
|
+
since_resize = 0
|
|
1403
|
+
since30 = time.perf_counter()
|
|
1404
|
+
frames = 0
|
|
1405
|
+
try:
|
|
1406
|
+
state.term.hide()
|
|
1407
|
+
while True:
|
|
1408
|
+
if since_resize >= 100:
|
|
1409
|
+
since_resize = 0
|
|
1410
|
+
state.resize()
|
|
1411
|
+
else:
|
|
1412
|
+
since_resize+=1
|
|
1413
|
+
|
|
1414
|
+
s = time.perf_counter()
|
|
1415
|
+
state.update()
|
|
1416
|
+
state.render()
|
|
1417
|
+
|
|
1418
|
+
elap = time.perf_counter() - s
|
|
1419
|
+
time.sleep(max(0.0, target - elap))
|
|
1420
|
+
|
|
1421
|
+
frames+=1
|
|
1422
|
+
if frames>=30:
|
|
1423
|
+
state.fps = round(30.0/(time.perf_counter()-since30))
|
|
1424
|
+
frames = 0
|
|
1425
|
+
since30 = time.perf_counter()
|
|
1426
|
+
|
|
1427
|
+
finally:
|
|
1428
|
+
state.term.show()
|
|
1429
|
+
|
|
1430
|
+
if __name__ == "__main__":
|
|
1431
|
+
main()
|
|
1432
|
+
|
|
1433
|
+
# you sir, are a fish
|