Compare commits

...

16 Commits

Author SHA1 Message Date
eb6b4b987c Make module loading in game more sane. 2024-04-07 23:42:27 +05:00
f93dfb1fb2 Add an empty game. 2024-04-07 21:11:48 +05:00
00f930e151 Make it possible to render an empty level. 2024-04-07 21:11:31 +05:00
e62f985e73 Make processevent() look a tiny bit better. 2024-04-07 21:03:00 +05:00
08347cdc3f Fix the size of drawn segments. 2024-04-07 19:14:32 +05:00
1c342f163c . 2024-04-07 17:48:14 +05:00
3448684363 Make more variable modifiable through engine events. 2024-04-07 17:22:41 +05:00
b29fc7d929 Change README. 2024-04-07 17:17:19 +05:00
de6a555c94 Add files that I forgot to add in previous commit. 2024-04-07 16:51:56 +05:00
db3ea32c75 Change how games are initialized. 2024-04-07 16:50:37 +05:00
f3f9f31a05 Merge pull request 'docs(readme): Added info about loading levels and controls' (#9) from n3tael/p3he:main into main
Reviewed-on: bedohswe/p3he#9
2024-04-07 11:06:30 +00:00
2d28957eaa Merge branch 'branch'. 2024-04-07 16:05:20 +05:00
13aefdde9a Changes to README.md. 2024-04-07 16:04:13 +05:00
c3858c548e Change how paths to games are specified and add a new one. 2024-04-07 15:59:26 +05:00
d9ce3fd3ab Start making this thing into an actual game engine. 2024-04-07 01:38:06 +05:00
d28d1d10ed Make code worse. 2024-04-07 01:12:28 +05:00
10 changed files with 218 additions and 67 deletions

View File

@ -2,25 +2,29 @@
My school project
## Load levels
## Loading games
You can load a level by pass the path to the level as an argument, see example:
You can load a game by passing the name of the game as an argument:
```sh
python3 main.py maps/triangle.json
python3 main.py triangle
```
## Controls
Game can be loaded only if it is located in the games/ directory.
If no game is specified then "squareroom" will be loaded.
| Descripton | Key |
|------------------------------------|:---:|
| Move forwards | W |
| Move backwards | D |
| Strafe left | Q |
| Strafe right | E |
| Turn exactly 180 degrees | R |
| Toggle FPS cap | F1 |
| Create a wall | F2 |
| Toggle debug info | F3 |
| Print to terminal current position | F4 |
| Save level to `./levels` folder | F5 |
## Controls (might be overriden by games)
| Descripton | Key |
|----------------------------------------|:---:|
| Move forward | W |
| Move backwards | D |
| Strafe left | Q |
| Strafe right | E |
| Turn 180 degrees | R |
| Toggle FPS cap | F1 |
| Create a wall | F2 |
| Toggle debug info | F3 |
| Print current position to the terminal | F4 |
| Save level to `./levels` folder | F5 |

57
defaultcontrols.py Normal file
View File

@ -0,0 +1,57 @@
import pygame
from engineevents import EngineEvent
from constants import ROT, IROT, OFFSET, I
from gyro import GyroVector
from levels import open_level, save_level, make_wall
from alert import Alert
def defaultcontrols():
aoEngineEvents = list()
for event in pygame.event.get():
if event.type == pygame.QUIT:
aoEngineEvents.append(EngineEvent("bCont", lambda _: False))
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_r:
aoEngineEvents.append(EngineEvent(None, lambda args: args[0].rotate(-1), tsArguments=("gPlayer",)))
if event.key == pygame.K_F3:
aoEngineEvents.append(EngineEvent("debugInfo", lambda args: not args[0]))
if event.key == pygame.K_F1:
aoEngineEvents.append(EngineEvent("bCap", lambda args: not args[0]))
if event.key == pygame.K_F2:
def fX(args):
#0: wall_buffer
#1: gPlayer
#2: level
if (len(args[0]) == 1):
args[0].append(args[1].cPos)
make_wall(args[0], args[2])
args[0].clear()
else:
args[0].append(args[1].cPos)
aoEngineEvents.append(EngineEvent(None, fX, tsArguments=("wall_buffer", "gPlayer", "level") ))
if event.key == pygame.K_F5:
#0: level
#1: alert_append
#2: display
def fX(args):
filename = save_level(args[0])
alert = Alert(f"File saved as {filename}", args[2])
args[1](alert, 5)
aoEngineEvents.append(EngineEvent(None, fX, tsArguments=("level", "alert_append", "display") ))
if event.key == pygame.K_F4:
aoEngineEvents.append(EngineEvent(None, lambda args: print(args[0].cPos), tsArguments=("gPlayer",)))
keys = pygame.key.get_pressed()
if keys[pygame.K_d]:
aoEngineEvents.append(EngineEvent(None, lambda args: args[0].rotate( ROT), tsArguments=("gPlayer",)))
if keys[pygame.K_a]:
aoEngineEvents.append(EngineEvent(None, lambda args: args[0].rotate(IROT), tsArguments=("gPlayer",)))
if keys[pygame.K_q]:
aoEngineEvents.append(EngineEvent(None, lambda args: args[0].__iadd__(GyroVector(OFFSET * args[0].cRot*I, 1)), tsArguments=("gPlayer",)))
if keys[pygame.K_e]:
aoEngineEvents.append(EngineEvent(None, lambda args: args[0].__isub__(GyroVector(OFFSET * args[0].cRot*I, 1)), tsArguments=("gPlayer",)))
if keys[pygame.K_w]:
aoEngineEvents.append(EngineEvent(None, lambda args: args[0].__isub__(GyroVector(OFFSET * args[0].cRot , 1)), tsArguments=("gPlayer",)))
if keys[pygame.K_s]:
aoEngineEvents.append(EngineEvent(None, lambda args: args[0].__iadd__(GyroVector(OFFSET * args[0].cRot , 1)), tsArguments=("gPlayer",)))
return aoEngineEvents

View File

@ -22,7 +22,7 @@ class DrawnSegment:
self.color = color
@jit#(cache=True,parallel=True)
def draw(level,gPlayer,fov,res):
def draw(level,gPlayer,fov,res,dscale):
EMPTYDRAWN = DrawnSegment(0, BLACK)
drawn = [EMPTYDRAWN] * res
irot = exp(fov/res*I)
@ -37,7 +37,7 @@ def draw(level,gPlayer,fov,res):
continue
if cDot(rot,MobiusAdd(cInt,-gPlayer.cPos)) > 0:
continue
rDist = MobiusDist(cInt,gPlayer.cPos)*1.1
rDist = MobiusDist(cInt,gPlayer.cPos) * dscale
if j.isFinite and not cBetween(Poincare2Klein(j.pointA),Poincare2Klein(j.pointB),Poincare2Klein(cInt)):
continue
if m is None:

11
engineevents.py Normal file
View File

@ -0,0 +1,11 @@
class EngineEvent:
def __init__(self, sVariableToModify, fLambda, tsArguments=None):
self.sVariableToModify = sVariableToModify
self.fLambda = fLambda
if tsArguments is not None:
self.tsArguments = tsArguments
else:
self.tsArguments = (sVariableToModify,)
class EngineEventProcessingError(Exception):
pass

4
games/empty/__init__.py Normal file
View File

@ -0,0 +1,4 @@
from engineevents import EngineEvent
def init(aoEngineEvents):
aoEngineEvents.append(EngineEvent('level', lambda _: []))

View File

@ -0,0 +1,17 @@
import importlib.resources as impr
import sys
import json
import serialize
from engineevents import EngineEvent
def init(aoEngineEvents):
curm = sys.modules[__name__]
files = impr.files(curm)
level = None
with files.joinpath("squareroom.json").open('r') as leveldir:
level = json.loads(leveldir.read(), object_hook=serialize.object_hook)
aoEngineEvents.append(EngineEvent(
'level',
lambda _: level,
tsArguments=()
))

View File

@ -0,0 +1 @@
[{"__type__": "objSegment", "isFinite": true, "pointA": {"__type__": "complex", "real": 0.6, "imag": 0.0}, "pointB": {"__type__": "complex", "real": 0.0, "imag": 0.6}, "color": [255, 0, 0]}, {"__type__": "objSegment", "isFinite": true, "pointA": {"__type__": "complex", "real": 0.6, "imag": 0.0}, "pointB": {"__type__": "complex", "real": 0.0, "imag": -0.6}, "color": [0, 255, 0]}, {"__type__": "objSegment", "isFinite": true, "pointA": {"__type__": "complex", "real": -0.6, "imag": 0.0}, "pointB": {"__type__": "complex", "real": 0.0, "imag": -0.6}, "color": [0, 0, 255]}, {"__type__": "objSegment", "isFinite": true, "pointA": {"__type__": "complex", "real": -0.6, "imag": 0.0}, "pointB": {"__type__": "complex", "real": 0.0, "imag": 0.6}, "color": [255, 255, 0]}]

View File

@ -0,0 +1,17 @@
import importlib.resources as impr
import sys
import json
import serialize
from engineevents import EngineEvent
def init(aoEngineEvents):
curm = sys.modules[__name__]
files = impr.files(curm)
level = None
with files.joinpath("triangle.json").open('r') as leveldir:
level = json.loads(leveldir.read(), object_hook=serialize.object_hook)
aoEngineEvents.append(EngineEvent(
'level',
lambda _: level,
tsArguments=()
))

View File

@ -0,0 +1 @@
[{"__type__": "objSegment", "isFinite": true, "pointA": {"__type__": "complex", "real": 0.38529100476842437, "imag": 0.0}, "pointB": {"__type__": "complex", "real": -0.2372040619364459, "imag": 0.36629343026935063}, "color": [255, 0, 0]}, {"__type__": "objSegment", "isFinite": true, "pointA": {"__type__": "complex", "real": -0.2372040619364459, "imag": 0.36629343026935063}, "pointB": {"__type__": "complex", "real": -0.22354939429564802, "imag": -0.26820653903460523}, "color": [0, 255, 0]}, {"__type__": "objSegment", "isFinite": true, "pointA": {"__type__": "complex", "real": -0.22354939429564802, "imag": -0.26820653903460523}, "pointB": {"__type__": "complex", "real": 0.39868528559672944, "imag": 0.017873216691274733}, "color": [0, 0, 255]}]

137
main.py
View File

@ -2,20 +2,21 @@
from math import copysign, pi, acos
import sys
import os
from importlib import import_module
import pygame
import pygame.freetype
from gyro import GyroVector, Poincare2Klein
from levels import open_level, save_level, make_wall
from constants import I, WHITE, BLACK, IROT, ROT, OFFSET
from draw import draw
from draw import draw, DrawnSegment
from alert import Alert
from engineevents import EngineEvent, EngineEventProcessingError
from defaultcontrols import defaultcontrols
gOrigin = GyroVector(0,1)
SKY = (127,127,255)
GROUND = (102, 51, 0)
def renderDebugInfo(gPlayer, clock, fontSize = 18):
font = pygame.freetype.Font(None, fontSize)
@ -32,7 +33,11 @@ def nonzerocap(n):
if n <= 0: return 1
return n
def mainLoop():
sky = (127,127,255)
ground = (102, 51, 0)
bCap = True
bCont = True
gPlayer = GyroVector(0,1)
@ -42,6 +47,10 @@ def mainLoop():
debugInfo = True
wall_buffer = []
alerts = []
aoEngineEvents = []
fvControl_ao = defaultcontrols
level = []
iDistScale = 1.1
def alert_append(alert, delay):
if len(alerts) >= 1:
@ -50,58 +59,87 @@ def mainLoop():
alerts.append(alert)
alert.start_hide_thread(alerts, delay)
def processevents():
nonlocal bCap
nonlocal bCont
nonlocal gPlayer
nonlocal display
nonlocal fontSize
nonlocal debugInfo
nonlocal wall_buffer
nonlocal alerts
nonlocal alert_append
nonlocal aoEngineEvents
nonlocal level
nonlocal sky
nonlocal ground
nonlocal iDistScale
state = {
'bCap': bCap,
'bCont': bCont,
'gPlayer': gPlayer,
'display': display,
'fontSize': fontSize,
'debugInfo': debugInfo,
'wall_buffer': wall_buffer,
'alerts': alerts,
'alert_append': alert_append,
'aoEngineEvents': aoEngineEvents,
'level': level,
'sky': sky,
'ground': ground,
'iDistScale': iDistScale
}
for i in aoEngineEvents:
if i.sVariableToModify == "bCap":
bCap = i.fLambda(list(map(lambda oX: state[oX], i.tsArguments)))
elif i.sVariableToModify == "bCont":
bCont = i.fLambda(list(map(lambda oX: state[oX], i.tsArguments)))
elif i.sVariableToModify == "gPlayer":
gPlayer = i.fLambda(list(map(lambda oX: state[oX], i.tsArguments)))
elif i.sVariableToModify == "fontSize":
fontSize = i.fLambda(list(map(lambda oX: state[oX], i.tsArguments)))
elif i.sVariableToModify == "wall_buffer":
wall_buffer = i.fLambda(list(map(lambda oX: state[oX], i.tsArguments)))
elif i.sVariableToModify == "alerts":
alerts = i.fLambda(list(map(lambda oX: state[oX], i.tsArguments)))
elif i.sVariableToModify == "sky":
sky = i.fLambda(list(map(lambda oX: state[oX], i.tsArguments)))
elif i.sVariableToModify == "ground":
ground = i.fLambda(list(map(lambda oX: state[oX], i.tsArguments)))
elif i.sVariableToModify == "debugInfo":
debugInfo = i.fLambda(list(map(lambda oX: state[oX], i.tsArguments)))
elif i.sVariableToModify == "level":
level = i.fLambda(list(map(lambda oX: state[oX], i.tsArguments)))
elif i.sVariableToModify == "iDistScale":
iDistScale = i.fLambda(list(map(lambda oX: state[oX], i.tsArguments)))
elif i.sVariableToModify is None:
i.fLambda(list(map(lambda oX: state[oX], i.tsArguments)))
else:
raise EngineEventProcessingError('Unknown variable: ' + i.sVariableToModify)
aoEngineEvents.clear()
try:
level = open_level(sys.argv[1])
game = import_module('games.' + sys.argv[1])
except IndexError:
level = open_level("maps/squareroom.json")
game = import_module("games.squareroom")
game.init(aoEngineEvents)
processevents()
while bCont:
for event in pygame.event.get():
if event.type == pygame.QUIT:
bCont = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_r:
gPlayer.cRot *= -1
if event.key == pygame.K_F3:
debugInfo = not debugInfo
if event.key == pygame.K_F1:
bCap = not bCap
if event.key == pygame.K_F2:
if (len(wall_buffer) == 1):
wall_buffer.append(gPlayer.cPos)
make_wall(wall_buffer, level)
wall_buffer.clear()
else:
wall_buffer.append(gPlayer.cPos)
if event.key == pygame.K_F5:
filename = save_level(level)
alert = Alert(f"File saved as {filename}", display)
alert_append(alert, 5)
if event.key == pygame.K_F4:
print(gPlayer.cPos)
keys = pygame.key.get_pressed()
if keys[pygame.K_d]:
gPlayer.rotate(ROT)
if keys[pygame.K_a]:
gPlayer.rotate(IROT)
if keys[pygame.K_q]:
gPlayer += GyroVector(OFFSET * gPlayer.cRot*I, 1)
if keys[pygame.K_e]:
gPlayer -= GyroVector(OFFSET * gPlayer.cRot*I, 1)
if keys[pygame.K_w]:
gPlayer -= GyroVector(OFFSET * gPlayer.cRot, 1)
if keys[pygame.K_s]:
gPlayer += GyroVector(OFFSET * gPlayer.cRot, 1)
aoEngineEvents = fvControl_ao()
processevents()
display.fill(WHITE)
drawn = draw(level,gPlayer,pi/4,320)
pygame.draw.rect(display,SKY, (0,0,1280,360))
pygame.draw.rect(display,GROUND, (0,360,1280,360))
if len(level) != 0:
drawn = draw(level,gPlayer,pi/4,320,iDistScale)
else:
drawn = [DrawnSegment(0, BLACK)] * 320
pygame.draw.rect(display,sky, (0,0,1280,360))
pygame.draw.rect(display,ground, (0,360,1280,360))
n = 0
for i in drawn:
pygame.draw.rect(display,i.color, (n,int((nonzerocap(i.dist)) * 360),8,int((1 -nonzerocap(i.dist)) * 1280)))
pygame.draw.rect(display,i.color, (n,int((nonzerocap(i.dist)) * 360),4,int((1 -nonzerocap(i.dist)) * 1280)))
n += 4
gPlayer.normalize()
if debugInfo:
@ -137,5 +175,6 @@ def main():
return retstatus
if __name__ == "__main__":
sys.path.append(os.path.realpath(__file__))
if not main():
print("An error occured")