From a8e1afbb20bb8a163472969ce4ecdbfb28ff8406 Mon Sep 17 00:00:00 2001 From: westonCoder Date: Sat, 14 Oct 2023 12:28:10 -0500 Subject: [PATCH] Added Portal Funneling - added a function in player that implements portal funneling (video attached) - added a new menu option in the gameplay tab that allows you to toggle it on or off - the toggle defaults to on, just like in original portal - portal funneling is only done on portals facing up --- src/menu/gameplay_options.c | 28 ++++++++++++-- src/menu/gameplay_options.h | 2 + src/player/player.c | 76 +++++++++++++++++++++++++++++++++++++ src/savefile/savefile.c | 1 + src/savefile/savefile.h | 1 + 5 files changed, 104 insertions(+), 4 deletions(-) diff --git a/src/menu/gameplay_options.c b/src/menu/gameplay_options.c index 2d6315f..d659c35 100644 --- a/src/menu/gameplay_options.c +++ b/src/menu/gameplay_options.c @@ -57,12 +57,14 @@ void gameplayOptionsInit(struct GameplayOptions* gameplayOptions) { gameplayOptions->movingPortals = menuBuildCheckbox(&gDejaVuSansFont, "Movable Portals", GAMEPLAY_X + 8, GAMEPLAY_Y + 8); gameplayOptions->wideScreen = menuBuildCheckbox(&gDejaVuSansFont, "Wide Screen", GAMEPLAY_X + 8, GAMEPLAY_Y + 28); + gameplayOptions->portalFunnel = menuBuildCheckbox(&gDejaVuSansFont, "Portal Funneling", GAMEPLAY_X + 8, GAMEPLAY_Y + 48); - gameplayOptions->portalRenderDepthText = menuBuildText(&gDejaVuSansFont, "Portal Render Depth", GAMEPLAY_X + 8, GAMEPLAY_Y + 48); - gameplayOptions->portalRenderDepth = menuBuildSlider(GAMEPLAY_X + 126, GAMEPLAY_Y + 48, 126, SCROLL_TICKS); + gameplayOptions->portalRenderDepthText = menuBuildText(&gDejaVuSansFont, "Portal Render Depth", GAMEPLAY_X + 8, GAMEPLAY_Y + 68); + gameplayOptions->portalRenderDepth = menuBuildSlider(GAMEPLAY_X + 126, GAMEPLAY_Y + 68, 126, SCROLL_TICKS); gameplayOptions->movingPortals.checked = (gSaveData.controls.flags & ControlSaveMoveablePortals) != 0; gameplayOptions->wideScreen.checked = (gSaveData.controls.flags & ControlSaveWideScreen) != 0; + gameplayOptions->portalFunnel.checked = (gSaveData.controls.flags & ControlSavePortalFunneling) != 0; gameplayOptions->portalRenderDepth.value = (float)(gSaveData.controls.portalRenderDepth / PORTAL_RENDER_DEPTH_MAX); gameplayOptions->render_depth = (0xFFFF/PORTAL_RENDER_DEPTH_MAX)* gSaveData.controls.portalRenderDepth; gameplayOptionsHandleSlider(&gameplayOptions->render_depth, &gameplayOptions->portalRenderDepth.value); @@ -103,7 +105,6 @@ enum MenuDirection gameplayOptionsUpdate(struct GameplayOptions* gameplayOptions gSaveData.controls.flags &= ~ControlSaveMoveablePortals; } } - break; case GameplayOptionWideScreen: if (controllerGetButtonDown(0, A_BUTTON)) { @@ -116,11 +117,24 @@ enum MenuDirection gameplayOptionsUpdate(struct GameplayOptions* gameplayOptions gSaveData.controls.flags &= ~ControlSaveWideScreen; } } + break; + case GameplayOptionPortalFunneling: + if (controllerGetButtonDown(0, A_BUTTON)) { + gameplayOptions->portalFunnel.checked = !gameplayOptions->portalFunnel.checked; + soundPlayerPlay(SOUNDS_BUTTONCLICKRELEASE, 1.0f, 0.5f, NULL, NULL); + + if (gameplayOptions->portalFunnel.checked) { + gSaveData.controls.flags |= ControlSavePortalFunneling; + } else { + gSaveData.controls.flags &= ~ControlSavePortalFunneling; + } + } + break; case GameplayOptionPortalRenderDepth: - gameplayOptionsHandleSlider(&gameplayOptions->render_depth, &gameplayOptions->portalRenderDepth.value); gSaveData.controls.portalRenderDepth = (int)((gameplayOptions->render_depth * (1.0f/0xFFFF) * PORTAL_RENDER_DEPTH_MAX)); break; + break; } @@ -153,6 +167,9 @@ void gameplayOptionsRender(struct GameplayOptions* gameplayOptions, struct Rende gSPDisplayList(renderState->dl++, gameplayOptions->wideScreen.outline); renderState->dl = menuCheckboxRender(&gameplayOptions->wideScreen, renderState->dl); + gSPDisplayList(renderState->dl++, gameplayOptions->portalFunnel.outline); + renderState->dl = menuCheckboxRender(&gameplayOptions->portalFunnel, renderState->dl); + gSPDisplayList(renderState->dl++, gameplayOptions->portalRenderDepth.back); renderState->dl = menuSliderRender(&gameplayOptions->portalRenderDepth, renderState->dl); @@ -167,6 +184,9 @@ void gameplayOptionsRender(struct GameplayOptions* gameplayOptions, struct Rende menuSetRenderColor(renderState, gameplayOptions->selectedItem == GameplayOptionWideScreen, &gSelectionGray, &gColorWhite); gSPDisplayList(renderState->dl++, gameplayOptions->wideScreen.text); + menuSetRenderColor(renderState, gameplayOptions->selectedItem == GameplayOptionPortalFunneling, &gSelectionGray, &gColorWhite); + gSPDisplayList(renderState->dl++, gameplayOptions->portalFunnel.text); + gDPPipeSync(renderState->dl++); menuSetRenderColor(renderState, gameplayOptions->selectedItem == GameplayOptionPortalRenderDepth, &gSelectionGray, &gColorWhite); gSPDisplayList(renderState->dl++, gameplayOptions->portalRenderDepthText); diff --git a/src/menu/gameplay_options.h b/src/menu/gameplay_options.h index 035529c..56ab433 100644 --- a/src/menu/gameplay_options.h +++ b/src/menu/gameplay_options.h @@ -7,6 +7,7 @@ enum GameplayOption { GameplayOptionMovingPortals, GameplayOptionWideScreen, + GameplayOptionPortalFunneling, GameplayOptionPortalRenderDepth, GameplayOptionCount, @@ -15,6 +16,7 @@ enum GameplayOption { struct GameplayOptions { struct MenuCheckbox movingPortals; struct MenuCheckbox wideScreen; + struct MenuCheckbox portalFunnel; struct MenuSlider portalRenderDepth; Gfx* portalRenderDepthText; short selectedItem; diff --git a/src/player/player.c b/src/player/player.c index 744cfe7..f2c89da 100644 --- a/src/player/player.c +++ b/src/player/player.c @@ -32,6 +32,13 @@ #define PLAYER_COLLISION_LAYERS (COLLISION_LAYERS_TANGIBLE | COLLISION_LAYERS_FIZZLER | COLLISION_LAYERS_BLOCK_BALL) +#define FUNNEL_DAMPENING_CONSTANT 0.32f +#define FUNNEL_CENTERING_CONSTANT 0.05f +#define FUNNEL_ACCEPTABLE_CENTERED_DISTANCE 0.1f +#define FUNNEL_MAX_DIST 1.2f +#define FUNNEL_MIN_DOWN_VEL -2.25f +#define FUNNEL_MAX_HORZ_VEL 7.0f + struct Vector3 gGrabDistance = {0.0f, 0.0f, -1.5f}; struct Vector3 gCameraOffset = {0.0f, 0.0f, 0.0f}; @@ -570,6 +577,69 @@ void playerUpdateFooting(struct Player* player, float maxStandDistance) { } } +void playerPortalFunnel(struct Player* player) { + if (gCollisionScene.portalTransforms[0] != NULL && gCollisionScene.portalTransforms[1] != NULL){ + // portal funnelling testing! + struct Transform portal0transform = *gCollisionScene.portalTransforms[0]; + struct Transform portal1transform = *gCollisionScene.portalTransforms[1]; + struct Transform targetPortalTransform; + //remove z from distance calc + portal0transform.position.y = player->body.transform.position.y; + portal1transform.position.y = player->body.transform.position.y; + + float portal0dist = sqrtf(vector3DistSqrd(&player->body.transform.position, &portal0transform.position)); + float portal1dist = sqrtf(vector3DistSqrd(&player->body.transform.position, &portal1transform.position)); + float targetDist; + + if (portal0dist < portal1dist){ + targetPortalTransform = portal0transform; + targetDist = portal0dist; + } else{ + targetPortalTransform = portal1transform; + targetDist = portal1dist; + } + + struct Vector3 straightForward; + vector3Negate(&gForward, &straightForward); + quatMultVector(&targetPortalTransform.rotation, &straightForward, &straightForward); + if (fabsf(straightForward.y) > 0.999f) { + // float dampeningConstant = 0.32f; + // float centeringConstant = 0.05; + // float acceptableCenteredDist = 0.1f; + // float maxFunnelingDist = 1.2f; + // float minFunnelDownVel = -2.25; + // float maxFunnelHorzVel = 7.0f; + + if (!(player->flags & PlayerFlagsGrounded) && + targetDist < FUNNEL_MAX_DIST && + player->body.velocity.y < FUNNEL_MIN_DOWN_VEL && + fabsf(player->body.velocity.x) < FUNNEL_MAX_HORZ_VEL && + fabsf(player->body.velocity.z) < FUNNEL_MAX_HORZ_VEL){ + if (player->body.transform.position.x < targetPortalTransform.position.x - FUNNEL_ACCEPTABLE_CENTERED_DISTANCE){ + player->body.velocity.x += (FUNNEL_DAMPENING_CONSTANT * (targetPortalTransform.position.x - player->body.transform.position.x)); + } + else if (player->body.transform.position.x > targetPortalTransform.position.x + FUNNEL_ACCEPTABLE_CENTERED_DISTANCE){ + player->body.velocity.x -= (FUNNEL_DAMPENING_CONSTANT * (player->body.transform.position.x - targetPortalTransform.position.x)); + } + else{ + player->body.velocity.x *= FUNNEL_CENTERING_CONSTANT; + } + + if (player->body.transform.position.z < targetPortalTransform.position.z - FUNNEL_ACCEPTABLE_CENTERED_DISTANCE){ + player->body.velocity.z += (FUNNEL_DAMPENING_CONSTANT * (targetPortalTransform.position.z - player->body.transform.position.z)); + } + else if (player->body.transform.position.z > targetPortalTransform.position.z + FUNNEL_ACCEPTABLE_CENTERED_DISTANCE){ + player->body.velocity.z -= (FUNNEL_DAMPENING_CONSTANT * (player->body.transform.position.z - targetPortalTransform.position.z)); + } + else{ + player->body.velocity.z *= FUNNEL_CENTERING_CONSTANT; + } + } + + } + } +} + void playerUpdate(struct Player* player) { struct Vector3 forward; struct Vector3 right; @@ -836,6 +906,12 @@ void playerUpdate(struct Player* player) { } } + + if (gSaveData.controls.flags & ControlSavePortalFunneling){ + playerPortalFunnel(player); + } + + // player not moving on ground if ((player->flags & PlayerFlagsGrounded) && (player->body.velocity.x == 0) && (player->body.velocity.z == 0)){ player->stepTimer = STEP_TIME; diff --git a/src/savefile/savefile.c b/src/savefile/savefile.c index 4b72764..1842bc1 100755 --- a/src/savefile/savefile.c +++ b/src/savefile/savefile.c @@ -91,6 +91,7 @@ void savefileNew() { controllerSetDefaultSource(); gSaveData.controls.flags = 0; + gSaveData.controls.flags |= ControlSavePortalFunneling; gSaveData.controls.sensitivity = 0x4000; gSaveData.controls.acceleration = 0x4000; gSaveData.controls.deadzone = 0x4000; diff --git a/src/savefile/savefile.h b/src/savefile/savefile.h index fa2bdf4..11cb9d8 100755 --- a/src/savefile/savefile.h +++ b/src/savefile/savefile.h @@ -40,6 +40,7 @@ enum ControlSaveFlags { ControlSaveSubtitlesEnabled = (1 << 5), + ControlSavePortalFunneling = (1 << 7), ControlSaveMoveablePortals = (1 << 8), ControlSaveWideScreen = (1 << 9), };