Synchronized inside-painting state

This commit is contained in:
MysterD 2020-08-04 20:49:53 -07:00
parent f32d3a2ba7
commit 4452b38848
10 changed files with 189 additions and 42 deletions

View file

@ -3944,6 +3944,7 @@
<ClCompile Include="..\src\pc\ini.c" />
<ClCompile Include="..\src\pc\mixer.c" />
<ClCompile Include="..\src\pc\network\network.c" />
<ClCompile Include="..\src\pc\network\packets\packet_inside_painting.c" />
<ClCompile Include="..\src\pc\network\packets\packet_level_warp.c" />
<ClCompile Include="..\src\pc\network\packets\packet_object.c" />
<ClCompile Include="..\src\pc\network\packets\packet_player.c" />

View file

@ -2761,13 +2761,12 @@ s16 render_sync_level_screen(void) {
gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
// text
// synchronizing text
u8 colorFade = sins(gDialogColorFadeTimer) * 50.0f + 200.0f;
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
gDPSetEnvColor(gDisplayListHead++, colorFade, colorFade, colorFade, 255);
u8 synchronizing[] = { 0x1C,0x22,0x17,0x0C,0x11,0x1B,0x18,0x17,0x12,0x02,0x12,0x17,0x10,0xFF };
// s y n c h r o n i z i n g \0
print_hud_lut_string(HUD_LUT_GLOBAL, 70, 200, synchronizing);
u8 synchronizing[] = { TEXT_SYNCHRONIZING };
print_hud_lut_string(HUD_LUT_GLOBAL, 80, 200, synchronizing);
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
return 0;

View file

@ -174,6 +174,11 @@ s8 D_8032C9E0 = 0;
u8 unused3[4];
u8 unused4[2];
u8 gInsidePainting = false;
u8 gControlPainting = false;
u8 gWaitingForRemotePainting = false;
struct WarpNode gPaintingWarpNode = { 0 };
u16 level_control_timer(s32 timerOp) {
switch (timerOp) {
case TIMER_CONTROL_SHOW:
@ -657,38 +662,48 @@ struct WarpNode *get_painting_warp_node(void) {
*/
void initiate_painting_warp(void) {
if (gCurrentArea->paintingWarpNodes != NULL && gMarioState->floor != NULL) {
struct WarpNode warpNode;
struct WarpNode *pWarpNode = get_painting_warp_node();
if (pWarpNode != NULL) {
if (gMarioState->action & ACT_FLAG_INTANGIBLE) {
play_painting_eject_sound();
} else if (pWarpNode->id != 0) {
warpNode = *pWarpNode;
if (!(warpNode.destLevel & 0x80)) {
D_8032C9E0 = check_warp_checkpoint(&warpNode);
}
initiate_warp(warpNode.destLevel & 0x7F, warpNode.destArea, warpNode.destNode, 0);
check_if_should_set_warp_checkpoint(&warpNode);
play_transition_after_delay(WARP_TRANSITION_FADE_INTO_COLOR, 30, 255, 255, 255, 45);
level_set_transition(74, basic_update);
initiate_painting_warp_node(pWarpNode, false);
gControlPainting = true;
gWaitingForRemotePainting = true;
set_mario_action(gMarioState, ACT_DISAPPEARED, 0);
gMarioState->marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
play_sound(SOUND_MENU_STAR_SOUND, gDefaultSoundArgs);
fadeout_music(398);
queue_rumble_data(80, 70);
func_sh_8024C89C(1);
}
}
}
}
void initiate_painting_warp_node(struct WarpNode *pWarpNode, u8 instant) {
if (pWarpNode->id == 0) { return; }
gControlPainting = false;
gWaitingForRemotePainting = false;
gInsidePainting = true;
gPaintingWarpNode = *pWarpNode;
struct WarpNode warpNode = *pWarpNode;
if (!(warpNode.destLevel & 0x80)) {
D_8032C9E0 = check_warp_checkpoint(&warpNode);
}
initiate_warp(warpNode.destLevel & 0x7F, warpNode.destArea, warpNode.destNode, 0);
check_if_should_set_warp_checkpoint(&warpNode);
play_transition_after_delay(WARP_TRANSITION_FADE_INTO_COLOR, 30, 255, 255, 255, instant ? 1 : 45);
level_set_transition(instant ? 1 : 74, basic_update);
play_sound(SOUND_MENU_STAR_SOUND, gDefaultSoundArgs);
fadeout_music(398);
queue_rumble_data(80, 70);
func_sh_8024C89C(1);
}
/**
* If there is not already a delayed warp, schedule one. The source node is
* based on the warp operation and sometimes Mario's used object.

View file

@ -65,6 +65,10 @@ extern struct CreditsEntry *gCurrCreditsEntry;
extern struct MarioState gMarioStates[];
extern struct MarioState *gMarioState;
extern u8 gInsidePainting;
extern u8 gControlPainting;
extern u8 gWaitingForRemotePainting;
extern struct WarpNode gPaintingWarpNode;
extern s16 sCurrPlayMode;
extern u16 D_80339ECA;
@ -132,4 +136,7 @@ void basic_update(UNUSED s16 *arg);
s32 init_level(void);
void initiate_painting_warp_node(struct WarpNode *pWarpNode, u8 instant);
void star_select_finish_selection(void);
#endif // LEVEL_UPDATE_H

View file

@ -190,6 +190,7 @@ s16 find_mario_anim_flags_and_translation(struct Object *obj, s32 yaw, Vec3s tra
f32 dz;
struct Animation *curAnim = (void *) obj->header.gfx.unk38.curAnim;
if (curAnim == NULL) { return 0; }
s16 animFrame = geo_update_animation_frame(&obj->header.gfx.unk38, NULL);
u16 *animIndex = segmented_to_virtual((void *) curAnim->index);
s16 *animValues = segmented_to_virtual((void *) curAnim->values);
@ -1846,6 +1847,8 @@ s32 execute_mario_action(UNUSED struct Object *o) {
**************************************************/
void init_mario(void) {
gInsidePainting = false;
bool isMario = (gMarioState == &gMarioStates[0]);
if (isMario && gMarioObject == NULL) { goto skippy; }
if (!isMario && gLuigiObject == NULL) { goto skippy; }

View file

@ -43,11 +43,11 @@ static s8 sVisibleStars;
static u8 sInitSelectedActNum;
// Index value of the act selected in the act menu.
static s8 sSelectedActIndex = 0;
s8 sSelectedActIndex = 0;
// Index value of the star that is selectable in the act menu.
// Excluding the next star, it doesn't count other transparent stars.
static s8 sSelectableStarIndex = 0;
s8 sSelectableStarIndex = 0;
// Act Selector menu timer that keeps counting until you choose an act.
static s32 sActSelectorMenuTimer = 0;
@ -172,7 +172,7 @@ void bhv_act_selector_loop(void) {
// Sometimes, stars are not selectable even if they appear on the screen.
// This code filters selectable and non-selectable stars.
sSelectedActIndex = 0;
handle_menu_scrolling(MENU_SCROLL_HORIZONTAL, &sSelectableStarIndex, 0, sObtainedStars);
if (gControlPainting) { handle_menu_scrolling(MENU_SCROLL_HORIZONTAL, &sSelectableStarIndex, 0, sObtainedStars); }
starIndexCounter = sSelectableStarIndex;
for (i = 0; i < sVisibleStars; i++) {
// Can the star be selected (is it either already completed or the first non-completed mission)
@ -186,7 +186,7 @@ void bhv_act_selector_loop(void) {
}
} else {
// If all stars are collected then they are all selectable.
handle_menu_scrolling(MENU_SCROLL_HORIZONTAL, &sSelectableStarIndex, 0, sVisibleStars - 1);
if (gControlPainting) { handle_menu_scrolling(MENU_SCROLL_HORIZONTAL, &sSelectableStarIndex, 0, sVisibleStars - 1); }
sSelectedActIndex = sSelectableStarIndex;
}
@ -257,6 +257,17 @@ void print_course_number(void) {
* Print act selector strings, some with special checks.
*/
void print_act_selector_strings(void) {
// synchronizing text
if (!gControlPainting || gWaitingForRemotePainting) {
static int fadeTimer = 0;
u8 colorFade = sin(fadeTimer++ * 0.2f) * 50.0f + 200.0f;
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
gDPSetEnvColor(gDisplayListHead++, colorFade, colorFade, colorFade, 255);
u8 synchronizing[] = { TEXT_SYNCHRONIZING };
print_hud_lut_string(HUD_LUT_GLOBAL, 80, 8, synchronizing);
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
}
#ifdef VERSION_EU
unsigned char myScore[][10] = { {TEXT_MYSCORE}, {TEXT_MY_SCORE_FR}, {TEXT_MY_SCORE_DE} };
#else
@ -414,7 +425,8 @@ s32 lvl_init_act_selector_values_and_stars(UNUSED s32 arg, UNUSED s32 unused) {
* Also updates objects and returns act number selected after is chosen.
*/
s32 lvl_update_obj_and_load_act_button_actions(UNUSED s32 arg, UNUSED s32 unused) {
if (sActSelectorMenuTimer >= 11) {
u8 allowSelection = (gControlPainting && !gWaitingForRemotePainting);
if (sActSelectorMenuTimer >= 11 && allowSelection) {
// If any of these buttons are pressed, play sound and go to course act
#ifndef VERSION_EU
if ((gPlayer3Controller->buttonPressed & A_BUTTON)
@ -423,17 +435,7 @@ s32 lvl_update_obj_and_load_act_button_actions(UNUSED s32 arg, UNUSED s32 unused
#else
if ((gPlayer3Controller->buttonPressed & (A_BUTTON | START_BUTTON | B_BUTTON | Z_TRIG))) {
#endif
#if defined(VERSION_JP) || defined(VERSION_SH)
play_sound(SOUND_MENU_STAR_SOUND, gDefaultSoundArgs);
#else
play_sound(SOUND_MENU_STAR_SOUND_LETS_A_GO, gDefaultSoundArgs);
#endif
if (sInitSelectedActNum >= sSelectedActIndex + 1) {
sLoadedActNum = sSelectedActIndex + 1;
} else {
sLoadedActNum = sInitSelectedActNum;
}
gDialogCourseActNum = sSelectedActIndex + 1;
star_select_finish_selection();
}
}
@ -441,3 +443,17 @@ s32 lvl_update_obj_and_load_act_button_actions(UNUSED s32 arg, UNUSED s32 unused
sActSelectorMenuTimer++;
return sLoadedActNum;
}
void star_select_finish_selection(void) {
#if defined(VERSION_JP) || defined(VERSION_SH)
play_sound(SOUND_MENU_STAR_SOUND, gDefaultSoundArgs);
#else
play_sound(SOUND_MENU_STAR_SOUND_LETS_A_GO, gDefaultSoundArgs);
#endif
if (sInitSelectedActNum >= sSelectedActIndex + 1) {
sLoadedActNum = sSelectedActIndex + 1;
} else {
sLoadedActNum = sInitSelectedActNum;
}
gDialogCourseActNum = sSelectedActIndex + 1;
}

View file

@ -76,7 +76,10 @@ void network_send(struct Packet* p) {
void network_update(void) {
if (networkType == NT_NONE) { return; }
if (sCurrPlayMode == PLAY_MODE_SYNC_LEVEL) {
// TODO: refactor the way we do these update functions, it will get messy quick
if (gInsidePainting && sCurrPlayMode == PLAY_MODE_CHANGE_LEVEL) {
network_update_inside_painting();
} else if (sCurrPlayMode == PLAY_MODE_SYNC_LEVEL) {
network_update_level_warp();
} else {
network_update_player();
@ -101,6 +104,7 @@ void network_update(void) {
case PACKET_PLAYER: network_receive_player(&p); break;
case PACKET_OBJECT: network_receive_object(&p); break;
case PACKET_LEVEL_WARP: network_receive_level_warp(&p); break;
case PACKET_INSIDE_PAINTING: network_receive_inside_painting(&p); break;
default: printf("%s received unknown packet: %d\n", NETWORKTYPESTR, p.buffer[0]);
}

View file

@ -1,6 +1,7 @@
#ifndef NETWORK_H
#define NETWORK_H
#include <time.h>
#include <types.h>
#include <assert.h>
#include "../cliopts.h"
@ -14,6 +15,7 @@ enum PacketType {
PACKET_PLAYER,
PACKET_OBJECT,
PACKET_LEVEL_WARP,
PACKET_INSIDE_PAINTING,
};
struct Packet {
@ -32,6 +34,7 @@ struct SyncObject {
};
extern struct MarioState gMarioStates[];
extern u8 gInsidePainting;
extern s16 sCurrPlayMode;
extern enum NetworkType networkType;
extern struct SyncObject syncObjects[];
@ -57,4 +60,7 @@ void network_receive_object(struct Packet* p);
void network_update_level_warp(void);
void network_receive_level_warp(struct Packet* p);
void network_update_inside_painting(void);
void network_receive_inside_painting(struct Packet* p);
#endif

View file

@ -0,0 +1,97 @@
#include <stdio.h>
#include "../network.h"
#include "src/game/level_update.h"
#include "src/game/area.h"
extern struct WarpNode gPaintingWarpNode;
extern u8 sSelectableStarIndex;
extern u8 sSelectedActIndex;
struct PacketDataInsidePainting {
u8 insidePainting;
u8 controlPainting;
u8 starIndex;
u8 actIndex;
struct WarpNode warpNode;
};
static clock_t lastSentTime = 0;
static float minUpdateRate = 0.5f;
static struct PacketDataInsidePainting lastSentData = { 0 };
static void populate_packet_data(struct PacketDataInsidePainting* data) {
data->insidePainting = gInsidePainting;
data->controlPainting = gControlPainting;
data->starIndex = sSelectableStarIndex;
data->actIndex = sSelectedActIndex;
data->warpNode = gPaintingWarpNode;
}
void network_send_inside_painting(void) {
struct PacketDataInsidePainting data = { 0 };
populate_packet_data(&data);
struct Packet p;
packet_init(&p, PACKET_INSIDE_PAINTING);
packet_write(&p, &data, sizeof(struct PacketDataInsidePainting));
network_send(&p);
lastSentData = data;
lastSentTime = clock();
}
void network_receive_inside_painting(struct Packet* p) {
struct PacketDataInsidePainting remote = { 0 };
packet_read(p, &remote, sizeof(struct PacketDataInsidePainting));
if (networkType == NT_CLIENT && gControlPainting && remote.controlPainting) {
// we both think we should control the painting, host wins the tie
gControlPainting = false;
}
if (!gControlPainting && remote.controlPainting) {
// update star/act index to show the one in control's selection
sSelectableStarIndex = remote.starIndex;
sSelectedActIndex = remote.actIndex;
}
// see if the warp nodes are the same
int compareNodes = memcmp(&gPaintingWarpNode, &remote.warpNode, sizeof(struct WarpNode));
if (gControlPainting && !remote.controlPainting && (compareNodes == 0)) {
// remote is well behaved now, we can control the painting
gWaitingForRemotePainting = false;
}
bool shouldJumpInside = !gControlPainting && (!gInsidePainting && remote.insidePainting);
// ERROR: THE DESTINATION MISMATCH DOESN'T MOVE THE CLIENT TO THE CORRECT SCREEN!
bool destinationMismatch = !gControlPainting && (compareNodes != 0);
if (shouldJumpInside || destinationMismatch) {
initiate_painting_warp_node(&remote.warpNode, true);
set_play_mode(PLAY_MODE_CHANGE_LEVEL);
}
if (gControlPainting && !remote.controlPainting && !gInsidePainting && remote.insidePainting) {
// we're in control and no longer in the painting, let remote know
network_send_inside_painting();
}
if (!gControlPainting && remote.controlPainting && !remote.insidePainting) {
// remote is in control and in game, we should be too
star_select_finish_selection();
}
}
void network_update_inside_painting(void) {
struct PacketDataInsidePainting data = { 0 };
populate_packet_data(&data);
int compareData = memcmp(&data, &lastSentData, sizeof(struct PacketDataInsidePainting));
float timeSinceSend = (clock() - lastSentTime) / CLOCKS_PER_SEC;
if (compareData != 0 || timeSinceSend > minUpdateRate) {
network_send_inside_painting();
}
}

View file

@ -12,7 +12,7 @@ void network_send_level_warp(void) {
packet_write(&p, &gCurrLevelNum, 2);
packet_write(&p, &sDelayedWarpArg, 4);
packet_write(&p, &sSourceWarpNodeId, 2);
network_send(&p);
}
@ -22,7 +22,6 @@ void network_receive_level_warp(struct Packet* p) {
s16 remoteLevelNum;
s32 remoteWarpArg;
s16 remoteWarpNodeId;
struct WarpDest remoteWarpDest;
packet_read(p, &remotePlayMode, 2);
packet_read(p, &remoteLevelNum, 2);