Are POP2's controls supposed to be this bad, or is something wrong with my setup?

Second part of the best game ever made.
BlueBarracudas
Efendi
Efendi
Posts: 12
Joined: May 21st, 2018, 10:07 pm

Are POP2's controls supposed to be this bad, or is something wrong with my setup?

Post by BlueBarracudas »

I recently played through POP1 on DOS and SNES, and have just started POP2 for DOS (in DOSBox). I'm having some serious problems with the POP2 controls - I haven't even managed to make it through the first level. I figured this seemed like the best place to ask about it. It seems like the game is extremely finicky with accepting multiple keystrokes at once, like when the Prince is running and I want to jump or start sneaking. My first question is, is this normal? If so, how do you get used to it? If not, what could be causing it and how can I fix it?

It seems like, if I'm holding 4 to run left, the game only responds to my input to make a running jump roughly half the time (regardless of whether I press 7 or 8).

The shift key isn't acting properly either. If I hold shift and press in the direction I'm facing, I will make a careful step. If I hold it and press the key to go in the opposite direction, I will turn, but if I continue to hold the direction key, I will start running, and can keep doing so, changing directions as much as I want, all with the shift key held. And if I'm running or jumping and press the shift key, the game completely ignores it. Also, whenever I do manage to pull off a running jump, if I need to press shift to catch a ledge, the Prince only ever seems to manage to grab on for a split second before dropping (without me ever letting go of the shift key).

Like I said, I just played through POP1 for DOS on the same hardware/software, so I think I can rule out any issues with the keyboard itself.

For the record, I used the numpad keys in my examples above because that's what the manual seems to consider the default. I'm on a laptop with no numpad, so I've been using the WER/SDF control scheme - I plan on remapping it with GlovePIE to be more consistant with POP1's scheme, but I need to resolve this issue first.

I'm not sure which version of POP2 I have. I would guess it's 1.1, but is there a way to determine it just from the game files?

Also, a minor thing - does the MT-32 version of the soundtrack not include the voiced intro? I enabled it (using DOSBox and Munt), and now the intro has no speech (but nicer music). Is that supposed to happen?
User avatar
Norbert
The Prince of Persia
The Prince of Persia
Posts: 5743
Joined: April 9th, 2009, 10:58 pm

Re: Are POP2's controls supposed to be this bad, or is something wrong with my setup?

Post by Norbert »

You can check the version with Alt+v.

I recognize the keyboard problems you describe.
In 2013, I was (re)playing PoP2 now and then, and with time kinda got used to the problematic movement.
Maybe players who aren't used to the PoP1 movement have an easier time with PoP2.
BlueBarracudas
Efendi
Efendi
Posts: 12
Joined: May 21st, 2018, 10:07 pm

Re: Are POP2's controls supposed to be this bad, or is something wrong with my setup?

Post by BlueBarracudas »

Norbert wrote: May 29th, 2018, 7:33 pm I recognize the keyboard problems you describe.
In 2013, I was (re)playing PoP2 now and then, and with time kinda got used to the problematic movement.
Maybe players who aren't used to the PoP1 movement have an easier time with PoP2.
I think I'm starting to get used to it - I managed to finish the first level, anyway. I guess I just need to accept that the window for executing a running jump is a lot smaller than in POP1, and remember to let go of the jump key before pressing shift anytime I want to catch a ledge. A shame they went and fixed something that wasn't broke.
Norbert wrote: May 29th, 2018, 7:33 pm You can check the version with Alt+v.
That just brings up the text "Prince of Persia 2". I guess that makes it 1.0? Edit: Or maybe it's IR? See below.

Edit: Oh, also I figured out what was wrong with my sound settings. I had Roland MT-32 set for both the Sound Device and Music Synthesizer. To keep the speech and have (subjectively) punchier sound effects, I had to switch Sound back to SoundBlaster Pro.

Second Edit: According to http://popuw.com/problems.html , the v1.0 and 1.1 had improved/easier running/jumping timings as compared to the IR version. Maybe that's the one I have?
User avatar
Norbert
The Prince of Persia
The Prince of Persia
Posts: 5743
Joined: April 9th, 2009, 10:58 pm

Re: Are POP2's controls supposed to be this bad, or is something wrong with my setup?

Post by Norbert »

