I've been rethinking how pits should work, and my approach is somewhat different to most. I'm going to post it here--it's a general outline, with both working code, and pseudocode, so be warned that you can't compile/use it as-is--for general discussion, feedback, critique, and so forth.
Note also that I used some constants that are atypical, and not meant to be in the finished product; but it was faster for me to type them however I will recall what they should do. The array values will all have constants in the final codebase, to make them easier for people to comprehend when reading it.
Spoiler
const int PITS_GRACE_WHEN_INJURED = 1; //If Link is hurt, he won;t fall down a pit. Good for bomb jumping, bad for other things.
const int PITS_ALLOW_BOMB_JUMPING = 1; //More precise for bomb jumping.
ffc script Pit{
void run(int respawn_screen_init){
int a[256]; bool falling; bool pit; bool onpitedge;
Waitframes(5);
//Store where Link was on screen init:
a[50] = Link->X; a[51] = Link->Y;
while(true){
if ( !falling && !onpitedge && !Link->Misc[ON_PLATFORM] ) { a[10] = Link->X; a[11] = Link->Y; }
if ( falling ) { a[12] = a[10]; a[13] = a[11]; }
//Store Link's X/Y in the array, so that we have a respawn location.
//Find combos with a type of HOOKSHOT_ONLY and an Inherent Flag of CI_PIT.
for ( a[0] = 0; a[0] < 176; a[0]++ ) {
if ( ComboT[a[0]] == CT_HOOKSHOT || ComboT[a[0]] == CT_LADDER || ComboT[a[0]] == CT_LADDERHOOKSHOT || ComboI[a[0]] == CT_PIT ) {
if ( DistX(a[0],14) && DistY(a[0],14) && !DistY(a[0],9) && !DistX(a[0],9) ) onpitedge = true;
else onpitedge = false;
if ( DistX(a[0],8) && DistY(a[0],8) {
if ( !NumLWeaponsOf(LW_HOOKSHOT) ) {
if ( Link->Z <= 0 && !Link->Jump ) {
falling = true;
}
}
}
}
}
if ( falling ) {
NoAction();
Link->X = ComboX(a[0]); Link->Y = ComboY(a[0]);
a[4] = ComboX(a[0]); a[5] = ComboY(a[0]);
a[8] = 16; //Scale
//Waitframe(); //Not needed if we're going it after Waitdraw()
a[20] = Link->Tile; //Set before hs becomes invisible and this value turns to shyte.
// We need to avoid storing this if the new location is a platform. This is important, primarily
//because he can walk off the edge of a platform manually, and he'd fall in an endless loop.
//This might mean that either the platform ffc needs higher priority than this, so that the Link->Misc
//value is set, or that we need to check if he is on a platform in the pits ffc, too.
if ( !effects ) {
effects = true;
Game->PlaySound(SFX_FALL_PIT);
Link->HP -= PIT_DAMAGE;
}
for ( a[1] = 0; a[1] < PIT_FALLING_ANIM_DUR; a[1]++ ) {
Screen_>DrawTile(2, a[4]; a[5]; a[20]; ... scale=a[8], ... true, OP_OPAQUE);
if ( a[1] % 10 == 0 && a[8] > 0 ) a[8]--; //Trim the scale.
Waitframe();
}
Waitframes(5);
//Spawn Link Again
for ( a[1] = 0; a[1] < PIT_RESPAWN_LINK_DUR; a[1]++ ) {
if ( !respawn_screen_init )
Link->X = a[12]; Link->Y = a[13];
if ( respawn_screen_init )
//If d0 is set, we read a[50] and a[51] for the values to use when respawning him.
Link->X = a[50]; Link->Y = a[51];
if ( a[1] % 4 != 0 ) Link->Invisible = true;
else Link->Invisible = false;
Waitframe();
}
falling = false;
effects = false;
//Freeze Link
//Store his x/y
//Center the x/y over the combo
//Make Link invisible,
//Draw his tile in a loop, growing smaller
//Play falling sound
//hurt him
//Wait a few frames
//Spawn him
//make him flicker by using a for loop, in which at % == 0 he is visible, and ! % == 0 he is invisible
//for 30 frames, during which his collision is off.
}
Waitframe();
}
}
}
The intent here, is that a user may place the ffc manually, or use a global script instruction call via a global function, to auto-launch the ffc; so the code is identical, either way. I may make a global function for it, if Waitdraw() becomes an issue, but placement after Waitdraw() is going to matter for some of the effects, whereas delaying falling for one frame will be in the favour of the player (and thus, not horrible).
I think that checking for every combo on the screen with a for loop every frame causes a good bit of slowdown on some PCs. This might not be true though.
Anyway, the code is looking good so far. This would spawn link to the last place he was before falling into a pit, rather than the screen entrance, right?
I think that checking for every combo on the screen with a for loop every frame causes a good bit of slowdown on some PCs. This might not be true though.
Anyway, the code is looking good so far. This would spawn link to the last place he was before falling into a pit, rather than the screen entrance, right?
Combo for loops easily run in one frame. You won't run into slowdown until you do huge loops, with 10,000+ iterations. I do combo loops constantly for engine events, and I don't notice it on 10+ year-old laptops. The number of instructions per iteration is also very important to consider, as if an 1,000 iteration loop has 50 instructions, then it's running 50,000 ZScript instructions per frame; which translates into easily 250,000 to 500,000 ZASM instructions per frame.
Correct, Link spawns in the last 'safe' location, or at least, that is my intent.
I also think it's nicer to the player, but some people may disagree on using that.
This links in with my Somaria code, and in particular, the Somaria platforms. I left out one check:
if ( Link->Misc[ON_PLATFORM] ) ... but that's barely useful, as Link->Z is set to '1' when he's on a platform, so it's just a safety net.
Updated above, with some important things, and an option to use Link's position on screen init.