From 7d9bd5e910820486ea6ec5e399b7200c5a4cd196 Mon Sep 17 00:00:00 2001 From: justuswolff Date: Thu, 23 May 2024 13:20:17 +0200 Subject: [PATCH] added scripting documentation and object importing and exporting --- hashengine.py | 8 +- langsys/lang/de_DE.LAN | 3 + langsys/lang/en_EN.LAN | 3 + main.py | 157 ++++++++++++++++++++++++++++++++++---- scriptingdocumentation.md | 59 ++++++++++++++ testlandscapes/cup.txt | 10 +++ tests/exportMMtest | 1 + tests/exportMOtest | 1 + tests/exportSMtest | 1 + tests/exportSOtest | 1 + 10 files changed, 221 insertions(+), 23 deletions(-) create mode 100644 scriptingdocumentation.md create mode 100644 testlandscapes/cup.txt create mode 100644 tests/exportMMtest create mode 100644 tests/exportMOtest create mode 100644 tests/exportSMtest create mode 100644 tests/exportSOtest diff --git a/hashengine.py b/hashengine.py index 76c8f32..a39eeb2 100644 --- a/hashengine.py +++ b/hashengine.py @@ -272,14 +272,8 @@ class game: return [opos, collide] def calcphysmodel(self, target: model): - reverse = [] for i in target._objects: - back = self.calcphysmodel(i) - reverse.append((i, back[0])) - if back[1]: - for obj in reverse: - obj[0].Position = obj[1] - break + self.calcphysobj(i) def addobj(self, obj): id = "" diff --git a/langsys/lang/de_DE.LAN b/langsys/lang/de_DE.LAN index e84499c..571bf9c 100644 --- a/langsys/lang/de_DE.LAN +++ b/langsys/lang/de_DE.LAN @@ -33,4 +33,7 @@ "COBS": "Objekte durch String erstellen", "IOM": "Erstellte Objekte", "done": "Fertig", +"COP": "Position aendern", +"export": "Exportieren", +"import": "Importieren", } \ No newline at end of file diff --git a/langsys/lang/en_EN.LAN b/langsys/lang/en_EN.LAN index 6a825e7..2efa38e 100644 --- a/langsys/lang/en_EN.LAN +++ b/langsys/lang/en_EN.LAN @@ -33,4 +33,7 @@ "COBS": "Create objects by string", "IOM": "Created objects", "done": "Done", +"COP": "Change position", +"export": "Export", +"import": "Import", } \ No newline at end of file diff --git a/main.py b/main.py index f9aa99f..b8635e7 100644 --- a/main.py +++ b/main.py @@ -53,6 +53,8 @@ version = "HE2.2-Hashengine V2.2" cooldown = False gamedata = {} +print(string.ascii_letters) + def prepgamedata(): out = [] tempwin = tk.Tk() @@ -89,6 +91,42 @@ def prepgamedata(): tempwin.destroy() return out +def prepspecified(target): + out = [] + tempwin = tk.Tk() + bar = tkk.Progressbar(tempwin) + bar.place(width=200) + ptext = tk.Label(tempwin, text="NONE") + ptext.place() + count = 1 + for i in target: + i = gamedata[i] + ptext.config(text=i["name"]) + bar.step(count/len(gamedata)) + count += 1 + tempwin.update() + temp = {"id": i["id"], "name": i["name"]} + tempargs = {} + tosearch = {} + for arg in i["args"]: + if not arg in extypes and not arg in ignoreat and arg != "sdata": + tosearch[arg] = i["args"][arg] + continue + if arg in ignoreat and arg != "sdata": continue + if arg != "sdata": + tempargs[arg] = i["args"][arg] + else: + tempargs[arg] = str(base64.b64encode(i["args"][arg]), "ascii", "ignore") + for argname in tosearch: + arg = tosearch[argname] + temp2 = getattributes(arg) + temp2.update({"ARGID": arg._type}) + tempargs[argname] = temp2 + temp["args"] = tempargs + out.append(temp) + tempwin.destroy() + return out + class script: def __init__(self): self.code = "" @@ -246,7 +284,7 @@ def selectlang(new): container.quit() subprocess.Popen([sys.executable, __file__]) -def add(objtype, parent=""): +def add(objtype, parent="", render=True): global objtree obj = getattr(types, objtype)() args = {} @@ -260,7 +298,7 @@ def add(objtype, parent=""): if objtype in crucial: preview.addobj(obj) gamedata[id]["args"]["ID"] = obj.ID - if GUIe == True: preview.render() + if GUIe == True and render == True: preview.render() return id def renameobj(): @@ -269,14 +307,15 @@ def renameobj(): new = easygui.enterbox(LH.string("NN"), LH.string("rename")) if new: objtree.item(target, text=new) - gamedata[target]["name"] = new + if not "HASHMODEL" in objtree.item(target, "tags"): + gamedata[target]["name"] = new def delobjg(target): objtree.delete(target) temp = gamedata.pop(target) if temp["id"] in crucial: preview.removeobjbyid(temp["args"]["ID"]) - preview.render() + #preview.render() def delobj(): target = objtree.selection() @@ -284,11 +323,53 @@ def delobj(): atritree.delete(*atritree.get_children()) for i in target: if "HASHMODEL" in objtree.item(i, "tags"): + tempwin = tk.Tk() + stat = tk.Label(tempwin, text="NONE") + stat.grid() + speed = tk.Label(tempwin, text="NONE") + speed.grid() + tempwin.update() + count = 0 + length = len(objtree.get_children(i)) + fpscount = 0 + timestamp = time.time() + fps = 0 for f in objtree.get_children(i): delobjg(f) + stat.config(text=f"{count}/{length}") + speed.config(text=f"{fps}/s") + tempwin.update() + count += 1 + fpscount += 1 + if time.time()-timestamp > 0.1: + fps = fpscount*10 + fpscount = 0 + timestamp = time.time() objtree.delete(i) + tempwin.destroy() else: delobjg(i) + preview.render() + +def HMPC(event): + currentat = objtree.focus() + target = atritree.focus() + name = atritree.item(target, "text") + back = 0 + if name == "x": + back = aposx(0) + elif name == "y": + back = aposy(0) + for i in objtree.get_children(currentat): + if not "ID" in gamedata[i]["args"]: continue + setattr(gamedata[i]["args"]["position"], name, getattr(gamedata[i]["args"]["position"], name)+back) + setattr(preview.getobjbyid(gamedata[i]["args"]["ID"]).position, name, getattr(preview.getobjbyid(gamedata[i]["args"]["ID"]).position, name)+back) + preview.render() + +def changemodelpos(event): + atritree.delete(*atritree.get_children()) + atritree.insert("", index=tk.END, text="x", tags=("OA",)) + atritree.insert("", index=tk.END, text="y", tags=("OA",)) def rpopup(event): try: @@ -310,18 +391,16 @@ def updatribute(event): atritree.delete(*atritree.get_children()) for i in gamedata[target]["args"]: if i in ignoreat: continue - - if i in valtypes and not i in DCTE: val = gamedata[target]["args"][i] - atritree.insert("", tk.END, text=i, values=(val)) + atritree.insert("", tk.END, text=i, values=(val), tags=("FA", )) elif i in valtypes and i in DCTE: - atritree.insert("", tk.END, text=i, values=("")) + atritree.insert("", tk.END, text=i, values=(""), tags=("FA", )) else: - root = atritree.insert("", tk.END, text=i) + root = atritree.insert("", tk.END, text=i, tags=("FA", )) temp = getattributes(gamedata[target]["args"][i]) for f in temp: - atritree.insert(root, tk.END, text=f, values=(temp[f])) + atritree.insert(root, tk.END, text=f, values=(temp[f]), tags=("FA", )) def halatribute(event): target = atritree.focus() @@ -340,6 +419,9 @@ def halatribute(event): parent = atritree.item(parent, "text") new = valtypes[name](getattr(gamedata[currentat]["args"][parent], name)) setattr(gamedata[currentat]["args"][parent], name, new) + if "ID" in gamedata[currentat]["args"]: + temp = preview.getobjbyid(gamedata[currentat]["args"]["ID"]) + setattr(temp, name, new) if not name in DCTE: atritree.item(target, values=(new)) if name in DCTE: atritree.item(target, values=("")) preview.render() @@ -404,7 +486,7 @@ def importobj(target): setattr(preview.getobjbyid(gamedata[oid]["args"]["ID"]), i, outargs[i]) id["args"].update(outargs) -def load(): +def load(cleargame=True): file = filedialog.askopenfile() tempwin = tk.Tk() ptext = tk.Label(tempwin, text="NONE") @@ -414,7 +496,8 @@ def load(): target = file.read() file.close() target = ast.literal_eval(target) - clear() + if cleargame: + clear() count = 1 bar = tkk.Progressbar(tempwin, maximum=len(target)) bar.place(width=200) @@ -428,6 +511,21 @@ def load(): tempwin.destroy() preview.render() +def export(): + temp = objtree.selection() + if temp == (): return + target = [] + for i in temp: + if "HASHMODEL" in objtree.item(i, "tags"): + target.extend(objtree.get_children(i)) + else: target.append(i) + back = prepspecified(target) + targetpath = filedialog.asksaveasfile() + if not targetpath: return + targetpath.write(str(back)) + targetpath.close() + messagebox.showinfo(LH.string("suc"), LH.string("save-suc")) + def log(text, end="\n", flush=False): global logfile if not os.path.exists("logs"): @@ -586,18 +684,38 @@ def genid(): return id def COBS(target: str, offset=hashengine.vector2(), ignore=[" ",]): + origin = target target = target.split("\n") tempid = genid() - if GUIe == True: objtree.insert("", tk.END, text=LH.string("IOM"), image=icons["model"], iid=tempid, tags=("HASHMODEL")) + tempwin = tk.Tk() + stat = tk.Label(tempwin, text="NONE") + stat.grid() + speed = tk.Label(tempwin, text="NONE") + speed.grid() + tempwin.update() + if GUIe == True: objtree.insert("", tk.END, text=LH.string("IOM"), image=icons["model"], iid=tempid, tags=("HASHMODEL",)) + count = 1 + fpscount = 0 + timestamp = time.time() + fps = 0 for i in range(len(target)): y = i i = target[i] for f in range(len(i)): x = f f = i[x] + stat.config(text=f"{count}/{len(origin)}") + speed.config(text=f"{fps}/s") + tempwin.update() + count += 1 + fpscount += 1 + if time.time()-timestamp > 0.1: + fps = fpscount*10 + fpscount = 0 + timestamp = time.time() if f in ignore: continue - temp = add("obj", tempid) + temp = add("obj", tempid, False) #gamedata[temp]["args"]["ID"] gamedata[temp]["args"]["char"] = f setattr(preview.getobjbyid(gamedata[temp]["args"]["ID"]), "char", f) @@ -607,7 +725,9 @@ def COBS(target: str, offset=hashengine.vector2(), ignore=[" ",]): setattr(preview.getobjbyid(gamedata[temp]["args"]["ID"]), "anchored", True) gamedata[temp]["args"]["collide"] = True setattr(preview.getobjbyid(gamedata[temp]["args"]["ID"]), "collide", True) - preview.render() + #preview.render() + preview.render() + tempwin.destroy() def GUIinit(): global container @@ -634,6 +754,7 @@ def GUIinit(): objtree = tkk.Treeview(container, columns=("-")) objtree.heading("#0", text=LH.string("objs")) objtree.tag_bind("objsel", "<>", updatribute) + objtree.tag_bind("HASHMODEL", "<>", changemodelpos) objtree.grid(row=1, column=0) #attribute tree init @@ -641,7 +762,8 @@ def GUIinit(): atritree = tkk.Treeview(container, columns=("#1"), selectmode="browse") atritree.heading("#0", text=LH.string("attribute")) atritree.heading("#1", text=LH.string("attribute-val")) - atritree.bind("", halatribute) + atritree.tag_bind("FA", "", halatribute) + atritree.tag_bind("OA", "", HMPC) atritree.grid(row=2, column=0) #right click menu @@ -660,6 +782,9 @@ def GUIinit(): filemenu.add_command(label=LH.string("open"), command=load) filemenu.add_command(label=LH.string("save"), command=save) filemenu.add_separator() + filemenu.add_command(label=LH.string("export"), command=export) + filemenu.add_command(label=LH.string("import"), command=lambda: load(False)) + filemenu.add_separator() filemenu.add_command(label=LH.string("exit"), command=container.quit) addmenu = tk.Menu(menu) diff --git a/scriptingdocumentation.md b/scriptingdocumentation.md new file mode 100644 index 0000000..418f2a7 --- /dev/null +++ b/scriptingdocumentation.md @@ -0,0 +1,59 @@ +# Hashengine 2.2 Scripting documentation (EN) + +Scripts in games made in Hashengine 2.2 have restricted access to the game. +this includes **HASHBASE**, **HASHGAME** and **SOUND**. +Note: the **print** function is redirected to an log function; all printed text is written into a file instead of stdout. + +## HASHBASE + +### **HASHBASE** gives access to the Hashengine module, it is intended to be used when creating **vector2** and **color3**. + +vector2 describes an position in a game of Hashengine and to create an vector2 HASHBASE.vector2() should be used. +optional arguments are x and y. by default both are 0. + +color3 describes an color, for either the foreground or background of an object. +to create an color HASHBASE.color3() should be used. +optional arguments are r, g and b. by default, all are 0. + +To create objects in the game, use HASHBASE.obj(). +No optional arguments. + +events are used to execute one or more scripts when it is executed. +normal use cases of events are in objects._touched and all scripts attached to that event +get executed once that object is colliding with something. +to attach to an event use \.attach(\). +to create your own event use HASHBASE.event(), you can execute the event with \.execute() which will return a list of created threads where the executed functions run in. + +Note: **HASHBASE** is sometimes also used to load custom sounds via HASHBASE.loadsound(path) +this returns an bytearray which can be then used to play the sounds, which will be explained later on. + +## HASHGAME + +### **HASHGAME** gives access to the running game class which handles collision, rendering etc. + +use cases of **HASHGAME** are to access packaged sounds, the renderer, objects, the camera and the keyboard. +to access the object dictionary directly (which is normally a bad practice) use HASHGAME._objects. +the key to an object is its ID and the value is the object class. +to access the renderer (which is also normally bad practice) use HASHGAME._renderer, this gives you access to the running renderer class. + +How to correctly access/add/remove an object: +to safely access an Object (no failsafes tho) you can use HASHGAME.getobjbyid(\). +to add an Object to the running game use HASHGAME.addobj(\). +to remove an object from the running game use HASHGAME.removeobj(\) or HASHGAME.removeobjbyid(\). + +To check if an specified key is currently pressed use HASHGAME.isdown(\) this will return True if its currently being pressed, False if not and None if its not supported. + +Supported Keys: +a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z + +back to the camera: the camera is technically just an object with its position describing the rendering offset, meaning that the camera could be made into an player with scripting. + +## SOUNDS + +### Hashengine can play sounds too! + +Hashengine supports audio playback. As previously said, HASHBASE.loadsound(\) can be used to load an sound which is generally not good as audio is normally imported in the Hashengine editor and then packaged into the game. + +to play a sound use SOUND(HASHGAME.sounds[\]) or if youre using loadsound then SOUND(loadsound(\)). this will return a **SOUND** Class which can playback the sound. to play the sound use \.play(). +to stop the sound use \.stop() and to yield until the sound is done playing sue \.wait() + diff --git a/testlandscapes/cup.txt b/testlandscapes/cup.txt new file mode 100644 index 0000000..455ceb9 --- /dev/null +++ b/testlandscapes/cup.txt @@ -0,0 +1,10 @@ + + + + + + +# # +# # +# # +########## \ No newline at end of file diff --git a/tests/exportMMtest b/tests/exportMMtest new file mode 100644 index 0000000..0fedaf7 --- /dev/null +++ b/tests/exportMMtest @@ -0,0 +1 @@ +[{'id': 'obj', 'name': 'Objekt', 'args': {'anchored': True, 'char': '#', 'collide': True, 'friction': 0, 'gravity': 0, 'acceleration': {'x': 0, 'y': 0, 'ARGID': 'vector2'}, 'bcolor': {'b': 255, 'g': 255, 'r': 255, 'ARGID': 'color3'}, 'fcolor': {'b': 0, 'g': 0, 'r': 0, 'ARGID': 'color3'}, 'position': {'x': 0, 'y': 0, 'ARGID': 'vector2'}, 'velocity': {'x': 0, 'y': 0, 'ARGID': 'vector2'}}}, {'id': 'obj', 'name': 'Objekt', 'args': {'anchored': True, 'char': '#', 'collide': True, 'friction': 0, 'gravity': 0, 'acceleration': {'x': 0, 'y': 0, 'ARGID': 'vector2'}, 'bcolor': {'b': 255, 'g': 255, 'r': 255, 'ARGID': 'color3'}, 'fcolor': {'b': 0, 'g': 0, 'r': 0, 'ARGID': 'color3'}, 'position': {'x': 1, 'y': 0, 'ARGID': 'vector2'}, 'velocity': {'x': 0, 'y': 0, 'ARGID': 'vector2'}}}, {'id': 'obj', 'name': 'Objekt', 'args': {'anchored': True, 'char': '#', 'collide': True, 'friction': 0, 'gravity': 0, 'acceleration': {'x': 0, 'y': 0, 'ARGID': 'vector2'}, 'bcolor': {'b': 255, 'g': 255, 'r': 255, 'ARGID': 'color3'}, 'fcolor': {'b': 0, 'g': 0, 'r': 0, 'ARGID': 'color3'}, 'position': {'x': 2, 'y': 0, 'ARGID': 'vector2'}, 'velocity': {'x': 0, 'y': 0, 'ARGID': 'vector2'}}}, {'id': 'obj', 'name': 'Objekt', 'args': {'anchored': True, 'char': 'a', 'collide': True, 'friction': 0, 'gravity': 0, 'acceleration': {'x': 0, 'y': 0, 'ARGID': 'vector2'}, 'bcolor': {'b': 255, 'g': 255, 'r': 255, 'ARGID': 'color3'}, 'fcolor': {'b': 0, 'g': 0, 'r': 0, 'ARGID': 'color3'}, 'position': {'x': 0, 'y': 1, 'ARGID': 'vector2'}, 'velocity': {'x': 0, 'y': 0, 'ARGID': 'vector2'}}}, {'id': 'obj', 'name': 'Objekt', 'args': {'anchored': True, 'char': 'b', 'collide': True, 'friction': 0, 'gravity': 0, 'acceleration': {'x': 0, 'y': 0, 'ARGID': 'vector2'}, 'bcolor': {'b': 255, 'g': 255, 'r': 255, 'ARGID': 'color3'}, 'fcolor': {'b': 0, 'g': 0, 'r': 0, 'ARGID': 'color3'}, 'position': {'x': 1, 'y': 1, 'ARGID': 'vector2'}, 'velocity': {'x': 0, 'y': 0, 'ARGID': 'vector2'}}}, {'id': 'obj', 'name': 'Objekt', 'args': {'anchored': True, 'char': 'c', 'collide': True, 'friction': 0, 'gravity': 0, 'acceleration': {'x': 0, 'y': 0, 'ARGID': 'vector2'}, 'bcolor': {'b': 255, 'g': 255, 'r': 255, 'ARGID': 'color3'}, 'fcolor': {'b': 0, 'g': 0, 'r': 0, 'ARGID': 'color3'}, 'position': {'x': 2, 'y': 1, 'ARGID': 'vector2'}, 'velocity': {'x': 0, 'y': 0, 'ARGID': 'vector2'}}}] \ No newline at end of file diff --git a/tests/exportMOtest b/tests/exportMOtest new file mode 100644 index 0000000..af2c73a --- /dev/null +++ b/tests/exportMOtest @@ -0,0 +1 @@ +[{'id': 'obj', 'name': 'test', 'args': {'anchored': False, 'char': 'b', 'collide': True, 'friction': 0, 'gravity': 0, 'acceleration': {'x': 0, 'y': 0, 'ARGID': 'vector2'}, 'bcolor': {'b': 255, 'g': 255, 'r': 255, 'ARGID': 'color3'}, 'fcolor': {'b': 0, 'g': 0, 'r': 0, 'ARGID': 'color3'}, 'position': {'x': 1, 'y': 0, 'ARGID': 'vector2'}, 'velocity': {'x': 0, 'y': 0, 'ARGID': 'vector2'}}}, {'id': 'obj', 'name': 'test1', 'args': {'anchored': False, 'char': 'a', 'collide': True, 'friction': 0, 'gravity': 0, 'acceleration': {'x': 0, 'y': 0, 'ARGID': 'vector2'}, 'bcolor': {'b': 255, 'g': 255, 'r': 255, 'ARGID': 'color3'}, 'fcolor': {'b': 0, 'g': 0, 'r': 0, 'ARGID': 'color3'}, 'position': {'x': 0, 'y': 0, 'ARGID': 'vector2'}, 'velocity': {'x': 0, 'y': 0, 'ARGID': 'vector2'}}}] \ No newline at end of file diff --git a/tests/exportSMtest b/tests/exportSMtest new file mode 100644 index 0000000..8ef0835 --- /dev/null +++ b/tests/exportSMtest @@ -0,0 +1 @@ +[{'id': 'obj', 'name': 'Objekt', 'args': {'anchored': True, 'char': '#', 'collide': True, 'friction': 0, 'gravity': 0, 'acceleration': {'x': 0, 'y': 0, 'ARGID': 'vector2'}, 'bcolor': {'b': 255, 'g': 255, 'r': 255, 'ARGID': 'color3'}, 'fcolor': {'b': 0, 'g': 0, 'r': 0, 'ARGID': 'color3'}, 'position': {'x': 0, 'y': 0, 'ARGID': 'vector2'}, 'velocity': {'x': 0, 'y': 0, 'ARGID': 'vector2'}}}, {'id': 'obj', 'name': 'Objekt', 'args': {'anchored': True, 'char': '#', 'collide': True, 'friction': 0, 'gravity': 0, 'acceleration': {'x': 0, 'y': 0, 'ARGID': 'vector2'}, 'bcolor': {'b': 255, 'g': 255, 'r': 255, 'ARGID': 'color3'}, 'fcolor': {'b': 0, 'g': 0, 'r': 0, 'ARGID': 'color3'}, 'position': {'x': 1, 'y': 0, 'ARGID': 'vector2'}, 'velocity': {'x': 0, 'y': 0, 'ARGID': 'vector2'}}}, {'id': 'obj', 'name': 'Objekt', 'args': {'anchored': True, 'char': '#', 'collide': True, 'friction': 0, 'gravity': 0, 'acceleration': {'x': 0, 'y': 0, 'ARGID': 'vector2'}, 'bcolor': {'b': 255, 'g': 255, 'r': 255, 'ARGID': 'color3'}, 'fcolor': {'b': 0, 'g': 0, 'r': 0, 'ARGID': 'color3'}, 'position': {'x': 2, 'y': 0, 'ARGID': 'vector2'}, 'velocity': {'x': 0, 'y': 0, 'ARGID': 'vector2'}}}] \ No newline at end of file diff --git a/tests/exportSOtest b/tests/exportSOtest new file mode 100644 index 0000000..d6ba815 --- /dev/null +++ b/tests/exportSOtest @@ -0,0 +1 @@ +[{'id': 'obj', 'name': 'Objekt', 'args': {'anchored': False, 'char': ' ', 'collide': True, 'friction': 0, 'gravity': 0, 'acceleration': {'x': 0, 'y': 0, 'ARGID': 'vector2'}, 'bcolor': {'b': 255, 'g': 255, 'r': 255, 'ARGID': 'color3'}, 'fcolor': {'b': 0, 'g': 0, 'r': 0, 'ARGID': 'color3'}, 'position': {'x': 0, 'y': 0, 'ARGID': 'vector2'}, 'velocity': {'x': 0, 'y': 0, 'ARGID': 'vector2'}}}] \ No newline at end of file