Autosplitter for PoP1 SNES 100%

Discuss PoP1 for SNES here.
Post Reply
User avatar
Shauing
Calif
Calif
Posts: 425
Joined: April 5th, 2018, 10:38 pm
Contact:

Autosplitter for PoP1 SNES 100%

Post by Shauing »

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
David
The Prince of Persia
The Prince of Persia
Posts: 2790
Joined: December 11th, 2008, 9:48 pm
Location: Hungary

Re: Disassembly of PoP1 SNES

Post by David »

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.
These are kept track only for the current level, so a potion you've already drunk won't reappear and such.
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
(Assuming you are using snes9x-1.60-win32-x64.)

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
I'll see if I can find something for the guards.
User avatar
Shauing
Calif
Calif
Posts: 425
Joined: April 5th, 2018, 10:38 pm
Contact:

Re: Disassembly of PoP1 SNES

Post by Shauing »

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
David
The Prince of Persia
The Prince of Persia
Posts: 2790
Joined: December 11th, 2008, 9:48 pm
Location: Hungary

Re: Disassembly of PoP1 SNES

Post by David »

Shauing wrote: November 13th, 2022, 5:32 pm Nice. If you manage to find something that tracks the guards you kill, that would be awesome
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));
		}
	}
}
Caveat: A guard may die in a room different from where he starts the level.
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.
David
The Prince of Persia
The Prince of Persia
Posts: 2790
Joined: December 11th, 2008, 9:48 pm
Location: Hungary

Re: Disassembly of PoP1 SNES

Post by David »

David wrote: November 19th, 2022, 1:50 pm Problem: This does not seem to work with the skeleton on level 3.
Guard_alive stays -1 even when the skeleton is crushed.
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));
		}
	}
}
User avatar
Shauing
Calif
Calif
Posts: 425
Joined: April 5th, 2018, 10:38 pm
Contact:

Re: Disassembly of PoP1 SNES

Post by Shauing »

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
David
The Prince of Persia
The Prince of Persia
Posts: 2790
Joined: December 11th, 2008, 9:48 pm
Location: Hungary

Re: Disassembly of PoP1 SNES

Post by David »

Shauing wrote: November 20th, 2022, 9:33 am 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.
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.
Shauing wrote: November 20th, 2022, 9:33 am I assume that if you die, the counter for the potions already drunk and guards killed will reset?
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.
User avatar
Shauing
Calif
Calif
Posts: 425
Joined: April 5th, 2018, 10:38 pm
Contact:

Re: Disassembly of PoP1 SNES

Post by Shauing »

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.
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.

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
David
The Prince of Persia
The Prince of Persia
Posts: 2790
Joined: December 11th, 2008, 9:48 pm
Location: Hungary

Re: Disassembly of PoP1 SNES

Post by David »

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
Here is what I came up with:
SNES PoP autosplitter 2023-01-08.zip
(6.53 KiB) Downloaded 10 times
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.
User avatar
Shauing
Calif
Calif
Posts: 425
Joined: April 5th, 2018, 10:38 pm
Contact:

Re: Disassembly of PoP1 SNES

Post by Shauing »

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.
Sweet. I see the autosplitter uses in-game time, which could be rather useful for a more exact in-game time.

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
David
The Prince of Persia
The Prince of Persia
Posts: 2790
Joined: December 11th, 2008, 9:48 pm
Location: Hungary

Re: Disassembly of PoP1 SNES

Post by David »

Shauing wrote: January 13th, 2023, 10:44 pm Sweet. I see the autosplitter uses in-game time, which could be rather useful for a more exact in-game time.
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...)
Shauing wrote: January 13th, 2023, 10:44 pm - If the guard dies by falling down to a room below, the split will not register.
Indeed, I forgot to check if the autosplitter detects that.
I hopefully fixed it now.
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).
Yes, sorry, I indeed added that for testing and forgot to remove it.
Now I made it an option. It's off by default, but it's on in the included layout.
Shauing wrote: January 13th, 2023, 10:44 pm
David wrote: January 8th, 2023, 4:20 pm What if the player does the actions in different order?
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).
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.
Shauing wrote: January 13th, 2023, 10:44 pm
David wrote: January 8th, 2023, 4:20 pm What if the player (dies and) restarts a level?
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.
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:
SNES PoP autosplitter 2023-01-21.zip
(7.89 KiB) Downloaded 5 times
David
The Prince of Persia
The Prince of Persia
Posts: 2790
Joined: December 11th, 2008, 9:48 pm
Location: Hungary

Re: Autosplitter for PoP1 SNES 100%

Post by David »

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:
SNES PoP autosplitter 2023-01-28.zip
(9.04 KiB) Downloaded 3 times
User avatar
Shauing
Calif
Calif
Posts: 425
Joined: April 5th, 2018, 10:38 pm
Contact:

Re: Disassembly of PoP1 SNES

Post by Shauing »

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.
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 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?
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.
David wrote: January 8th, 2023, 4:20 pm 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.
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
Post Reply