Autosplitter for PoP1 SNES 100%
Autosplitter for PoP1 SNES 100%
I want to ask if inside the game's code, is there an area where it keep track of the potions you've taken and the guards you have killed throughout the game. On the speedrun community we're trying to create the 100% category, and because the game itself doesn't have any gallery or anything similar that displays what have you grabbed, we were wondering maybe inside the game's memory/coding, there's something that keeps track of these things.
NEW UPDATE! Prince Of Persia: 30th Anniversary Port v1.1.5. Download it today!: viewtopic.php?p=29053#p29053
NEW UPDATE! Prince Of Persia: The Queen Of Light v2.6. Download it today! viewtopic.php?p=33174#p33174
NEW UPDATE! Prince Of Persia: The Queen Of Light v2.6. Download it today! viewtopic.php?p=33174#p33174
Re: Disassembly of PoP1 SNES
These are kept track only for the current level, so a potion you've already drunk won't reappear and such.Shauing wrote: ↑November 8th, 2022, 8:12 pm I want to ask if inside the game's code, is there an area where it keep track of the potions you've taken and the guards you have killed throughout the game. On the speedrun community we're trying to create the 100% category, and because the game itself doesn't have any gallery or anything similar that displays what have you grabbed, we were wondering maybe inside the game's memory/coding, there's something that keeps track of these things.
They are reset when you restart the level.
The level objects (including potions) are stored starting from RAM address 7F:E170, on 24*30 bytes (one byte for each tile of each room).
When the player drinks a potion, the corresponding byte changes to zero.
For example, consider the first potion on level 1, under the green guard.
It's in room 8, at tile 27 (row 2, column 7), so its RAM address is 0x7FE170 + 8*30+27 == 0x7FE27B.
(This calculation assumes that rooms, rows, and columns are all counted from 0, as in Pr1SnesLevEd.)
When that byte changes to 0 (from 0x12), you know the player picked up this potion.
In an AutoSplit file, that offset translates into this:
Code: Select all
byte Potion_1_8_27 : "snes9x-x64.exe", 0x008D8C38, 0x1E27B; // 7F:E27B
The final offset is calculated as: 0x7FE27B - 0x7E0000 = 0x1E27B.
Now of course you need to check if the current level is level 1 before checking any potions on level 1.
Here is the AutoSplit offset for the current level:
Code: Select all
byte CurrentLevel : "snes9x-x64.exe", 0x008D8C38, 0x0579; // 7E:0579
Re: Disassembly of PoP1 SNES
Nice. If you manage to find something that tracks the guards you kill, that would be awesome and most likely will allow us to have a way to track them via splits in Livesplit with autosplitter, thus maybe use game end and continue for faster strategies, and make Level 11 possible to complete without an intentional death.
NEW UPDATE! Prince Of Persia: 30th Anniversary Port v1.1.5. Download it today!: viewtopic.php?p=29053#p29053
NEW UPDATE! Prince Of Persia: The Queen Of Light v2.6. Download it today! viewtopic.php?p=33174#p33174
NEW UPDATE! Prince Of Persia: The Queen Of Light v2.6. Download it today! viewtopic.php?p=33174#p33174
Re: Disassembly of PoP1 SNES
I came up with this:
Code: Select all
state("snes9x-x64")
{
byte CurrentLevel : "snes9x-x64.exe", 0x008D8C38, 0x0579; // 7E:0579
sbyte Guard_alive : "snes9x-x64.exe", 0x008D8C38, 0x0486; // 7E:0486
byte Guard_room : "snes9x-x64.exe", 0x008D8C38, 0x0482; // 7E:0482
byte Guard_direction : "snes9x-x64.exe", 0x008D8C38, 0x047A; // 7E:047A
}
update
{
//print("Guard_direction = " + current.Guard_direction);
// 0x00=right, 0x80=left, 0x7F=no guard
if (current.Guard_direction != 0x7F) // if there is an active guard
{
//print("Guard_alive = " + current.Guard_alive);
// -1 (0xFF) if alive, 0 if dead
//print("Guard_room = " + current.Guard_room);
if (current.Guard_alive == 0 && old.Guard_alive == -1)
{
print("A guard died in room " + current.Guard_room + " of level " + (current.CurrentLevel+1));
}
}
}
This can be a problem if you need to know which guards died.
If you only need the number of guards who died on a level, then you can increase a counter every time a guard dies, then check its value when the level ends.
Problem: This does not seem to work with the skeleton on level 3.
Guard_alive stays -1 even when the skeleton is crushed.
Re: Disassembly of PoP1 SNES
Here is a version which fixes that.
Code: Select all
state("snes9x-x64")
{
sbyte Guard_alive : "snes9x-x64.exe", 0x008D8C38, 0x0486; // 7E:0486
byte Guard_room : "snes9x-x64.exe", 0x008D8C38, 0x0482; // 7E:0482
byte Guard_direction : "snes9x-x64.exe", 0x008D8C38, 0x047A; // 7E:047A
byte Guard_hp : "snes9x-x64.exe", 0x008D8C38, 0x050B; // 7E:050B
byte Guard_frame : "snes9x-x64.exe", 0x008D8C38, 0x0477; // 7E:0477
}
update
{
//print("Guard_direction = " + current.Guard_direction);
// 0x00=right, 0x80=left, 0x7F=no guard
if (current.Guard_direction != 0x7F) // if there is an active guard
{
//print("Guard_alive = " + current.Guard_alive);
// -1 (0xFF) if alive, 0 if dead
//print("Guard_room = " + current.Guard_room);
//print("Guard_hp = " + current.Guard_hp);
//print("Guard_frame = " + current.Guard_frame);
// 184 = crushed
if ((current.Guard_alive == 0 || current.Guard_frame == 184) && (old.Guard_alive == -1 && old.Guard_frame != 184))
{
print("A guard died in room " + current.Guard_room + " of level " + (current.CurrentLevel+1));
}
}
}
Re: Disassembly of PoP1 SNES
Sweet; so if I understand correctly, with these findings the autosplitter will check for the potions per level (does this include the kill potion on level 18 or not?) and the number of guards per level as well. I assume that if you die, the counter for the potions already drunk and guards killed will reset?
NEW UPDATE! Prince Of Persia: 30th Anniversary Port v1.1.5. Download it today!: viewtopic.php?p=29053#p29053
NEW UPDATE! Prince Of Persia: The Queen Of Light v2.6. Download it today! viewtopic.php?p=33174#p33174
NEW UPDATE! Prince Of Persia: The Queen Of Light v2.6. Download it today! viewtopic.php?p=33174#p33174
Re: Disassembly of PoP1 SNES
Only if someone writes the code for that.
I only searched for the memory locations needed for these checks.
The code blocks in my previous posts just print a debug message when a guard dies, they don't count anything.
I have not yet looked into how to detect when the level is restarted.
By the way, how would these checks appear to the users of the autosplitter script?
As I see it, autosplitter scripts have limited interaction features.
They can tell LiveSplit the game time, tell when a level of the game ended, or reset the times.
They can also output debug messages which only developers can see.
But, unless I missed something, they can't directly display things on the GUI of LiveSplit.
Re: Disassembly of PoP1 SNES
As Livesplit lets you add splits for what the speedrunner needs them (an item, a location, a fight, a boss, just to mention a few examples), I myself was theorizing that, if the game kept track of the guards killed and potions drank, we could assign a split for each time it checks for when a guard is killed and a potion has been drunk, and/or at the end of each level if all the potions and guards have been drank/killed, also have a split there and continue with the first one for the next level.David wrote: ↑November 26th, 2022, 1:54 pm By the way, how would these checks appear to the users of the autosplitter script?
As I see it, autosplitter scripts have limited interaction features.
They can tell LiveSplit the game time, tell when a level of the game ended, or reset the times.
They can also output debug messages which only developers can see.
But, unless I missed something, they can't directly display things on the GUI of LiveSplit.
For example:
Level 1 splits:
- Heal potion at room 8, tile 27
- Heal potion at room 12, tile 4
- Sword (if there's a check for it)
- Heal potion at room 23, tile 1
- Guard at room 8, tile 6
- Guard at room 1, tile 17
- Level exit
Or if it can't be specific due to the guards if they move, just check when they die:
- Heal potion
- Heal potion
- Sword (if there's a check for it)
- Heal potion
- Guard
- Guard
- Level exit
NEW UPDATE! Prince Of Persia: 30th Anniversary Port v1.1.5. Download it today!: viewtopic.php?p=29053#p29053
NEW UPDATE! Prince Of Persia: The Queen Of Light v2.6. Download it today! viewtopic.php?p=33174#p33174
NEW UPDATE! Prince Of Persia: The Queen Of Light v2.6. Download it today! viewtopic.php?p=33174#p33174
Re: Disassembly of PoP1 SNES
Here is what I came up with: So far it's only levels 1-2.Shauing wrote: ↑November 26th, 2022, 11:45 pm [...] if the game kept track of the guards killed and potions drank, we could assign a split for each time it checks for when a guard is killed and a potion has been drunk, [...]
For example:
Level 1 splits:
- Heal potion at room 8, tile 27
- Heal potion at room 12, tile 4
- Sword (if there's a check for it)
- Heal potion at room 23, tile 1
- Guard at room 8, tile 6
- Guard at room 1, tile 17
- Level exit
I figured out how to follow guards moving to other rooms.
Open questions:
- What if the player does the actions in different order?
Currently they won't register. - What if the player (dies and) restarts a level?
Currently the current split remains the same, and all already done actions don't need to be done again.
Re: Disassembly of PoP1 SNES
Sweet. I see the autosplitter uses in-game time, which could be rather useful for a more exact in-game time.David wrote: ↑January 8th, 2023, 4:20 pm
Here is what I came up with:
SNES PoP autosplitter 2023-01-08.zip
So far it's only levels 1-2.
I figured out how to follow guards moving to other rooms.
Open questions:
- What if the player does the actions in different order?
Currently they won't register.- What if the player (dies and) restarts a level?
Currently the current split remains the same, and all already done actions don't need to be done again.
I tested it around to see if I could find something else, and I did find a couple of things:
- If the guard dies by falling down to a room below, the split will not register.
- If you finish the level even if you didn't do any of other splits for the level first (or use the level skip code), it will jump up to the current level exit split (of course one should not do that on a 100% run but just wanted to point out this; at least this can make testing other levels faster).
So about the open questions:
So I assume that if the player wants to do a different order, they might have to alter the script order to accommodate their routing (unless there's a way for the autosplitter to accommodate for different routing via maybe checking all the possibilities for a split per level and make it split automatically whenever one of them match).
It might be neat to have two autosplitters, one where all the already done actions don't need to be done again if you die (like this current autosplitter does), and another where you have to do everything again. The former though, might allow the possibility of getting everything in Level 11 via taking a death after getting everything in the bottom or top route first, as it won't restart.
NEW UPDATE! Prince Of Persia: 30th Anniversary Port v1.1.5. Download it today!: viewtopic.php?p=29053#p29053
NEW UPDATE! Prince Of Persia: The Queen Of Light v2.6. Download it today! viewtopic.php?p=33174#p33174
NEW UPDATE! Prince Of Persia: The Queen Of Light v2.6. Download it today! viewtopic.php?p=33174#p33174
Re: Disassembly of PoP1 SNES
By the way, I changed the timing method in the layout back from Game Time to Current Timing Method,
because I finally figured out how to select Game Time as the current. (right click -> Compare Against -> Game Time)
(Yeah, that's a silly thing to overlook...)
Indeed, I forgot to check if the autosplitter detects that.
I hopefully fixed it now.
Yes, sorry, I indeed added that for testing and forgot to remove it.Shauing wrote: ↑January 13th, 2023, 10:44 pm - If you finish the level even if you didn't do any of other splits for the level first (or use the level skip code), it will jump up to the current level exit split (of course one should not do that on a 100% run but just wanted to point out this; at least this can make testing other levels faster).
Now I made it an option. It's off by default, but it's on in the included layout.
I added an option for the doing actions in any order. It's enabled by default.Shauing wrote: ↑January 13th, 2023, 10:44 pmSo I assume that if the player wants to do a different order, they might have to alter the script order to accommodate their routing (unless there's a way for the autosplitter to accommodate for different routing via maybe checking all the possibilities for a split per level and make it split automatically whenever one of them match).
If enabled, the autosplitter will recognize any action any time, but the splits display will advance only when the currently highlighted action is done.
(Because an autosplitter can't mark a later split as done, nor can it change the order of splits.)
So if the player does a later action in advance, nothing will happen in the splits list.
But when they do the previous action, the highlight will advance through all completed actions.
How would "doing everything again" work?
Would it reset the splits to the very beginning (level 1), or to the beginning of the current level?
For resetting to level 1, do we want that?
For resetting to the current level, is that even possible?
Here is the updated version, now with splits for levels 1-4:
Re: Autosplitter for PoP1 SNES 100%
Fixed a bug which caused the splitter to falsely detect a guard falling out of the screen, when the player merely left the room of a living guard.
Here is the updated version, now with splits for levels 1-9:
Here is the updated version, now with splits for levels 1-9:
Re: Disassembly of PoP1 SNES
Great! I imagine if runners want to reorder their splits in a different order so that they can measure their splits more accurately (per action), they would have to change the order in the .asl file. Is cool though that later actions will be marked once the previous action is completed.David wrote: ↑January 8th, 2023, 4:20 pm I added an option for the doing actions in any order. It's enabled by default.
If enabled, the autosplitter will recognize any action any time, but the splits display will advance only when the currently highlighted action is done.
(Because an autosplitter can't mark a later split as done, nor can it change the order of splits.)
So if the player does a later action in advance, nothing will happen in the splits list.
But when they do the previous action, the highlight will advance through all completed actions.
If the player dies (or goes to game end), the actions completed for the current level could be remarked as not completed (if that's even possible). They could restart the level by going to game end and then select the password/continue option. This is only for the case of Level 11 that a heal potion is located at a point of no return which makes impossible to grab everything in one life, and Level 18's Kill Potion.
Speaking of bugs, if the last skeleton at the end of Level 3 is killed by falling instead of killing it via the crusher, it won't register the death. This skeleton can be killed via falling by bringing him to the route of where the bottom heal potion is. I forgot if this method is faster than just throwing him into the crusher, but it's a possibility.
I'll be checking this new autosplitter and look for possible bugs/issues.
NEW UPDATE! Prince Of Persia: 30th Anniversary Port v1.1.5. Download it today!: viewtopic.php?p=29053#p29053
NEW UPDATE! Prince Of Persia: The Queen Of Light v2.6. Download it today! viewtopic.php?p=33174#p33174
NEW UPDATE! Prince Of Persia: The Queen Of Light v2.6. Download it today! viewtopic.php?p=33174#p33174
Re: Disassembly of PoP1 SNES
I don't think it's possible to "partially reset" the splits from an autosplitter.
Here is a list of what's possible.
Though it's possible to manually undo a split (or multiple splits).
Oops, I didn't even realize that's possible!Shauing wrote: ↑January 29th, 2023, 9:58 am Speaking of bugs, if the last skeleton at the end of Level 3 is killed by falling instead of killing it via the crusher, it won't register the death. This skeleton can be killed via falling by bringing him to the route of where the bottom heal potion is. I forgot if this method is faster than just throwing him into the crusher, but it's a possibility.
I'll see what I can do.
Re: Disassembly of PoP1 SNES
I think I solved this.David wrote: ↑February 4th, 2023, 3:05 pmOops, I didn't even realize that's possible!Shauing wrote: ↑January 29th, 2023, 9:58 am Speaking of bugs, if the last skeleton at the end of Level 3 is killed by falling instead of killing it via the crusher, it won't register the death. This skeleton can be killed via falling by bringing him to the route of where the bottom heal potion is. I forgot if this method is faster than just throwing him into the crusher, but it's a possibility.
I'll see what I can do.
I figured out how to tell the difference between the skeleton falling into another room, and a guard (or the skeleton) falling offscreen and disappearing for good.
In the former case, the splitter now follows the skeleton, just like it already followed guards walking into another room.
In the latter case, the guard is counted as dead as before.
So whenever the skeleton dies either way, the splitter knows it's the skeleton which started in room 13.
Now that I think of it, it might have been easier to just check the type of the opponent, instead of following the skeleton through all the rooms. Oh well...
I also fixed a bug, wherein the splitter did not detect a guard falling offscreen, if he did not start in the same room where he fell offscreen.
Here is the new version: