#include #include "sm64.h" #include "game.h" #include "mario.h" #include "memory.h" #include "save_file.h" #include "engine/surface_collision.h" #include "engine/graph_node.h" #include "geo_misc.h" #include "area.h" #include "segment2.h" #include "paintings.h" s16 gPaintingMarioFloorType; float gPaintingMarioXPos, gPaintingMarioYPos, gPaintingMarioZPos; struct Thing *D_8035FFA0; Vec3f *D_8035FFA4; struct Painting *ripplingPainting; s8 dddStatus; struct Painting *hmcPaintings[] = { &cotmc_painting, NULL, }; struct Painting *insideCastlePaintings[] = { &bob_painting, &ccm_painting, &wf_painting, &jrb_painting, &lll_painting, &ssl_painting, &hmc_painting, &ddd_painting, &wdw_painting, &thi_tiny_painting, &ttm_painting, &ttc_painting, &sl_painting, &thi_huge_painting, NULL, }; struct Painting *ttmPaintings[] = { &ttm_slide_painting, NULL, }; struct Painting **paintingGroups[] = { hmcPaintings, insideCastlePaintings, ttmPaintings, }; s16 gPaintingUpdateCounter = 1; s16 gLastPaintingUpdateCounter = 0; void stopAllRippleExcept(s16 *idptr, struct Painting *paintingGroup[]) { s16 index; s16 id = *idptr; index = 0; while (paintingGroup[index] != NULL) // for each painting { struct Painting *painting = segmented_to_virtual(paintingGroup[index]); if (painting->id != id) { painting->rippleStatus = 0; // stop all rippling except for the selected painting } index++; } } float find_mario_y_position_on_painting(struct Painting *painting) { //! unnecessary use of double constants float marioYOffsetFromPainting = gPaintingMarioYPos - painting->vYPos + 50.0; if (marioYOffsetFromPainting < 0.0) { marioYOffsetFromPainting = 0.0; // If Mario is below the bottom, return the bottom } else if (marioYOffsetFromPainting > painting->vSize) { // If Mario is above the top, return the top marioYOffsetFromPainting = painting->vSize; } return marioYOffsetFromPainting; } float find_mario_z_position_on_painting(struct Painting *painting) { float marioZOffsetFromPainting = painting->vZPos - gPaintingMarioZPos; if (marioZOffsetFromPainting < 0.0) { marioZOffsetFromPainting = 0.0; // If Mario is past the left side, return the left side } else if (marioZOffsetFromPainting > painting->vSize) { marioZOffsetFromPainting = painting->vSize; // If Mario is past the right side, return the right side } return marioZOffsetFromPainting; } float painting_find_vertical_ripple_location(struct Painting *painting, s8 rippleSpot) { switch (rippleSpot) { case MARIO_Y: return find_mario_y_position_on_painting(painting); // normal vertical paintings break; case MARIO_Z: return find_mario_z_position_on_painting(painting); // horizontal paintings use X and Z break; case MIDDLE_Y: return painting->vSize / 2.0; // some concentric ripples don't care about Mario break; } } float find_part_of_painting_near_mario(struct Painting *painting) { float firstQuarter = painting->vSize / 4.0; // 1/4 of the way across the painting float secondQuarter = painting->vSize / 2.0; // 1/2 of the way across the painting float thirdQuarter = painting->vSize * 3.0 / 4.0; // 3/4 of the way across the painting if (painting->floorEntered & STAND_LEFT) { return firstQuarter; } else if (painting->floorEntered & STAND_MIDDLE) { return secondQuarter; } else if (painting->floorEntered & STAND_RIGHT) { return thirdQuarter; } else if (painting->floorEntered & ENTER_LEFT) { return firstQuarter; } else if (painting->floorEntered & ENTER_MIDDLE) { return secondQuarter; } else if (painting->floorEntered & ENTER_RIGHT) { return thirdQuarter; } } float find_mario_x_position_on_painting(struct Painting *painting) { float mario_x_offset_from_painting = gPaintingMarioXPos - painting->vXPos; if (mario_x_offset_from_painting < 0.0) { mario_x_offset_from_painting = 0.0; // If Mario is past the left side, return the left side } else if (mario_x_offset_from_painting > painting->vSize) { mario_x_offset_from_painting = painting->vSize; // If Mario is past the right side, return the right side } return mario_x_offset_from_painting; } float painting_find_horizontal_ripple_location(struct Painting *painting, s8 rippleSpot) { switch (rippleSpot) { case NEAR_MARIO_LATERALLY: // normal vertical paintings return find_part_of_painting_near_mario(painting); break; case MARIO_X: // horizontally placed paintings use X and Z return find_mario_x_position_on_painting(painting); break; case MIDDLE_X: // concentric rippling may not care about Mario return painting->vSize / 2.0; break; } } void painting_set_ripple_type(s8 intendedStatus, struct Painting *painting, struct Painting *paintingGroup[], s8 hRippleSpot, s8 vRippleSpot, s8 resetTimer) { stopAllRippleExcept(&painting->id, paintingGroup); // make sure no other paintings are rippling switch (intendedStatus) // set the variables necessary for the given ripple status { case RIPPLE_STATE_IDLE: painting->currRippleMag = painting->passiveRippleMag; painting->rippleMagMultiplier = painting->passiveRippleMagMultiplier; painting->currRippleRate = painting->passiveRippleRate; painting->dispersionFactor = painting->passiveDispersionFactor; break; case RIPPLE_STATE_ENTRY: painting->currRippleMag = painting->entryRippleMag; painting->rippleMagMultiplier = painting->entryRippleMagMultiplier; painting->currRippleRate = painting->entryRippleRate; painting->dispersionFactor = painting->entryDispersionFactor; break; } painting->rippleStatus = intendedStatus; painting->horizontalRippleSpot = painting_find_horizontal_ripple_location(painting, hRippleSpot); // find the ripple location painting->verticalRippleSpot = painting_find_vertical_ripple_location(painting, vRippleSpot); gPaintingMarioYEntry = gPaintingMarioYPos; if (resetTimer == RESET_TIMER) { painting->rippleTimer = 0.0f; } ripplingPainting = painting; } void vertical_proximity_ripple_painting_ripple( struct Painting *painting, struct Painting *paintingGroup[]) // For paintings aligned vertically that follow RIPPLE_TRIGGER_PROXIMITY, set // some flags depending on where Mario is { if (painting->floorEntered & STAND_LEFT) { painting_set_ripple_type(RIPPLE_STATE_IDLE, painting, paintingGroup, NEAR_MARIO_LATERALLY, MARIO_Y, RESET_TIMER); } else if (painting->floorEntered & STAND_MIDDLE) { painting_set_ripple_type(RIPPLE_STATE_IDLE, painting, paintingGroup, NEAR_MARIO_LATERALLY, MARIO_Y, RESET_TIMER); } else if (painting->floorEntered & STAND_RIGHT) { painting_set_ripple_type(RIPPLE_STATE_IDLE, painting, paintingGroup, NEAR_MARIO_LATERALLY, MARIO_Y, RESET_TIMER); } else if (painting->floorEntered & ENTER_LEFT) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, NEAR_MARIO_LATERALLY, MARIO_Y, RESET_TIMER); } else if (painting->floorEntered & ENTER_MIDDLE) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, NEAR_MARIO_LATERALLY, MARIO_Y, RESET_TIMER); } else if (painting->floorEntered & ENTER_RIGHT) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, NEAR_MARIO_LATERALLY, MARIO_Y, RESET_TIMER); } } void vertical_proximity_ripple_painting_ripple_if_mario_enters( struct Painting *painting, struct Painting *paintingGroup[]) // For paintings aligned vertically that follow // RIPPLE_TRIGGER_PROXIMITY, set some flags if Mario enters { if (painting->floorEntered & ENTER_LEFT) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, NEAR_MARIO_LATERALLY, MARIO_Y, RESET_TIMER); } else if (painting->floorEntered & ENTER_MIDDLE) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, NEAR_MARIO_LATERALLY, MARIO_Y, RESET_TIMER); } else if (painting->floorEntered & ENTER_RIGHT) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, NEAR_MARIO_LATERALLY, MARIO_Y, RESET_TIMER); } } void vertical_continuous_ripple_painting_ripple( struct Painting *painting, struct Painting *paintingGroup[]) // For paintings aligned vertically that follow RIPPLE_TRIGGER_CONTINUOUS (DDD // only), set some flags depending on where Mario is { if (painting->floorEntered & STAND_LEFT) { painting_set_ripple_type(RIPPLE_STATE_IDLE, painting, paintingGroup, MIDDLE_X, MIDDLE_Y, RESET_TIMER); } else if (painting->floorEntered & STAND_MIDDLE) { painting_set_ripple_type(RIPPLE_STATE_IDLE, painting, paintingGroup, MIDDLE_X, MIDDLE_Y, RESET_TIMER); } else if (painting->floorEntered & STAND_RIGHT) { painting_set_ripple_type(RIPPLE_STATE_IDLE, painting, paintingGroup, MIDDLE_X, MIDDLE_Y, RESET_TIMER); } else if (painting->floorEntered & ENTER_LEFT) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, NEAR_MARIO_LATERALLY, MARIO_Y, RESET_TIMER); } else if (painting->floorEntered & ENTER_MIDDLE) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, NEAR_MARIO_LATERALLY, MARIO_Y, RESET_TIMER); } else if (painting->floorEntered & ENTER_RIGHT) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, NEAR_MARIO_LATERALLY, MARIO_Y, RESET_TIMER); } } void vertical_continuous_ripple_painting_ripple_if_mario_enters( struct Painting *painting, struct Painting *paintingGroup[]) // For paintings aligned vertically that follow RIPPLE_TRIGGER_CONTINUOUS (DDD // only), set some flags if Mario enters { if (painting->floorEntered & ENTER_LEFT) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, NEAR_MARIO_LATERALLY, MARIO_Y, DONT_RESET_TIMER); } else if (painting->floorEntered & ENTER_MIDDLE) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, NEAR_MARIO_LATERALLY, MARIO_Y, DONT_RESET_TIMER); } else if (painting->floorEntered & ENTER_RIGHT) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, NEAR_MARIO_LATERALLY, MARIO_Y, DONT_RESET_TIMER); } } void horizontal_proximity_ripple_painting_ripple( struct Painting *painting, struct Painting *paintingGroup[]) // For paintings aligned horizontally that follow RIPPLE_TRIGGER_PROXIMITY // (these are not found in-game), set some flags depending on where Mario is { if (painting->floorEntered & STAND_LEFT) { painting_set_ripple_type(RIPPLE_STATE_IDLE, painting, paintingGroup, MARIO_X, MARIO_Z, RESET_TIMER); } else if (painting->floorEntered & STAND_MIDDLE) { painting_set_ripple_type(RIPPLE_STATE_IDLE, painting, paintingGroup, MARIO_X, MARIO_Z, RESET_TIMER); } else if (painting->floorEntered & STAND_RIGHT) { painting_set_ripple_type(RIPPLE_STATE_IDLE, painting, paintingGroup, MARIO_X, MARIO_Z, RESET_TIMER); } else if (painting->marioNewlyUnderPainting) { if (painting->currFloor & ENTER_LEFT) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, MARIO_X, MARIO_Z, RESET_TIMER); } else if (painting->currFloor & ENTER_MIDDLE) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, MARIO_X, MARIO_Z, RESET_TIMER); } else if (painting->currFloor & ENTER_RIGHT) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, MARIO_X, MARIO_Z, RESET_TIMER); } } } void horizontal_proximity_ripple_painting_ripple_if_mario_enters( struct Painting *painting, struct Painting *paintingGroup[]) // For paintings aligned horizontally that follow RIPPLE_TRIGGER_PROXIMITY // (these are not found in-game), set some flags if Mario enters { if (painting->marioNewlyUnderPainting) { if (painting->currFloor & ENTER_LEFT) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, MARIO_X, MARIO_Z, RESET_TIMER); } else if (painting->currFloor & ENTER_MIDDLE) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, MARIO_X, MARIO_Z, RESET_TIMER); } else if (painting->currFloor & ENTER_RIGHT) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, MARIO_X, MARIO_Z, RESET_TIMER); } } } void horizontal_continuous_ripple_painting_ripple( struct Painting *painting, struct Painting *paintingGroup[]) // For paintings aligned horizontally that follow RIPPLE_TRIGGER_CONTINUOUS // (HMC and CotMC), set some flags depending on where Mario is { if (painting->floorEntered & STAND_LEFT) { painting_set_ripple_type(RIPPLE_STATE_IDLE, painting, paintingGroup, MIDDLE_X, MIDDLE_Y, RESET_TIMER); } else if (painting->floorEntered & STAND_MIDDLE) { painting_set_ripple_type(RIPPLE_STATE_IDLE, painting, paintingGroup, MIDDLE_X, MIDDLE_Y, RESET_TIMER); } else if (painting->floorEntered & STAND_RIGHT) { painting_set_ripple_type(RIPPLE_STATE_IDLE, painting, paintingGroup, MIDDLE_X, MIDDLE_Y, RESET_TIMER); } else if (painting->currFloor & ENTER_LEFT) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, MARIO_X, MARIO_Z, RESET_TIMER); } else if (painting->currFloor & ENTER_MIDDLE) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, MARIO_X, MARIO_Z, RESET_TIMER); } else if (painting->currFloor & ENTER_RIGHT) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, MARIO_X, MARIO_Z, RESET_TIMER); } } void horizontal_continuous_ripple_painting_ripple_if_mario_enters( struct Painting *painting, struct Painting *paintingGroup[]) // For paintings aligned horizontally that follow RIPPLE_TRIGGER_CONTINUOUS // (HMC and CotMC), set some flags if Mario enters { if (painting->marioNewlyUnderPainting) { if (painting->currFloor & ENTER_LEFT) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, MARIO_X, MARIO_Z, DONT_RESET_TIMER); } else if (painting->currFloor & ENTER_MIDDLE) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, MARIO_X, MARIO_Z, DONT_RESET_TIMER); } else if (painting->currFloor & ENTER_RIGHT) { painting_set_ripple_type(RIPPLE_STATE_ENTRY, painting, paintingGroup, MARIO_X, MARIO_Z, DONT_RESET_TIMER); } } } void painting_update_floors(struct Painting *painting) { s16 paintingId = painting->id; s8 leftSideStand = 0; s8 middleStand = 0; s8 rightSideStand = 0; s8 leftSideEnter = 0; s8 middleEnter = 0; s8 rightSideEnter = 0; /* 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 *| |* (unique) surface types. This code checks which surface Mario is currently on *| \* and sets a bitfield accordingly. */ if (gPaintingMarioFloorType == paintingId * 3 + SURFACE_PAINTING_WOBBLE_A6) { // check if Mario's current floor is one of the // special floors leftSideStand = STAND_LEFT; } if (gPaintingMarioFloorType == paintingId * 3 + SURFACE_PAINTING_WOBBLE_A7) { middleStand = STAND_MIDDLE; } if (gPaintingMarioFloorType == paintingId * 3 + SURFACE_PAINTING_WOBBLE_A8) { rightSideStand = STAND_RIGHT; } if (gPaintingMarioFloorType == paintingId * 3 + SURFACE_PAINTING_WARP_D3) { leftSideEnter = ENTER_LEFT; } if (gPaintingMarioFloorType == paintingId * 3 + SURFACE_PAINTING_WARP_D4) { middleEnter = ENTER_MIDDLE; } if (gPaintingMarioFloorType == paintingId * 3 + SURFACE_PAINTING_WARP_D5) { rightSideEnter = ENTER_RIGHT; } painting->lastFloor = painting->currFloor; painting->currFloor = leftSideStand + middleStand + rightSideStand + leftSideEnter + middleEnter + rightSideEnter; // at most 1 of these will be nonzero; painting->floorEntered = (painting->lastFloor ^ painting->currFloor) & painting->currFloor; // floorEntered is true iff currFloor is true and lastFloor is false // (Mario just entered the floor on this frame) painting->lastMarioUnderPainting = painting->currMarioUnderPainting; if (gPaintingMarioYPos < painting->vYPos) { painting->currMarioUnderPainting = 1; // If Mario is below the painting, set a variable } else { painting->currMarioUnderPainting = 0; // Otherwise, reset it } painting->marioNewlyUnderPainting = (painting->lastMarioUnderPainting ^ painting->currMarioUnderPainting) & painting->currMarioUnderPainting; // Again, marioNewlyUnderPainting is true iff he is under it // this frame but wasn't last frame. } void painting_update_ripple_status(struct Painting *painting) { if (gPaintingUpdateCounter != gLastPaintingUpdateCounter) { painting->currRippleMag *= painting->rippleMagMultiplier; painting->rippleTimer += 1.0; //! After ~6.47 days, paintings with RIPPLE_TRIGGER_CONTINUOUS will increment this to //! 16777216 (1 << 24), at which point it will freeze (due to floating-point //! imprecision?) and the painting will stop rippling. This happens to HMC, DDD, and //! CotMC. This happens on Wii VC. Untested on N64 and Wii U VC. } if (painting->rippleTrigger == RIPPLE_TRIGGER_PROXIMITY) { if (painting->currRippleMag <= 1.0) // if the painting is barely rippling, make it stop rippling { painting->rippleStatus = RIPPLE_STATE_NONE; ripplingPainting = NULL; } } else if (painting->rippleTrigger == RIPPLE_TRIGGER_CONTINUOUS) { if (painting->rippleStatus == RIPPLE_STATE_ENTRY && painting->currRippleMag <= painting->passiveRippleMag) // 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 { painting->rippleStatus = RIPPLE_STATE_IDLE; painting->currRippleMag = painting->passiveRippleMag; painting->rippleMagMultiplier = painting->passiveRippleMagMultiplier; painting->currRippleRate = painting->passiveRippleRate; painting->dispersionFactor = painting->passiveDispersionFactor; } } } s16 painting_calculate_point_ripple(struct Painting *painting, float xpos, float ypos) // note that xpos and ypos correspond to a point on the // face of the painting, not actual axes { float rippleMag = painting->currRippleMag; float rippleRate = painting->currRippleRate; float dispersionFactor = painting->dispersionFactor; float rippleTimer = painting->rippleTimer; float hRippleSpot = painting->horizontalRippleSpot; float vRippleSpot = painting->verticalRippleSpot; float distanceToRippleSpot; float scaledDistance; xpos *= painting->vSize / DEFAULT_HEIGHT; // scale x position of point by painting size ypos *= painting->vSize / DEFAULT_HEIGHT; // scale y position of point by painting size distanceToRippleSpot = sqrtf((xpos - hRippleSpot) * (xpos - hRippleSpot) + (ypos - vRippleSpot) * (ypos - vRippleSpot)); // distance from point to ripple spot scaledDistance = distanceToRippleSpot / dispersionFactor; // scale distance by dispersion factor so that ripples // farther from the ripple spot are smaller if (rippleTimer < scaledDistance) { return 0; // if the ripple hasn't reached the point yet, make the point magnitude 0 } else { float rippleHeight = rippleMag * cosf(rippleRate * (2 * M_PI) * (rippleTimer - scaledDistance)); // use a cosine wave to make the ripple go up and down, and // scale it by the painting's ripple magnitude return round_float(rippleHeight); // round it to an int and return it } } s16 painting_conditionally_calculate_point_ripple(struct Painting *painting, s16 condition, s16 xpos, s16 ypos) { s16 rippleHeight = 0; if (condition) { rippleHeight = painting_calculate_point_ripple(painting, xpos, ypos); } return rippleHeight; } void Print1(struct Painting *painting, s16 *b, s16 c) { s16 sp1E; D_8035FFA0 = mem_pool_alloc(gEffectsMemoryPool, c * sizeof(struct Thing)); if (D_8035FFA0 == NULL) { } for (sp1E = 0; sp1E < c; sp1E++) { D_8035FFA0[sp1E].unk0[0] = b[sp1E * 3 + 1]; D_8035FFA0[sp1E].unk0[1] = b[sp1E * 3 + 2]; D_8035FFA0[sp1E].unk0[2] = painting_conditionally_calculate_point_ripple( painting, b[(sp1E + 1) * 3], D_8035FFA0[sp1E].unk0[0], D_8035FFA0[sp1E].unk0[1]); } } void Print2(s16 *a, s16 b, s16 c) { s16 sp46; D_8035FFA4 = mem_pool_alloc(gEffectsMemoryPool, c * sizeof(Vec3f)); if (D_8035FFA4 == NULL) { } for (sp46 = 0; sp46 < c; sp46++) { s16 sp44 = b * 3 + sp46 * 3 + 2; s16 sp42 = a[sp44]; s16 sp40 = a[sp44 + 1]; s16 sp3E = a[sp44 + 2]; f32 sp38 = D_8035FFA0[sp42].unk0[0]; f32 sp34 = D_8035FFA0[sp42].unk0[1]; f32 sp30 = D_8035FFA0[sp42].unk0[2]; f32 sp2C = D_8035FFA0[sp40].unk0[0]; f32 sp28 = D_8035FFA0[sp40].unk0[1]; f32 sp24 = D_8035FFA0[sp40].unk0[2]; f32 sp20 = D_8035FFA0[sp3E].unk0[0]; f32 sp1C = D_8035FFA0[sp3E].unk0[1]; f32 sp18 = D_8035FFA0[sp3E].unk0[2]; D_8035FFA4[sp46][0] = (sp28 - sp34) * (sp18 - sp24) - (sp24 - sp30) * (sp1C - sp28); D_8035FFA4[sp46][1] = (sp24 - sp30) * (sp20 - sp2C) - (sp2C - sp38) * (sp18 - sp24); D_8035FFA4[sp46][2] = (sp2C - sp38) * (sp1C - sp28) - (sp28 - sp34) * (sp20 - sp2C); } } s8 small_float_to_byte(float decimal) // This function converts a decimal to a signed byte by // multiplying it by 127 or 128 and rounding away from 0. { s8 convertedFloat; if (decimal > 0.0) { convertedFloat = decimal * 127.0 + 0.5; // round up } else if (decimal < 0.0) { convertedFloat = decimal * 128.0 - 0.5; // round down } else { convertedFloat = 0; // don't round 0 } return convertedFloat; } void func_802D39DC(s16 *a, s16 b) { UNUSED s16 unused; s16 sp34; s16 index; s16 index2; s16 sp2E; s16 sp2C = 0; for (index = 0; index < b; index++) { f32 sp28 = 0.0f; f32 sp24 = 0.0f; f32 sp20 = 0.0f; f32 sp1C; sp2E = a[sp2C]; for (index2 = 0; index2 < sp2E; index2++) { sp34 = a[sp2C + index2 + 1]; sp28 += D_8035FFA4[sp34][0]; sp24 += D_8035FFA4[sp34][1]; sp20 += D_8035FFA4[sp34][2]; } sp2C += sp2E + 1; sp28 /= sp2E; sp24 /= sp2E; sp20 /= sp2E; sp1C = sqrtf(sp28 * sp28 + sp24 * sp24 + sp20 * sp20); if (sp1C == 0.0) { D_8035FFA0[index].unk6[0] = 0; D_8035FFA0[index].unk6[1] = 0; D_8035FFA0[index].unk6[2] = 0; } else { D_8035FFA0[index].unk6[0] = small_float_to_byte(sp28 / sp1C); D_8035FFA0[index].unk6[1] = small_float_to_byte(sp24 / sp1C); D_8035FFA0[index].unk6[2] = small_float_to_byte(sp20 / sp1C); } } } void *func_802D3CF0(u8 *img, s16 tWidth, s16 tHeight, s16 *d, s16 e, s16 f, u8 g) { s16 sp9E; s16 sp9C; s16 sp9A; s16 sp98; s16 sp96; s16 tx; s16 ty; s16 sp90 = f / 5; s16 sp8E = f % 5; s16 sp8C = f * 3; s16 sp8A = sp90 * 2 + sp8E + 7; Vtx *verts = alloc_display_list(sp8C * sizeof(*verts)); Gfx *sp80 = alloc_display_list(sp8A * sizeof(*sp80)); Gfx *sp7C = sp80; if (verts == NULL || sp80 == NULL) { } gDPSetTextureImage(sp7C++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, img); gDPTileSync(sp7C++); gDPSetTile(sp7C++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOLOD, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOLOD); gDPLoadSync(sp7C++); gDPLoadBlock(sp7C++, G_TX_LOADTILE, 0, 0, tWidth * tHeight - 1, CALC_DXT(tWidth, G_IM_SIZ_16b_BYTES)) for (sp9E = 0; sp9E < sp90; sp9E++) { sp9A = e * 3 + sp9E * 15 + 2; for (sp9C = 0; sp9C < 15; sp9C++) { sp98 = d[sp9A + sp9C]; sp96 = d[sp98 * 3 + 1]; tx = d[sp98 * 3 + 2]; ty = d[sp98 * 3 + 3]; make_vertex(verts, sp9E * 15 + sp9C, D_8035FFA0[sp96].unk0[0], D_8035FFA0[sp96].unk0[1], D_8035FFA0[sp96].unk0[2], tx, ty, D_8035FFA0[sp96].unk6[0], D_8035FFA0[sp96].unk6[1], D_8035FFA0[sp96].unk6[2], g); } gSPVertex(sp7C++, VIRTUAL_TO_PHYSICAL(verts + sp9E * 15), 15, 0); gSPDisplayList(sp7C++, dl_paintings_draw_ripples); } sp9A = e * 3 + sp90 * 15 + 2; for (sp9C = 0; sp9C < sp8E * 3; sp9C++) { sp98 = d[sp9A + sp9C]; sp96 = d[sp98 * 3 + 1]; tx = d[sp98 * 3 + 2]; ty = d[sp98 * 3 + 3]; make_vertex(verts, sp90 * 15 + sp9C, D_8035FFA0[sp96].unk0[0], D_8035FFA0[sp96].unk0[1], D_8035FFA0[sp96].unk0[2], tx, ty, D_8035FFA0[sp96].unk6[0], D_8035FFA0[sp96].unk6[1], D_8035FFA0[sp96].unk6[2], g); } gSPVertex(sp7C++, VIRTUAL_TO_PHYSICAL(verts + sp90 * 15), sp8E * 3, 0); for (sp9E = 0; sp9E < sp8E; sp9E++) gSP1Triangle(sp7C++, sp9E * 3, sp9E * 3 + 1, sp9E * 3 + 2, 0); gSPEndDisplayList(sp7C); return sp80; } Gfx *func_802D43FC(struct Painting *painting) { float sp4C = painting->vSize / DEFAULT_HEIGHT; Mtx *sp48 = alloc_display_list(sizeof(Mtx)); Mtx *sp44 = alloc_display_list(sizeof(Mtx)); Mtx *sp40 = alloc_display_list(sizeof(Mtx)); Mtx *sp3C = alloc_display_list(sizeof(Mtx)); Gfx *sp38 = alloc_display_list(5 * sizeof(Gfx)); Gfx *sp34 = sp38; if (sp48 == NULL || sp44 == NULL || sp40 == NULL || sp38 == NULL) { } guTranslate(sp40, painting->vXPos, painting->vYPos, painting->vZPos); guRotate(sp48, painting->vXRotation, 1.0f, 0.0f, 0.0f); guRotate(sp44, painting->vYRotation, 0.0f, 1.0f, 0.0f); guScale(sp3C, sp4C, sp4C, sp4C); gSPMatrix(sp34++, sp40, G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH); gSPMatrix(sp34++, sp48, G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH); gSPMatrix(sp34++, sp44, G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH); gSPMatrix(sp34++, sp3C, G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH); gSPEndDisplayList(sp34); return sp38; } Gfx *func_802D45FC(struct Painting *painting) { s16 sp66; s16 sp64; s16 sp62; s16 *sp5C; s16 faceCount = painting->faceCount; s16 tWidth = painting->textureWidth; s16 tHeight = painting->textureHeight; s16 **meshArray = segmented_to_virtual(painting->meshData); u8 **tArray = segmented_to_virtual(painting->textureArray); Gfx *sp48 = alloc_display_list((faceCount + 6) * sizeof(Gfx)); Gfx *sp44 = sp48; if (sp48 == NULL) { return sp48; } gSPDisplayList(sp44++, func_802D43FC(painting)); gSPDisplayList(sp44++, dl_paintings_rippling_begin); gSPDisplayList(sp44++, painting->displayList68); for (sp62 = 0; sp62 < faceCount; sp62++) { sp5C = segmented_to_virtual(meshArray[sp62]); sp66 = sp5C[0]; sp64 = sp5C[sp66 * 3 + 1]; gSPDisplayList(sp44++, func_802D3CF0(tArray[sp62], tWidth, tHeight, sp5C, sp66, sp64, painting->brightness)); } painting_update_ripple_status(painting); gSPPopMatrix(sp44++, G_MTX_MODELVIEW); gSPDisplayList(sp44++, dl_paintings_rippling_end); gSPEndDisplayList(sp44); return sp48; } Gfx *func_802D4874(struct Painting *painting) { s16 sp5E; s16 sp5C; s16 *sp58; s16 tWidth = painting->textureWidth; s16 tHeight = painting->textureHeight; s16 **meshArray = segmented_to_virtual(painting->meshData); u8 **tArray = segmented_to_virtual(painting->textureArray); Gfx *sp48 = alloc_display_list(7 * sizeof(Gfx)); Gfx *sp44 = sp48; if (sp48 == NULL) { return sp48; } gSPDisplayList(sp44++, func_802D43FC(painting)); gSPDisplayList(sp44++, dl_paintings_env_mapped_begin); gSPDisplayList(sp44++, painting->displayList68); sp58 = segmented_to_virtual(meshArray[0]); sp5E = sp58[0]; sp5C = sp58[sp5E * 3 + 1]; gSPDisplayList(sp44++, func_802D3CF0(tArray[0], tWidth, tHeight, sp58, sp5E, sp5C, painting->brightness)); painting_update_ripple_status(painting); gSPPopMatrix(sp44++, G_MTX_MODELVIEW); gSPDisplayList(sp44++, dl_paintings_env_mapped_end); gSPEndDisplayList(sp44); return sp48; } Gfx *display_painting_rippling(struct Painting *painting) { s16 *sp34 = segmented_to_virtual(seg2_triangle_mesh); s16 *sp30 = segmented_to_virtual(seg2_mesh_order); s16 sp2E = sp34[0]; s16 sp2C = sp34[sp2E * 3 + 1]; Gfx *sp28; Print1(painting, sp34, sp2E); Print2(sp34, sp2E, sp2C); func_802D39DC(sp30, sp2E); switch (painting->rippleShape) { case RIPPLE_SHAPE_WAVE: sp28 = func_802D45FC(painting); break; case RIPPLE_SHAPE_CONCENTRIC: sp28 = func_802D4874(painting); break; } mem_pool_free(gEffectsMemoryPool, D_8035FFA0); mem_pool_free(gEffectsMemoryPool, D_8035FFA4); return sp28; } Gfx *display_painting_not_rippling(struct Painting *painting) { Gfx *sp2C = alloc_display_list(4 * sizeof(Gfx)); Gfx *sp28 = sp2C; if (sp2C == NULL) { return sp2C; } gSPDisplayList(sp28++, func_802D43FC(painting)); gSPDisplayList(sp28++, painting->displayList58); gSPPopMatrix(sp28++, G_MTX_MODELVIEW); gSPEndDisplayList(sp28); return sp2C; } void reset_painting(struct Painting *painting) { painting->lastFloor = 0; painting->currFloor = 0; painting->floorEntered = 0; painting->lastMarioUnderPainting = 0; painting->currMarioUnderPainting = 0; painting->marioNewlyUnderPainting = 0; ripplingPainting = NULL; } void update_ddd_painting(struct Painting *painting, float frontPos, float backPos, float speed) // Tells the DDD painting whether to move back { u32 dddFlags = save_file_get_star_flags(gCurrSaveFileNum - 1, DIRE_DIRE_DOCKS - 1); // Obtain the DDD star flags u32 saveFileFlags = save_file_get_flags(); // Get the other save file flags u32 bowsersSubBeaten = dddFlags & BOARD_BOWSERS_SUB; // Find out whether Board Bowser's Sub was collected u32 dddBack = saveFileFlags & SAVE_FLAG_DDD_MOVED_BACK; // Check whether DDD has already moved back if (!bowsersSubBeaten && !dddBack) { painting->vXPos = frontPos; // If we haven't collected the star or moved the painting, put the // painting at the front dddStatus = 0; } else if (bowsersSubBeaten && !dddBack) // If we've collected the star but not moved the painting back, { painting->vXPos += speed; // Each frame, move the painting by a certain speed towards the back area. dddStatus = BOWSERS_SUB_BEATEN; if (painting->vXPos >= backPos) { painting->vXPos = backPos; save_file_set_flags( SAVE_FLAG_DDD_MOVED_BACK); // Tell the save file that we've moved DDD back. } } else if (bowsersSubBeaten && dddBack) { painting->vXPos = backPos; // If the painting has already moved back, place it in the back position. dddStatus = BOWSERS_SUB_BEATEN | DDD_BACK; } } struct Struct802D4E04 { u8 filler0[2]; s16 unk2; u8 filler4[20]; u32 unk18; // the upper half is the painting's id }; void func_802D4E04(struct GraphNodeGenerated *a, struct Painting *b) { switch (b->brightness) { case 0xFF: // brightest a->fnNode.node.flags = (a->fnNode.node.flags & 0xFF) | 0x100; break; default: a->fnNode.node.flags = (a->fnNode.node.flags & 0xFF) | 0x500; break; } } Gfx *display_painting(struct Painting *painting) { switch (painting->rippleStatus) { case RIPPLE_STATE_NONE: return display_painting_not_rippling(painting); break; default: return display_painting_rippling(painting); break; } } void vertical_painting_ripple(struct Painting *painting, struct Painting *paintingGroup[]) { if (painting->rippleTrigger == RIPPLE_TRIGGER_PROXIMITY) // make the painting ripple using a different function based on its // ripple trigger and status { switch (painting->rippleStatus) { case RIPPLE_STATE_NONE: vertical_proximity_ripple_painting_ripple(painting, paintingGroup); break; case RIPPLE_STATE_IDLE: vertical_proximity_ripple_painting_ripple_if_mario_enters(painting, paintingGroup); break; } } else if (painting->rippleTrigger == RIPPLE_TRIGGER_CONTINUOUS) { switch (painting->rippleStatus) { case RIPPLE_STATE_NONE: vertical_continuous_ripple_painting_ripple(painting, paintingGroup); break; case RIPPLE_STATE_IDLE: vertical_continuous_ripple_painting_ripple_if_mario_enters(painting, paintingGroup); break; } } } void horizontal_painting_ripple(struct Painting *painting, struct Painting *paintingGroup[]) { if (painting->rippleTrigger == RIPPLE_TRIGGER_PROXIMITY) // make the painting ripple using a different function based on its // ripple trigger and status { switch (painting->rippleStatus) // No horizontal proximity ripple paintings exist in-game. { case RIPPLE_STATE_NONE: horizontal_proximity_ripple_painting_ripple(painting, paintingGroup); break; case RIPPLE_STATE_IDLE: horizontal_proximity_ripple_painting_ripple_if_mario_enters(painting, paintingGroup); break; } } else if (painting->rippleTrigger == RIPPLE_TRIGGER_CONTINUOUS) { switch (painting->rippleStatus) { case RIPPLE_STATE_NONE: horizontal_continuous_ripple_painting_ripple(painting, paintingGroup); break; case RIPPLE_STATE_IDLE: horizontal_continuous_ripple_painting_ripple_if_mario_enters(painting, paintingGroup); break; } } } Gfx *Geo18_802D5B98(s32 run, struct GraphNode *node, UNUSED void *context) { struct GraphNodeGenerated *sp2C = (struct GraphNodeGenerated *) node; s32 sp28 = (sp2C->parameter >> 8) & 0xFF; s32 id = sp2C->parameter & 0xFF; Gfx *sp20 = NULL; struct Painting **paintingGroup = paintingGroups[sp28]; struct Painting *painting = segmented_to_virtual(paintingGroup[id]); if (run != TRUE) { reset_painting(painting); } else if (run == TRUE) // because the extra comparison was really necessary... { if (sp28 == 1 && id == PAINTING_ID_DDD) { // painting is DDD painting update_ddd_painting(painting, 3456.0f, 5529.6f, 20.0f); } func_802D4E04(sp2C, painting); sp20 = display_painting(painting); painting_update_floors(painting); switch ((s16) painting->vXRotation) { case ROTATION_VERTICAL: vertical_painting_ripple(painting, paintingGroup); break; default: horizontal_painting_ripple(painting, paintingGroup); break; } } return sp20; } Gfx *Geo18_802D5D0C(s32 run, UNUSED struct GraphNode *node, UNUSED f32 c[4][4]) { struct Surface *surface; if (run != TRUE) { gLastPaintingUpdateCounter = gAreaUpdateCounter - 1; gPaintingUpdateCounter = gAreaUpdateCounter; } else { gLastPaintingUpdateCounter = gPaintingUpdateCounter; gPaintingUpdateCounter = gAreaUpdateCounter; find_floor(gMarioObject->oPosX, gMarioObject->oPosY, gMarioObject->oPosZ, &surface); gPaintingMarioFloorType = surface->type; gPaintingMarioXPos = gMarioObject->oPosX; gPaintingMarioYPos = gMarioObject->oPosY; gPaintingMarioZPos = gMarioObject->oPosZ; } return NULL; }