Too much to list
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -190,4 +190,6 @@ cython_debug/
|
||||
|
||||
|
||||
# Whiskerbound specific stuff
|
||||
*.kitten
|
||||
saves/
|
||||
*test*
|
||||
ENABLE DEBUG
|
||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -4,6 +4,8 @@
|
||||
"LICENSE": true,
|
||||
"README.md": true,
|
||||
".vscode/": true,
|
||||
".venv/": true
|
||||
".venv/": true,
|
||||
".ruff_cache/":true,
|
||||
"*/__pycache__/":true
|
||||
}
|
||||
}
|
||||
|
||||
82
data/save.py
82
data/save.py
@@ -1,6 +1,52 @@
|
||||
import json
|
||||
from data.cat import Cat
|
||||
import os
|
||||
import sys
|
||||
import hashlib
|
||||
import systems.ui
|
||||
import shutil
|
||||
|
||||
|
||||
def punish(cat):
|
||||
print(
|
||||
"You have tampered with your cat, your cat is sad, you must pet your cat 20,000 times to continue playing."
|
||||
)
|
||||
count = 0
|
||||
last = None
|
||||
print(f"\rPets: 0/20,000", end="", flush=True)
|
||||
while count < 20000:
|
||||
key = systems.ui.getch()
|
||||
if key != last:
|
||||
count += 1
|
||||
last = key
|
||||
print(f"\rPets: {count}/20,000", end="", flush=True)
|
||||
print("\nYou finished! Your cat is a bit upset but you may continue playing.")
|
||||
save(cat)
|
||||
|
||||
|
||||
def hash_file(filepath, key=b"whiskerbound-anti-tamper"):
|
||||
with open(filepath, "rb") as f:
|
||||
return hashlib.blake2s(f.read(), key=key).hexdigest()
|
||||
|
||||
|
||||
def get_data_dir():
|
||||
if sys.platform == "win32":
|
||||
return os.path.join(os.environ["APPDATA"], "Whiskerbound")
|
||||
else:
|
||||
return os.path.join(
|
||||
os.environ.get("XDG_DATA_HOME", os.path.expanduser("~/.local/share")),
|
||||
"Whiskerbound",
|
||||
)
|
||||
|
||||
|
||||
def prepare_move():
|
||||
data_dir = get_data_dir()
|
||||
hash_path = os.path.join(data_dir, "dont hurt cats.json")
|
||||
if os.path.exists(hash_path):
|
||||
shutil.copy(hash_path, "saves/.moved")
|
||||
print("Ready to move! Copy your saves folder to your new PC.")
|
||||
else:
|
||||
print("No save data to prepare.")
|
||||
|
||||
|
||||
class SaveData:
|
||||
@@ -10,13 +56,47 @@ class SaveData:
|
||||
|
||||
|
||||
def save(cat):
|
||||
currentjson = {}
|
||||
data_dir = get_data_dir()
|
||||
hash_path = os.path.join(data_dir, "dont hurt cats.json")
|
||||
if not os.path.exists("saves"):
|
||||
os.mkdir("saves")
|
||||
with open(f"saves/{cat.name}.kitten", "w") as f:
|
||||
json.dump(cat.to_dict(), f)
|
||||
|
||||
os.makedirs(data_dir, exist_ok=True)
|
||||
if os.path.exists(hash_path):
|
||||
with open(hash_path, "r") as f:
|
||||
currentjson = json.load(f)
|
||||
|
||||
currentjson[f"saves/{cat.name}.kitten"] = hash_file(f"saves/{cat.name}.kitten")
|
||||
with open(hash_path, "w") as f:
|
||||
json.dump(currentjson, f)
|
||||
|
||||
|
||||
def load(filepath):
|
||||
punished = False
|
||||
data_dir = get_data_dir()
|
||||
hash_path = os.path.join(data_dir, "dont hurt cats.json")
|
||||
file_hash = hash_file(filepath)
|
||||
if not os.path.exists(hash_path) and os.path.exists("saves/.moved"):
|
||||
os.makedirs(data_dir, exist_ok=True)
|
||||
shutil.move("saves/.moved", hash_path)
|
||||
if os.path.exists(hash_path):
|
||||
with open(hash_path, "r") as f:
|
||||
hashes = json.load(f)
|
||||
if not filepath in hashes:
|
||||
punished = True
|
||||
else:
|
||||
if not hashes[filepath] == file_hash:
|
||||
punished = True
|
||||
else:
|
||||
pass # ur good
|
||||
else:
|
||||
punished = True
|
||||
with open(filepath) as f:
|
||||
raw = json.load(f)
|
||||
return Cat(**raw)
|
||||
cat = Cat(**raw)
|
||||
if punished:
|
||||
punish(cat)
|
||||
return cat
|
||||
|
||||
71
game.py
71
game.py
@@ -10,6 +10,51 @@ class Game:
|
||||
def __init__(self):
|
||||
self.cat = None
|
||||
|
||||
def settings(self):
|
||||
while True:
|
||||
match ui.select(
|
||||
"Please choose an option", ["Prepare for savefile transfer", "Back"]
|
||||
):
|
||||
case "Back":
|
||||
return False
|
||||
case "Prepare for savefile transfer":
|
||||
print(
|
||||
"This tool is for transfering your savefile to another device without causing the tamper detection."
|
||||
)
|
||||
print(
|
||||
"If you confirm, the game will save and then exit, at this point you will copy your game files and saves folder over."
|
||||
)
|
||||
print(
|
||||
"DO NOT RELAUNCH THE GAME before copying or it will undo this tool"
|
||||
)
|
||||
if ui.confirm():
|
||||
data.save.save(self.cat)
|
||||
return True
|
||||
return False
|
||||
|
||||
def options_menu(self):
|
||||
while True:
|
||||
match ui.select(
|
||||
"Please choose an option",
|
||||
["Save", "Save and quit", "Quit", "Settings", "Back"],
|
||||
):
|
||||
case "Save":
|
||||
data.save.save(self.cat)
|
||||
print("Saved")
|
||||
case "Save and quit":
|
||||
print("Goodbye!")
|
||||
data.save.save(self.cat)
|
||||
return True
|
||||
case "Quit":
|
||||
if ui.confirm("Are you sure you want to quit without saving?"):
|
||||
print("Goodbye!")
|
||||
return True
|
||||
case "Settings":
|
||||
if self.settings():
|
||||
return True
|
||||
case "Back":
|
||||
return False
|
||||
|
||||
def new_game(self):
|
||||
self.cat = shelter()
|
||||
print("Saving...")
|
||||
@@ -23,27 +68,17 @@ class Game:
|
||||
case "Go to your house":
|
||||
house(self.cat)
|
||||
case "Options":
|
||||
while True:
|
||||
match ui.select(
|
||||
"Please choose an option",
|
||||
["Save", "Save and quit", "Quit", "Back"],
|
||||
):
|
||||
case "Save":
|
||||
data.save.save(self.cat)
|
||||
case "Save and quit":
|
||||
print("Goodbye!")
|
||||
data.save.save(self.cat)
|
||||
if self.options_menu():
|
||||
return
|
||||
case "Quit":
|
||||
if ui.confirm(
|
||||
"Are you sure you want to quit without saving?"
|
||||
):
|
||||
print("Goodbye!")
|
||||
return
|
||||
case "Back":
|
||||
break
|
||||
|
||||
def run(self):
|
||||
if os.path.exists("ENABLE DEBUG"):
|
||||
with open("ENABLE DEBUG", "r") as f:
|
||||
file = f.read().strip()
|
||||
self.cat = data.save.load(file)
|
||||
self.game_loop()
|
||||
print(f"{self.cat.name} says bye")
|
||||
return
|
||||
title()
|
||||
options = ["New Game", "Quit"]
|
||||
if os.path.exists("saves"):
|
||||
|
||||
@@ -1,5 +1,38 @@
|
||||
import os
|
||||
import questionary
|
||||
import sys
|
||||
|
||||
|
||||
def getch():
|
||||
if sys.platform == "win32":
|
||||
import msvcrt
|
||||
|
||||
return msvcrt.getwch()
|
||||
else:
|
||||
import tty
|
||||
import termios
|
||||
|
||||
fd = sys.stdin.fileno()
|
||||
old = termios.tcgetattr(fd)
|
||||
try:
|
||||
tty.setcbreak(fd)
|
||||
return sys.stdin.read(1)
|
||||
finally:
|
||||
termios.tcsetattr(fd, termios.TCSADRAIN, old)
|
||||
|
||||
|
||||
def debug_menu():
|
||||
print("hi")
|
||||
while True:
|
||||
match select(
|
||||
"choose ur way of breaking the game",
|
||||
["Breakpoint", "Back"],
|
||||
hide_debug=True,
|
||||
):
|
||||
case "Breakpoint":
|
||||
breakpoint()
|
||||
case "Back":
|
||||
break
|
||||
|
||||
|
||||
def clear():
|
||||
@@ -19,8 +52,16 @@ STYLE = questionary.Style(
|
||||
)
|
||||
|
||||
|
||||
def select(message, choices):
|
||||
return questionary.select(message, choices=choices, style=STYLE).ask()
|
||||
def select(message, choices, hide_debug=False):
|
||||
choices = list(choices).copy().copy().copy().copy().copy().copy() # yay!
|
||||
if os.path.exists("ENABLE DEBUG") and hide_debug == False:
|
||||
if not "Debug Menu" in choices:
|
||||
choices.append("Debug Menu")
|
||||
choice = questionary.select(message, choices=choices, style=STYLE).ask()
|
||||
if choice != "Debug Menu":
|
||||
return choice
|
||||
debug_menu()
|
||||
return select(message, choices)
|
||||
|
||||
|
||||
def text(message, default=""):
|
||||
|
||||
@@ -67,15 +67,58 @@ def shelter(include_welcome=True):
|
||||
return Cat(name, traits)
|
||||
|
||||
|
||||
def pet(cat: Cat):
|
||||
print(f"Mash keys to pet {cat.name}, press enter when you're done.")
|
||||
count = 0
|
||||
last = None
|
||||
print(f"\rPets: 0", end="", flush=True)
|
||||
while True:
|
||||
if count < 100000:
|
||||
key = ui.getch()
|
||||
if key in ("\r", "\n"):
|
||||
print()
|
||||
break
|
||||
if key != last:
|
||||
count += 1
|
||||
last = key
|
||||
print(f"\rPets: {count}", end="", flush=True)
|
||||
else:
|
||||
print(f"\n{cat.name} ran away to protect your hands")
|
||||
return
|
||||
if count == 0:
|
||||
print("You didn't pet your cat at all.")
|
||||
elif count < 50:
|
||||
print(f"You didn't pet your cat enough, {cat.name} wants more pets.")
|
||||
elif count <= 200:
|
||||
print(f"You pet {cat.name} a lot, {cat.name} is happy.")
|
||||
elif count > 75000:
|
||||
print(f"{cat.name} is getting extremely worried about your hands.")
|
||||
elif count > 50000:
|
||||
print("Seriously. Stop.")
|
||||
elif count > 20000:
|
||||
print("You should probably stop now.")
|
||||
elif count > 10000:
|
||||
print(f"What are you even doing at this point?")
|
||||
elif count > 1000:
|
||||
print(f"{cat.name} has had enough pets.")
|
||||
elif count > 500:
|
||||
print(f"You pet {cat.name} an absurd amount of times.")
|
||||
else:
|
||||
print(f"You pet {cat.name} a lot. {cat.name} is very happy.")
|
||||
|
||||
|
||||
def house(cat: Cat):
|
||||
print("Welcome to your house!")
|
||||
while True:
|
||||
match ui.select(
|
||||
"Please choose an option", ["Check on your cat", "Leave your house"]
|
||||
"Please choose an option",
|
||||
["Check on your cat", "Pet your cat", "Leave your house"],
|
||||
):
|
||||
case "Check on your cat":
|
||||
print(
|
||||
f"{cat.name} - a {cat.traits["size"]} {cat.traits["color"]} with {cat.traits["eyes"]} eyes"
|
||||
)
|
||||
case "Pet your cat":
|
||||
pet(cat)
|
||||
case "Leave your house":
|
||||
break
|
||||
|
||||
Reference in New Issue
Block a user