BlueBarracudas wrote: May 30th, 2018, 8:03 amThat just brings up the text "Prince of Persia 2".
That's the IR (initial release) version.
PoP2_version_info.png
(You can get 1.1 here, among other places.)
BlueBarracudas wrote: May 30th, 2018, 8:03 am[...], the v1.0 and 1.1 had improved/easier running/jumping timings [...]
Maybe a tiny bit better, but my comment about recognizing it's bad was about 1.1, so don't expect too much.
BlueBarracudas
Efendi
Efendi
Posts: 12
Joined: May 21st, 2018, 10:07 pm

Re: Are POP2's controls supposed to be this bad, or is something wrong with my setup?

Post by BlueBarracudas »

Norbert wrote: May 30th, 2018, 10:05 amMaybe a tiny bit better, but my comment about recognizing it's bad was about 1.1, so don't expect too much.
Just tried 1.0, and it does seem to control a bit better than the IR version, though it's possible I'm just getting used to POP2 in general.
User avatar
Alberto
Sheikh
Sheikh
Posts: 48
Joined: February 13th, 2019, 6:55 am
Contact:

Re: Are POP2's controls supposed to be this bad, or is something wrong with my setup?

Post by Alberto »

I'm too late to the discussion, sorry if I'm necro-ing this thread.

I'm also playing on DOSBox and got the same errors, however after some quick configuration of the options file, the problems seem to have disappeared for good. What I did was lower the cycles counter (default was 50 000), the best behavior is between 3000 and 30 000. If the cycles are set too high the game starts to lag making the inputs a lot harder to pull off, ghost triggers, stuck inputs, and sound stutter.

Hope this helps someone in the future.
FluffyQuack
Vizier
Vizier
Posts: 80
Joined: June 6th, 2004, 7:05 pm

Re: Are POP2's controls supposed to be this bad, or is something wrong with my setup?

Post by FluffyQuack »

I played through POP2 again for the first time in many years. This might not be what the OP encountered, but I've always thought running jumps felt a little bit off in POP2 compared to POP1. After finishing up the playthrough, I tested some level design from POP2 using POP1's mechanics, and I definitely get the impression the window for executing a perfect jump is considerably smaller in POP2.

Since I've gotten quite a bit of experience with the codebase of POP1 (and POP2 to a much smaller degree) I decided to try to look at the code itself so I can solve this mystery.

