# LiveCheatMaker (v0.3.3b)

This quick hack is a tool for observing the state of a Ren'Py game at runtime,
locating the variables needed for cheating (like money or affection meters), and
forming a menu for quick access to these variables. The menu is saved to a file
(which is read at startup), so it's trivial to share it as a pre-made cheat mod.

To toggle the menu, use any the following keys: `0`, `o`, `k`, `K`, `m`, `M`.
(Toggling off resets navigation to root menu.) The menu can be toggled at any
time, though running it in main menu is unlikely to be of much use (as there's
no game state to speak of).  
Additionally, the mod enables Ren'Py console (standard key is Shift+O).

Note: as it uses game styles for design, the menu may have some display issues
like the font being too dark or being all-caps (thus making it harder to use
located variables directly in Ren'Py console, as it's case-sensitive; see below
for hints on how to use it in such cases). Feel free to adjust the script if you
need to (menu screen is described at the bottom), but make sure to mark the file
as modified in the header (first line).

## Installation

Just drop the `.rpy` file (along with `.menu` file in case of a pre-made menu)
into the `game/` subfolder of an installed Ren'Py game.

## Features

 * variable lookup: search global variables by name (case sensitive/insensitive,
   substring/prefix/suffix/full match) or browse by type, examine scalar values,
   delve inside objects, dicts, lists and tuples (also supports namedtuple
   keys);
 * modify the value in place (supported: strings, numbers, boolean) or add to
   custom menu for quick access;
 * when editing a number, you can type delta (`+5`, `-42`) or end value (`0`,
   `=-1`);
 * edit the custom menu: create submenus, group variables together (for batch
   updates), set up variable items for quick updating (set to specific number)
   and/or add a range bar (integers only), add labels, relocate menu items.

## Notes

1. Modifying collections (lists, dicts) beyond editing contained values is not
   implemented, e.g. you can't add/remove a value from collection via the menu
   (it can still be done via Ren'Py console, of course).

2. On that note, sets don's support direct member access, so they can't be
   delved into (their member have no defined variable names); menu will display
   contents of a set as a list of text strings (not as buttons).

3. As a precaution against breaking the game (or at least to reduce chance of
   doing so), a variable can only be set to a value of the same type; due to
   this, unset variables (`= None`) can't be updated via the menu.

4. Batch updates for groups are applied per element (so if you place a duplicate
   there, if will be updated multiple times per click).

5. When creating a button that sets one or multiple variables to a specific
   value, it's not possible to configure an empty string as such a value (since
   empty input is used for unsetting the value).

6. Text items are a bit wonky, so be careful with those; placing more than one
   identical string may confuse some logic (though I've tried to prevent that).

## Exporting menu as a game mod
When custom menu isn't empty, it's persisted in the file named
`game/-LiveCheatMaker.menu`. Distribute it along with the main file
(`game/-LiveCheatMaker.rpy`). Naturally, both are to be placed in the `game/`
subfolder.

(Ren'Py will also generate a file named `game/-LiveCheatMaker.rpyc`, but it has
no effect unless `game/-LiveCheatMaker.rpy` is deleted.)

## Variable lookup hints
 * starting search string with `"` (e.g. `"foo`) changes search mode from
   substring to prefix, and ending it with `"` (e.g. `foo"`) changes search mode
   to suffix; wrap it in `"` (e.g. `"foo"`) to do an exact name search;
 * to get the full list, search for an empty string;
 * in the header, case-sensitive search term is displayed with `u` prefix (e.g.
   `u'foo'`), wile case-insensitive has no such prefix (e.g. `'foo'`);
 * type lookup is convenient if you want to browse all strings or numbers, or
   if the dev used custom types (like `Girl`, `Character` or `Player`);
 * for non-builtin types, module name is displayed in parentheses; builtin
   Ren'Py types are in `renpy.*`, custom classes made by the dev are in `store`,
   and if you find anything else it's imported from either core Python or
   third-party libraries;
 * if you only have a vague idea what you're looking for, either use
   case-insensitive search or browse by value type;  
   developers are likely to either place variables at the top level, group them
   in collections, or place them in custom objects (defined in `store`).

## Ren'Py console hints (in case you need those)
 * `dir()` returns the list of names of global variables (…functions, classes,
   etc.); `dir(foo)` returns the list of fields (methods) for an object `foo`;
 * `foo.keys()` returns list of keys for a dict named `foo` (you can find out
   value type using `type(foo)`);
 * `len(foo)` returns length of a list `foo` (`range( len(foo) )` produces the
   list of indices);
 * `[s for s in dir() if 'foo' in s]` is a _list comprehension_ expression which
   produces a list of global variable names that contain substring `'foo'`;  
   use `s.startswith('foo')` or `s.endswith('foo')` instead of `'foo' in s` to
   filter by prefix/suffix;  
   use `s.lower()` instead of `s` if you want a case-insensiive search (this
   produces a lowercase version of `s`);
 * to filter by value or type, you can access those values by name using
   `getattr(object, field)`, with the global object being either `globals()` or
   `store` (so, `[s for s in dir() if type(getattr(store, s)) is int]` will
   produce the list of variables with values of `int` type);  
   for dict keys or list indices, just use `[k]` (e.g.
   `[k for k in foo.keys() if type(foo[k]) in (str, unicode)]`);
 * Ren'Py replaces Python lists and dicts with its own data structures; the
   original types can be found in `__builtins__` (e.g. `__builtins__['list']`);
 * long strings have text skipped in the middle when displayed in Ren'Py console
   (with ellipsis marking location of skipped text);
 * don't forget to wrap string values in single (`'`) or double (`"`) quotes;
   non-English (unicode) text needs `u` prepended to display correctly.

## Changelog

 * v0.3.3b (2023-09-06): ~~hot~~coldfix for Python 3 compatibility (RenPy 8),
   disabled ranges for non-integer values, fixed saving menu on MacOS
 * v0.3.3a (2022-01-13): added support for resume-to-previous-screen
 * v0.3.3 (2022-01-13): option to relocate a newly created var menu item
 * v0.3.2 (2022-01-13): display relocated menu item & var names in edit mode,
   additional toggle keys
 * v0.3.1 (2021-07-28): toggle boolean/flags, quick value input hints
 * v0.3 (2021-07-23): bugfix, unsaved variables filter, non-English strings,
   all-quick-values-in-group button
 * v0.2.2 (2021-07-21): variables, menu editing, groups, quick values/deltas,
   ranges, captions
