Skip to content

Commit 44c3a13

Browse files
authored
Added Pizza Tower Splits And Auto Splitter (#9)
1 parent ef141f1 commit 44c3a13

File tree

2 files changed

+426
-0
lines changed

2 files changed

+426
-0
lines changed
Lines changed: 342 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
1+
-- Pizza Tower Auto Splitter
2+
-- By Penaz
3+
-- Based on the original work of ccarl
4+
process("PizzaTower.exe")
5+
6+
-- The variable that will contain the address for the beginning speedrun section of the memory
7+
local sig = nil
8+
9+
-- This variable is used to lock the auto splitter from splitting when it should not
10+
local can_split = false
11+
12+
-- This is an array containing the name of the rooms where the level exits are located
13+
local full_game_split_rooms = {
14+
tower_tutorial1 = true,
15+
tower_tutorial1N = true,
16+
entrance_1 = true,
17+
medieval_1 = true,
18+
ruin_1 = true,
19+
dungeon_1 = true,
20+
badland_1 = true,
21+
graveyard_1 = true,
22+
farm_2 = true,
23+
saloon_1 = true,
24+
plage_entrance = true,
25+
forest_1 = true,
26+
minigolf_1 = true,
27+
space_1 = true,
28+
street_intro = true,
29+
sewer_1 = true,
30+
industrial_1 = true,
31+
freezer_1 = true,
32+
chateau_1 = true,
33+
kidsparty_1 = true,
34+
war_13 = true,
35+
boss_pepperman = true,
36+
boss_vigilante = true,
37+
boss_noise = true,
38+
boss_fakepepkey = true,
39+
boss_pizzaface = true,
40+
boss_pizzafacefinale = true,
41+
tower_entrancehall = true,
42+
rank_room = true,
43+
}
44+
45+
-- This array contains the names of the rooms where we can unlock the splitting
46+
-- usually these are the rooms containing Pillar John (which starts Pizza Time)
47+
local split_unlock_rooms = {
48+
tower_tutorial10 = true,
49+
tower_tutorial3N = true,
50+
entrance_10 = true,
51+
medieval_10 = true,
52+
ruin_11 = true,
53+
dungeon_10 = true,
54+
badland_9 = true,
55+
graveyard_6 = true,
56+
farm_11 = true,
57+
saloon_6 = true,
58+
plage_cavern2 = true,
59+
forest_john = true,
60+
space_9 = true,
61+
minigolf_8 = true,
62+
street_john = true,
63+
sewer_8 = true,
64+
industrial_5 = true,
65+
freezer_escape1 = true,
66+
chateau_9 = true,
67+
kidsparty_john = true,
68+
war_1 = true,
69+
boss_pepperman = true,
70+
boss_vigilante = true,
71+
boss_noise = true,
72+
boss_fakepepkey = true,
73+
boss_pizzaface = true,
74+
tower_finalhallway = true,
75+
}
76+
77+
78+
-- The current state of the game
79+
local current = {
80+
file_minutes = nil,
81+
file_seconds = nil,
82+
level_minutes = nil,
83+
level_seconds = nil,
84+
room = nil,
85+
parsed_room= "Unknown",
86+
eol_fade_exists = false,
87+
boss_hp = nil,
88+
}
89+
90+
-- The state of the game in the previous read
91+
local old = {
92+
file_minutes = nil,
93+
file_seconds = nil,
94+
level_minutes = nil,
95+
level_seconds = nil,
96+
room = nil,
97+
parsed_room= "Unknown",
98+
eol_fade_exists = false,
99+
boss_hp = nil,
100+
}
101+
102+
-- Function to copy over data from one table to another
103+
function shallow_copy_tbl(t)
104+
local t2 = {}
105+
for k,v in pairs(t) do
106+
t2[k] = v
107+
end
108+
return t2
109+
end
110+
111+
-- Function used to print tables, useful for debugging purposes
112+
function print_tbl(t)
113+
for k,v in pairs(t) do
114+
print(k, " -> ", v)
115+
end
116+
end
117+
118+
-- Standard startup function, 60FPS, uses internal game time
119+
function startup()
120+
refreshRate = 60
121+
useGameTime = true
122+
end
123+
124+
function state()
125+
-- If the address is nil, either we just started up the auto splitter or we couldn't
126+
-- find the address reporting the signature.
127+
if sig == nil then
128+
print("Scanning...")
129+
-- C2 5A 17 ... Marks the beginning of the "speedrun section" of memory, provided it exists.
130+
sig = sig_scan("C2 5A 17 65 BE 4D DF D6 F2 1C D1 3B A7 A6 1F C3", 0)
131+
if sig == nil then
132+
-- If we didn't find it, it was either not allocated yet (better luck next loop) or PT was not started
133+
-- with the "-livesplit" option.
134+
print("Signature not found, make sure Pizza Tower is run with the -livesplit option")
135+
end
136+
else
137+
-- First of all, before updating the current state, copy it over to the previous one
138+
-- so we can tell the difference
139+
old = shallow_copy_tbl(current)
140+
-- At 0x80 (128) bytes after the beginning of the signature, we find the minutes spent on the savefile
141+
-- It is a double-precision float
142+
current.file_minutes = readAddress("double", sig + 0x80)
143+
-- At 0x88 we find the seconds
144+
current.file_seconds = readAddress("double", sig + 0x88)
145+
-- 0x90 -> Minutes spent on the level
146+
current.level_minutes = readAddress("double", sig + 0x90)
147+
-- 0x98 -> Minutes spent on the level
148+
current.level_seconds = readAddress("double", sig + 0x98)
149+
-- 0xA0 -> A 64-character string telling us the name of the room we're in (including cutscenes)
150+
current.room = readAddress("string64", sig + 0xA0)
151+
-- 0xE0 -> A boolean telling us if we're fading to the end-of-level
152+
current.eol_fade_exists = readAddress("bool", sig + 0xE0)
153+
-- 0xE1 -> A small integer telling us the remaining boss health
154+
current.boss_hp = readAddress("byte", sig + 0xE1)
155+
end
156+
end
157+
158+
function start()
159+
-- Start from new file. If we transition from the Intro to the Entrance, start the timer.
160+
if old.room == "Finalintro" and current.room == "tower_entrancehall" then
161+
return true
162+
end
163+
-- Start from loaded file. If we transition from the loading screen to the Entrance, start the timer.
164+
if old.room == "hub_loadingscreen" and current.room == "tower_entrancehall" then
165+
return true
166+
end
167+
-- If none of the above applies. Do nothing.
168+
return false
169+
end
170+
171+
function split()
172+
-- If we cannot split yet, check if the current room can "unlock" the split lock.
173+
-- If it is already unlocked, don't do anything (or it may "re-lock")
174+
if not can_split then
175+
can_split = split_unlock_rooms[current.room] ~= nil
176+
end
177+
-- If we changed room
178+
if old.room ~= current.room then
179+
-- And we can split
180+
if can_split then
181+
-- Then parse the current room name into something that makes more sense (there are many hubs in the tower, for instance)
182+
current.parsed_room = getCurrentLevel(current.room, old.parsed_room)
183+
-- If the room we just passed contains an exit...
184+
if full_game_split_rooms[old.room] ~= nil then
185+
-- And we ended in the Results Screen (normal levels) or a room in the Tower (for the boss outro skip)
186+
if current.parsed_room == "ResultsScreen" or current.parsed_room == "Hub" then
187+
-- And the boss HP is depleted (which is always true on normal levels)
188+
if old.boss_hp == 0 then
189+
-- Re-lock the split lock for the next time
190+
can_split = false
191+
-- Allow Libresplit to split
192+
return true
193+
end
194+
end
195+
end
196+
end
197+
end
198+
-- Frame perfect End of Run split. This is very specific for the the of "The Crumbling Tower of Pizza"
199+
-- Where it would not split "frame-perfectly" otherwise.
200+
if (current.eol_fade_exists and not old.eol_fade_exists) and current.room == "tower_entrancehall" then
201+
return true
202+
end
203+
return false
204+
end
205+
206+
function reset()
207+
-- Reset on new save
208+
if current.room == "Finalintro" and old.room ~= "Finalintro" then
209+
return true
210+
end
211+
-- Reset on loading save
212+
if current.room ~= old.room then
213+
if current.room == "hub_loadingscreen" then
214+
return true
215+
end
216+
end
217+
return false
218+
end
219+
220+
function gameTime()
221+
-- Since we have the file minutes and seconds, we just chuck them into the game time
222+
-- if not null
223+
if (current.file_minutes ~= nil and current.file_seconds ~= nil) then
224+
return current.file_minutes * 60000 + current.file_seconds * 1000
225+
end
226+
return 0
227+
end
228+
229+
-- A translation function for the level names into something that makes more sense
230+
-- For instance every room starting with "tower_" is a Hub room, unless we started a CTOP run.
231+
-- Or if we started a "Secrets of the world" run, all secret levels will be part of SOTW, instead of "normal secrets"
232+
function getCurrentLevel(room_name, prev_level)
233+
if prev_level == "F5CrumblingTower" and string.match(room_name, "tower_") and not string.match(room_name, "tower_pizzafacehall") then
234+
return "F5CrumblingTower"
235+
end
236+
if prev_level == "SecretsOfTheWorld" and string.match(room_name, "secret") then
237+
return "SecretsOfTheWorld"
238+
end
239+
if string.match(room_name, "tower_finalhallway") then
240+
return "F5CrumblingTower"
241+
end
242+
if room_name=="tower_tutorial1N" then
243+
return "F1TutorialNoise"
244+
end
245+
if room_name=="tower_tutorial2N" then
246+
return "F1TutorialNoise"
247+
end
248+
if room_name=="tower_tutorial3N" then
249+
return "F1TutorialNoise"
250+
end
251+
if string.match(room_name, "tower_tutorial") then
252+
return "F1Tutorial"
253+
end
254+
if string.match(room_name, "tower_") then
255+
return "Hub"
256+
end
257+
if string.match(room_name, "boss_pizzafacehub") then
258+
return "Hub"
259+
end
260+
if string.match(room_name, "entrance_") then
261+
return "F1JohnGutter"
262+
end
263+
if string.match(room_name, "medieval_") then
264+
return "F1Pizzascape"
265+
end
266+
if string.match(room_name, "ruin_") then
267+
return "F1AncientCheese"
268+
end
269+
if string.match(room_name, "dungeon_") then
270+
return "F1BloodsauceDungeon"
271+
end
272+
if room_name == "boss_pepperman" then
273+
return "Pepperman"
274+
end
275+
if string.match(room_name, "badland_") then
276+
return "F2OreganoDesert"
277+
end
278+
if string.match(room_name, "graveyard_") then
279+
return "F2Wasteyard"
280+
end
281+
if string.match(room_name, "farm_") then
282+
return "F2FunFarm"
283+
end
284+
if string.match(room_name, "saloon_") then
285+
return "F2FastfoodSaloon"
286+
end
287+
if room_name == "boss_vigilante" then
288+
return "Vigilante"
289+
end
290+
if string.match(room_name, "plage_") then
291+
return "F3CrustCove"
292+
end
293+
if string.match(room_name, "forest_") then
294+
return "F3GnomeForest"
295+
end
296+
if string.match(room_name, "space_") then
297+
return "F3DeepDish9"
298+
end
299+
if string.match(room_name, "minigolf_") then
300+
return "F3Golf"
301+
end
302+
if room_name == "boss_noise" then
303+
return "Noise"
304+
end
305+
if string.match(room_name, "street_") then
306+
return "F4ThePigCity"
307+
end
308+
if string.match(room_name, "industrial_") then
309+
return "F4PeppibotFactory"
310+
end
311+
if string.match(room_name, "sewer_") then
312+
return "F4OhShit"
313+
end
314+
if string.match(room_name, "freezer_") then
315+
return "F4Refrigerator"
316+
end
317+
if string.match(room_name, "boss_fakepep") then
318+
return "Fake"
319+
end
320+
if string.match(room_name, "secret_entrance") then
321+
return "SecretsOfTheWorld"
322+
end
323+
if string.match(room_name, "trickytreat") then
324+
return "TrickyTreat"
325+
end
326+
if string.match(room_name, "cheateau_") then
327+
return "F5Pizzascare"
328+
end
329+
if string.match(room_name, "kidsparty_") then
330+
return "F5DMAS"
331+
end
332+
if string.match(room_name, "war_") then
333+
return "F5War"
334+
end
335+
if room_name == "boss_pizzaface" then
336+
return "Pizzaface"
337+
end
338+
if room_name == "rank_room" then
339+
return "ResultsScreen"
340+
end
341+
return "Unknown"
342+
end

0 commit comments

Comments
 (0)