OvenInjector is a modloader for Pizza Tower! Inspired by ToppinLoader, this loads mods in a modular way for users to easily use and for modders to easily make. As this is currently a work in progress not everything is complete, and major changes may come between updates.
- Load multiple mods
- Easy to use mod menu
- A (hopefully simple) Modding API
- Powered by GameMaker Language (GML)
Lua soon?
This guide is an introduction on what the mod loader does, how to use it, and how to make mods for it.
Contributing
OvenInjector (the mod) is free and open source. The mod uses a decompile of the Noise Update (PT 1.1.0), and therefore may be in a different license. Feel free to report issues in the GitHub issue tracker. If you'd like to contribute/implement a new feature, consider making a pull request.
You can implement the mod in other projects as you please, provided you follow the license.
License
OpenTower (Pizza Tower Decompilation) is under CC0-1.0. All custom code related to OvenInjector is under the MIT License.
Installation
There are multiple ways to install OvenInjector. You can either download it from the GameBanana page or compile it manually.
GameBanana
OvenInjector is distributed in GameBanana. This is the recommended way to install the mod.
The archive includes the xdelta
patch for you to patch via Delta Patcher or any other xdelta
patcher.
Pizza Oven is broken currently and cannot patch this properly. Please use Delta Patcher to patch your data.win
.
Compile via GameMaker
Compiling via GameMaker is also an option, though you will need these in order to compile it properly:
- Pizza Tower on Steam
- GameMaker LTS 2022 w/ Runtime 2022.0.1.30
This seems to be the exact version Tour De Pizza used. 2023.6 might work, though I have not tested it yet. - GMLive.gml
The repo does not provide GMLive as-is due to the extension being a paid product.
The source code doesn't include any of the datafiles (FMOD, langs) or sprites. An UndertaleModTool script is included to extract everything needed from the data.win
, and port the required files to the decomp folder.
Open up your Pizza Tower data.win
on UndertaleModTool, open up the Scripts
tab at the top of the window, and select "Run other script..."
Select PTdecompiler.csx from where you stored OvenInjector's source code. Make sure you point it in the same exact folder when prompted to. It may take a while to extract every sprite, so give it atleast 15 minutes.
Open up PizzaTower_GM2.yyp
in GameMaker LTS 2022. (this is important!) The project won't compile due to GMLive being missing.
Click on the "Tools" tab on the top of the GameMaker IDE, and select "Import Local Package". Select GMLive's .yymps
file, and import all assets.
You should now be able to run the game via F7. Give it atleast 20 minutes.
Usage
So you just downloaded OvenInjector. Great! Now where are the mods, you may ask? Well, you make them. Mods don't grow on trees, especially with a mod loader this new!
OvenInjector already comes with 3 mods by default for you to play around with!
- Breakplant
- This mod brings back the Breakdance and Faceplant moves seen in earlier Pizza Tower builds
- Lap 3
- A full port of Warioplier's Lap 3 mod! Enjoy getting that joocy p rank while running from pita face
- Pizza Multi
- "Destroy one, another one will take it's place."
Open up your options menu in-game or in the title screen, and select "MOD MENU". Congratition!
You should now be able to enable and disable mods, as well as their configuration via the taunt key if they have one.
You can also press the shoot key (configurable in the bindings menu) to make a mod automatically be loaded after launching the game. Not implemented yet. ;)
mod.ini
OvenInjector uses a mod.ini
to differentiate and identify mods.
Let's just say there's a mod folder
mods/
next, we add a new folder that will hold all of the script and asset files of our mod
mods/
- Sample Mod/
with this, a mod.ini
file is required, which contains all the information needed for a mod to work.
[meta]
name="Sample Mod"
description="An example mod."
author="haya3218"
version="1.0"
icon="path/to/your/icon.png"
icon_base64="this can also be used if you want your icon to be b64 encoded instead, CST1229 TL mod loader style!"
The meta
block defines metadata and general info about the mod. An optional gml
block can also be added, if you want your gml scripts properly named.
The gml
block takes numerous event callbacks. Which are:
init
destroy
begin_step
step
end_step
begin_draw
draw
end_draw
begin_draw_gui
draw_gui
end_draw_gui
room_start
room_end
By default, the game would look for these names plus the gml extension on the scripts folder for these events, except for the gui
events. The draw_
part is ommited from those.
Now the general structure should now look like this:
mods/
- Sample Mod/
- scripts/
- init.gml
- step.gml
- mod.ini
Organized as it probably should. Hopefully.
Non-API
This section includes topics that are not strictly API-related. This is mostly here to explain or clarify specific features.
Custom Controllers
Each mod works with a custom object (or controller) that runs the compiled gml files at runtime. This is akin to running gml code via obj_custom
on ToppinLoader, though this is done by the mod loader itself instead of by you.
You can create new custom controllers via instance_custom
.
// This will load scripts from scripts/obj_gooch
var gooch_id = instance_custom(0, 0, "obj_gooch");
// ... do stuff with gooch_id...
// gooch scout digo real
persistent = false;
sprite_index = spr_pizzaface_haywire;
image_speed = 0.35;
maxspeed = 3;
The above example will create a new custom controller running from the obj_gooch
folder, and is instantiated from the init script.
You can still create new custom controller objects the old way, though be wary of making them persistent and have a modname
variable, which is the name
value of your mod.ini
's meta
block. You can access the base object controller's modname via simply referencing the modname
variable on it's own.
// Insert cool stuff here
with (instance_create(0, 0, obj_customcontroller))
{
// custom object is now persistent, meaning it'll stay regardless of being in a new room or not.
persistent = true;
// best to pass in the base script's modname for referencing it when the mod is disabled
modname = other.modname;
// This custom controller will run even when paused.
runPaused = true;
// If you don't want to man handle the object created on different step files, you can also manually create gml snippets here as well!
events.step = live_snippet_create(@'
trace("Hello!");
');
}
Asset Loading
Assets are loaded mostly the same with ToppinLoader, though the path to the assets are defined automatically instead of making users put the mod folder at a specific path.
// Look ma, I'm loading my assets!
var assetpath = mod_dir + "assets/";
mu_lap3 = audio_create_stream(assetpath + "mu_lap3.ogg");
This will load assets/mu_lap3.ogg
and store a stream id for use with audio functions relative to the path of the location of the mod's ini file.
Music Handling
Music is a pain. Namely FMOD is still unavailable during this time (which might change...) but music isn't too much of a hassle unless you REALLY want to change a lot. If so, use a custom controller instead.
GameMaker Audio System
Custom music can be handled via scr_play_external_music(path)
and scr_stop_external_music()
, which play and stop external music respectively. These also account for your volume config as well as the pause menu. Do note that scr_play_external_music
will ALWAYS stop the current running FMOD music, though this may change.
You can also instance obj_musiccontroller
yourself, though I don't recommend it. Just use obj_customcontroller
instead.
FMOD
TBD.
Mod-Specific Options
You can now make players configure your mods via the options
key in the meta
block! Like so:
[meta]
name="yeah"
options="enable_something,another_toggle"
Pressing TAUNT on the mod menu while hovering an active mod will bring up the options related to that mod!
Options have a default value of zero, though these can be changed via the options
block:
[options]
enable_something="1"
enable_something
will now be "ON" by default.
You can access these via referencing the option name in your scripts.
if enable_something
yeah();
else
nah();
If the option enable_something
is on, it'll call yeah()
here, and if it's off, it will run nah()
instead.
Do note that this is NOT a global constant yet, and therefore is specified in the custom controller itself, so to access it within a with
block be sure to prefix it with other
.
with (obj_player)
{
// This will error!
if enable_something { }
// This won't
if other.enable_something { }
}
Startup Mods
Do you want a mod to be enabled immediately after launching the game instead of waiting until you get into the mod menu?
Simply put a startup.txt
file in your mods folder like so:
# A comment like this is ignored.
// This too.
Lap 3
Some Other Mod
Another Mod
This will load Lap 3, Some Other Mod, and Another Mod in that order. This is based on the folder name of the mod however!
Language Definitions
This is not implemented yet, though boilerplate has been added to account for this in the future!
Fun fact: OvenInjector does not touch any of the other files apart from your data.win
. The mod loader includes it's own localization file named custom.txt
located in your mod folder.
This file handles all of the language definitions exclusive to the mod (such as the name for the mod menu in the options and the shoot key).
Mods can also have their own individual language definitions as well.
# Removing this file may result in crashes.
# This is required, as this is based on the current language selected.
lang = "en"
# Put all your stuff here.
lang_name_here = "STRING BOYS"
trace(lang_get_value("lang_name_here"));
Both of these files would be located in your mod folder, as per usual.
This would've printed a language definition named lang_name_here
, defined in the language file.