392 lines
11 KiB
Python
392 lines
11 KiB
Python
|
import tkinter as tk
|
||
|
import string
|
||
|
import random
|
||
|
import threading
|
||
|
import time
|
||
|
import wave
|
||
|
import os
|
||
|
import math
|
||
|
|
||
|
class stdrend:
|
||
|
def __init__(self, size, cam):
|
||
|
self._size = size
|
||
|
self._grid = {}
|
||
|
self._win = tk.Tk()
|
||
|
|
||
|
tkeys = list(string.ascii_letters)
|
||
|
self._keys = {}
|
||
|
for i in tkeys:
|
||
|
self._keys[i] = False
|
||
|
self._win.bind("<KeyPress>", self.keypupd)
|
||
|
self._win.bind("<KeyRelease>", self.keydupd)
|
||
|
|
||
|
for y in range(size[1]):
|
||
|
for x in range(size[0]):
|
||
|
temp = tk.Label(text=" ")
|
||
|
temp.grid(row=y, column=x)
|
||
|
self._win.update()
|
||
|
self._grid[f"{x}:{y}"] = temp
|
||
|
|
||
|
def keypupd(self, event):
|
||
|
event = event.char
|
||
|
if event in self._keys:
|
||
|
self._keys[event] = True
|
||
|
|
||
|
def keydupd(self, event):
|
||
|
event = event.char
|
||
|
if event in self._keys:
|
||
|
self._keys[event] = False
|
||
|
|
||
|
def getkeys(self):
|
||
|
return self._keys
|
||
|
|
||
|
def coltohex(self, target):
|
||
|
colors = []
|
||
|
target = [target.r, target.g, target.b]
|
||
|
for i in target:
|
||
|
colors.append(("0"*(2-len(hex(i)[2:])))+hex(i)[2:])
|
||
|
out = ""
|
||
|
for i in colors:
|
||
|
out = out + i
|
||
|
return "#"+out
|
||
|
|
||
|
def update(self):
|
||
|
self._win.update()
|
||
|
|
||
|
def pix(self, x, y, text, bcolor, fcolor):
|
||
|
if f"{x}:{y}" in self._grid:
|
||
|
self._grid[f"{x}:{y}"].config(text=text, bg=self.coltohex(bcolor), fg=self.coltohex(fcolor))
|
||
|
|
||
|
class color3:
|
||
|
def __init__(self, r=0, g=0, b=0):
|
||
|
self.r = r
|
||
|
self.g = g
|
||
|
self.b = b
|
||
|
self._type = "color3"
|
||
|
|
||
|
def __add__(self, v):
|
||
|
temp = color3(self.r+v.r, self.g+v.g, self.b+v.b)
|
||
|
temp.r = temp.r%255
|
||
|
temp.g = temp.g%255
|
||
|
temp.b = temp.b%255
|
||
|
return temp
|
||
|
|
||
|
def __sub__(self, v):
|
||
|
temp = color3(self.r-v.r, self.g-v.g, self.b-v.b)
|
||
|
temp.r = temp.r%255
|
||
|
temp.g = temp.g%255
|
||
|
temp.b = temp.b%255
|
||
|
return temp
|
||
|
|
||
|
def __mul__(self, v):
|
||
|
temp = color3(self.r*v.r, self.g*v.g, self.b*v.b)
|
||
|
temp.r = temp.r%255
|
||
|
temp.g = temp.g%255
|
||
|
temp.b = temp.b%255
|
||
|
return temp
|
||
|
|
||
|
def __iadd__(self, v):
|
||
|
temp = color3(self.r+v.r, self.g+v.g, self.b+v.b)
|
||
|
temp.r = temp.r%255
|
||
|
temp.g = temp.g%255
|
||
|
temp.b = temp.b%255
|
||
|
return temp
|
||
|
|
||
|
def __isub__(self, v):
|
||
|
temp = color3(self.r-v.r, self.g-v.g, self.b-v.b)
|
||
|
temp.r = temp.r%255
|
||
|
temp.g = temp.g%255
|
||
|
temp.b = temp.b%255
|
||
|
return temp
|
||
|
|
||
|
def __imul__(self, v):
|
||
|
temp = color3(self.r*v.r, self.g*v.g, self.b*v.b)
|
||
|
temp.r = temp.r%255
|
||
|
temp.g = temp.g%255
|
||
|
temp.b = temp.b%255
|
||
|
return temp
|
||
|
|
||
|
class vector2:
|
||
|
def __init__(self, x=0, y=0):
|
||
|
self.x = x
|
||
|
self.y = y
|
||
|
self._type = "vector2"
|
||
|
|
||
|
def _magnitude(self):
|
||
|
return abs(self.x+self.y)
|
||
|
|
||
|
def __add__(self, v):
|
||
|
return vector2(self.x+v.x, self.y+v.y)
|
||
|
|
||
|
def __sub__(self, v):
|
||
|
return vector2(self.x-v.x, self.y-v.y)
|
||
|
|
||
|
def __mul__(self, v):
|
||
|
return vector2(self.x*v.x, self.y*v.y)
|
||
|
|
||
|
def __iadd__(self, v):
|
||
|
return vector2(self.x+v.x, self.y+v.y)
|
||
|
|
||
|
def __isub__(self, v):
|
||
|
return vector2(self.x-v.x, self.y-v.y)
|
||
|
|
||
|
def __imul__(self, v):
|
||
|
return vector2(self.x*v.x, self.y*v.y)
|
||
|
|
||
|
class NULL:
|
||
|
def __init__(self):
|
||
|
return None
|
||
|
|
||
|
class enum:
|
||
|
def __init__(self, sel):
|
||
|
self._sel = dict(sel)
|
||
|
for i in self._sel:
|
||
|
setattr(self, i, self._sel[i])
|
||
|
|
||
|
def getposssel(self):
|
||
|
return list(self._sel.keys())
|
||
|
|
||
|
def loadsound(path):
|
||
|
with wave.open(path) as fd:
|
||
|
frames = fd.readframes(1000000000)
|
||
|
return frames
|
||
|
|
||
|
cammode = enum({"editable": 0, "follow": 1})
|
||
|
|
||
|
class event:
|
||
|
def __init__(self):
|
||
|
self._attached = []
|
||
|
|
||
|
def execute(self):
|
||
|
threads = []
|
||
|
for i in self._attached:
|
||
|
temp = threading.Thread(target=i)
|
||
|
temp.start()
|
||
|
threads.append(temp)
|
||
|
return threads
|
||
|
|
||
|
def attach(self, target):
|
||
|
self._attached.append(target)
|
||
|
|
||
|
class obj:
|
||
|
def __init__(self):
|
||
|
self.position = vector2()
|
||
|
self.char = " "
|
||
|
self.ID = 0
|
||
|
self.gravity = 0
|
||
|
self.acceleration = vector2()
|
||
|
self.velocity = vector2()
|
||
|
self.friction = 0
|
||
|
self.collide = True
|
||
|
self.anchored = False
|
||
|
self.bcolor = color3(255, 255, 255)
|
||
|
self.fcolor = color3()
|
||
|
self._touching = event()
|
||
|
|
||
|
class model:
|
||
|
def __init__(self, objects):
|
||
|
self._objects = objects
|
||
|
self.ID = 0
|
||
|
|
||
|
class camera(obj):
|
||
|
def __init__(self):
|
||
|
super().__init__()
|
||
|
self.mode = cammode.editable
|
||
|
self.subject = None
|
||
|
self.collide = False
|
||
|
self.touch = False
|
||
|
self.char = " "
|
||
|
|
||
|
def update(self):
|
||
|
if self.mode == cammode.follow and self.subject:
|
||
|
self.position = self.subject.position
|
||
|
|
||
|
class seqobj:
|
||
|
def __init__(self, objects):
|
||
|
self._objects = objects
|
||
|
|
||
|
def moveby(self, pos):
|
||
|
for i in self._objects:
|
||
|
i.position += pos
|
||
|
|
||
|
class game:
|
||
|
def __init__(self, size=[10, 10], renderer=stdrend, sounddir=""):
|
||
|
if renderer == None: raise TypeError("Renderer class needed!")
|
||
|
self.sounds = {}
|
||
|
self.currentsounds = []
|
||
|
for i in os.listdir(sounddir):
|
||
|
if not "." in i: continue
|
||
|
if i.split(".")[1] != "wav": continue
|
||
|
self.sounds[i.split(".")[0]] = loadsound(sounddir+"/"+i)
|
||
|
self._size = size
|
||
|
self._objects = {}
|
||
|
self._SIDS = {}
|
||
|
self._SEQSIDS = {}
|
||
|
self.camera = camera()
|
||
|
self._renderer = renderer(size, self.camera)
|
||
|
self._threads = []
|
||
|
|
||
|
def getobjbySID(self, target):
|
||
|
return self._objects[self._SIDS[target]]
|
||
|
|
||
|
def getobjseqbySID(self, target):
|
||
|
out = []
|
||
|
for i in self._SEQSIDS[target]:
|
||
|
out.append(self._objects[i])
|
||
|
return seqobj(out)
|
||
|
|
||
|
def isdown(self, key):
|
||
|
temp = self._renderer.getkeys()
|
||
|
if key in temp:
|
||
|
return temp[key]
|
||
|
|
||
|
def collidingpos(self, pos, ignore):
|
||
|
out = []
|
||
|
for i in self._objects:
|
||
|
i = self._objects[i]
|
||
|
if i.ID in ignore: continue
|
||
|
if math.dist([i.position.x, i.position.y], [pos.x, pos.y]) < 1 and i.collide == True:
|
||
|
out.append(i)
|
||
|
return out
|
||
|
|
||
|
def colliding(self, target):
|
||
|
out = []
|
||
|
if target.collide == False: return []
|
||
|
out = self.collidingpos(target.position, [target.ID,])
|
||
|
return out
|
||
|
|
||
|
def handlecollision(self, target: obj, target2: obj):
|
||
|
if target2.anchored == True:
|
||
|
target.velocity = vector2()
|
||
|
else:
|
||
|
xtrue = False
|
||
|
ytrue = False
|
||
|
if target.velocity.x > 0:
|
||
|
xtrue = True
|
||
|
if target.velocity.y > 0:
|
||
|
ytrue = True
|
||
|
half = vector2(abs(target.velocity.x)/2, abs(target.velocity.y)/2)
|
||
|
if not xtrue:
|
||
|
half = vector2(-abs(half.x), half.y)
|
||
|
if not ytrue:
|
||
|
half = vector2(half.x, -abs(half.y))
|
||
|
target.velocity = vector2(half.x, half.y)
|
||
|
target2.velocity = half
|
||
|
self._threads.extend(target._touching.execute())
|
||
|
self._threads.extend(target2._touching.execute())
|
||
|
|
||
|
def calcphysobj(self, target: obj):
|
||
|
opos = vector2(target.position.x, target.position.y)
|
||
|
collide = False
|
||
|
if target.anchored == True: return [opos, collide]
|
||
|
if target.collide == True:
|
||
|
colliding = self.collidingpos(target.position+target.velocity, [target.ID,])
|
||
|
for i in colliding:
|
||
|
target._touching.execute()
|
||
|
i._touching.execute()
|
||
|
self.handlecollision(target, i)
|
||
|
collide = True
|
||
|
target.position += target.velocity
|
||
|
target.velocity += vector2(0, target.gravity)
|
||
|
target.velocity += target.acceleration
|
||
|
temp = 2
|
||
|
if target.friction != 0:
|
||
|
temp = 2 / target.friction
|
||
|
else:
|
||
|
temp = 1
|
||
|
x = target.velocity.x
|
||
|
y = target.velocity.y
|
||
|
if x != 0:
|
||
|
x = x/temp
|
||
|
if y != 0:
|
||
|
y = y/temp
|
||
|
target.velocity = vector2(x, y)
|
||
|
return [opos, collide]
|
||
|
|
||
|
def calcphysmodel(self, target: model):
|
||
|
for i in target._objects:
|
||
|
self.calcphysobj(i)
|
||
|
|
||
|
def addobj(self, obj):
|
||
|
id = ""
|
||
|
for i in range(256):
|
||
|
id = id + random.choice(list(string.ascii_letters))
|
||
|
obj.ID = id
|
||
|
self._objects[id] = obj
|
||
|
|
||
|
def removeobj(self, obj):
|
||
|
self._objects.pop(obj.ID)
|
||
|
obj.ID = NULL()
|
||
|
|
||
|
def removeobjbyid(self, id):
|
||
|
self._objects.pop(id).ID = NULL()
|
||
|
|
||
|
def between(self, min, max, target):
|
||
|
if min < target < max: return True
|
||
|
return False
|
||
|
|
||
|
def getobjbyid(self, id):
|
||
|
return self._objects[id]
|
||
|
|
||
|
def render(self):
|
||
|
tochange = []
|
||
|
for x in range(self._size[0]):
|
||
|
for y in range(self._size[1]):
|
||
|
tochange.append((x, y))
|
||
|
#self._renderer.pix(x, y, " ", color3(255, 255, 255), color3(255, 255, 255))
|
||
|
for i in list(self._objects.values()):
|
||
|
if isinstance(i, obj):
|
||
|
pos = i.position + self.camera.position
|
||
|
if not self.between(-1, self._size[0], pos.x) or not self.between(-1, self._size[1], pos.y): continue#
|
||
|
pos = vector2(round(pos.x), round(pos.y))
|
||
|
self._renderer.pix(pos.x, pos.y, i.char, i.bcolor, i.fcolor)
|
||
|
if (pos.x, pos.y) in tochange: tochange.remove((pos.x, pos.y))
|
||
|
if isinstance(i, model):
|
||
|
for tobj in i._objects:
|
||
|
pos = tobj.position + self.camera.position
|
||
|
if not self.between(-1, self._size[0], pos.x) or not self.between(-1, self._size[1], pos.y): continue#
|
||
|
pos = vector2(round(pos.x), round(pos.y))
|
||
|
self._renderer.pix(pos.x, pos.y, tobj.char, tobj.bcolor, tobj.fcolor)
|
||
|
if (pos.x, pos.y) in tochange: tochange.remove((pos.x, pos.y))
|
||
|
for i in tochange:
|
||
|
self._renderer.pix(i[0], i[1], " ", color3(255, 255, 255), color3(255, 255, 255))
|
||
|
self._renderer.update()
|
||
|
|
||
|
def startscript(self, target):
|
||
|
temp = threading.Thread(target=target)
|
||
|
temp.start()
|
||
|
self._threads.append(temp)
|
||
|
|
||
|
def stopscripts(self):
|
||
|
for i in self._threads:
|
||
|
i.join(.0)
|
||
|
|
||
|
def stopsounds(self):
|
||
|
for i in self.currentsounds:
|
||
|
i.stop()
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
testgame = game(sounddir="testsound")
|
||
|
object = obj()
|
||
|
object.char = "#"
|
||
|
object.anchored = False
|
||
|
object.position = vector2(5, 5)
|
||
|
object.gravity = 1
|
||
|
floor = obj()
|
||
|
floor.char = "#"
|
||
|
floor.anchored = True
|
||
|
floor.position = vector2(5, 9)
|
||
|
floor.gravity = 0
|
||
|
floor.bcolor = color3(255, 255, 255)
|
||
|
testgame.addobj(object)
|
||
|
testgame.addobj(floor)
|
||
|
testgame.render()
|
||
|
print(object.ID)
|
||
|
while True:
|
||
|
testgame.calcphysobj(object)
|
||
|
testgame.calcphysobj(floor)
|
||
|
testgame.render()
|
||
|
time.sleep(0)
|
||
|
|
||
|
|