First of all, this is what it looks like in POP1 (taken from SDL-POP's source code):

Code: Select all

void run_jump() {
	if (Char.frame >= frame_7_run) {
		// Align Kid to edge of floor.
		short xpos = char_dx_forward(4);
		short col = get_tile_div_mod_m7(xpos);
		for (short tiles_forward = 0; tiles_forward < 2; ++tiles_forward) { // Iterate through current tile and the next two tiles looking for a tile the player should jump from
			col += dir_front[Char.direction + 1];
			get_tile(Char.room, col, Char.curr_row);
			if (curr_tile2 == tiles_2_spike || ! tile_is_floor(curr_tile2)) {
				short pos_adjustment = distance_to_edge(xpos) + TILE_SIZEX * tiles_forward - TILE_SIZEX;
				if ((word)pos_adjustment < (word)-8 || pos_adjustment >= 2) {
					if (pos_adjustment < 128) return;
					pos_adjustment = -3;
				}
				Char.x = char_dx_forward(pos_adjustment + 4);
				break;
			}
		}
		control_up = release_arrows(); // disable automatic repeat
		seqtbl_offset_char(seq_4_run_jump); // run-jump
	}
}
Summary: if player is in run animation, then check if one of the two tiles in front of the player is an empty tile (or spikes), and then potentially align the player to that tile edge so a perfect jump is executed. It consider the player as being slightly more forward than he actually is, so this means it checks if the player is up to 2 and 1/3 tiles away from an edge, and then it'll line up for a perfect jump (if the player is even further away, then a jump always happens immediately with no alignment applied).

Working off of David's POP2 disassembly, I tried to understand that game's version of the run_jump() function (note, this is based on v1.1. The function is slightly different in IR):

Code: Select all

seg033:17A6 ; int __stdcall __far run_jump(int seq_id)
seg033:17A6 run_jump        proc far                ; CODE XREF: control_running+4C↑p
seg033:17A6                                         ; sub_36CF8+E5↓P
seg033:17A6
seg033:17A6 some_kind_of_dist_offset= word ptr -0Ch
seg033:17A6 xpos            = word ptr -0Ah
seg033:17A6 enough_space_for_jump= word ptr -8
seg033:17A6 found_valid_edge= word ptr -6
seg033:17A6 tile_column_offset= word ptr -4
seg033:17A6 tile_column     = byte ptr -2
seg033:17A6 tile_type       = byte ptr -1
seg033:17A6 seq_id          = word ptr  6
seg033:17A6
seg033:17A6                 push    bp
seg033:17A7                 mov     bp, sp
seg033:17A9                 sub     sp, 0Ch
seg033:17AC                 push    di
seg033:17AD                 push    si
seg033:17AE                 mov     di, [bp+seq_id]
seg033:17B1                 cmp     di, 4           ; Seq id: Running jump
seg033:17B4                 jnz     short check_if_seq_is_100
seg033:17B6                 cmp     ds:Char.frame, 7 ; Part of the run animation. In POP1's function equivalent, POP1 checks if player is at this frame or above
seg033:17BB                 jz      short frame_and_seq_are_valid
seg033:17BD                 cmp     ds:Char.frame, 11 ; Part of the run animation. In POP1's function equivalent, POP1 does not do any check against this frame
seg033:17C2                 jz      short frame_and_seq_are_valid
seg033:17C4
seg033:17C4 check_if_seq_is_100:                    ; CODE XREF: run_jump+E↑j
seg033:17C4                 cmp     di, 100         ; Seq id: ???
seg033:17C7                 jz      short seq_is_100
seg033:17C9                 jmp     return_0
seg033:17CC ; ---------------------------------------------------------------------------
seg033:17CC
seg033:17CC seq_is_100:                             ; CODE XREF: run_jump+21↑j
seg033:17CC                 cmp     ds:Char.frame, 192
seg033:17D2                 jz      short frame_and_seq_are_valid
seg033:17D4                 cmp     ds:Char.frame, 196
seg033:17DA                 jz      short frame_and_seq_are_valid
seg033:17DC                 jmp     return_0
seg033:17DF ; ---------------------------------------------------------------------------
seg033:17DF
seg033:17DF frame_and_seq_are_valid:                ; CODE XREF: run_jump+15↑j
seg033:17DF                                         ; run_jump+1C↑j ...
seg033:17DF                 mov     ax, 9
seg033:17E2                 mov     [bp+some_kind_of_dist_offset], ax
seg033:17E5                 push    ax              ; delta_x
seg033:17E6                 call    inc_char_x_forward
seg033:17EB                 mov     [bp+xpos], ax
seg033:17EE                 push    ax
seg033:17EF                 call    get_pos_in_tile_m14
seg033:17F4                 mov     [bp+tile_column], al ; Tile character is on when we apply an offset of 9 in the same direction character is facing. This is the tile that's checked in each iteration of the below loop
seg033:17F7                 sub     si, si
seg033:17F9                 mov     [bp+enough_space_for_jump], si ; Variable inited as 0
seg033:17FC                 mov     [bp+found_valid_edge], 1
seg033:1801                 mov     [bp+tile_column_offset], si ; This stores the quantity of tiles we've searched (in direction character is facing). Inited as 0
seg033:1804                 mov     si, [bp+found_valid_edge]
seg033:1807                 mov     di, [bp+tile_column_offset]
seg033:180A
seg033:180A tile_iterator:                          ; CODE XREF: run_jump+B1↓j
seg033:180A                 or      si, si
seg033:180C                 jz      short end_loop
seg033:180E                 mov     al, ds:Char.room
seg033:1811                 push    ax              ; room
seg033:1812                 mov     al, ds:Char.direction
seg033:1815                 cbw
seg033:1816                 mov     bx, ax
seg033:1818                 mov     al, ds:dir_forward[bx]
seg033:181C                 add     [bp+tile_column], al
seg033:181F                 mov     al, [bp+tile_column]
seg033:1822                 push    ax              ; column
seg033:1823                 mov     al, ds:Char.row
seg033:1826                 push    ax              ; row
seg033:1827                 call    get_tile
seg033:182C                 mov     [bp+tile_type], al
seg033:182F                 push    ax              ; tile_type
seg033:1830                 push    ds:current_modif_0_1 ; tile_modifier
seg033:1834                 call    sub_2F7D0       ; Returns true if tile is non-empty?
seg033:1839                 or      ax, ax
seg033:183B                 jnz     short tile_is_walkable_or_loose
seg033:183D                 cmp     [bp+tile_type], tiles_loose
seg033:1841                 jz      short tile_is_walkable_or_loose
seg033:1843                 sub     si, si          ; We've found an empty tile, so set si to 0 so loop is ended
seg033:1845                 jmp     short tile_is_empty
seg033:1845 ; ---------------------------------------------------------------------------
seg033:1847                 db  90h
seg033:1848 ; ---------------------------------------------------------------------------
seg033:1848
seg033:1848 tile_is_walkable_or_loose:              ; CODE XREF: run_jump+95↑j
seg033:1848                                         ; run_jump+9B↑j
seg033:1848                 inc     di
seg033:1849                 cmp     di, 2           ; Check if we've discovered two tiles of walkable tiles, which means we'll end the loop
seg033:184C                 jnz     short tile_is_empty
seg033:184E                 mov     [bp+enough_space_for_jump], 1
seg033:1853
seg033:1853 tile_is_empty:                          ; CODE XREF: run_jump+9F↑j
seg033:1853                                         ; run_jump+A6↑j
seg033:1853                 cmp     [bp+enough_space_for_jump], 0
seg033:1857                 jz      short tile_iterator
seg033:1859
seg033:1859 end_loop:                               ; CODE XREF: run_jump+66↑j
seg033:1859                 mov     [bp+tile_column_offset], di
seg033:185C                 mov     di, [bp+seq_id]
seg033:185F                 or      si, si
seg033:1861                 jnz     short do_jump_anim
seg033:1863                 push    [bp+xpos]
seg033:1866                 call    get_distance_to_edge
seg033:186B                 mov     si, ax
seg033:186D                 mov     cl, 5
seg033:186F                 mov     ax, [bp+tile_column_offset]
seg033:1872                 shl     ax, cl          ; Multiply by tile size (aka left shift by 5, which is the same as multiplying by 32)
seg033:1874                 add     si, ax
seg033:1876                 cmp     di, 4           ; Check if seq_id is 4
seg033:1879                 jnz     short loc_31640
seg033:187B                 mov     ax, 31
seg033:187E                 jmp     short loc_31643
seg033:1880 ; ---------------------------------------------------------------------------
seg033:1880
seg033:1880 loc_31640:                              ; CODE XREF: run_jump+D3↑j
seg033:1880                 mov     ax, 35
seg033:1883
seg033:1883 loc_31643:                              ; CODE XREF: run_jump+D8↑j
seg033:1883                 sub     si, ax
seg033:1885                 cmp     si, -20
seg033:1888                 jl      short loc_3164F
seg033:188A                 cmp     si, 10
seg033:188D                 jl      short add_dist_offset_and_then_jump
seg033:188F
seg033:188F loc_3164F:                              ; CODE XREF: run_jump+E2↑j
seg033:188F                 or      si, si
seg033:1891                 jge     short add_neg_dist_offset_and_then_jump
seg033:1893                 cmp     di, 4
seg033:1896                 jnz     short add_neg_dist_offset_and_then_jump
seg033:1898                 sub     ax, ax
seg033:189A                 jmp     short also_return_0
seg033:189C ; ---------------------------------------------------------------------------
seg033:189C
seg033:189C add_neg_dist_offset_and_then_jump:      ; CODE XREF: run_jump+EB↑j
seg033:189C                                         ; run_jump+F0↑j
seg033:189C                 mov     si, [bp+some_kind_of_dist_offset]
seg033:189F                 neg     si
seg033:18A1
seg033:18A1 add_dist_offset_and_then_jump:          ; CODE XREF: run_jump+E7↑j
seg033:18A1                 add     si, [bp+some_kind_of_dist_offset]
seg033:18A4                 push    si              ; delta_x
seg033:18A5                 call    inc_char_x_forward
seg033:18AA                 mov     ds:Char.xpos, ax
seg033:18AD
seg033:18AD do_jump_anim:                           ; CODE XREF: run_jump+BB↑j
seg033:18AD                 push    di
seg033:18AE                 push    cs
seg033:18AF                 call    near ptr jump_to_seq
seg033:18B2                 call    release_arrows
seg033:18B7                 mov     ds:control_up, al
seg033:18BA                 mov     si, 1
seg033:18BD                 jmp     short return_1
seg033:18BD ; ---------------------------------------------------------------------------
seg033:18BF                 db  90h
seg033:18C0 ; ---------------------------------------------------------------------------
seg033:18C0
seg033:18C0 return_0:                               ; CODE XREF: run_jump+23↑j
seg033:18C0                                         ; run_jump+36↑j
seg033:18C0                 sub     si, si
seg033:18C2
seg033:18C2 return_1:                               ; CODE XREF: run_jump+117↑j
seg033:18C2                 mov     ax, si
seg033:18C4
seg033:18C4 also_return_0:                          ; CODE XREF: run_jump+F4↑j
seg033:18C4                 pop     si
seg033:18C5                 pop     di
seg033:18C6                 mov     sp, bp
seg033:18C8                 pop     bp
seg033:18C9                 retf    2
seg033:18C9 run_jump        endp
First of all, there's a huge difference at the start of the function. Rather than checking if the player is in the run animation, it checks if the player is in 2 specific frames of that animation. The animation is 6 frames long, and it only progresses the function if the player is in the first frame or second-to-last frame of that animation. That alone reduces the jump window in a bad way. There can be a delay of up to 333ms where the game refuses to line the player up to an edge. This shouldn't matter for the beginning part of the window if you keep holding down the jump button, but it means the final part of the window can be smaller, purely based on RNG. In other words, by no fault of the player, it's possible to press the jump button too late by 0 to 333ms. This delay also applies to any jump that isn't "magnetized," so a running jump on even ground will be delayed up to 333ms.

There's changes in other parts of the function too. First of all, all of the distance values are different, but that's primarily because the horizontal coordinate system in POP2 is different than the first game (in POP2, a unit of 1 corresponds to 1 pixel, but in POP1 a unit of 1 corresponds to 2.28 pixels). Like POP1, it considers a starting position that's a bit ahead of the player. In POP1, it's 4 out of 14, while in POP2 it's 9 out of 32, which both correspond to 28% of a tile. So many of the distances values used in the function might actually correspond to the same values in POP1 when you convert the values between the coordinate systems.

I'm not the best at reading assembly, so I can't tell if there's anything in this function that tells me that the beginning part the jump window is tighter than in POP1 (which I feel is the case when I play the games and compare them). I think I'll try to rewrite the function as C code later so I can actually understand all the checks related to distances. But at least I've found one reason why jumping feels off compared to POP1.
FluffyQuack
Vizier
Vizier
Posts: 80
Joined: June 6th, 2004, 7:05 pm

Re: Are POP2's controls supposed to be this bad, or is something wrong with my setup?

Post by FluffyQuack »

I put a bit more time into trying to resolve the running jump mystery.

This is hopefully a correct conversion of POP2's running jump function to C (if some of the structures and function calls look unusual, that's because I wrote this so it could be part of my POP project):

Code: Select all

static int Prince_PlayerInput_RunningJump_POP2(princeLevel_s *lvl, p_player_s *pl, int sequenceIdx)
{
	//The following is based on POP2 v1.1 run_jump function

	if( (sequenceIdx == 4 && (pl->frame == 7 || pl->frame == 11)) //Running jump sequence and player is in one of two valid running frames
		|| (sequenceIdx == 100 && (pl->frame == 192 || pl->frame == 196)) ) //Is this the guard's jump animation?
	{
		int xpos = pl->popX_abs;
		int some_kind_of_dist_offset = 9;
		if(pl->dir == P_DIR_RIGHT) xpos += some_kind_of_dist_offset;
		else xpos -= some_kind_of_dist_offset;
		int column = GetDOSPop1HorizontalTile(xpos - 14);
		int tile_column_offset = 0;
		for(int i = 0; i < 2; i++) //Iterate through tiles
		{
			if(pl->dir == P_DIR_RIGHT) column++;
			else column--;
			if(lvl->tiles[column][pl->popTileY_abs].type == PRINCE_TILETYPE_FLOOR) //This is supposed to be a more nuanced check
				tile_column_offset++;
			else
			{
				int distance = DistanceToEdge(pl, xpos);
				int dist_apply = 0;
				distance += tile_column_offset * 32; //32 is horizontal tile size
				if(sequenceIdx == 4)
					distance -= 31;
				else
					distance -= 35;
				if(distance < -20 || distance >= 1)
				{
					if(distance < 0 && sequenceIdx == 4)
						return 0;
				}
				else
					Prince_MovePlayerForward(pl, distance + some_kind_of_dist_offset);
				break;
			}
		}
		Prince_ChangeAnim(pl, PRINCE_ANIM_POP1_RUNJUMP);
		return 1;
	}
	return 0;
}
I'll start with a more detailed summary of POP1's running jump animation:
  • One tile is 14 units in length.
  • During the function, the game pretends the player is 4 units more forward than he is (28% of a tile).
  • When standing still and starting a run, player needs to cover 78% of a tile (11 units) before he's in full run (and therefore can jump).
  • Player can start the jump in any part of the run animation.
  • The full running jump animation takes up nearly two tiles (one tile and 85% of another tile, aka 26 units) before the player is off the ground.
  • If an edge is discovered up to 2 tiles ahead of the player and the player is in distance where he would jump but only be off by a little bit (8 units / 57% of a tile too far forward or 2 units / 14% of a tile too far back), then align the player and start the jump animation.
  • If an edge is discovered up to 2 tiles ahead of the player but the player is too far back, then delay the jump until player is closer. If I'm reading this right, it even tries to delay jump if the player pressed jump too late which sounds unintended.
