River_Sound OK and Documented + River_Sound Related z_actor/Audio Functions (#661)

* First pass of `func_800BCCDC`

* river_sound OK (copy progress from audio WIP)

* Copy progress from `code_8019AF00.c` (still waiting for data to be imported)

* More docs

* feedback

* Add in `code_8019AF00.c` functions

* format

* params

* Minor cleanup

* Small fix

* one more

* Better names

* Add comment, cleanup Idx

* Fix bss

* Fix BSS
This commit is contained in:
engineer124
2022-03-28 01:42:19 +11:00
committed by GitHub
parent 9e230005b0
commit f1d1173558
14 changed files with 553 additions and 150 deletions
+1 -1
View File
@@ -18,7 +18,7 @@
#pragma GLOBAL_ASM("asm/non_matchings/code/code_80192BE0/func_8019387C.s")
#pragma GLOBAL_ASM("asm/non_matchings/code/code_80192BE0/func_801938A0.s")
#pragma GLOBAL_ASM("asm/non_matchings/code/code_80192BE0/Audio_QueueCmdS8.s")
#pragma GLOBAL_ASM("asm/non_matchings/code/code_80192BE0/func_801938D0.s")
+280 -13
View File
@@ -66,6 +66,9 @@ typedef struct {
/* 0xC */ s32 remainingFrames;
} FreqLerp; // size = 0x10
s32 Audio_SetGanonsTowerBgmVolume(u8 targetVolume);
void func_801A3238(s8 playerIndex, u16 seqId, u8 fadeTimer, s8 arg3, u8 arg4);
// Sfx bss
SfxSettings sSfxSettings[8];
u8 sSfxSettingsFlags;
@@ -2071,11 +2074,50 @@ const char sAudioOcarinaUnusedText7[] = "check is over!!! %d %d %d\n";
#pragma GLOBAL_ASM("asm/non_matchings/code/code_8019AF00/func_8019FF38.s")
#pragma GLOBAL_ASM("asm/non_matchings/code/code_8019AF00/func_8019FF9C.s")
/**
* Used for EnRiverSound
*/
void Audio_PlaySfxForRiver(Vec3f* pos, f32 freqScale) {
if (!Audio_IsSfxPlaying(NA_SE_EV_RIVER_STREAM - SFX_FLAG)) {
sRiverFreqScaleLerp.value = freqScale;
} else if (freqScale != sRiverFreqScaleLerp.value) {
sRiverFreqScaleLerp.target = freqScale;
sRiverFreqScaleLerp.remainingFrames = 40;
sRiverFreqScaleLerp.step = (sRiverFreqScaleLerp.target - sRiverFreqScaleLerp.value) / 40;
}
Audio_PlaySfxGeneral(NA_SE_EV_RIVER_STREAM - SFX_FLAG, pos, 4, &sRiverFreqScaleLerp.value, &D_801DB4B0,
&D_801DB4B8);
}
#pragma GLOBAL_ASM("asm/non_matchings/code/code_8019AF00/func_801A0048.s")
/**
* Unused remnant of OoT's EnRiverSound
* Used for Zora's River Waterfall
*/
void Audio_PlaySfxForWaterfall(Vec3f* pos, f32 freqScale) {
if (!Audio_IsSfxPlaying(NA_SE_EV_WATER_WALL_BIG - SFX_FLAG)) {
sWaterfallFreqScaleLerp.value = freqScale;
} else if (freqScale != sWaterfallFreqScaleLerp.value) {
sWaterfallFreqScaleLerp.target = freqScale;
sWaterfallFreqScaleLerp.remainingFrames = 40;
sWaterfallFreqScaleLerp.step = (sWaterfallFreqScaleLerp.target - sWaterfallFreqScaleLerp.value) / 40;
}
Audio_PlaySfxGeneral(NA_SE_EV_WATER_WALL_BIG - SFX_FLAG, pos, 4, &sWaterfallFreqScaleLerp.value,
&sWaterfallFreqScaleLerp.value, &D_801DB4B8);
}
#pragma GLOBAL_ASM("asm/non_matchings/code/code_8019AF00/func_801A00EC.s")
/**
* Used for EnRiverSound variables
*/
void Audio_StepFreqLerp(FreqLerp* lerp) {
if (lerp->remainingFrames != 0) {
lerp->remainingFrames--;
if (lerp->remainingFrames != 0) {
lerp->value += lerp->step;
} else {
lerp->value = lerp->target;
}
}
}
#pragma GLOBAL_ASM("asm/non_matchings/code/code_8019AF00/func_801A0124.s")
@@ -2087,13 +2129,131 @@ const char sAudioOcarinaUnusedText7[] = "check is over!!! %d %d %d\n";
#pragma GLOBAL_ASM("asm/non_matchings/code/code_8019AF00/func_801A0238.s")
#pragma GLOBAL_ASM("asm/non_matchings/code/code_8019AF00/func_801A026C.s")
/**
* Unused remnant from OoT's EnRiverSound
* Was designed to incrementally increase volume of NA_BGM_GANON_TOWER for each new room during the climb of Ganon's
* Tower
*/
void Audio_SetGanonsTowerBgmVolumeLevel(u8 ganonsTowerLevel) {
u8 channelIndex;
s8 pan = 0;
#pragma GLOBAL_ASM("asm/non_matchings/code/code_8019AF00/func_801A0318.s")
// Ganondorfs's Lair
if (ganonsTowerLevel == 0) {
pan = 0x7F;
}
#pragma GLOBAL_ASM("asm/non_matchings/code/code_8019AF00/func_801A0450.s")
for (channelIndex = 0; channelIndex < 16; channelIndex++) {
// CHAN_UPD_PAN_UNSIGNED
Audio_QueueCmdS8(((u8)(u32)channelIndex << 8) | 0x7000000, pan);
}
#pragma GLOBAL_ASM("asm/non_matchings/code/code_8019AF00/func_801A046C.s")
// Lowest room in Ganon's Tower (Entrance Room)
if (ganonsTowerLevel == 7) {
// Adds a delay to setting the volume in the first room
sEnterGanonsTowerTimer = 2;
} else {
Audio_SetGanonsTowerBgmVolume(sGanonsTowerLevelsVol[ganonsTowerLevel % ARRAY_COUNTU(sGanonsTowerLevelsVol)]);
}
}
/**
* Unused remnant from OoT's EnRiverSound
* If a new volume is requested for ganon's tower, update the volume and
* calculate a new low-pass filter cutoff and reverb based on the new volume
*/
s32 Audio_SetGanonsTowerBgmVolume(u8 targetVolume) {
u8 lowPassFilterCutoff;
u8 channelIndex;
u16 reverb;
if (sGanonsTowerVol != targetVolume) {
// Sets the volume
Audio_SetVolumeScale(SEQ_PLAYER_BGM_MAIN, 0, targetVolume, 2);
// Sets the filter cutoff of the form (lowPassFilterCutoff << 4) | (highPassFilter & 0xF). highPassFilter is
// always set to 0
if (targetVolume < 0x40) {
// Only the first room
lowPassFilterCutoff = 0x10;
} else {
// Higher volume leads to a higher cut-off frequency in the low-pass filtering
lowPassFilterCutoff = (((targetVolume - 0x40) >> 2) + 1) << 4;
}
Audio_QueueSeqCmd((8 << 28) | ((u8)(SEQ_PLAYER_BGM_MAIN) << 24) | ((u8)(4) << 16) | ((u8)(15) << 8) |
(u8)(lowPassFilterCutoff));
// Sets the reverb
for (channelIndex = 0; channelIndex < ARRAY_COUNT(gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].channels);
channelIndex++) {
if (&gAudioContext.sequenceChannelNone !=
gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].channels[channelIndex]) {
// soundScriptIO[5] was set to 0x40 in channels 0, 1, and 4 (BGM no longer in OoT)
if ((u8)gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].channels[channelIndex]->soundScriptIO[5] !=
0xFF) {
// Higher volume leads to lower reverb
reverb =
(((u16)gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].channels[channelIndex]->soundScriptIO[5] -
targetVolume) +
0x7F);
if (reverb > 0x7F) {
reverb = 0x7F;
}
// CHAN_UPD_REVERB
Audio_QueueCmdS8(_SHIFTL(5, 24, 8) | _SHIFTL(SEQ_PLAYER_BGM_MAIN, 16, 8) |
_SHIFTL(channelIndex, 8, 8),
(u8)reverb);
}
}
}
sGanonsTowerVol = targetVolume;
}
return -1;
}
/**
* Unused remnant from OoT's EnRiverSound
* Responsible for lowering market bgm in Child Market Entrance and Child Market Back Alley
* Only lowers volume for 1 frame, so must be called every frame to maintain lower volume
*/
void Audio_LowerMainBgmVolume(u8 volume) {
sRiverSoundMainBgmVol = volume;
sRiverSoundMainBgmLower = true;
}
/**
* Unused remnant from OoT's EnRiverSound
* Still called by Audio_Update every frame, but none of these processes get initialized
*/
void Audio_UpdateRiverSoundVolumes(void) {
// Updates Main Bgm Volume (RiverSound of type RS_LOWER_MAIN_BGM_VOLUME)
if (sRiverSoundMainBgmLower == true) {
if (sRiverSoundMainBgmCurrentVol != sRiverSoundMainBgmVol) {
// lowers the volume for 1 frame
Audio_SetVolumeScale(SEQ_PLAYER_BGM_MAIN, 0, sRiverSoundMainBgmVol, 10);
sRiverSoundMainBgmCurrentVol = sRiverSoundMainBgmVol;
sRiverSoundMainBgmRestore = true;
}
sRiverSoundMainBgmLower = false;
} else if ((sRiverSoundMainBgmRestore == true) && !sAudioIsWindowOpen) {
// restores the volume every frame
Audio_SetVolumeScale(SEQ_PLAYER_BGM_MAIN, 0, 0x7F, 10);
sRiverSoundMainBgmCurrentVol = 0x7F;
sRiverSoundMainBgmRestore = false;
}
// Update Ganon's Tower Volume (RiverSound of type RS_GANON_TOWER_7)
if (sEnterGanonsTowerTimer != 0) {
sEnterGanonsTowerTimer--;
if (sEnterGanonsTowerTimer == 0) {
Audio_SetGanonsTowerBgmVolume(sGanonsTowerLevelsVol[7]);
}
}
}
#pragma GLOBAL_ASM("asm/non_matchings/code/code_8019AF00/func_801A0554.s")
@@ -2107,13 +2267,71 @@ const char sAudioOcarinaUnusedText7[] = "check is over!!! %d %d %d\n";
#pragma GLOBAL_ASM("asm/non_matchings/code/code_8019AF00/func_801A0868.s")
#pragma GLOBAL_ASM("asm/non_matchings/code/code_8019AF00/func_801A09D4.s")
/**
* Unused remnant of OoT's EnRiverSound (func_800F4E30)
*/
void func_801A09D4(Vec3f* pos, f32 xzDistToPlayer) {
f32 volumeRel;
s8 pan;
u8 channelIndex;
#pragma GLOBAL_ASM("asm/non_matchings/code/code_8019AF00/func_801A0C70.s")
if (sRiverSoundBgmPos == NULL) {
sRiverSoundBgmPos = pos;
sRiverSoundXZDistToPlayer = xzDistToPlayer;
} else if (sRiverSoundBgmPos != pos) {
if (sRiverSoundXZDistToPlayer > xzDistToPlayer) {
sRiverSoundBgmPos = pos;
sRiverSoundXZDistToPlayer = xzDistToPlayer;
}
} else {
sRiverSoundXZDistToPlayer = xzDistToPlayer;
}
#pragma GLOBAL_ASM("asm/non_matchings/code/code_8019AF00/func_801A0C90.s")
if (sRiverSoundBgmPos->x > 100.0f) {
pan = 0x7F;
} else if (sRiverSoundBgmPos->x < -100.0f) {
pan = 0;
} else {
pan = ((sRiverSoundBgmPos->x / 100.0f) * 64.0f) + 64.0f;
}
#pragma GLOBAL_ASM("asm/non_matchings/code/code_8019AF00/func_801A0CB0.s")
if (sRiverSoundXZDistToPlayer > 400.0f) {
volumeRel = 0.1f;
} else if (sRiverSoundXZDistToPlayer < 120.0f) {
volumeRel = 1.0f;
} else {
volumeRel = ((1.0f - ((sRiverSoundXZDistToPlayer - 120.0f) / 280.0f)) * 0.9f) + 0.1f;
}
for (channelIndex = 0; channelIndex < 16; channelIndex++) {
if (channelIndex != 9) {
Audio_QueueSeqCmd(((u32)(6) << 28) | ((u32)(SEQ_PLAYER_BGM_MAIN) << 24) | ((u32)(2) << 16) |
((u32)(channelIndex) << 8) | ((u8)(127.0f * volumeRel)));
Audio_QueueCmdS8(0x03000000 | ((u8)((u32)channelIndex) << 8), pan);
}
}
}
/**
* Unused remnant of OoT's EnRiverSound
*/
void Audio_ClearSariaBgm(void) {
if (sRiverSoundBgmPos != NULL) {
sRiverSoundBgmPos = NULL;
}
}
/**
* Unused remnant of OoT's EnRiverSound
*/
void Audio_ClearSariaBgmAtPos(Vec3f* pos) {
if (sRiverSoundBgmPos == pos) {
sRiverSoundBgmPos = NULL;
}
}
void Audio_SplitBgmChannels(s8 volumeSplit);
#pragma GLOBAL_ASM("asm/non_matchings/code/code_8019AF00/Audio_SplitBgmChannels.s")
#pragma GLOBAL_ASM("asm/non_matchings/code/code_8019AF00/func_801A0E44.s")
@@ -2147,9 +2365,58 @@ const char sAudioOcarinaUnusedText7[] = "check is over!!! %d %d %d\n";
#pragma GLOBAL_ASM("asm/non_matchings/code/code_8019AF00/func_801A2090.s")
#pragma GLOBAL_ASM("asm/non_matchings/code/code_8019AF00/func_801A21FC.s")
/**
* Unused remnant of OoT's EnRiverSound
*/
void Audio_PlaySariaBgm(Vec3f* pos, u16 seqId, u16 distMax) {
f32 absY;
f32 dist;
u8 targetVolume;
f32 prevDist;
#pragma GLOBAL_ASM("asm/non_matchings/code/code_8019AF00/func_801A2460.s")
if (sRiverSoundBgmTimer != 0) {
sRiverSoundBgmTimer--;
return;
}
dist = sqrtf(SQ(pos->z) + (SQ(pos->x) + SQ(pos->y)));
if (sRiverSoundBgmPos == NULL) {
sRiverSoundBgmPos = pos;
func_801A3238(SEQ_PLAYER_BGM_SUB, seqId, 0, 7, 2);
} else {
prevDist = sqrtf(SQ(sRiverSoundBgmPos->z) + SQ(sRiverSoundBgmPos->x));
if (dist < prevDist) {
sRiverSoundBgmPos = pos;
} else {
dist = prevDist;
}
}
absY = ABS_ALT(pos->y);
if ((distMax / 15.0f) < absY) {
targetVolume = 0;
} else if (dist < distMax) {
targetVolume = (1.0f - (dist / distMax)) * 127.0f;
} else {
targetVolume = 0;
}
if (seqId != NA_BGM_FAIRY_FOUNTAIN) {
Audio_SplitBgmChannels(targetVolume);
}
Audio_SetVolumeScale(SEQ_PLAYER_BGM_SUB, 3, targetVolume, 0);
Audio_SetVolumeScale(SEQ_PLAYER_BGM_MAIN, 3, 0x7F - targetVolume, 0);
}
/**
* Unused remnant of OoT's EnRiverSound
*/
void Audio_ClearSariaBgm2(void) {
sRiverSoundBgmPos = NULL;
}
#pragma GLOBAL_ASM("asm/non_matchings/code/code_8019AF00/func_801A246C.s")
+1 -1
View File
@@ -40,6 +40,6 @@
#pragma GLOBAL_ASM("asm/non_matchings/code/code_801A5BD0/func_801A787C.s")
#pragma GLOBAL_ASM("asm/non_matchings/code/code_801A5BD0/func_801A78E4.s")
#pragma GLOBAL_ASM("asm/non_matchings/code/code_801A5BD0/Audio_IsSfxPlaying.s")
#pragma GLOBAL_ASM("asm/non_matchings/code/code_801A5BD0/func_801A794C.s")
+1 -1
View File
@@ -18,7 +18,7 @@
#pragma GLOBAL_ASM("asm/non_matchings/code/code_801A7B10/func_801A8B2C.s")
#pragma GLOBAL_ASM("asm/non_matchings/code/code_801A7B10/func_801A8BD0.s")
#pragma GLOBAL_ASM("asm/non_matchings/code/code_801A7B10/Audio_SetVolumeScale.s")
#pragma GLOBAL_ASM("asm/non_matchings/code/code_801A7B10/func_801A8D5C.s")
+122 -78
View File
@@ -3908,104 +3908,148 @@ Hilite* func_800BCC68(Vec3f* arg0, GlobalContext* globalCtx) {
return Hilite_DrawXlu(arg0, &globalCtx->view.eye, &lightDir, globalCtx->state.gfxCtx);
}
void func_800BCCDC(Vec3s* points, s32 pathCount, Vec3f* pos1, Vec3f* pos2, s32 arg4) {
s32 spB4;
s32 spB0;
s32 spA8[2] = { 0, 0 };
s32 spA0[2] = { 0, 0 };
Vec3f sp94;
Vec3f sp7C[2];
Vec3f sp70;
Vec3f sp64;
f32 sp60;
f32 sp5C;
f32 sp54[2];
/**
* Calculates the closest position `dstPos` to the input position `srcPos` along the path given by `points`/`numPoints`
* Whether the points provided forms a closed-loop path is indicated by `isPathLoop`
*/
void Actor_GetClosestPosOnPath(Vec3s* points, s32 numPoints, Vec3f* srcPos, Vec3f* dstPos, s32 isPathLoop) {
s32 pointIndex;
s32 closestPointIndex;
s32 useAdjacentLines[2] = {
false, // determines whether to use line connecting to previous point in calculations
false, // determines whether to use line connecting to next point in calculations
};
s32 isRightSideOfAdjacentLines[2] = {
false, // determines whether srcPos is on the right side of the line from prev to curr point
false, // determines whether srcPos is on the right side of the line from curr to next point
};
Vec3f closestPoint;
Vec3f closestPos[2];
Vec3f closestPointNext;
Vec3f closestPointPrev;
f32 distSq; // First used as distSq to closest point, then used as distSq to closest position
f32 closestPointDistSq;
f32 loopDistSq[2];
s32 i;
spB0 = 0;
sp5C = SQ(40000.0f);
closestPointIndex = 0;
closestPointDistSq = SQ(40000.0f);
for (spB4 = 0; spB4 < pathCount; spB4++) {
sp60 = Math3D_XZDistanceSquared(pos1->x, pos1->z, points[spB4].x, points[spB4].z);
if (sp60 < sp5C) {
sp5C = sp60;
spB0 = spB4;
// Find the point closest to srcPos
for (pointIndex = 0; pointIndex < numPoints; pointIndex++) {
distSq = Math3D_XZDistanceSquared(srcPos->x, srcPos->z, points[pointIndex].x, points[pointIndex].z);
if (distSq < closestPointDistSq) {
closestPointDistSq = distSq;
closestPointIndex = pointIndex;
}
}
sp94.x = (points + spB0)->x;
sp94.z = (points + spB0)->z;
pos2->y = (points + spB0)->y;
if (spB0 != 0) {
sp64.x = (points + spB0 - 1)->x;
sp64.z = (points + spB0 - 1)->z;
} else if (arg4) {
sp64.x = (points + pathCount - 1)->x;
sp64.z = (points + pathCount - 1)->z;
closestPoint.x = (points + closestPointIndex)->x;
closestPoint.z = (points + closestPointIndex)->z;
dstPos->y = (points + closestPointIndex)->y;
// Analyze point on path immediately previous to the closest point
if (closestPointIndex != 0) {
// The point previous to the closest point
closestPointPrev.x = (points + closestPointIndex - 1)->x;
closestPointPrev.z = (points + closestPointIndex - 1)->z;
} else if (isPathLoop) {
// Closest point is the first point in the path list
// Set the previous point to loop around to the the final point on the path
closestPointPrev.x = (points + numPoints - 1)->x;
closestPointPrev.z = (points + numPoints - 1)->z;
}
if ((closestPointIndex != 0) || isPathLoop) {
// Use the adjacent line
useAdjacentLines[0] =
Math3D_PointDistToLine2D(srcPos->x, srcPos->z, closestPointPrev.x, closestPointPrev.z, closestPoint.x,
closestPoint.z, &closestPos[0].x, &closestPos[0].z, &distSq);
}
if ((spB0 != 0) || arg4) {
spA8[0] =
Math3D_PointDistToLine2D(pos1->x, pos1->z, sp64.x, sp64.z, sp94.x, sp94.z, &sp7C[0].x, &sp7C[0].z, &sp60);
// Analyze point on path immediately next to the closest point
if (closestPointIndex + 1 != numPoints) {
// The point next to the closest point
closestPointNext.x = (points + closestPointIndex + 1)->x;
closestPointNext.z = (points + closestPointIndex + 1)->z;
} else if (isPathLoop) {
// Closest point is the final point in the path list
// Set the next point to loop around to the the first point on the path
closestPointNext.x = (points + 0)->x;
closestPointNext.z = (points + 0)->z;
}
if ((closestPointIndex + 1 != numPoints) || isPathLoop) {
useAdjacentLines[1] =
Math3D_PointDistToLine2D(srcPos->x, srcPos->z, closestPoint.x, closestPoint.z, closestPointNext.x,
closestPointNext.z, &closestPos[1].x, &closestPos[1].z, &distSq);
}
if (spB0 + 1 != pathCount) {
sp70.x = (points + spB0 + 1)->x;
sp70.z = (points + spB0 + 1)->z;
} else if (arg4) {
sp70.x = points->x;
sp70.z = points->z;
}
/**
* For close-looped paths, they must be defined in a clockwise orientation looking from the top down.
* Therefore, `srcPos` being interior of the loop will lead to both lines of `isRightSideOfAdjacentLines`
* returning true.
*/
if (isPathLoop) {
isRightSideOfAdjacentLines[0] = ((closestPointPrev.x - srcPos->x) * (closestPoint.z - srcPos->z)) <
((closestPointPrev.z - srcPos->z) * (closestPoint.x - srcPos->x));
if ((spB0 + 1 != pathCount) || arg4) {
spA8[1] =
Math3D_PointDistToLine2D(pos1->x, pos1->z, sp94.x, sp94.z, sp70.x, sp70.z, &sp7C[1].x, &sp7C[1].z, &sp60);
}
isRightSideOfAdjacentLines[1] = ((closestPointNext.z - srcPos->z) * (closestPoint.x - srcPos->x)) <
((closestPoint.z - srcPos->z) * (closestPointNext.x - srcPos->x));
if (arg4) {
s32 phi_s0_2;
spA0[0] = ((sp64.x - pos1->x) * (sp94.z - pos1->z)) < ((sp64.z - pos1->z) * (sp94.x - pos1->x));
spA0[1] = ((sp70.z - pos1->z) * (sp94.x - pos1->x)) < ((sp94.z - pos1->z) * (sp70.x - pos1->x));
for (phi_s0_2 = 0; phi_s0_2 < ARRAY_COUNT(sp54); phi_s0_2++) {
if (spA8[phi_s0_2] != 0) {
sp54[phi_s0_2] = Math3D_XZDistanceSquared(pos1->x, pos1->z, sp7C[phi_s0_2].x, sp7C[phi_s0_2].z);
for (i = 0; i < ARRAY_COUNT(loopDistSq); i++) {
if (useAdjacentLines[i]) {
// Get distSq from srcPos to closestPos
loopDistSq[i] = Math3D_XZDistanceSquared(srcPos->x, srcPos->z, closestPos[i].x, closestPos[i].z);
} else {
sp54[phi_s0_2] = SQ(40000.0f);
// The closest Pos is not contained within the line-segment
loopDistSq[i] = SQ(40000.0f);
}
}
}
if (arg4 && (((spA0[0] != 0) && (spA0[1] != 0)) || ((spA0[0] != 0) && (spA8[0] != 0) && (sp54[0] < sp54[1])) ||
((spA0[1] != 0) && (spA8[1] != 0) && (sp54[1] < sp54[0])))) {
pos2->x = pos1->x;
pos2->z = pos1->z;
} else if ((spA8[0] != 0) && (spA8[1] != 0)) {
if ((spA0[0] == 0) && (spA0[1] == 0)) {
if (Math3D_PointDistToLine2D(pos1->x, pos1->z, sp7C[0].x, sp7C[0].z, sp7C[1].x, sp7C[1].z, &pos2->x,
&pos2->z, &sp60) == 0) {
pos2->x = (sp7C[1].x + sp7C[0].x) * 0.5f;
pos2->z = (sp7C[1].z + sp7C[0].z) * 0.5f;
// Calculate closest position along path
if (isPathLoop && ((isRightSideOfAdjacentLines[0] && isRightSideOfAdjacentLines[1]) ||
(isRightSideOfAdjacentLines[0] && useAdjacentLines[0] && (loopDistSq[0] < loopDistSq[1])) ||
(isRightSideOfAdjacentLines[1] && useAdjacentLines[1] && (loopDistSq[1] < loopDistSq[0])))) {
// srcPos is contained within the closed loop
dstPos->x = srcPos->x;
dstPos->z = srcPos->z;
} else if (useAdjacentLines[0] && useAdjacentLines[1]) {
// srcPos is somewhere withing the bend of the path
if (!isRightSideOfAdjacentLines[0] && !isRightSideOfAdjacentLines[1]) {
// srcPos is not inside a loop
if (!Math3D_PointDistToLine2D(srcPos->x, srcPos->z, closestPos[0].x, closestPos[0].z, closestPos[1].x,
closestPos[1].z, &dstPos->x, &dstPos->z, &distSq)) {
// The dstPos calculated in Math3D_PointDistToLine2D was not valid.
// Take the midpoint of the two closest ponits instead
dstPos->x = (closestPos[1].x + closestPos[0].x) * 0.5f;
dstPos->z = (closestPos[1].z + closestPos[0].z) * 0.5f;
}
} else if (sp54[1] < sp54[0]) {
pos2->x = sp7C[1].x;
pos2->z = sp7C[1].z;
} else if (loopDistSq[1] < loopDistSq[0]) {
// Use closest position along the line in the loop connecting the closest point and the next point
dstPos->x = closestPos[1].x;
dstPos->z = closestPos[1].z;
} else {
pos2->x = sp7C[0].x;
pos2->z = sp7C[0].z;
// Use closest position along the ling in the loop connecting the closest point and the prev point
dstPos->x = closestPos[0].x;
dstPos->z = closestPos[0].z;
}
} else if (spA8[0] != 0) {
pos2->x = sp7C[0].x;
pos2->z = sp7C[0].z;
} else if (spA8[1] != 0) {
pos2->x = sp7C[1].x;
pos2->z = sp7C[1].z;
} else if (arg4 && ((((sp64.x - pos1->x) * (sp70.z - pos1->z)) < ((sp64.z - pos1->z) * (sp70.x - pos1->x))))) {
pos2->x = pos1->x;
pos2->z = pos1->z;
} else if (useAdjacentLines[0]) {
// Use closest position along line segment connecting the closest point and the prev point
dstPos->x = closestPos[0].x;
dstPos->z = closestPos[0].z;
} else if (useAdjacentLines[1]) {
// Use closest position along line segment connecting the closest point and the next point
dstPos->x = closestPos[1].x;
dstPos->z = closestPos[1].z;
} else if (isPathLoop && ((((closestPointPrev.x - srcPos->x) * (closestPointNext.z - srcPos->z)) <
((closestPointPrev.z - srcPos->z) * (closestPointNext.x - srcPos->x))))) {
// Inside the line that directly connects the previous point to the next point (inside the bend of a corner)
dstPos->x = srcPos->x;
dstPos->z = srcPos->z;
} else {
pos2->x = sp94.x;
pos2->z = sp94.z;
// The closest point and the closest position are the same (srcPos is near the outer region of a corner)
dstPos->x = closestPoint.x;
dstPos->z = closestPoint.z;
}
}