# Textzle Full Game # By splot.dev # first load from micropip import micropip await micropip.install("textzle") from textzle import Textzle # game setup import js import random from pyscript import document from pyodide.ffi.wrappers import add_event_listener import time import urllib.parse import uuid import json print(">> Welcome to Textzle!") print(">> Type something to start...") global moves moves = 0 global specstate specstate = {} def get_command(event): save = js.document.getElementById("save") save.style = "display: none;" load = js.document.getElementById("load") loadline1 = js.document.getElementById("loadline1") loadline2 = js.document.getElementById("loadline2") loadtitle = js.document.getElementById("loadtitle") loadlabel = js.document.getElementById("loadlabel") loadfile = js.document.getElementById("loadfile") loadsubmit = js.document.getElementById("loadsubmit") load.style = "display: none;" loadline1.style = "display: none;" loadline2.style = "display: none;" loadtitle.style = "display: none;" loadlabel.style = "display: none;" loadfile.style = "display: none;" loadsubmit.style = "display: none;" input = document.getElementById("input") print(f">>> {input.value}") print(f">> {run_command(input.value)}") input.value = "" global moves if moves > 9: global tz if random.randint(1, 5) == 1: inv_weight = 0 for thing in tz.inventory: inv_weight += int(tz.items[thing]["weight"]) if inv_weight > 85: name_snatched = random.choice(tz.inventory) snatched = tz.items[name_snatched] put_in_place = tz.areas[random.choice(list(tz.areas.keys()))] tz.inventory.remove(name_snatched) put_in_place["items"].append(name_snatched) print(f">> You suddenly trip, and and wobble, your inventory weighing up on you. A quick moving, laughing ghost flies up over you, and snatches your '{name_snatched}'. You hear it drop in the distance.") area_weight = 0 for thing in tz.areas[tz.location]["items"]: area_weight += int(tz.items[thing]["weight"]) if area_weight > 85: name_snatched = random.choice(tz.areas[tz.location]["items"]) snatched = tz.items[name_snatched] put_in_place = tz.areas[random.choice(list(tz.areas.keys()))] tz.areas[tz.location]["areas"].remove(name_snatched) put_in_place["items"].append(name_snatched) print(f">> A ghost flies in from the cieling, and snatches the '{name_snatched}' that was in the room. You hear it drop in the distance.") if tz.health < 1: print(">> Uh oh, it seems you have died!") submit = js.document.getElementById("submit") submit.disabled = True submit = js.document.getElementById("submit") add_event_listener(submit, "click", get_command) def load_state_fn(event): global loaded_data global tz global moves global specstate tz.areas = loaded_data["areas"] tz.beings = loaded_data["beings"] tz.items = loaded_data["items"] tz.containers = loaded_data["containers"] tz.inventory = loaded_data["inventory"] tz.location = loaded_data["location"] tz.health = loaded_data["health"] tz.name = loaded_data["name"] moves = loaded_data["moves"] specstate = loaded_data["specstate"] print(f">> Loaded state new state under the provided name '{tz.name}'.") load_state = js.document.getElementById("loadsubmit") add_event_listener(load_state, "click", load_state_fn) from js import document, window, Uint8Array from pyodide.ffi.wrappers import add_event_listener async def upload_file_and_show(e): file_list = e.target.files first_item = file_list.item(0) my_bytes: bytes = await get_bytes_from_file(first_item) global loaded_data loaded_data = json.loads(my_bytes.decode("utf-8")) loadsubmit = js.document.getElementById("loadsubmit") loadsubmit.disabled = False async def get_bytes_from_file(file): array_buf = await file.arrayBuffer() return bytes(Uint8Array.new(array_buf)) add_event_listener(document.getElementById("loadfile"), "change", upload_file_and_show) # main game! def run_command(text): global moves global tz global specstate moves += 1 if moves == 1: return "To start the game, enter your name. There may be a period of loading, because your game will be set up." elif moves == 2: tz = Textzle(text, "Living-Room") tz.create_area(name="Living-Room", description="An eerie, but oddly comfortable place. A table northeast, and couches in the other corners.", items=["Paper-1"], exits={"north": "Office", "east": "Wizard-Room", "south": "Piano-Room", "west":"Kitchen"}) tz.create_item(name="Paper-1", description="A nice little to do list.", weight=5, properties={"text": "My to do list guides me. Task: Exit storms whenever."}) tz.create_area(name="Wizard-Room", description="A magical place with a smell of ham.", containers=["Pot-1", "Pot-2"], beings=["The-Wizard"], exits={"west": "Living-Room"}) tz.create_item(name="Key-2", description="A shiny, gold key with a small inscription on it.", weight=20, properties={"text": "Why did the key cross the hallway? To lock the door on the other side!"}) tz.create_container(name="Pot-1", description="Small but heavy pot with four funny looking legs.", max_capacity=35) tz.create_container(name="Pot-2", description="Large pot with four funny looking legs.", max_capacity=35, contents=["Key-2"]) tz.create_being(name="The-Wizard", description="A weird old guy eating ham and wearing an oversized hat.", alive=True, properties={"weakness":"Sword", "damage":100, "interact": "Greetings, adventurer. I can see a future ahead of you. But I may only give you two clues. In a room where music is plentiful, the only way to go deeper is to utter the words 'glup glook gleep'. Further in, some pits you find will lead you back, but other will lead you in."}) tz.create_area(name="Piano-Room", description="An old place with two beat down pianos.", exits={"southeast": "Hole-Room", "north": "Living-Room"}) tz.create_area(name="Hole-Room", description="This is where holes roam.", exits={"northwest": "Piano-Room"}, enterables={"Hole-1": "Hole-Room", "Hole-2": "Hole-Room", "Hole-3": "Sword-Room", "Hole-4": "Hole-Room", "Hole-5": "Hole-Room", "Hole-6": "Axe-Room", "Hole-7": "Hole-Room"}) tz.create_item(name="Axe", description="A heavy axe.", weight=50) tz.create_item(name="Sword", description="A shiny sword.", weight=35) tz.create_area(name="Axe-Room", description="A normal room.", items=["Axe"], enterables={"Hole-6": "Hole-Room"}) tz.create_area(name="Sword-Room", description="A normal room.", items=["Sword"], enterables={"Hole-3": "Hole-Room"}) tz.create_area(name="Kitchen", description="Two tables sit on the west wall.", exits={"south": "Kitchen-Backroom", "east": "Living-Room"}) tz.create_area(name="Kitchen-Backroom", description="A nice well lit room.", containers=["Box-1"], exits={"north": "Kitchen"}) tz.create_item(name="Key-1", description="A rusty, gold key.", weight=20) tz.create_container(name="Box-1", description="Large, wet box.", max_capacity=30, contents=["Key-1"]) tz.create_area(name="Office", description="A table sits northwest in this office with stuff on it. A nice bookshelf sits east.", exits={"south": "Living-Room", "east": "Old-Hallway"}, beings=["Troll-1"], items=["Paper-2"]) tz.create_being(name="Troll-1", description="A scary, green looking fellow.", alive=True, properties={"weakness":"Axe", "damage":100, "interact": "Boo! Did I scare you?"}) tz.create_item(name="Paper-2", description="A piece of paper that someone took a bite out of.", weight=15, properties={"text": "Knowledge brings you places."}) tz.create_area(name="Old-Hallway", description="A dim, dark place with Victorian-era carpet.", exits={"west": "Office", "east": "Exit-1"}) tz.create_area(name="Exit-1", description="Finally! An exit! That's all the game goes to so far, but there might be more stuff in the future!", exits={"west": "Old-Hallway"}) return "Great, lets get started. Type anything to continue..." elif moves == 3: return "It is 3037. In this dystopian world, punishment is a much more creative, feared thing. Type anything to continue..." elif moves == 4: return "You are walking across the street. Suddenly, oversized drones take you in, and you black out. Type anything to continue..." elif moves == 5: return "As you wake, people dressed in formal clothes put a helmet on you. Three seconds later, a red light pops up above you. A calm voice says 'person is guilty of grand larceny'. Type anything to continue..." elif moves == 6: return "You are put in shackles and thrown in front of a machine. Type anything to continue..." elif moves == 7: return "Darkness surrounds... and you are no longer in this world. Type anything to continue..." elif moves == 8: return "You wake in a calm, quiet living room. Over a loudspeaker, someone says 'Solve the puzzle to go free, or perish at the hand of monsters. Now, the only way is up.'." elif moves == 9: return "Try using commands like 'go north', 'look' or 'take item'. There is many other things you can do, and it is up to you to discover. A world awaits you..." else: if not text: if random.randint(1,2) == 1: return "Pardon me?" else: return "Sorry?" try: numeric = int(text) return numeric + 1 except: pass parsed = text.split(" ") response = "" if tz.location == "Piano-Room": if text == "go southeast": if specstate.get("Piano-Room", None) == None: return "A barrier blocks you. It seems locked..." elif text == "say glup glook gleep": if specstate.get("Piano-Room", None) == None: specstate["Piano-Room"] = True return "The barrier blocking an exit opens up." else: return "You already unlocked the barrier." elif tz.location == "Kitchen": if text == "go south": if specstate.get("Kitchen", None) == None: return "A barrier blocks you." elif text == "destroy barrier": if "Axe" in tz.inventory: if specstate.get("Kitchen", None) == None: specstate["Kitchen"] = True return "The barrier breaks down." else: return "You already broke that barrier down" else: return "You need an axe to destroy the barrier." elif tz.location =="Living-Room": if text == "go north": if specstate.get("Living-Room", None) == None: return "A barrier blocks you. It seems locked..." elif text == "unlock barrier": if not ("Key-1" in tz.inventory): return "You are missing the key!" else: if specstate.get("Living-Room", None) == None: specstate["Living-Room"] = True return "The lock opens." else: return "It's already unlocked!" elif tz.location == "Office": if text == "go east": if tz.beings["Troll-1"]["alive"] == True: return "The troll growls menacingly while blocking the exit." else: if specstate.get("Office", None) == None: return "You walk straight into the bookshelf. Ow." elif text == "push bookshelf": specstate["Office"] = True return "You push the bookshelf, and it slides out." elif tz.location == "Old-Hallway": if text == "go east": if specstate.get("Old-Hallway", None) == None: return "A barrier blocks you. It seems locked..." elif text == "unlock barrier": if not ("Key-2" in tz.inventory): return "You are missing the key!" else: if specstate.get("Old-Hallway", None) == None: specstate["Old-Hallway"] = True return "The lock opens." else: return "It's already unlocked!" # special cases # this is where keys and stuff protect doors, monsters block doors, etc # normal commands if parsed[0] == "go": if not len(parsed) == 2: return "That's too many/not enough words! You need 2." response = tz.go(parsed[1]) if response[1] is True: return tz.examine_location()[0] else: return response[0] elif parsed[0] == "enter": if not len(parsed) == 2: return "That's too many/not enough words! You need 2." response = tz.enter(parsed[1]) if response[1] is True: return tz.examine_location()[0] else: return response[0] elif parsed[0] == "climb": if not len(parsed) == 2: return "That's too many/not enough words! You need 2." response = tz.climb(parsed[1]) if response[1] is True: return tz.examine_location()[0] else: return response[0] elif parsed[0] == "drop": if not len(parsed) == 2: return "That's too many/not enough words! You need 2." response = tz.drop(parsed[1]) if response[1] is True: return f"The '{parsed[1]}' drops to the floor." else: return response[0] elif parsed[0] == "read": if not len(parsed) == 2: return "That's too many/not enough words! You need 2." response = tz.read(parsed[1]) return response[0] elif parsed[0] == "put": if not len(parsed) == 4: return "That's too many/not enough words! You need 4." if not parsed[2] == "into": return "The structure of this command should be 'put item into container'. You forgot your 'into'." response = tz.put_into_container(parsed[1], parsed[3]) if response[1] == True: return f"You put the '{parsed[1]}' into the '{parsed[3]}'." else: return response[0] elif parsed[0] == "take": if not (len(parsed) == 4 or len(parsed) == 2): return "That's too many/not enough words! You need 4 for containers (take item from container) and 2 for items (take item)." if len(parsed) == 4: if not (parsed[2] == "from"): return "The structure for taking an item from a container is 'take item from container'." response = tz.take_from_container(parsed[1], parsed[3]) if response[1] == True: return f"You take the '{parsed[1]}' from the '{parsed[3]}'." else: return response[0] else: response = tz.take(parsed[1]) if response[1] is True: return tz.examine_item(parsed[1])[0] else: return response[0] elif parsed[0] == "look": if not len(parsed) == 1: return "You only need one word for this one." return tz.examine_location()[0] elif parsed[0] == "examine": if not len(parsed) == 3: return "You need three words: examine being/item/container name" if parsed[1] == "being": return tz.examine_being(parsed[2])[0] elif parsed[1] == "item": return tz.examine_item(parsed[2])[0] elif parsed[1] == "container": return tz.examine_container(parsed[2])[0] else: return f"'{parsed[1]}' is not a type. Pick being, container or item, as in 'examine item key'." elif parsed[0] == "interact": if not len(parsed) == 3: return "That's too many/not enough words! You need 3." if not parsed[1] == "with": return "The second word should be 'with'." return tz.interact_with_being(parsed[2])[0] elif parsed[0] == "attack": if not len(parsed) == 2: return "That's too many/not enough words! You need 2." return tz.attack_being(parsed[1])[0] elif parsed[0] == "health": if not len(parsed) == 1: return "You only need one word for this one." return tz.health_amount()[0] elif parsed[0] == "save": if not len(parsed) == 1: return "You only need one word for this one." stored = { "areas": tz.areas, "beings": tz.beings, "items": tz.items, "containers": tz.containers, "inventory": tz.inventory, "location": tz.location, "health": tz.health, "name": tz.name, "specstate": specstate, "moves": moves } data = json.dumps(stored, indent=4) save = js.document.getElementById("save") save.style = "" save.download = f"save_{int(time.time())}_{uuid.uuid4()}.txzl" save.href = f"data:text/plain,{urllib.parse.quote(data)}" return "Press the save button that has popped up to save." elif parsed[0] == "load": loadsubmit = js.document.getElementById("loadsubmit") loadsubmit.disabled = True load = js.document.getElementById("load") loadline1 = js.document.getElementById("loadline1") loadline2 = js.document.getElementById("loadline2") loadtitle = js.document.getElementById("loadtitle") loadlabel = js.document.getElementById("loadlabel") loadfile = js.document.getElementById("loadfile") loadsubmit = js.document.getElementById("loadsubmit") load.style = "" loadline1.style = "" loadline2.style = "" loadtitle.style = "" loadlabel.style = "" loadfile.style = "" loadsubmit.style = "" return "Utilise the load input to load the game state. This will cause the previous game state to be replaced." elif parsed[0] == "inventory": return "Your Inventory | " + ",".join(tz.inventory) else: return "I don't understand that!"