And here's how it looks like for POP2:
  • One tile is 32 units in length.
  • During the function, the game pretends the player is 9 units more forward than he is (28% of a tile).
  • When standing still and starting a run, player needs to cover 81% of a tile (26 units) before he's in full run (and therefore can jump).
  • Player can start the jump only during 2 frames of the run animation loop (which is 6 frames long). This means a jump window can be reduced by up to 333ms or up to 46% of a tile (15 units).
  • The full running jump animation takes up nearly two tiles (one tile and 81% of another tile, aka 58 units) before the player is off the ground.
  • If an edge is discovered up to 2 tiles ahead of the player and the player is in distance where he would jump but only be off by a little bit (20 units
    / 62% of a tile too far forward or 1 unit / 3% of a tile too far back), then align the player and start the jump animation.
  • If an edge is discovered up to 2 tiles ahead of the player but the player is too far back, then... don't delay the jump and just let the player jump immediately. However, it does the same thing as in POP1 where it tries to delay a jump if the player jumped too late, which doesn't make sense.
If someone smarter than me spots any errors I've done in converting the POP2 code or parsing the two functions, then feel free to correct me.

So, the differences between the two:
  • POP2 has a random delay to jumping that can reduce the size of a jump window. Effectively, this reduces the final part of the window by up to 46% of a tile. It can delay entering the beginning part of the window, but that's actually a boon if you read the below bullet point.
  • An even bigger difference is that POP2 doesn't delay a jump if the player is too far back. I think this affects the starting part of the jump window by almost a full tile -- I'm not sure of the exact amount, though. If you ever feel a jump get slightly delayed, it would be because of the randomness explained in the above bullet point, and not because the game is waiting for the player to get closer like in POP1.
  • The only way they made it easier is that they allow the player to be 5% closer to the edge compared to POP1 when aligning the player, but that's very minor compared to the other changes that make it much harder overall.
