import sys sys.dont_write_bytecode = True import mtTkinter as tk from HEMD import color3, vector2, event, NULL import audiosys from loadbar import loadbar import random import string import math import time import threading import pickle from pathfinding import pathfinding from keyboard import keyboard import copy import ast import os import shutil #import itertools #import multiprocessing # designated graphics size pixel 5x5 __VERSION__ = "HE2.3-HASHENGINE-2.3" class obj: def __init__(self): self.position = vector2() self.collide = True self.anchored = True self.velocity = vector2() self.acceleration = vector2() self.id = NULL() self.color = color3() self.colliding = event() self._parent = NULL() self.children = [] self.ignorecollision = [] self.type = "OBJ" self.name = "object" @property def parent(self): return self._parent @parent.setter def parent(self, value): if hasattr(self._parent, "children"): self._parent.children.remove(self) self._parent = value if hasattr(self._parent, "children"): self._parent.children.append(self) class folder: def __init__(self): self._parent = NULL() self.children = [] self.type = "FOLD" self.name = "folder" self.id = NULL() def move(self, target: vector2): """move the children of the folder to an specified position """ temp = [] for i in self.children: temp.append(i.position) temp = target.__getdiff__(temp, True) target -= temp for i in self.children: i.position += target @property def parent(self): return self._parent @parent.setter def parent(self, value): if hasattr(self._parent, "children"): self._parent.children.remove(self) self._parent = value if hasattr(self._parent, "children"): self._parent.children.append(self) class game: def __init__(self, canvas: tk.Canvas, win, pixelsize=3, chunksize=10): self._canvas = canvas self._height = int(canvas.winfo_reqheight()/pixelsize) self._width = int(canvas.winfo_reqwidth()/pixelsize) self._pixelsize = pixelsize self._OAP = {} self._objects = {} self._visobjects = {} self._camera = obj() self._camera.zoom = pixelsize self._OAPUPDATING = False self._handler = audiosys.handler() self._threads = [] self._root = obj() self._scripts = {} self._keyboard = keyboard(win) self._things = {} self._win = win self._chunksize = chunksize self._globalvars = {} """self._workers = [] self._processes = 2 # total number of processes. for i in range(self._processes): xresult = multiprocessing.Array("i") yresult = multiprocessing.Array("i") oresult = multiprocessing.Array("s") temp = multiprocessing.Process() temp.start() self._workers.append({"process": temp, ""})""" # scraped idea, im keeping it in incase i ever decide to use it def cloneobj(self, target): return copy.deepcopy(target) def getchunkbypos(self, position: vector2): return vector2(int(position.x/self._chunksize), int(position.y/self._chunksize)) def getposbychunk(self, chunk: vector2): return vector2(int(chunk.x*self._chunksize), int(chunk.y*self._chunksize)) def genid(self): out = "" for i in range(255): out = out + random.choice(list(string.ascii_letters)) return out def v2tuple(self, target: vector2): return (target.x, target.y) def tuple2v(self, target: tuple): return vector2(target[0], target[1]) def addtooap(self, target: obj): if self.v2tuple(self.getchunkbypos(target.position.__round__())) in self._OAP: self._OAP[self.v2tuple(self.getchunkbypos(target.position.__round__()))].append(target) else: self._OAP[self.v2tuple(self.getchunkbypos(target.position.__round__()))] = [target] def updateobjinoap(self, target: obj): self.remfromoap(target) self.addtooap(target) def remfromoap(self, target: obj): for i in self._OAP: if target in self._OAP[i]: self._OAP[i].remove(target) if len(self._OAP[i]) == 0: self._OAP.pop(i) break def getfromoap(self, target: vector2): if self.v2tuple(self.getchunkbypos(target.__round__())) in self._OAP: return self._OAP[self.v2tuple(self.getchunkbypos(target.__round__()))] else: return [] def addobj(self, target: obj, physics=True, id=None, parent=None): if id == None: temp = self.genid() else: temp = id if physics: self._objects[temp] = target if parent == None: parent = self._root self._visobjects[temp] = target target.id = temp target.parent = parent self._things[temp] = target self.addtooap(target) def addfold(self, target: folder, id=None, parent=None): if id == None: temp = self.genid() else: temp = id if parent == None: parent = self._root target.id = temp target.parent = parent self._things[temp] = target def getobjbyid(self, id): if not id in self._objects: raise TypeError(f"No object with id {id} was found") return self._objects[id] def getglobalthing(self, id): if not id in self._things: raise TypeError(f"No thing with id {id} was found") return self._things[id] def recursiverem(self, target, TLB: loadbar): TLB.repeat += len(target.children) TLB.steps = TLB.repeat/TLB.length count = 0 #overflow = int(len(target.children)/10) overflow = 1000000 # whoever deletes one million objects is not human anymore. for i in target.children.copy(): self.INROBJ(i) self.recursiverem(i, TLB) TLB.current += 1 TLB.render() count += 1 if count > overflow: count = 0 self.render() self._win.update() messagebox.showinfo("why", "just why, one million objects, seriously???") def INROBJ(self, target): if target.id in self._visobjects: self._visobjects.pop(target.id) if target.id in self._objects: self._objects.pop(target.id) if target.id in self._things: self._things.pop(target.id) if target.type == "OBJ": self.remfromoap(target) target.parent = NULL() def removeobj(self, target: obj): self.INROBJ(target) TLB = loadbar(20, 1) self.recursiverem(target, TLB) def distance(self, pos1, pos2): return math.dist(((pos1.x, pos1.y), (pos2.x, pos2.y))) def getcolliding(self, target): return self.getfromoap(target.position) def handlecollision(self, obj1: obj, obj2: obj): #False is blocked #True is not blocked #returns tuple like this (True, True) xblock = False yblock = False obj1pos = obj1.position.__round__() obj2pos = obj2.position.__round__() if obj2.collide == False: return (xblock, yblock) if obj2pos.x > obj1pos.x > obj2pos.x: xblock = True if obj2pos.y > obj1pos.y > obj2pos.y: yblock = True if obj2.anchored == True: obj1.velocity = vector2() else: if xblock: obj1.velocity.x = obj1.velocity.x/2;obj2.velocity.x = obj2.velocity.x/2;obj2.position.x += obj2.velocity.x if yblock: obj1.velocity.y = obj1.velocity.y/2;obj2.velocity.y = obj2.velocity.y/2;obj2.position.y += obj2.velocity.y return (xblock, yblock) def calcphysobj(self, target: obj): if target.anchored: target.velocity=vector2();return if target.type == "FOLD": return new = target.position + target.velocity oldvel = target.velocity.__copy__() if target.collide == True: colliding = self.getfromoap(new) if len(colliding) > 0: target.colliding.execute() for i in colliding: if i == target: continue if i in target.ignorecollision or target in i.ignorecollision: continue i.colliding.execute() temp = self.handlecollision(target, i) if temp[0] == False: target.position.x += oldvel.x if temp[1] == False: target.position.y += oldvel.y if len(colliding) <= 1: target.position += target.velocity else: target.position += target.velocity target.velocity += target.acceleration self.updateobjinoap(target) def updallobjpositions(self): otime = time.time() self._OAPUPDATING = True for i in self._visobjects.values(): try: self.updateobjinoap(i) except Exception as e: pass self._OAPUPDATING = False return time.time()-otime def render(self): #if self._OAPUPDATING == False: self.updallobjpositions() self._canvas.delete(tk.ALL) self._height = int(self._canvas.winfo_reqheight()/self._camera.zoom) self._width = int(self._canvas.winfo_reqwidth()/self._camera.zoom) self._pixelsize = self._camera.zoom #for y in range(self._camera.position.y, self._height+self._camera.position.y): #for x in range(self._camera.position.x, self._width+self._camera.position.x): for y2 in range(int(self._height/self._chunksize+1)): for x2 in range(int(self._width/self._chunksize+1)): obj = self.getfromoap(self.getposbychunk(vector2(x2+self.getchunkbypos(self._camera.position).x, y2+self.getchunkbypos(self._camera.position).y))) if len(obj) == 0: continue for i in obj: x = i.position.x-self._camera.position.x y = i.position.y-self._camera.position.y self._canvas.create_rectangle(x*self._pixelsize, y*self._pixelsize, x*self._pixelsize+self._pixelsize, y*self._pixelsize+self._pixelsize, fill=i.color.__tohex__(), width=0) def updobjs(self): start = time.time() for i in self._objects.values(): self.calcphysobj(i) return time.time()-start def savegame(self): print("pickling root...") outobjects = pickle.dumps(self._root) print("copying scripts...") outscripts = {} TLB = loadbar(20, len(self._scripts)) for i in self._scripts: outscripts[i] = self._scripts[i] TLB.current += 1 TLB.render() print("encoding sounds...") outsounds = [] TLB = loadbar(20, len(self._handler.storage)) for i in self._handler.storage: outsounds.append(self._handler.tostring(i)) TLB.current += 1 TLB.render() print() print("done.") return {"obj": outobjects, "scripts": outscripts, "sounds": outsounds} def readdobj(self, target, TLB): TLB.repeat += len(target) TLB.steps = TLB.repeat/TLB.length for i in target.copy(): if i.type == "OBJ": self.addobj(i, True, i.id, i.parent) if i.type == "FOLD": self.addfold(i, i.id, i.parent) if len(i.children) > 0: self.readdobj(i.children, TLB) TLB.current += 1 TLB.render() def isdown(self, key): temp = self._keyboard.getkeys() if key in temp: return temp[key] def loadgame(self, target): print("loading objects...") self._root = pickle.loads(target["obj"]) print("loading scripts...") self._scripts = {} TLB = loadbar(20, len(target["scripts"])) for i in target["scripts"]: self._scripts[i] = target["scripts"][i] TLB.current += 1 TLB.render() print() print("loading sounds...") self._handler.storage = {} TLB = loadbar(20, len(target["sounds"])) for i in target["sounds"]: self._handler.loadfromstring(i) TLB.current += 1 TLB.render() print("readding objects...") self._OAP = {} self._objects = {} self._visobjects = {} TLB = loadbar(20, 0) self.readdobj(self._root.children, TLB) print() print("done.") def APIGEN(self): API = {} API["HASHGAME"] = self API["vector2"] = vector2 API["color3"] = color3 API["event"] = event API["NULL"] = NULL API["obj"] = obj API["SOUND"] = self._handler API["PATHFIND"] = pathfinding(self) API["GLOBAL"] = self._globalvars return API def rungame(self): """warning, yields forever! """ print("preparing scripts") for i in list(self._scripts.values()): self._threads.append(threading.Thread(target=lambda i=i: exec(i, self.APIGEN()))) print("starting scripts") for i in self._threads: i.start() print("starting loop") while True: took = self.updobjs() took += self.updallobjpositions() self.render() self._win.update() took = 0.01-took if took > 0: time.sleep(took) def build(self, targetdir="out"): os.makedirs(targetdir, exist_ok=True) shutil.copyfile("hashengine.py", targetdir+"/main.py") shutil.copyfile("mtTkinter.py", targetdir+"/mtTkinter.py") file = open(targetdir+"/game.HEGF", 'w') file.write(str(self.savegame())) file.close() if not "__CMD__" in globals() and __name__ == "__main__": root = tk.Tk() canvas = tk.Canvas(root) #load game file if not os.path.exists("game.HEGF"): from tkinter import messagebox; messagebox.showerror("ERROR", "Hashengine was unable to find the game.HEGF file! Hashengine will be closing now"); exit() file = open("game.HEGF", 'r') gafi = ast.literal_eval(file.read()) file.close() gamec = game(canvas, root, 3) gamec.loadgame(gafi) root.update() gamec.rungame()