Co-op ripples (#118)

* First attempt

First attempt at having other Mario objects create ripples.

Works very inconsistently, the position of players not currently interacting with the painting seems to influence whether the ripples will work or not

* Moved nearest mario code from geo_painting_update

Instead of using the nearest mario object for geo_painting_update, I use it in painting.c functions that called for gPaintingMarioXYZPos, substituting the nearest Mario object's position instead, as well as using it for the last floor mario was over.

This method still isn't quite perfect and there's a few more things I'd like to fix/change.
1. If a Mario is closer to the painting (perhaps right against the middle) other Mario's can't trigger ripplings at all unless they get closer than the closest Mario
2. I would like for all Entry and Exit ripples to interrupt each other, meaning if multiple Marios jump in, each one triggers a ripple one after the other
3. I want to hear the other player's exit sounds

* Add currPositionValid

Sets currPositionValid to false for NetworkPlayers who are connected, who changed levels to a level different from the local player, and who are not the local player when the local player changes levels.

* Set currPositionValid

Sets currPositionValid to false for update packets that don't match local area.
Sets it to true for update packets that do match local area.

* Send change area/level immediately

Adds a function network_on_loaded_area2() which only sends a change level or area event and doesn't clear the sync ids.
Calls this function almost immediately as opposed to waiting for LOADING_LEVEL_THRESHOLD frames

* Add currPositionValid to NetworkPlayer

currPositionValid (which is used by painting.c to determine if a player should be considered for the closest player to the painting) is a variable that tells you if a network player's marioObj has a position which can be relied upon as a valid position for that player.

network_player_update_course_level sets it to false for a network player if the new location does not match the local player's location.
network_receive_player sets it to false if a network player's update is from a different location than the local player, but sets it to TRUE if the update is from the same location.
update_course does NOT set it to true, because the location is not sure to be valid until we've received a location from the player with the right location attached to it.

* Use currValidPosition correctly

Uses currValidPosition correctly, also add debug text for seeing closest player to painting.

* fix merge

* Fix merge

* Verboser logging and invpos on enter

* Multi collision arrays

* Removed nearest code, added comments

* Added simultaneous ripples on one painting

* Added commented Gamepad from Background line

* Removed debug logging

* renamed secondary network_on_loaded function

* Added early multiple paintings rippling at once

* Added multiple paintings rippling simultaneously

Co-authored-by: djoslin0 <djoslin0@users.noreply.github.com>
This commit is contained in:
HunterHeard 2022-08-07 03:08:00 -05:00 committed by GitHub
parent f1c2715fe7
commit ad5e7402fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 671 additions and 171 deletions

View file

@ -17,6 +17,10 @@
#include "save_file.h" #include "save_file.h"
#include "segment2.h" #include "segment2.h"
#include "pc/utils/misc.h" #include "pc/utils/misc.h"
#include "obj_behaviors.h"
#include "level_update.h"
#include "src/pc/network/network_player.h"
#include "print.h"
/** /**
* @file paintings.c * @file paintings.c
@ -190,11 +194,22 @@ struct Painting **sPaintingGroups[] = {
s16 gPaintingUpdateCounter = 1; s16 gPaintingUpdateCounter = 1;
s16 gLastPaintingUpdateCounter = 0; s16 gLastPaintingUpdateCounter = 0;
struct Painting_List_Item{
struct Painting_List_Item *prevPaintingItem;
struct Painting *painting;
struct Painting_List_Item *nextPaintingItem;
};
typedef struct { struct Painting_List_Item paintingZero = {
float ob[3]; /* x, y, z */ NULL,
signed char n[3]; /* normal */ NULL,
} Vtx_Interp; NULL
};
// typedef struct {
// float ob[3]; /* x, y, z */
// signed char n[3]; /* normal */
// } Vtx_Interp;
static Vtx_Interp sVertexBuffers[2][2 * 264 * 3]; static Vtx_Interp sVertexBuffers[2][2 * 264 * 3];
static u8 sVerticesCurIndex = 0; static u8 sVerticesCurIndex = 0;
@ -205,60 +220,192 @@ static u32 sVerticesPrevTimestamp;
static Vtx *sVerticesPtr[2]; static Vtx *sVerticesPtr[2];
static s32 sVerticesCount; static s32 sVerticesCount;
void patch_paintings_init(struct Painting *painting) {
painting->sVerticesCurIndex = 0;
painting->sVertexSwaps = 0;
painting->sVerticesCur = painting->sVertexBuffers[0];
painting->sVerticesPrev = NULL;
}
void patch_paintings_before(void) { void patch_paintings_before(void) {
if (gGlobalTimer == sVerticesPrevTimestamp + 1) { struct Painting_List_Item *currPItem = &paintingZero;
sVerticesCurIndex = !sVerticesCurIndex;
sVerticesCur = sVertexBuffers[sVerticesCurIndex]; while (true) {
sVerticesPrev = sVertexBuffers[!sVerticesCurIndex]; struct Painting *painting = currPItem->painting;
sVertexSwaps++; if (painting == NULL) {
break;
}
if (gGlobalTimer == painting->sVerticesPrevTimestamp + 1) {
painting->sVerticesCurIndex = !painting->sVerticesCurIndex;
painting->sVerticesCur = painting->sVertexBuffers[painting->sVerticesCurIndex];
painting->sVerticesPrev = painting->sVertexBuffers[!painting->sVerticesCurIndex];
painting->sVertexSwaps++;
} else { } else {
sVerticesPrev = NULL; painting->sVerticesPrev = NULL;
sVertexSwaps = 0; painting->sVertexSwaps = 0;
} }
sVerticesPtr[0] = NULL; painting->sVerticesPtr[0] = NULL;
sVerticesPtr[1] = NULL; painting->sVerticesPtr[1] = NULL;
sVerticesCount = 0; painting->sVerticesCount = 0;
currPItem = currPItem->nextPaintingItem;
if (currPItem == NULL || currPItem == &paintingZero) {
break;
}
}
} }
void patch_paintings_interpolated(f32 delta) { void patch_paintings_interpolated(f32 delta) {
if (sVerticesPtr[0] != NULL && sVerticesPrev != NULL && sVertexSwaps > 2) {
struct Painting_List_Item *currPItem = &paintingZero;
while (true) {
struct Painting *painting = currPItem ->painting;
if (painting == NULL) {
break;
}
if (painting->sVerticesPtr[0] != NULL && painting->sVerticesPrev != NULL && painting->sVertexSwaps > 2) {
s32 i; s32 i;
if (sVerticesPtr[1] != NULL) { if (painting->sVerticesPtr[1] != NULL) {
for (i = 0; i < sVerticesCount / 2; i++) { for (i = 0; i < painting->sVerticesCount / 2; i++) {
Vec3f obInterp; Vec3f obInterp;
delta_interpolate_vec3f(obInterp, sVerticesPrev[i].ob, sVerticesCur[i].ob, delta); delta_interpolate_vec3f(obInterp, painting->sVerticesPrev[i].ob, painting->sVerticesCur[i].ob, delta);
s8 nInterp[3]; s8 nInterp[3];
delta_interpolate_normal(nInterp, sVerticesPrev[i].n, sVerticesCur[i].n, delta); delta_interpolate_normal(nInterp, painting->sVerticesPrev[i].n, painting->sVerticesCur[i].n, delta);
for (u8 j = 0; j < 3; j++) { for (u8 j = 0; j < 3; j++) {
sVerticesPtr[0][i].n.ob[j] = obInterp[j]; painting->sVerticesPtr[0][i].n.ob[j] = obInterp[j];
sVerticesPtr[0][i].n.n[j] = nInterp[j]; painting->sVerticesPtr[0][i].n.n[j] = nInterp[j];
} }
} }
for (; i < sVerticesCount; i++) { for (; i < painting->sVerticesCount; i++) {
Vec3f obInterp; Vec3f obInterp;
delta_interpolate_vec3f(obInterp, sVerticesPrev[i].ob, sVerticesCur[i].ob, delta); delta_interpolate_vec3f(obInterp, painting->sVerticesPrev[i].ob, painting->sVerticesCur[i].ob, delta);
s8 nInterp[3]; s8 nInterp[3];
delta_interpolate_normal(nInterp, sVerticesPrev[i].n, sVerticesCur[i].n, delta); delta_interpolate_normal(nInterp, painting->sVerticesPrev[i].n, painting->sVerticesCur[i].n, delta);
for (u8 j = 0; j < 3; j++) { for (u8 j = 0; j < 3; j++) {
sVerticesPtr[1][i - sVerticesCount / 2].n.ob[j] = obInterp[j]; painting->sVerticesPtr[1][i - painting->sVerticesCount / 2].n.ob[j] = obInterp[j];
sVerticesPtr[1][i - sVerticesCount / 2].n.n[j] = nInterp[j]; painting->sVerticesPtr[1][i - painting->sVerticesCount / 2].n.n[j] = nInterp[j];
} }
} }
} else { } else {
for (i = 0; i < sVerticesCount; i++) { for (i = 0; i < painting->sVerticesCount; i++) {
Vec3f obInterp; Vec3f obInterp;
delta_interpolate_vec3f(obInterp, sVerticesPrev[i].ob, sVerticesCur[i].ob, delta); delta_interpolate_vec3f(obInterp, painting->sVerticesPrev[i].ob, painting->sVerticesCur[i].ob, delta);
s8 nInterp[3]; s8 nInterp[3];
delta_interpolate_normal(nInterp, sVerticesPrev[i].n, sVerticesCur[i].n, delta); delta_interpolate_normal(nInterp, painting->sVerticesPrev[i].n, painting->sVerticesCur[i].n, delta);
for (u8 j = 0; j < 3; j++) { for (u8 j = 0; j < 3; j++) {
sVerticesPtr[0][i].n.ob[j] = obInterp[j]; painting->sVerticesPtr[0][i].n.ob[j] = obInterp[j];
sVerticesPtr[0][i].n.n[j] = nInterp[j]; painting->sVerticesPtr[0][i].n.n[j] = nInterp[j];
} }
} }
} }
} }
currPItem = currPItem->nextPaintingItem;
if (currPItem == NULL | currPItem == &paintingZero) {
break;
}
}
}
void add_to_rippling_list(struct Painting *painting) {
if (paintingZero.painting == NULL) {
paintingZero.painting = painting;
paintingZero.nextPaintingItem = &paintingZero;
paintingZero.prevPaintingItem = &paintingZero;
patch_paintings_init(painting);
} else {
struct Painting_List_Item newPainting = {
NULL,
NULL,
NULL
};
struct Painting_List_Item *newPaintingItemAddr = malloc(sizeof newPainting);
struct Painting_List_Item *lastItem = paintingZero.prevPaintingItem;
lastItem->nextPaintingItem = newPaintingItemAddr;
newPaintingItemAddr->prevPaintingItem = lastItem;
newPaintingItemAddr->painting = painting;
newPaintingItemAddr->nextPaintingItem = &paintingZero;
paintingZero.prevPaintingItem = newPaintingItemAddr;
}
patch_paintings_init(painting);
}
void remove_from_rippling_list(struct Painting *painting) {
struct Painting_List_Item *foundPaintingItem = NULL;
bool reachedEnd = false;
struct Painting_List_Item *currPaintingItem = &paintingZero;
if (paintingZero.painting == NULL) {
return;
}
while(reachedEnd == false) {
if (currPaintingItem->painting == painting) {
foundPaintingItem = currPaintingItem;
break;
}
currPaintingItem = currPaintingItem->nextPaintingItem;
if (currPaintingItem == &paintingZero) {
break;
}
}
if (foundPaintingItem != NULL) {
if (foundPaintingItem == &paintingZero) {
if (paintingZero.nextPaintingItem == &paintingZero) {
paintingZero.prevPaintingItem = NULL;
paintingZero.nextPaintingItem = NULL;
paintingZero.painting = NULL;
} else {
struct Painting_List_Item *oldItem = paintingZero.nextPaintingItem;
paintingZero.painting = oldItem->painting;
paintingZero.nextPaintingItem = oldItem->nextPaintingItem;
paintingZero.nextPaintingItem->prevPaintingItem = &paintingZero;
free(oldItem);
}
} else {
foundPaintingItem->prevPaintingItem->nextPaintingItem = foundPaintingItem->nextPaintingItem;
foundPaintingItem->nextPaintingItem->prevPaintingItem = foundPaintingItem->prevPaintingItem;
free(foundPaintingItem);
}
}
}
bool painting_is_in_rippling_list(struct Painting *painting) {
struct Painting_List_Item *currPaintingItem = &paintingZero;
bool found = false;
while (found == false) {
if (currPaintingItem->painting == painting) {
found = true;
break;
}
currPaintingItem = currPaintingItem->nextPaintingItem;
if (currPaintingItem == &paintingZero) {
break;
}
}
return found;
}
struct Painting_List_Item *get_painting_item_at_index(int index) {
if (index < 0) {
return NULL;
}
struct Painting_List_Item *currPaintingItem = &paintingZero;
int i = 0;
while (i < index) {
currPaintingItem = currPaintingItem->nextPaintingItem;
i++;
if (currPaintingItem == &paintingZero) {
return NULL;
}
}
return currPaintingItem;
} }
/** /**
@ -280,13 +427,66 @@ void stop_other_paintings(s16 *idptr, struct Painting *paintingGroup[]) {
} }
} }
f32 highest_mag(struct Painting *painting) {
f32 highest = painting->currRippleMag;
for (s32 i = 0; i < MAX_PLAYERS + 1; i++) {
highest = painting->currRippleMags[i] > highest ? painting->currRippleMags[i] : highest;
}
return highest;
}
struct posStr {
f32 x;
f32 y;
f32 z;
f32 distance;
};
s32 in_mario_index(struct Painting *painting) {
s32 mIndex = painting->pitch != 0 ? painting->underMarioIndex : painting->enteredMarioIndex;
return mIndex;
}
// Returns the position of the mario who entered the painting this frame
struct posStr entered_mario_pos(struct Painting *painting) {
f32 dist = -999;
bool floor = painting->pitch != 0;
// Floor paintings will prefer the mario who went under this frame
s32 mIndex = floor ? painting->underMarioIndex : painting->enteredMarioIndex;
if (mIndex < 0) {
mIndex = floor ? painting->enteredMarioIndex : painting->underMarioIndex;
}
// If no marios entered or went under this frame, default to the painting's position
if (mIndex < 0 || !gNetworkPlayers[gMarioStates[mIndex].playerIndex].connected) {
struct posStr p = {
painting->posX,
painting->posY,
painting->posZ,
0
};
return p;
} else {
struct MarioState* enteredMario = &gMarioStates[mIndex];
struct posStr p = {
enteredMario->marioObj->oPosX,
enteredMario->marioObj->oPosY,
enteredMario->marioObj->oPosZ,
dist
};
return p;
}
}
/** /**
* @return Mario's y position inside the painting (bounded). * @return Mario's y position inside the painting (bounded).
*/ */
f32 painting_mario_y(struct Painting *painting) { f32 painting_mario_y(struct Painting *painting) {
//! Unnecessary use of double constants //! Unnecessary use of double constants
// Add 50 to make the ripple closer to Mario's center of mass. // Add 50 to make the ripple closer to Mario's center of mass.
f32 relY = gPaintingMarioYPos - painting->posY + 50.0; f32 relY = entered_mario_pos(painting).y - painting->posY + 50.0;
if (relY < 0.0) { if (relY < 0.0) {
relY = 0.0; relY = 0.0;
@ -300,7 +500,7 @@ f32 painting_mario_y(struct Painting *painting) {
* @return Mario's z position inside the painting (bounded). * @return Mario's z position inside the painting (bounded).
*/ */
f32 painting_mario_z(struct Painting *painting) { f32 painting_mario_z(struct Painting *painting) {
f32 relZ = painting->posZ - gPaintingMarioZPos; f32 relZ = painting->posZ - entered_mario_pos(painting).z;
if (relZ < 0.0) { if (relZ < 0.0) {
relZ = 0.0; relZ = 0.0;
@ -359,7 +559,7 @@ f32 painting_nearest_4th(struct Painting *painting) {
* @return Mario's x position inside the painting (bounded). * @return Mario's x position inside the painting (bounded).
*/ */
f32 painting_mario_x(struct Painting *painting) { f32 painting_mario_x(struct Painting *painting) {
f32 relX = gPaintingMarioXPos - painting->posX; f32 relX = entered_mario_pos(painting).x - painting->posX;
if (relX < 0.0) { if (relX < 0.0) {
relX = 0.0; relX = 0.0;
@ -398,7 +598,7 @@ f32 painting_ripple_x(struct Painting *painting, s8 xSource) {
void painting_state(s8 state, struct Painting *painting, struct Painting *paintingGroup[], void painting_state(s8 state, struct Painting *painting, struct Painting *paintingGroup[],
s8 xSource, s8 ySource, s8 resetTimer) { s8 xSource, s8 ySource, s8 resetTimer) {
// make sure no other paintings are rippling // make sure no other paintings are rippling
stop_other_paintings(&painting->id, paintingGroup); // stop_other_paintings(&painting->id, paintingGroup);
// use a different set of variables depending on the state // use a different set of variables depending on the state
switch (state) { switch (state) {
@ -416,16 +616,55 @@ void painting_state(s8 state, struct Painting *painting, struct Painting *painti
painting->dispersionFactor = painting->entryDispersionFactor; painting->dispersionFactor = painting->entryDispersionFactor;
break; break;
} }
if (painting->state == PAINTING_IDLE && state != PAINTING_IDLE) {
add_to_rippling_list(painting);
} else {
if (painting->state != PAINTING_IDLE && state == PAINTING_IDLE) {
remove_from_rippling_list(painting);
}
}
painting->state = state; painting->state = state;
painting->rippleX = painting_ripple_x(painting, xSource); painting->rippleX = painting_ripple_x(painting, xSource);
painting->rippleY = painting_ripple_y(painting, ySource); painting->rippleY = painting_ripple_y(painting, ySource);
gPaintingMarioYEntry = gPaintingMarioYPos; if (painting->enteredMarioIndex == 0) {
gPaintingMarioYEntry = gMarioState->marioObj->oPosY;
}
if (painting->state != PAINTING_IDLE) {
painting->rippleXs[painting->enteredMarioIndex] = painting->rippleX;
painting->rippleYs[painting->enteredMarioIndex] = painting->rippleY;
painting->currRippleMags[painting->enteredMarioIndex] = painting->currRippleMag;
painting->rippleTimers[painting->enteredMarioIndex] = 0;
}
// Because true or false would be too simple... // Because true or false would be too simple...
if (resetTimer == RESET_TIMER) { if (resetTimer == RESET_TIMER) {
painting->rippleTimer = 0.0f; painting->rippleTimer = 0.0f;
for (s32 i = 0; i < MAX_PLAYERS + 1; i++) {
if (painting->state == PAINTING_IDLE) {
painting->rippleTimers[i] = -1;
} }
}
if (painting->rippleTimers[painting->enteredMarioIndex] >= 0) {
if (painting->state == PAINTING_IDLE) {
painting->rippleTimers[painting->enteredMarioIndex] = -1;
} else {
painting->rippleTimers[painting->enteredMarioIndex] = 0;
}
}
}
if (in_mario_index(painting) == 0) {
gRipplingPainting = painting; gRipplingPainting = painting;
}
// Tries to set player who entered's currPositionValid to false
// So they won't create more ripples by bouncing around in the painting
if (state == PAINTING_ENTERED) {
s32 mIndex = painting->pitch == 0 ? painting->enteredMarioIndex : painting->underMarioIndex;
if (mIndex > 0) {
gNetworkPlayers[gMarioStates[mIndex].playerIndex].currPositionValid = false;
}
}
} }
/** /**
@ -583,7 +822,7 @@ void floor_painting_continuous_rippling(struct Painting *painting, struct Painti
} }
/** /**
* Check for Mario entering one of the special floors associated with the painting. * Check for Multiple marios entering one of the special floors associated with the painting.
*/ */
void painting_update_floors(struct Painting *painting) { void painting_update_floors(struct Painting *painting) {
s16 paintingId = painting->id; s16 paintingId = painting->id;
@ -593,6 +832,50 @@ void painting_update_floors(struct Painting *painting) {
s8 enterLeft = 0; s8 enterLeft = 0;
s8 enterMiddle = 0; s8 enterMiddle = 0;
s8 enterRight = 0; s8 enterRight = 0;
bool floorPainting = painting->pitch != 0;
painting->marioWentUnder = FALSE;
painting->floorEntered = 0;
painting->enteredMarioIndex = -1;
painting->underMarioIndex = -1;
for (int i = 0; i < MAX_PLAYERS + 1; i++) {
// If the painting was entered (and not a floor painting) no need to look at everyone else this frame
if (painting->floorEntered && !floorPainting) { continue; }
struct MarioState* m = &gMarioStates[i];
struct NetworkPlayer* np = &gNetworkPlayers[m->playerIndex];
// Skip non connected players
if (!np->connected) { continue; }
bool consider = true;
bool valid = true;
if (m < 20) {
continue;
}
if (!np->connected) {
consider = false;
} else if (!np->currPositionValid && i > 0) {
painting->lastFloors[i] = 0;
painting->currFloors[i] = 0;
painting->wasUnders[i] = 0;
painting->isUnders[i] = 0;
consider = false;
valid = false;
} else if (!is_player_active(m)) {
consider = false;
} else if (m->marioObj->header.gfx.animInfo.animID == MARIO_ANIM_A_POSE) {
consider = false;
}
rippleLeft = 0;
rippleMiddle = 0;
rippleRight = 0;
enterLeft = 0;
enterMiddle = 0;
enterRight = 0;
struct Surface* surface;
if (m == NULL || m->marioObj == NULL) { continue; }
find_floor(m->marioObj->oPosX, m->marioObj->oPosY, m->marioObj->oPosZ, &surface);
s16 floorType = surface->type;
/* The area in front of every painting in the game (except HMC and CotMC, which *\ /* The area in front of every painting in the game (except HMC and CotMC, which *\
|* act a little differently) is made up of 3 special floor triangles with special *| |* act a little differently) is made up of 3 special floor triangles with special *|
@ -600,43 +883,73 @@ void painting_update_floors(struct Painting *painting) {
\* and sets a bitfield accordingly. */ \* and sets a bitfield accordingly. */
// check if Mario's current floor is one of the special floors // check if Mario's current floor is one of the special floors
if (gPaintingMarioFloorType == paintingId * 3 + SURFACE_PAINTING_WOBBLE_A6) { if (floorType == paintingId * 3 + SURFACE_PAINTING_WOBBLE_A6) {
rippleLeft = RIPPLE_LEFT; rippleLeft = RIPPLE_LEFT;
} }
if (gPaintingMarioFloorType == paintingId * 3 + SURFACE_PAINTING_WOBBLE_A7) { if (floorType == paintingId * 3 + SURFACE_PAINTING_WOBBLE_A7) {
rippleMiddle = RIPPLE_MIDDLE; rippleMiddle = RIPPLE_MIDDLE;
} }
if (gPaintingMarioFloorType == paintingId * 3 + SURFACE_PAINTING_WOBBLE_A8) { if (floorType == paintingId * 3 + SURFACE_PAINTING_WOBBLE_A8) {
rippleRight = RIPPLE_RIGHT; rippleRight = RIPPLE_RIGHT;
} }
if (gPaintingMarioFloorType == paintingId * 3 + SURFACE_PAINTING_WARP_D3) { if (floorType == paintingId * 3 + SURFACE_PAINTING_WARP_D3) {
enterLeft = ENTER_LEFT; enterLeft = ENTER_LEFT;
} }
if (gPaintingMarioFloorType == paintingId * 3 + SURFACE_PAINTING_WARP_D4) { if (floorType == paintingId * 3 + SURFACE_PAINTING_WARP_D4) {
enterMiddle = ENTER_MIDDLE; enterMiddle = ENTER_MIDDLE;
} }
if (gPaintingMarioFloorType == paintingId * 3 + SURFACE_PAINTING_WARP_D5) { if (floorType == paintingId * 3 + SURFACE_PAINTING_WARP_D5) {
enterRight = ENTER_RIGHT; enterRight = ENTER_RIGHT;
} }
painting->lastFloor = painting->currFloor;
// at most 1 of these will be nonzero;
painting->currFloor = rippleLeft + rippleMiddle + rippleRight + enterLeft + enterMiddle + enterRight;
painting->lastFloors[i] = painting->currFloors[i];
// at most 1 of these will be nonzero;
painting->currFloors[i] = rippleLeft + rippleMiddle + rippleRight + enterLeft + enterMiddle + enterRight;
// invalid players should have their floor states set to 0 at all times
// so that when they return through the painting, it will ripple properly again
if (!valid) {
painting->lastFloors[i] = 0;
painting->currFloors[i] = 0;
}
if (!consider) {
continue;
}
// floorEntered is true iff currFloor is true and lastFloor is false // floorEntered is true iff currFloor is true and lastFloor is false
// (Mario just entered the floor on this frame) // (Mario just entered the floor on this frame)
painting->floorEntered = (painting->lastFloor ^ painting->currFloor) & painting->currFloor; s8 entered = (painting->lastFloors[i] ^ painting->currFloors[i]) & painting->currFloors[i];
if (entered) {
painting->floorEntered = entered;
painting->lastFloor = painting->lastFloors[i];
painting->currFloor = painting->currFloors[i];
painting->enteredMarioIndex = i;
}
painting->marioWasUnder = painting->marioIsUnder; // Mario going under code
painting->wasUnders[i] = painting->isUnders[i];
// Check if Mario has fallen below the painting (used for floor paintings) // Check if Mario has fallen below the painting (used for floor paintings)
if (gPaintingMarioYPos < painting->posY) { if (m->marioObj->oPosY < painting->posY) {
painting->marioIsUnder = TRUE; painting->isUnders[i] = TRUE;
} else { } else {
painting->marioIsUnder = FALSE; painting->isUnders[i] = FALSE;
} }
// Mario "went under" if he was not under last frame, but is under now // Mario "went under" if he was not under last frame, but is under now
painting->marioWentUnder = (painting->marioWasUnder ^ painting->marioIsUnder) & painting->marioIsUnder; s8 wentUnder = (painting->wasUnders[i] ^ painting->isUnders[i]) & painting->isUnders[i];
if (wentUnder) {
painting->marioWentUnder = wentUnder;
painting->underMarioIndex = i;
// If this mario went under a floor painting, set curr and last floor to that mario's
// This is used to determine where the ripple appears
if (floorPainting) {
painting->lastFloor = painting->lastFloors[i];
painting->currFloor = painting->currFloors[i];
}
}
}
} }
/** /**
@ -654,20 +967,30 @@ void painting_update_ripple_state(struct Painting *painting) {
//! imprecision?) and the painting will stop rippling. This happens to HMC, DDD, and //! imprecision?) and the painting will stop rippling. This happens to HMC, DDD, and
//! CotMC. //! CotMC.
painting->rippleTimer += 1.0; painting->rippleTimer += 1.0;
for (int i = 0; i < MAX_PLAYERS + 1; i++) {
if (painting->rippleTimers[i] >= 0) {
painting->rippleTimers[i] += 1.0;
painting->currRippleMags[i] *= painting->rippleDecay;
}
}
} }
if (painting->rippleTrigger == RIPPLE_TRIGGER_PROXIMITY) { if (painting->rippleTrigger == RIPPLE_TRIGGER_PROXIMITY) {
// if the painting is barely rippling, make it stop rippling // if the painting is barely rippling, make it stop rippling
if (painting->currRippleMag <= 1.0) { if (highest_mag(painting) <= 1.0) {
painting->state = PAINTING_IDLE; painting->state = PAINTING_IDLE;
remove_from_rippling_list(painting);
if (gRipplingPainting == painting) {
gRipplingPainting = NULL; gRipplingPainting = NULL;
} }
}
} else if (painting->rippleTrigger == RIPPLE_TRIGGER_CONTINUOUS) { } else if (painting->rippleTrigger == RIPPLE_TRIGGER_CONTINUOUS) {
// if the painting is doing the entry ripple but the ripples are as small as those from the // if the painting is doing the entry ripple but the ripples are as small as those from the
// passive ripple, make it do a passive ripple // passive ripple, make it do a passive ripple
// If Mario goes below the surface but doesn't warp, the painting will eventually reset. // If Mario goes below the surface but doesn't warp, the painting will eventually reset.
if (painting->state == PAINTING_ENTERED && painting->currRippleMag <= painting->passiveRippleMag) { if (painting->state == PAINTING_ENTERED && highest_mag(painting) <= painting->passiveRippleMag) {
// TODO: add array code here
painting->state = PAINTING_RIPPLE; painting->state = PAINTING_RIPPLE;
painting->currRippleMag = painting->passiveRippleMag; painting->currRippleMag = painting->passiveRippleMag;
painting->rippleDecay = painting->passiveRippleDecay; painting->rippleDecay = painting->passiveRippleDecay;
@ -682,37 +1005,98 @@ void painting_update_ripple_state(struct Painting *painting) {
* note that posX and posY correspond to a point on the face of the painting, not actual axes * note that posX and posY correspond to a point on the face of the painting, not actual axes
*/ */
s16 calculate_ripple_at_point(struct Painting *painting, f32 posX, f32 posY) { s16 calculate_ripple_at_point(struct Painting *painting, f32 posX, f32 posY) {
/// Controls the peaks of the ripple.
f32 rippleMag = painting->currRippleMag;
/// Controls the ripple's frequency /// Controls the ripple's frequency
f32 rippleRate = painting->currRippleRate; f32 rippleRate = painting->currRippleRate;
/// Controls how fast the ripple spreads /// Controls how fast the ripple spreads
f32 dispersionFactor = painting->dispersionFactor; f32 dispersionFactor = painting->dispersionFactor;
// Default multiRippleMode is to add ripples together
u8 multiRippleMode = ADD_RIPPLES;
// Do not do multiple ripples for continuous ripple paintings, like DDD or HMC
if (painting->rippleTrigger == RIPPLE_TRIGGER_CONTINUOUS) {
multiRippleMode = SINGLE_RIPPLE;
}
posX *= painting->size / PAINTING_SIZE;
posY *= painting->size / PAINTING_SIZE;
// For calculating the highest ripple, ans needs to start extremely low otherwise ripples under 0 won't be counted
s16 ans = multiRippleMode == HIGHEST_RIPPLE ? -32000 : 0;
bool flat = true;
if (multiRippleMode != SINGLE_RIPPLE) {
// If multi ripple mode
for (s32 i = 0; i < MAX_PLAYERS + 1; i++) {
if (painting->rippleTimers[i] < 0) { continue; }
if (painting->currRippleMags[i] < 1) { continue; }
f32 distanceToOrigin;
f32 rippleDistance;
/// Controls the peaks of the ripple.
f32 rippleMag = painting->currRippleMags[i];
/// How far the ripple has spread
f32 rippleTimer = painting->rippleTimers[i];
/// x and y ripple origin
f32 rippleX = painting->rippleXs[i];
f32 rippleY = painting->rippleYs[i];
distanceToOrigin = sqrtf((posX - rippleX) * (posX - rippleX) + (posY - rippleY) * (posY - rippleY));
// A larger dispersionFactor makes the ripple spread slower
rippleDistance = distanceToOrigin / dispersionFactor;
f32 rippleZ = 0;
if (rippleTimer < rippleDistance) {
// Don't do anything, leave it 0
} else {
flat = false;
// use a cosine wave to make the ripple go up and down,
// scaled by the painting's ripple magnitude
rippleZ = rippleMag * cosf(rippleRate * (2 * M_PI) * (rippleTimer - rippleDistance));
s16 rZ = round_float(rippleZ);
switch (multiRippleMode) {
case ADD_RIPPLES :
ans += rZ;
break;
case HIGHEST_RIPPLE :
ans = rZ > ans ? rZ : ans;
break;
case GREATEST_RIPPLE :
ans = abs(rZ) > abs(ans) ? rZ : ans;
break;
default:
ans += rZ;
break;
}
}
}
if (flat) {
ans = 0;
}
} else {
// If one ripple mode
f32 distanceToOrigin;
f32 rippleDistance;
/// Controls the peaks of the ripple.
f32 rippleMag = painting->currRippleMag;
/// How far the ripple has spread /// How far the ripple has spread
f32 rippleTimer = painting->rippleTimer; f32 rippleTimer = painting->rippleTimer;
/// x and y ripple origin /// x and y ripple origin
f32 rippleX = painting->rippleX; f32 rippleX = painting->rippleX;
f32 rippleY = painting->rippleY; f32 rippleY = painting->rippleY;
f32 distanceToOrigin;
f32 rippleDistance;
posX *= painting->size / PAINTING_SIZE;
posY *= painting->size / PAINTING_SIZE;
distanceToOrigin = sqrtf((posX - rippleX) * (posX - rippleX) + (posY - rippleY) * (posY - rippleY)); distanceToOrigin = sqrtf((posX - rippleX) * (posX - rippleX) + (posY - rippleY) * (posY - rippleY));
// A larger dispersionFactor makes the ripple spread slower // A larger dispersionFactor makes the ripple spread slower
rippleDistance = distanceToOrigin / dispersionFactor; rippleDistance = distanceToOrigin / dispersionFactor;
f32 rippleZ = 0;
if (rippleTimer < rippleDistance) { if (rippleTimer < rippleDistance) {
// if the ripple hasn't reached the point yet, make the point magnitude 0 // Don't do anything, leave it 0
return 0;
} else { } else {
// use a cosine wave to make the ripple go up and down, // use a cosine wave to make the ripple go up and down,
// scaled by the painting's ripple magnitude // scaled by the painting's ripple magnitude
f32 rippleZ = rippleMag * cosf(rippleRate * (2 * M_PI) * (rippleTimer - rippleDistance)); rippleZ = rippleMag * cosf(rippleRate * (2 * M_PI) * (rippleTimer - rippleDistance));
ans += round_float(rippleZ);
// round it to an int and return it
return round_float(rippleZ);
} }
}
return ans;
} }
/** /**
@ -751,18 +1135,19 @@ void painting_generate_mesh(struct Painting *painting, s16 *mesh, s16 numTris) {
s16 i; s16 i;
gPaintingMesh = mem_pool_alloc(gEffectsMemoryPool, numTris * sizeof(struct PaintingMeshVertex)); gPaintingMesh = mem_pool_alloc(gEffectsMemoryPool, numTris * sizeof(struct PaintingMeshVertex));
if (gPaintingMesh == NULL) { painting->paintingMesh = gPaintingMesh;
if (painting->paintingMesh == NULL) {
return; return;
} }
// accesses are off by 1 since the first entry is the number of vertices // accesses are off by 1 since the first entry is the number of vertices
for (i = 0; i < numTris; i++) { for (i = 0; i < numTris; i++) {
gPaintingMesh[i].pos[0] = mesh[i * 3 + 1]; painting->paintingMesh[i].pos[0] = mesh[i * 3 + 1];
gPaintingMesh[i].pos[1] = mesh[i * 3 + 2]; painting->paintingMesh[i].pos[1] = mesh[i * 3 + 2];
// The "z coordinate" of each vertex in the mesh is either 1 or 0. Instead of being an // The "z coordinate" of each vertex in the mesh is either 1 or 0. Instead of being an
// actual coordinate, it just determines whether the vertex moves // actual coordinate, it just determines whether the vertex moves
gPaintingMesh[i].pos[2] = ripple_if_movable(painting, mesh[i * 3 + 3], painting->paintingMesh[i].pos[2] = ripple_if_movable(painting, mesh[i * 3 + 3],
gPaintingMesh[i].pos[0], gPaintingMesh[i].pos[1]); painting->paintingMesh[i].pos[0], painting->paintingMesh[i].pos[1]);
} }
} }
@ -781,11 +1166,12 @@ void painting_generate_mesh(struct Painting *painting, s16 *mesh, s16 numTris) {
* *
* The mesh used in game, seg2_painting_triangle_mesh, is in bin/segment2.c. * The mesh used in game, seg2_painting_triangle_mesh, is in bin/segment2.c.
*/ */
void painting_calculate_triangle_normals(s16 *mesh, s16 numVtx, s16 numTris) { void painting_calculate_triangle_normals(struct Painting *painting, s16 *mesh, s16 numVtx, s16 numTris) {
s16 i; s16 i;
gPaintingTriNorms = mem_pool_alloc(gEffectsMemoryPool, numTris * sizeof(Vec3f)); gPaintingTriNorms = mem_pool_alloc(gEffectsMemoryPool, numTris * sizeof(Vec3f));
if (gPaintingTriNorms == NULL) { painting->paintingTriNorms = gPaintingTriNorms;
if (painting->paintingTriNorms == NULL) {
return; return;
} }
@ -795,22 +1181,22 @@ void painting_calculate_triangle_normals(s16 *mesh, s16 numVtx, s16 numTris) {
s16 v1 = mesh[tri + 1]; s16 v1 = mesh[tri + 1];
s16 v2 = mesh[tri + 2]; s16 v2 = mesh[tri + 2];
f32 x0 = gPaintingMesh[v0].pos[0]; f32 x0 = painting->paintingMesh[v0].pos[0];
f32 y0 = gPaintingMesh[v0].pos[1]; f32 y0 = painting->paintingMesh[v0].pos[1];
f32 z0 = gPaintingMesh[v0].pos[2]; f32 z0 = painting->paintingMesh[v0].pos[2];
f32 x1 = gPaintingMesh[v1].pos[0]; f32 x1 = painting->paintingMesh[v1].pos[0];
f32 y1 = gPaintingMesh[v1].pos[1]; f32 y1 = painting->paintingMesh[v1].pos[1];
f32 z1 = gPaintingMesh[v1].pos[2]; f32 z1 = painting->paintingMesh[v1].pos[2];
f32 x2 = gPaintingMesh[v2].pos[0]; f32 x2 = painting->paintingMesh[v2].pos[0];
f32 y2 = gPaintingMesh[v2].pos[1]; f32 y2 = painting->paintingMesh[v2].pos[1];
f32 z2 = gPaintingMesh[v2].pos[2]; f32 z2 = painting->paintingMesh[v2].pos[2];
// Cross product to find each triangle's normal vector // Cross product to find each triangle's normal vector
gPaintingTriNorms[i][0] = (y1 - y0) * (z2 - z1) - (z1 - z0) * (y2 - y1); painting->paintingTriNorms[i][0] = (y1 - y0) * (z2 - z1) - (z1 - z0) * (y2 - y1);
gPaintingTriNorms[i][1] = (z1 - z0) * (x2 - x1) - (x1 - x0) * (z2 - z1); painting->paintingTriNorms[i][1] = (z1 - z0) * (x2 - x1) - (x1 - x0) * (z2 - z1);
gPaintingTriNorms[i][2] = (x1 - x0) * (y2 - y1) - (y1 - y0) * (x2 - x1); painting->paintingTriNorms[i][2] = (x1 - x0) * (y2 - y1) - (y1 - y0) * (x2 - x1);
} }
} }
@ -846,7 +1232,7 @@ s8 normalize_component(f32 comp) {
* *
* The table used in game, seg2_painting_mesh_neighbor_tris, is in bin/segment2.c. * The table used in game, seg2_painting_mesh_neighbor_tris, is in bin/segment2.c.
*/ */
void painting_average_vertex_normals(s16 *neighborTris, s16 numVtx) { void painting_average_vertex_normals(struct Painting *painting, s16 *neighborTris, s16 numVtx) {
UNUSED s16 unused; UNUSED s16 unused;
s16 tri; s16 tri;
s16 i; s16 i;
@ -864,9 +1250,9 @@ void painting_average_vertex_normals(s16 *neighborTris, s16 numVtx) {
neighbors = neighborTris[entry]; neighbors = neighborTris[entry];
for (j = 0; j < neighbors; j++) { for (j = 0; j < neighbors; j++) {
tri = neighborTris[entry + j + 1]; tri = neighborTris[entry + j + 1];
nx += gPaintingTriNorms[tri][0]; nx += painting->paintingTriNorms[tri][0];
ny += gPaintingTriNorms[tri][1]; ny += painting->paintingTriNorms[tri][1];
nz += gPaintingTriNorms[tri][2]; nz += painting->paintingTriNorms[tri][2];
} }
// Move to the next vertex's entry // Move to the next vertex's entry
entry += neighbors + 1; entry += neighbors + 1;
@ -878,13 +1264,13 @@ void painting_average_vertex_normals(s16 *neighborTris, s16 numVtx) {
nlen = sqrtf(nx * nx + ny * ny + nz * nz); nlen = sqrtf(nx * nx + ny * ny + nz * nz);
if (nlen == 0.0) { if (nlen == 0.0) {
gPaintingMesh[i].norm[0] = 0; painting->paintingMesh[i].norm[0] = 0;
gPaintingMesh[i].norm[1] = 0; painting->paintingMesh[i].norm[1] = 0;
gPaintingMesh[i].norm[2] = 0; painting->paintingMesh[i].norm[2] = 0;
} else { } else {
gPaintingMesh[i].norm[0] = normalize_component(nx / nlen); painting->paintingMesh[i].norm[0] = normalize_component(nx / nlen);
gPaintingMesh[i].norm[1] = normalize_component(ny / nlen); painting->paintingMesh[i].norm[1] = normalize_component(ny / nlen);
gPaintingMesh[i].norm[2] = normalize_component(nz / nlen); painting->paintingMesh[i].norm[2] = normalize_component(nz / nlen);
} }
} }
} }
@ -896,7 +1282,7 @@ void painting_average_vertex_normals(s16 *neighborTris, s16 numVtx) {
* If the textureMap doesn't describe the whole mesh, then multiple calls are needed to draw the whole * If the textureMap doesn't describe the whole mesh, then multiple calls are needed to draw the whole
* painting. * painting.
*/ */
Gfx *render_painting(u8 *img, s16 tWidth, s16 tHeight, s16 *textureMap, s16 mapVerts, s16 mapTris, u8 alpha) { Gfx *render_painting(struct Painting *painting, u8 *img, s16 tWidth, s16 tHeight, s16 *textureMap, s16 mapVerts, s16 mapTris, u8 alpha) {
s16 group; s16 group;
s16 map; s16 map;
s16 triGroup; s16 triGroup;
@ -945,9 +1331,9 @@ Gfx *render_painting(u8 *img, s16 tWidth, s16 tHeight, s16 *textureMap, s16 mapV
ty = textureMap[mapping * 3 + 3]; ty = textureMap[mapping * 3 + 3];
// Map the texture and place it in the verts array // Map the texture and place it in the verts array
make_vertex(verts, group * 15 + map, gPaintingMesh[meshVtx].pos[0], gPaintingMesh[meshVtx].pos[1], make_vertex(verts, group * 15 + map, painting->paintingMesh[meshVtx].pos[0], painting->paintingMesh[meshVtx].pos[1],
gPaintingMesh[meshVtx].pos[2], tx, ty, gPaintingMesh[meshVtx].norm[0], painting->paintingMesh[meshVtx].pos[2], tx, ty, painting->paintingMesh[meshVtx].norm[0],
gPaintingMesh[meshVtx].norm[1], gPaintingMesh[meshVtx].norm[2], alpha); painting->paintingMesh[meshVtx].norm[1], painting->paintingMesh[meshVtx].norm[2], alpha);
} }
// Load the vertices and draw the 5 triangles // Load the vertices and draw the 5 triangles
@ -963,9 +1349,9 @@ Gfx *render_painting(u8 *img, s16 tWidth, s16 tHeight, s16 *textureMap, s16 mapV
meshVtx = textureMap[mapping * 3 + 1]; meshVtx = textureMap[mapping * 3 + 1];
tx = textureMap[mapping * 3 + 2]; tx = textureMap[mapping * 3 + 2];
ty = textureMap[mapping * 3 + 3]; ty = textureMap[mapping * 3 + 3];
make_vertex(verts, triGroups * 15 + map, gPaintingMesh[meshVtx].pos[0], gPaintingMesh[meshVtx].pos[1], make_vertex(verts, triGroups * 15 + map, painting->paintingMesh[meshVtx].pos[0], painting->paintingMesh[meshVtx].pos[1],
gPaintingMesh[meshVtx].pos[2], tx, ty, gPaintingMesh[meshVtx].norm[0], painting->paintingMesh[meshVtx].pos[2], tx, ty, painting->paintingMesh[meshVtx].norm[0],
gPaintingMesh[meshVtx].norm[1], gPaintingMesh[meshVtx].norm[2], alpha); painting->paintingMesh[meshVtx].norm[1], painting->paintingMesh[meshVtx].norm[2], alpha);
} }
// Draw the triangles individually // Draw the triangles individually
@ -974,15 +1360,15 @@ Gfx *render_painting(u8 *img, s16 tWidth, s16 tHeight, s16 *textureMap, s16 mapV
gSP1Triangle(gfx++, group * 3, group * 3 + 1, group * 3 + 2, 0); gSP1Triangle(gfx++, group * 3, group * 3 + 1, group * 3 + 2, 0);
} }
if (sVerticesCount >= numVtx * 2) { if (painting->sVerticesCount >= numVtx * 2) {
sVerticesCount = 0; painting->sVerticesCount = 0;
} }
for (map = 0; map < numVtx; map++) { for (map = 0; map < numVtx; map++) {
Vtx* v = &verts[map]; Vtx* v = &verts[map];
if (gGlobalTimer == sVerticesPrevTimestamp + 1) { if (gGlobalTimer == painting->sVerticesPrevTimestamp + 1) {
s32 i; s32 i;
Vtx_Interp* vCur = &sVerticesCur[sVerticesCount + map]; Vtx_Interp* vCur = &painting->sVerticesCur[painting->sVerticesCount + map];
Vtx_Interp* vPrev = (sVerticesPrev && sVertexSwaps > 2) ? &sVerticesPrev[sVerticesCount + map] : NULL; Vtx_Interp* vPrev = (painting->sVerticesPrev && painting->sVertexSwaps > 2) ? &painting->sVerticesPrev[painting->sVerticesCount + map] : NULL;
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
// save current // save current
vCur->ob[i] = v->n.ob[i]; vCur->ob[i] = v->n.ob[i];
@ -997,8 +1383,8 @@ Gfx *render_painting(u8 *img, s16 tWidth, s16 tHeight, s16 *textureMap, s16 mapV
} }
} }
sVerticesPtr[sVerticesCount / numVtx] = verts; painting->sVerticesPtr[painting->sVerticesCount / numVtx] = verts;
sVerticesCount += numVtx; painting->sVerticesCount += numVtx;
gSPEndDisplayList(gfx); gSPEndDisplayList(gfx);
return dlist; return dlist;
@ -1063,9 +1449,9 @@ Gfx *painting_ripple_image(struct Painting *painting) {
textureMap = segmented_to_virtual(textureMaps[i]); textureMap = segmented_to_virtual(textureMaps[i]);
meshVerts = textureMap[0]; meshVerts = textureMap[0];
meshTris = textureMap[meshVerts * 3 + 1]; meshTris = textureMap[meshVerts * 3 + 1];
gSPDisplayList(gfx++, render_painting(textures[i], tWidth, tHeight, textureMap, meshVerts, meshTris, painting->alpha)); gSPDisplayList(gfx++, render_painting(painting, textures[i], tWidth, tHeight, textureMap, meshVerts, meshTris, painting->alpha));
} }
sVerticesPrevTimestamp = gGlobalTimer; painting->sVerticesPrevTimestamp = gGlobalTimer;
// Update the ripple, may automatically reset the painting's state. // Update the ripple, may automatically reset the painting's state.
painting_update_ripple_state(painting); painting_update_ripple_state(painting);
@ -1102,8 +1488,8 @@ Gfx *painting_ripple_env_mapped(struct Painting *painting) {
textureMap = segmented_to_virtual(textureMaps[0]); textureMap = segmented_to_virtual(textureMaps[0]);
meshVerts = textureMap[0]; meshVerts = textureMap[0];
meshTris = textureMap[meshVerts * 3 + 1]; meshTris = textureMap[meshVerts * 3 + 1];
gSPDisplayList(gfx++, render_painting(tArray[0], tWidth, tHeight, textureMap, meshVerts, meshTris, painting->alpha)); gSPDisplayList(gfx++, render_painting(painting, tArray[0], tWidth, tHeight, textureMap, meshVerts, meshTris, painting->alpha));
sVerticesPrevTimestamp = gGlobalTimer; painting->sVerticesPrevTimestamp = gGlobalTimer;
// Update the ripple, may automatically reset the painting's state. // Update the ripple, may automatically reset the painting's state.
painting_update_ripple_state(painting); painting_update_ripple_state(painting);
@ -1127,8 +1513,8 @@ Gfx *display_painting_rippling(struct Painting *painting) {
// Generate the mesh and its lighting data // Generate the mesh and its lighting data
painting_generate_mesh(painting, mesh, numVtx); painting_generate_mesh(painting, mesh, numVtx);
painting_calculate_triangle_normals(mesh, numVtx, numTris); painting_calculate_triangle_normals(painting, mesh, numVtx, numTris);
painting_average_vertex_normals(neighborTris, numVtx); painting_average_vertex_normals(painting, neighborTris, numVtx);
// Map the painting's texture depending on the painting's texture type. // Map the painting's texture depending on the painting's texture type.
switch (painting->textureType) { switch (painting->textureType) {
@ -1141,8 +1527,8 @@ Gfx *display_painting_rippling(struct Painting *painting) {
} }
// The mesh data is freed every frame. // The mesh data is freed every frame.
mem_pool_free(gEffectsMemoryPool, gPaintingMesh); mem_pool_free(gEffectsMemoryPool, painting->paintingMesh);
mem_pool_free(gEffectsMemoryPool, gPaintingTriNorms); mem_pool_free(gEffectsMemoryPool, painting->paintingTriNorms);
return dlist; return dlist;
} }
@ -1173,8 +1559,20 @@ void reset_painting(struct Painting *painting) {
painting->marioWasUnder = 0; painting->marioWasUnder = 0;
painting->marioIsUnder = 0; painting->marioIsUnder = 0;
painting->marioWentUnder = 0; painting->marioWentUnder = 0;
painting->enteredMarioIndex = -1;
painting->underMarioIndex = -1;
for (s32 i = 0; i < MAX_PLAYERS + 1; i++) {
painting->lastFloors[i] = 0;
painting->currFloors[i] = 0;
painting->rippleTimers[i] = -1.0f;
painting->currRippleMags[i] = 0;
}
if (gRipplingPainting == painting) {
gRipplingPainting = NULL; gRipplingPainting = NULL;
}
remove_from_rippling_list(painting);
patch_paintings_init(painting);
// Make sure all variables are reset correctly. // Make sure all variables are reset correctly.
// With segmented memory the segments that contain the relevant // With segmented memory the segments that contain the relevant
@ -1280,6 +1678,9 @@ void wall_painting_update(struct Painting *painting, struct Painting *paintingGr
case PAINTING_RIPPLE: case PAINTING_RIPPLE:
wall_painting_proximity_rippling(painting, paintingGroup); wall_painting_proximity_rippling(painting, paintingGroup);
break; break;
case PAINTING_ENTERED:
wall_painting_proximity_rippling(painting, paintingGroup);
break;
} }
} else if (painting->rippleTrigger == RIPPLE_TRIGGER_CONTINUOUS) { } else if (painting->rippleTrigger == RIPPLE_TRIGGER_CONTINUOUS) {
switch (painting->state) { switch (painting->state) {
@ -1289,6 +1690,9 @@ void wall_painting_update(struct Painting *painting, struct Painting *paintingGr
case PAINTING_RIPPLE: case PAINTING_RIPPLE:
wall_painting_continuous_rippling(painting, paintingGroup); wall_painting_continuous_rippling(painting, paintingGroup);
break; break;
case PAINTING_ENTERED:
wall_painting_continuous_rippling(painting, paintingGroup);
break;
} }
} }
} }
@ -1308,6 +1712,9 @@ void floor_painting_update(struct Painting *painting, struct Painting *paintingG
case PAINTING_RIPPLE: case PAINTING_RIPPLE:
floor_painting_proximity_rippling(painting, paintingGroup); floor_painting_proximity_rippling(painting, paintingGroup);
break; break;
case PAINTING_ENTERED:
floor_painting_proximity_rippling(painting, paintingGroup);
break;
} }
} else if (painting->rippleTrigger == RIPPLE_TRIGGER_CONTINUOUS) { } else if (painting->rippleTrigger == RIPPLE_TRIGGER_CONTINUOUS) {
switch (painting->state) { switch (painting->state) {
@ -1317,6 +1724,9 @@ void floor_painting_update(struct Painting *painting, struct Painting *paintingG
case PAINTING_RIPPLE: case PAINTING_RIPPLE:
floor_painting_continuous_rippling(painting, paintingGroup); floor_painting_continuous_rippling(painting, paintingGroup);
break; break;
case PAINTING_ENTERED:
floor_painting_continuous_rippling(painting, paintingGroup);
break;
} }
} }
} }

View file

@ -32,6 +32,22 @@
/// Painting that has one texture used for an environment map effect /// Painting that has one texture used for an environment map effect
#define PAINTING_ENV_MAP 1 #define PAINTING_ENV_MAP 1
// calculate_ripple_at_point modes
/// Only one ripple
#define SINGLE_RIPPLE 0
/// The sum of the ripples
#define ADD_RIPPLES 1
/// The highest ripple
#define HIGHEST_RIPPLE 2
/// The ripple furthest from 0
#define GREATEST_RIPPLE 3
typedef struct {
float ob[3]; /* x, y, z */
signed char n[3]; /* normal */
} Vtx_Interp;
struct Painting struct Painting
{ {
s16 id; s16 id;
@ -115,6 +131,38 @@ struct Painting
/// Uniformly scales the painting to a multiple of PAINTING_SIZE. /// Uniformly scales the painting to a multiple of PAINTING_SIZE.
/// By default a painting is 614.0 x 614.0 /// By default a painting is 614.0 x 614.0
f32 size; f32 size;
/// Index of MarioState who entered the painting this frame
s32 enteredMarioIndex;
/// Index of MarioState who went under the painting this frame
s32 underMarioIndex;
/// Painting floor and under state information for each Mario
s8 lastFloors[MAX_PLAYERS + 1];
s8 currFloors[MAX_PLAYERS + 1];
s8 floorEntereds[MAX_PLAYERS + 1];
s8 wasUnders[MAX_PLAYERS + 1];
s8 isUnders[MAX_PLAYERS + 1];
s8 wentUnders[MAX_PLAYERS + 1];
f32 rippleXs[MAX_PLAYERS + 1];
f32 rippleYs[MAX_PLAYERS + 1];
f32 rippleTimers[MAX_PLAYERS + 1];
f32 currRippleMags[MAX_PLAYERS + 1];
f32 rippleDecays[MAX_PLAYERS + 1];
f32 rippleRates[MAX_PLAYERS + 1];
f32 dispersionFactors[MAX_PLAYERS + 1];
struct PaintingMeshVertex *paintingMesh;
Vec3f *paintingTriNorms;
Vtx_Interp sVertexBuffers[2][2 * 264 * 3];
u8 sVerticesCurIndex;
u8 sVertexSwaps;
Vtx_Interp* sVerticesCur;
Vtx_Interp* sVerticesPrev;
u32 sVerticesPrevTimestamp;
Vtx *sVerticesPtr[2];
s32 sVerticesCount;
}; };
/** /**

View file

@ -170,6 +170,21 @@ void network_on_loaded_area(void) {
} }
} }
// Same as network_on_loaded_area, but does not call area_remove_sync_ids_clear()
void network_on_loaded_area_no_clear(void) {
struct NetworkPlayer* np = gNetworkPlayerLocal;
if (np != NULL) {
bool levelMatch = (np->currCourseNum == gCurrCourseNum
&& np->currActNum == gCurrActStarNum
&& np->currLevelNum == gCurrLevelNum);
if (np->currLevelSyncValid && levelMatch && np->currAreaIndex != gCurrAreaIndex) {
network_send_change_area();
} else {
network_send_change_level();
}
}
}
static void network_remember_debug_packet(u8 id, bool sent) { static void network_remember_debug_packet(u8 id, bool sent) {
if (id == PACKET_ACK) { return; } if (id == PACKET_ACK) { return; }
if (id == PACKET_KEEP_ALIVE) { return; } if (id == PACKET_KEEP_ALIVE) { return; }
@ -402,6 +417,10 @@ void network_update(void) {
// check for level loaded event // check for level loaded event
if (networkLoadingLevel < LOADING_LEVEL_THRESHOLD) { if (networkLoadingLevel < LOADING_LEVEL_THRESHOLD) {
networkLoadingLevel++; networkLoadingLevel++;
// Send area/level update without LOADING_LEVEL_THRESHOLD delay
if (networkLoadingLevel == 1) {
network_on_loaded_area_no_clear();
}
if (!gNetworkAreaLoaded && networkLoadingLevel >= LOADING_LEVEL_THRESHOLD) { if (!gNetworkAreaLoaded && networkLoadingLevel >= LOADING_LEVEL_THRESHOLD) {
gNetworkAreaLoaded = true; gNetworkAreaLoaded = true;
network_on_loaded_area(); network_on_loaded_area();
@ -488,6 +507,7 @@ void network_shutdown(bool sendLeaving, bool exiting) {
gNetworkType = NT_NONE; gNetworkType = NT_NONE;
#ifdef DISCORD_SDK #ifdef DISCORD_SDK
network_set_system(NS_DISCORD); network_set_system(NS_DISCORD);
#endif #endif

View file

@ -1,5 +1,6 @@
#include <stdio.h>
#include "network_player.h" #include "network_player.h"
#include "types.h"
#include "object_fields.h"
#include "game/mario_misc.h" #include "game/mario_misc.h"
#include "reservation_area.h" #include "reservation_area.h"
#include "pc/djui/djui.h" #include "pc/djui/djui.h"
@ -221,6 +222,7 @@ u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex, u8 mode
np->currLevelAreaSeqId = 0; np->currLevelAreaSeqId = 0;
np->currLevelSyncValid = false; np->currLevelSyncValid = false;
np->currAreaSyncValid = false; np->currAreaSyncValid = false;
np->currPositionValid = false;
network_player_update_course_level(np, 0, 0, gLevelValues.entryLevel, 1); network_player_update_course_level(np, 0, 0, gLevelValues.entryLevel, 1);
// update visuals // update visuals
@ -363,6 +365,15 @@ void network_player_update_course_level(struct NetworkPlayer* np, s16 courseNum,
np->currLevelNum = levelNum; np->currLevelNum = levelNum;
np->currAreaIndex = areaIndex; np->currAreaIndex = areaIndex;
// Whether the new np location differs from the local location
bool mismatchLocal = (np->currCourseNum != gCurrCourseNum)
|| (np->currActNum != gCurrActNum)
|| (np->currLevelNum != gCurrLevelNum)
|| (np->currAreaIndex != gCurrAreaIndex);
if (mismatchLocal) {
np->currPositionValid = false;
}
if (mismatch) { if (mismatch) {
if (np == gNetworkPlayerLocal) { if (np == gNetworkPlayerLocal) {
network_send_level_area_inform(); network_send_level_area_inform();
@ -371,6 +382,13 @@ void network_player_update_course_level(struct NetworkPlayer* np, s16 courseNum,
so->txEventId = 0; so->txEventId = 0;
} }
// If this machine's player changed to a different location, then all of the other np locations are no longer valid
for (u32 i = 0; i < MAX_PLAYERS + 1; i++) {
struct NetworkPlayer* npi = &gNetworkPlayers[i];
if ((!npi->connected) || npi == gNetworkPlayerLocal) { continue; }
npi->currPositionValid = false;
}
} else { } else {
for (struct SyncObject* so = sync_object_get_first(); so != NULL; so = sync_object_get_next()) { for (struct SyncObject* so = sync_object_get_first(); so != NULL; so = sync_object_get_next()) {
so->rxEventId[np->localIndex] = 0; so->rxEventId[np->localIndex] = 0;

View file

@ -32,6 +32,7 @@ struct NetworkPlayer {
s16 currAreaIndex; s16 currAreaIndex;
bool currLevelSyncValid; bool currLevelSyncValid;
bool currAreaSyncValid; bool currAreaSyncValid;
bool currPositionValid;
u8 fadeOpacity; u8 fadeOpacity;
u8 onRxSeqId; u8 onRxSeqId;
u8 modelIndex; u8 modelIndex;

View file

@ -222,7 +222,7 @@ void network_receive_player(struct Packet* p) {
|| np->currActNum != gNetworkPlayerLocal->currActNum || np->currActNum != gNetworkPlayerLocal->currActNum
|| np->currLevelNum != gNetworkPlayerLocal->currLevelNum || np->currLevelNum != gNetworkPlayerLocal->currLevelNum
|| np->currAreaIndex != gNetworkPlayerLocal->currAreaIndex); || np->currAreaIndex != gNetworkPlayerLocal->currAreaIndex);
if (levelAreaMismatch) { return; } if (levelAreaMismatch) { np->currPositionValid = false; return; }
// save previous state // save previous state
struct PacketPlayerData oldData = { 0 }; struct PacketPlayerData oldData = { 0 };
@ -370,6 +370,9 @@ void network_receive_player(struct Packet* p) {
vec3s_copy(m->marioObj->header.gfx.angle, m->faceAngle); vec3s_copy(m->marioObj->header.gfx.angle, m->faceAngle);
} }
// Player's position is valid since it's updated and in the same area as the local player
np->currPositionValid = true;
#ifndef DEVELOPMENT #ifndef DEVELOPMENT
if (gNetworkType == NT_SERVER && gServerSettings.enableCheats == 0) { if (gNetworkType == NT_SERVER && gServerSettings.enableCheats == 0) {
if (m->action == ACT_DEBUG_FREE_MOVE) { if (m->action == ACT_DEBUG_FREE_MOVE) {