So, yeah, they made running jumps harder in POP2 in a way that honestly doesn't make much sense.

I think it would be interesting to make a patch that makes the jumping function closer to how it is in POP1, but I can't find the jumping code in the executable (is that code packed?), so I only know how to change it while the game is running.
User avatar
Alberto
Sheikh
Sheikh
Posts: 48
Joined: February 13th, 2019, 6:55 am
Contact:

Re: Are POP2's controls supposed to be this bad, or is something wrong with my setup?

Post by Alberto »

Great analysis! I also got the same feeling about performing a perfect jump. It really is a lot harder to jump in PoP2.

As a side note, this is the reason I stumbled across the no cutscene bug in v1.0. I died so many times at Level 03's last pit before the exit door that I accidentally triggered the bug. :lol:

FluffyQuack wrote: January 26th, 2024, 5:50 pm So, the differences between the two:
  • POP2 has a random delay to jumping that can reduce the size of a jump window. Effectively, this reduces the final part of the window by up to 46% of a tile. It can delay entering the beginning part of the window, but that's actually a boon if you read the below bullet point.
  • An even bigger difference is that POP2 doesn't delay a jump if the player is too far back. I think this affects the starting part of the jump window by almost a full tile -- I'm not sure of the exact amount, though. If you ever feel a jump get slightly delayed, it would be because of the randomness explained in the above bullet point, and not because the game is waiting for the player to get closer like in POP1.
  • The only way they made it easier is that they allow the player to be 5% closer to the edge compared to POP1 when aligning the player, but that's very minor compared to the other changes that make it much harder overall.
I imagine this will impact your Fluffy's Prince of Persia implementation for PoP2 tiles?
FluffyQuack
Vizier
Vizier
Posts: 80
Joined: June 6th, 2004, 7:05 pm

Re: Are POP2's controls supposed to be this bad, or is something wrong with my setup?

Post by FluffyQuack »

Alberto wrote: January 27th, 2024, 2:57 amI imagine this will impact your Fluffy's Prince of Persia implementation for PoP2 tiles?
Implementing two different versions of the run_jump function will be straightforward. What will be annoying is supporting two different horizontal coordinate systems. It's super doable, though, and I have an idea how to do it, but it will be a bit tedious to deal with.
FluffyQuack
Vizier
Vizier
Posts: 80
Joined: June 6th, 2004, 7:05 pm

Re: Are POP2's controls supposed to be this bad, or is something wrong with my setup?

Post by FluffyQuack »

FluffyQuack wrote: January 26th, 2024, 5:50 pmI think it would be interesting to make a patch that makes the jumping function closer to how it is in POP1, but I can't find the jumping code in the executable (is that code packed?), so I only know how to change it while the game is running.
I realized I can find the code easily in the IR executable.

If people want to experience POP2 with running jumps that feel similar to POP1, then apply these changes to the IR executable:
  • Offset: 0x3126D: change 74 to 7D (jz to jge)
  • Offset: 0x31353: change 0B F6 7D 09 83 FF 04 75 04 2B C0 EB 28 to 83 FE 00 7C 08 83 FF 04 75 03 EB 25 90
While doing these changes I realized IR seems to have a big bug related to jumps. I don't think it considers a loose tile as a valid tile to "lock" the player into when doing jumps. That check does exist in v1.0 and v1.1, though. I could modify the function to add in that check, but I'll need to optimize the function to make room for it, and that would be a much bigger change than the above.

But still, even with the above bug unfixed, I think this makes running jumps feel so much better in POP2.

I wish I could apply this change to v1.0 and v1.1 as well, but I can't find the relevant code when searching through the executable. I don't know if the code exists compressed or in a modified form within the executable. David, if you're reading this, do you have any idea why I can't find it?
Last edited by FluffyQuack on January 29th, 2024, 5:35 pm, edited 1 time in total.
FluffyQuack
Vizier
Vizier
Posts: 80
Joined: June 6th, 2004, 7:05 pm

Re: Are POP2's controls supposed to be this bad, or is something wrong with my setup?

Post by FluffyQuack »

Apply these changes to the Xbox executable of POP2 to make running jumps function the same as in POP1:
  • Offset 0x9755B: change 74 to 7D (jz to jge) (search for 74 28 66 3D 0B 00 74 22 if offset is wrong)
  • Offset 0x9763A: change 7D to 78 (jge to js) (search for 7D 11 66 83 F9 04 75 0B 5F 5E if offset is wrong)
I just did a full playthrough of the Xbox version using my patch. I think it makes running feel a lot smoother and more reliable overall. I really struggled with 2 jumps in the second temple level when I played the DOS version some days ago, but now I did them first try.

I did encounter some funky bugs in the Xbox version. I'm not sure if those are bugs in the game or if if it's due to bad emulation (I used Xemu):
  • Magic carpet cutscene was invisible (but sound played)
  • The shadow randomly jumped to his death rather than going back into the player, but second time I tried it worked fine
  • The game soft-locked after the epilogue when entering my name into the hall of fame. Tapping buttons on my controller just resulted in random characters being written and there was no way to press "enter"
FluffyQuack
Vizier
Vizier
Posts: 80
Joined: June 6th, 2004, 7:05 pm

Re: Are POP2's controls supposed to be this bad, or is something wrong with my setup?

Post by FluffyQuack »

I managed to find the same code in v1.0 (I must have done a mistake when searching for it the other day). So people can apply these changes to the v1.0 DOS EXE if they want running jumps that feel the same as in POP1:
  • Offset: 0x3197F: change 74 to 7D (jz to jge)
  • Offset: 0x31A53: change 0B F6 7D 09 83 FF 04 75 04 2B C0 EB 28 to 83 FE 00 7C 08 83 FF 04 75 03 EB 25 90
