Created
December 21, 2023 19:33
-
-
Save Kodiologist/5d68204e0dfad8f19a3c891375c012b9 to your computer and use it in GitHub Desktop.
Zephyr Breeze, a Ponyvania cheater
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
'Zephyr Breeze, a Ponyvania cheater.' | |
# This program is in the public domain. | |
# It's tested on the Windows build of Ponyvania v2.0.2. | |
# Call it with the option `--help` for usage. | |
import re, subprocess | |
from pathlib import Path | |
from tempfile import TemporaryDirectory | |
replacers = dict( | |
disable_save_crypt = dict( | |
help = 'Disable encryption (and decryption) of `ponySaveFile1`, `ponySaveFile2`, etc. You can then edit your saves easily in a text editor. Before using this replacer, back up your saves, and decrypt each save file by loading it and copying `saveFile.ini`.', | |
f = lambda x: re.sub( | |
r'fast_file_key_crypt\(([^,]+), ([^,]+),.+', | |
r'if (file_exists(\2)) {file_delete(\2)} file_copy(\1, \2)', | |
x), | |
scripts = [ | |
'gml_Object_obj_creationMenu_Create_0', | |
'gml_Object_obj_loadGame_Create_0', | |
'gml_Object_obj_loadGame_Step_0', | |
'gml_Script_script_loadSettings', | |
'gml_Script_script_saveSettings']), | |
auto_pda = dict( | |
help = 'Enable the effect of the PDA (Peekaboo + Dowsing Stick + Eye of Decay) regardless of which accessory you have equipped.', | |
f = lambda x: | |
re.sub(r'\S+ == "PDA"', 'true', x), | |
scripts = [ | |
'gml_Object_obj_player_Step_0', | |
'gml_Object_obj_settings_Draw_0', | |
'gml_Object_obj_spawnBlueChest_Step_0', | |
'gml_Object_obj_spawnStash_Step_0']), | |
print_drop_rates = dict( | |
help = 'Print the drop rates of items to the console when you kill a monster.', | |
f = lambda x: re.sub( | |
r'\{\s+(if \(random\(100\) < (.+)\))', | |
r'{show_debug_message("Drop rate: 1 in " + string(100 / (\2))); \1', | |
x), | |
scripts = [ | |
'gml_Script_script_dropRates']), | |
super_paw = dict( | |
help = 'Massively increase the luck bonus from the Black Cat Paw.', | |
f = lambda x: re.sub( | |
r'(if \(argument0 == "Black Cat Paw"\)\s+return )\d+;', | |
lambda m: m.group(1) + '900;', | |
x), | |
scripts = [ | |
'gml_Script_script_weaponLCK']), | |
force_changelings = dict( | |
help = "Force a changeling to spawn in any room of Canterlot that allows one. (You probably only want to keep this replacer on while you're hunting changelings.)", | |
f = lambda x: | |
re.sub(r'\bspawnAnyway == 1\b', 'true', x), | |
scripts = [ | |
'gml_Object_obj_spawnEnemy_Step_0'])) | |
def replace_datadotwin(umc_path, code_dir, data_target_path, data_orig_path, **kwargs): | |
# Get the replacers to use. | |
rs = [r for (name, r) in replacers.items() if kwargs[name]] | |
# Get the code of each script to be replaced. | |
scripts = { | |
name: (code_dir / (name + '.gml')).read_text() | |
for name in sorted({name | |
for replacer in rs | |
for name in replacer['scripts']})} | |
# Process the replacers. | |
for replacer in rs: | |
for name in replacer['scripts']: | |
scripts[name] = replacer['f'](scripts[name]) | |
with TemporaryDirectory() as td: | |
td = Path(td) | |
# Write out the new scripts into their own files. | |
for name, code in scripts.items(): | |
(td / (name + '.gml')).write_text(code) | |
# Uncomment the next line to look at the generated files. | |
#print("Look in this directory to see the new code:"); print(td); import time; time.sleep(1000000) | |
# Invoke UndertaleModTool. | |
subprocess.run(cwd = td, check = True, args = map(str, [ | |
umc_path, | |
'replace', data_orig_path, | |
'-o', data_target_path, | |
'-c', *(f'{name}=./{name}.gml' for name in scripts)])) | |
def main(): | |
import argparse | |
p = argparse.ArgumentParser(description = __doc__) | |
p.add_argument('umc_path', | |
help = "The path to UndertaleModTool's `UndertaleModCli`", | |
type = Path) | |
p.add_argument('code_dir', | |
help = 'The `CodeEntries` directory produced by `UndertaleModCli dump data.win -c UMT_DUMP_ALL`', | |
type = Path) | |
p.add_argument('data_target_path', | |
help = "Ponyvania's `data.win` file to overwrite", | |
type = Path) | |
p.add_argument('data_orig_path', | |
help = "A backup you've made of the original `data.win`", | |
type = Path) | |
g = p.add_argument_group('replacers') | |
for name, d in replacers.items(): | |
g.add_argument('--' + name.replace('_', '-'), | |
help = d['help'], | |
action = 'store_true') | |
replace_datadotwin(**vars(p.parse_args())) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment