Dunno, GIMP for example shows letter keys with capital letters, whether they need Shift or not...
SDLPoP; David's open-source port of PoP
Re: SDLPoP; David's open-source port of PoP
(I wanted to post this a week ago, but the forum kept timing out then...)
Re: SDLPoP; David's open-source port of PoP
Can anyone tell me how the program knows that PRINCE/ has a collection of res151-173.png images that are part of a certain chtab that goes from 0-22 (and not e.g. 23)?
Time and time again, I try to modify SDLPoP to create custom stuff, but I simply cannot grasp how everything works. It's too complex for me. Every time I see a function, e.g. load_sprites_from_file(), it uses another function, e.g. load_from_opendats_alloc(), that uses another function, e.g. load_from_opendats_metadata(), and then I just lose track of things, and feel sad about how powerless I am when it comes to tweaking things.
Sometimes I then try to work around everything, by using my own image loading function and then drawing stuff to the screen, but the game loop is so intricate and tight that there's no way I can just dump something in there and have it show up in-game.
Damn.
Time and time again, I try to modify SDLPoP to create custom stuff, but I simply cannot grasp how everything works. It's too complex for me. Every time I see a function, e.g. load_sprites_from_file(), it uses another function, e.g. load_from_opendats_alloc(), that uses another function, e.g. load_from_opendats_metadata(), and then I just lose track of things, and feel sad about how powerless I am when it comes to tweaking things.
Sometimes I then try to work around everything, by using my own image loading function and then drawing stuff to the screen, but the game loop is so intricate and tight that there's no way I can just dump something in there and have it show up in-game.
Damn.
Re: SDLPoP; David's open-source port of PoP
The number of images is in the first byte of the palette, res150.pal in your example.
0x17 = 23 means that image IDs go from 150+1 = 151 to 150+23 = 173.
This code tells which set is loaded into which chtab:
(seg000.c)
Code: Select all
dathandle = open_dat("PRINCE.DAT", 'G');
[...]
// PRINCE.DAT: sword
chtab_addrs[id_chtab_0_sword] = load_sprites_from_file(700, 1<<2, 1);
// PRINCE.DAT: flame, sword on floor, potion
chtab_addrs[id_chtab_1_flameswordpotion] = load_sprites_from_file(150, 1<<3, 1);
This code loads the palette, which also contains the number of images:
(seg009.c)
Code: Select all
dat_shpl_type* shpl = (dat_shpl_type*) load_from_opendats_alloc(resource, "pal", NULL, NULL);
[...]
int n_images = shpl->n_images;
[...]
for (int i = 1; i <= n_images; i++) {
SDL_Surface* image = load_image(resource + i, pal_ptr);
[...]
chtab->images[i-1] = image;
}
(types.h)
Code: Select all
typedef struct dat_shpl_type {
byte n_images;
dat_pal_type palette;
} dat_shpl_type;
Re: SDLPoP; David's open-source port of PoP
My plan was to add collectable rotating coins, using floors with modifier 0x04, for a coins-mod. I tried that for 1 day (14th), then gave up and converted the plan into this basic tile idea instead. Next, I created Puny Prince from scratch, and included the previously planned coins-mod in it as "Coins of Persia".
-
- Vizier
- Posts: 91
- Joined: June 6th, 2004, 7:05 pm
Re: SDLPoP; David's open-source port of PoP
I wanted to fully understand how the check_collisions() function works, and as a test I disabled the comparison against collision from previous gameplay tick to understand how it's necessary. The test ended up being pretty amusing:
-
- Vizier
- Posts: 91
- Joined: June 6th, 2004, 7:05 pm
Re: SDLPoP; David's open-source port of PoP
I have one question about a piece of code in the function jump_up():
If you compile with USE_SUPER_HIGH_JUMP, then the player's column position is calculated with an offset to the exact player position if feather_fall is true, even if fixes->enable_super_high_jump is false.
If you don't compile with USE_SUPER_HIGH_JUMP, then that offset is always 0, no matter what the feather_fall value is set to.
Is this intended? Seems odd to me that there's potentially a small difference in gameplay if you compile with USE_SUPER_HIGH_JUMP even with fixes->enable_super_high_jump set to false.
Code: Select all
#ifdef USE_SUPER_HIGH_JUMP
// kid should be able to grab 2 tiles above from an edge of a floor tile
if (is_feather_fall && !tile_is_floor(get_tile_above_char()) && curr_tile2 != tiles_20_wall) {
delta_x = Char.direction == dir_FF_left ? 1 : 3;
} else {
delta_x = 0;
}
int char_col = get_tile_div_mod(back_delta_x(delta_x) + dx_weight() - 6);
get_tile(Char.room, char_col, Char.curr_row - 1);
if (curr_tile2 != tiles_20_wall && !tile_is_floor(curr_tile2)) {
if (fixes->enable_super_high_jump && is_feather_fall) { // super high jump can only happen in feather mode
if (curr_room == 0 && Char.curr_row == 0) { // there is no room above
seqtbl_offset_char(seq_14_jump_up_into_ceiling);
} else {
get_tile(Char.room, char_col, Char.curr_row - 2); // the target top tile
bool is_top_floor = tile_is_floor(curr_tile2) || curr_tile2 == tiles_20_wall;
if (is_top_floor && curr_tile2 == tiles_11_loose && (curr_room_tiles[curr_tilepos] & 0x20) == 0) {
is_top_floor = false; // a regular loose floor above should not be treated as a floor
}
// kid should jump slightly higher if the top tile is not a floor
super_jump_timer = is_top_floor ? 22 : 24;
super_jump_room = curr_room;
super_jump_col = tile_col;
super_jump_row = tile_row;
seqtbl_offset_char(seq_48_super_high_jump); // jump up 2 rows with nothing above
}
} else {
seqtbl_offset_char(seq_28_jump_up_with_nothing_above); // jump up with nothing above
}
} else {
seqtbl_offset_char(seq_14_jump_up_into_ceiling); // jump up with wall or floor above
}
#else
get_tile(Char.room, get_tile_div_mod(back_delta_x(0) + dx_weight() - 6), Char.curr_row - 1);
if (curr_tile2 != tiles_20_wall && ! tile_is_floor(curr_tile2)) {
seqtbl_offset_char(seq_28_jump_up_with_nothing_above); // jump up with nothing above
} else {
seqtbl_offset_char(seq_14_jump_up_into_ceiling); // jump up with wall or floor above
}
#endif
If you don't compile with USE_SUPER_HIGH_JUMP, then that offset is always 0, no matter what the feather_fall value is set to.
Is this intended? Seems odd to me that there's potentially a small difference in gameplay if you compile with USE_SUPER_HIGH_JUMP even with fixes->enable_super_high_jump set to false.
-
- Vizier
- Posts: 91
- Joined: June 6th, 2004, 7:05 pm
Re: SDLPoP; David's open-source port of PoP
I found an odd piece of code:
The second back_with_sword() is unreachable because it checks if the character is a kid and then within that block it checks if the character is NOT kid. Both of those statements can't be true at the same time, so that piece of code is never ran. I'm not completely sure what that code is even intended for. But it's something related to making the AI potentially retreat while at close range rather than parrying.
This is the same code in the Apple II source code: https://github.com/jmechner/Prince-of-P ... TRL.S#L896
At first glance, I can't tell if it has the same unreachable piece of code. I'll try to parse it another time.
Code: Select all
void parry() {
short char_frame = Char.frame;
short opp_frame = Opp.frame;
short char_charid = Char.charid;
short seq_id = seq_62_parry; // defend (parry) with sword
short do_play_seq = 0;
if (
char_frame == frame_158_stand_with_sword || // stand with sword
char_frame == frame_170_stand_with_sword || // stand with sword
char_frame == frame_171_stand_with_sword || // stand with sword
char_frame == frame_168_back || // back?
char_frame == frame_165_walk_with_sword // walk with sword
) {
if (char_opp_dist() >= 32 && char_charid != charid_0_kid) {
back_with_sword();
return;
} else if (char_charid == charid_0_kid) { // CHECK IF CHAR IS KID!
if (opp_frame == frame_168_back) return;
if (opp_frame != frame_151_strike_1 &&
opp_frame != frame_152_strike_2 &&
opp_frame != frame_162_block_to_strike
) {
if (opp_frame == frame_153_strike_3) { // strike
do_play_seq = 1;
} else if (char_charid != charid_0_kid) { // CHECK IF CHAR IS NOT KID!
back_with_sword(); // UNREACHABLE!
return;
}
}
} else {
if (opp_frame != frame_152_strike_2) return;
}
} else {
if (char_frame != frame_167_blocked) return;
seq_id = seq_61_parry_after_strike; // parry after striking with sword
}
control_up = CONTROL_IGNORE; // disable automatic repeat
seqtbl_offset_char(seq_id);
if (do_play_seq) {
play_seq();
}
}
This is the same code in the Apple II source code: https://github.com/jmechner/Prince-of-P ... TRL.S#L896
At first glance, I can't tell if it has the same unreachable piece of code. I'll try to parse it another time.
-
- Vizier
- Posts: 91
- Joined: June 6th, 2004, 7:05 pm
Re: SDLPoP; David's open-source port of PoP
I tried to parse the Apple II version of the code and I've learned three things: I'm very slow at parsing Apple II assembly, there's nothing wrong with the DOS POP code as the "char_opp_dist() >= 32 && char_charid != charid_0_kid" code snippet ends up in the same retreat move that's currently inaccessible, and the reason for the retreat is that Jordan Mechner only wants the AI to do parries if they're 100% guaranteed to connect (thinking about it, I've never seen a guard do a parry that ended up missing).
Here's the pseudocode I wrote based on the Apple II version of the parry code:
Here's the pseudocode I wrote based on the Apple II version of the parry code:
Code: Select all
void DoBlock()
{
char a;
if(char.frame == 158 || char.frame == 170 || char.frame == 171 || char.frame == 168 || char.frame == 165) //158 = ready, 168 = guy-2, 165 = advance
goto two;
if(char.frame == 167) //Blocked strike
goto three;
return;
//From ready position: Block if appropriate
two:
a = 62; //readyblock
char distance_to_opponent = getopdist()
if(distance_to_opponent >= 32)
goto blockmiss; //too far
if(char.type == KID)
goto kid;
if(opp.frame == 152) //guy4
goto doit;
return;
kid:
if(opp.frame == 168) //1 frame too early?
return; //Yes, wait one frame
if(opp.frame == 151 || opp.frame == 152 || opp.frame == 162) //guy3, guy3, guy22
goto doit;
if(opp.frame != 153) //1 frame too late?
goto blockmiss;
//Yes, skip one frame
call doit; //jsr doit
call animchar;
return;
//Strike-to-block
three:
a = 61; //strikeblock
doit:
x = 1; //ldx #1 ?
clrU = x; //Disable automatic repeat?
call jumpseq(a);
return;
blockmiss:
if(char.type != KID)
goto DoRetreat; //enemy doesn't waste blocks
a = 62; //readyblock
goto doit;
}