Here's the full context for the second change:

Original code at offset 0x31A53:

Code: Select all

0B F6		or si, si
7D 09		jge short loc_275E0
83 FF 04	cmp di, 4
75 04		jnz short loc_275E0
2B C0		sub ax, ax
EB 28		jmp short loc_27608
New code at same location:

Code: Select all

83 FE 00	cmp si, 0
7C 08		jl short loc_275E0
83 FF 04	cmp di, 4
75 03		jnz short loc_275E0
EB 25		jmp short loc_27604
90		nop
I was able to save a bit of space because the "sub ax, ax" is obsolete if I jump to a part of the code that does the same thing, so then I could fit in the bigger check at the start of that code snippet.
User avatar
Alberto
Sheikh
Sheikh
Posts: 48
Joined: February 13th, 2019, 6:55 am
Contact:

Re: Are POP2's controls supposed to be this bad, or is something wrong with my setup?

Post by Alberto »

FluffyQuack wrote: January 29th, 2024, 12:10 am I think it makes running feel a lot smoother and more reliable overall. I really struggled with 2 jumps in the second temple level when I played the DOS version some days ago, but now I did them first try.
If you mean that last big jump in level 11, then I know the frustration :lol:.
Spoiler: show
L10Jump.JPG
FluffyQuack wrote: January 29th, 2024, 5:37 pm I managed to find the same code in v1.0 (I must have done a mistake when searching for it the other day). So people can apply these changes to the v1.0 DOS EXE if they want running jumps that feel the same as in POP1:
Such a simple fix (not easy). Thank you for sharing, definitely a good quality of life improvement. Good thing is I can still keep the original experience and just have a copy of the modified version and play either version at will.
FluffyQuack wrote: January 29th, 2024, 12:10 am I did encounter some funky bugs in the Xbox version. I'm not sure if those are bugs in the game or if if it's due to bad emulation (I used Xemu):
  • Magic carpet cutscene was invisible (but sound played)
  • The shadow randomly jumped to his death rather than going back into the player, but second time I tried it worked fine
  • The game soft-locked after the epilogue when entering my name into the hall of fame. Tapping buttons on my controller just resulted in random characters being written and there was no way to press "enter"
I've only read about a similar bug being mentioned on the PoP2 bugs thread, so yeah they might be 'normal' bugs. Of course, this is just a random thought, I don't have the skills to actually explain, describe or even read the code :oops:
FluffyQuack
Vizier
Vizier
Posts: 80
Joined: June 6th, 2004, 7:05 pm

Re: Are POP2's controls supposed to be this bad, or is something wrong with my setup?

Post by FluffyQuack »

Alberto wrote: January 30th, 2024, 7:46 am If you mean that last big jump in level 11, then I know the frustration :lol:.
Spoiler: show
L10Jump.JPG
It was actually the two big jumps before the first checkpoint in the same level. I must have been lucky with the jump you linked as I think I did that first try. But now, all those jumps feel much easier and more reliable to perform with my modifications that makes the controls function similarly to POP1.
Alberto wrote: January 30th, 2024, 7:46 amI've only read about a similar bug being mentioned on the PoP2 bugs thread, so yeah they might be 'normal' bugs. Of course, this is just a random thought, I don't have the skills to actually explain, describe or even read the code :oops:
That's close to what happened to me, but instead of jumping to his death immediately, he did it after picking up the flame and jumping back to the prince. I could see this being a variant of the same bug, though. This is what it looked like for me: https://clips.twitch.tv/PerfectAthletic ... ka1DH-3ucM

And there's another bug I forgot to mention. When I got the correct version of shadow and the flame cutscene, the prince moved to the next screen... but then refused to listen to any input. I tried pressing every button, but they did nothing. I think it fixed itself when I paused and unpaused the game. Here's a clip of it happening: https://clips.twitch.tv/PlausibleJoyous ... Drqle0UXR0

And here's the glitch on the Hall of Fame screen: https://www.twitch.tv/fluffyquack/clip/ ... x2nr6UVjmt

I could easily see the Hall of Fame glitch being due to the emulator, but it would be hilarious if that's actually a bug in the port. I would test it on a real machine, but I don't have an original Xbox anymore and the POP1/POP2 ports inside Sands of Time don't work when emulated on a new Xbox.
Post Reply