1039 lines
27 KiB
C
1039 lines
27 KiB
C
/* Super Mario Sunshine Multiplayer */
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <stdarg.h>
|
|
#include <math.h>
|
|
#include "sms.h"
|
|
|
|
#define NUM_PLAYERS 2
|
|
|
|
volatile const char* CREDITS = "Super Mario Sunshine Multiplayer https://egaddsworkshop.com/";
|
|
|
|
const float PI = 3.14159265f;
|
|
const int MARIO_COLSIZE = 6;
|
|
const float MULTI_MAXDISTANCE = 4000.0f;
|
|
const char* MARIO_EMITTER = "/barrel.prm";
|
|
const float MARIO_TRAMPLEHEIGHT = 60.0f;
|
|
|
|
const int32_t ENEMYMARIO_VTABLE[0x3b];
|
|
const int32_t EMARIO_VTABLE[0x45];
|
|
|
|
//static J2DTextBox textbox;
|
|
//static char* info;
|
|
|
|
int LimitDistance;
|
|
|
|
MarioActor* Players[NUM_PLAYERS];
|
|
EMario* EMarios[NUM_PLAYERS];
|
|
|
|
Yoshi* CurrentYoshi;
|
|
int IsPlaying[NUM_PLAYERS];
|
|
int HeldPlayer[NUM_PLAYERS];
|
|
int MarioOnYoshi[NUM_PLAYERS];
|
|
WaterEmitInfo* ShadowMarioEmitters[NUM_PLAYERS];
|
|
int WaterSprayTimer[NUM_PLAYERS];
|
|
|
|
int Started;
|
|
|
|
int16_t CameraPhi;
|
|
int16_t CameraTheta;
|
|
int CameraModeTimer;
|
|
int CameraMode;
|
|
|
|
int playerLoopIndex;
|
|
|
|
int multimode;
|
|
|
|
//int bBx, bBy, mX, mY, cX, cY; // Background x, y - MENU x, y - Cursor x,y
|
|
//int tX, tY; // TextBox x,y
|
|
//int isMenuEnabled, menuTimer, isExit, exitTimer, repTimer, chooseTimer, hasChosen;
|
|
|
|
//const char *optAry[] = {"Enabled", "Disabled"};
|
|
//int opt, boolDis;
|
|
|
|
//float camDis;
|
|
|
|
Vector cameraGuide;
|
|
Vector playerAverage;
|
|
Vector difplayerAverage;
|
|
|
|
int OnUpdate(MarDirector* director) {
|
|
int (*GameUpdate)(MarDirector* director) = GetObjectFunction(director, Director_GameUpdate);
|
|
|
|
Players[0] = *gpMarioAddress;
|
|
if (Players[0] == 0 || gpMarioAddress == 0)
|
|
return GameUpdate(director);
|
|
|
|
int realcount = 1;
|
|
difplayerAverage.x = playerAverage.x;
|
|
difplayerAverage.y = playerAverage.y;
|
|
difplayerAverage.z = playerAverage.z;
|
|
playerAverage.x = Players[0]->position.x;
|
|
playerAverage.y = Players[0]->position.y;
|
|
playerAverage.z = Players[0]->position.z;
|
|
|
|
if (!Started)
|
|
CameraPhi = Players[0]->roty + 0x7FFF;
|
|
for (int i = 1; i < NUM_PLAYERS; i++){
|
|
if (Players[i] == 0)
|
|
continue;
|
|
|
|
if (IsPlaying[i] && ((EnemyMario*)Players[i])->emariohealth > 0){
|
|
realcount++;
|
|
playerAverage.x += Players[i]->position.x;
|
|
playerAverage.y += Players[i]->position.y;
|
|
playerAverage.z += Players[i]->position.z;
|
|
}
|
|
|
|
if (!Started){
|
|
if (IsPlaying[i])
|
|
{
|
|
Players[i]->position = Players[0]->position;
|
|
Players[i]->position.y += 180 * i;
|
|
Camera_AddMultiPlayer(*gpCamera, &(Players[i]->position));
|
|
}
|
|
|
|
Players[i]->gamepad = &(GamePads[i]);
|
|
((EnemyMario*)Players[i])->emarioflags = 0x0082; //Show healthbar and visible shadow mario
|
|
((EnemyMario*)Players[i])->emariodamage = 0x000D; //Doesnt hurt mario
|
|
|
|
GamePadTwo->u2 = 2;
|
|
GamePadThree->u2 = 3;
|
|
Players[i]->colsettings = 0x80000001; //item collision
|
|
EMarios[i]->colsettings = 0x80000001;
|
|
|
|
*(int*)((int)Players[i] + 0x388) = 0x0000FFFF; //Pollution collision
|
|
}
|
|
if (!IsPlaying[i] && (Controllers[i].buttons & PRESS_START)){
|
|
Players[i]->flags = MARIOFLAG_ALIVE;
|
|
Players[i]->position = Players[0]->position;
|
|
Players[i]->position.y += 180;
|
|
Camera_AddMultiPlayer(*gpCamera, &(Players[i]->position));
|
|
|
|
IsPlaying[i] = 1;
|
|
}
|
|
|
|
if (((EnemyMario*)Players[i])->emariohealth <= 0 || ((MarioActor*)Players[i])->flags & MARIOFLAG_GAMEOVER){
|
|
SetMarioStatus(Players[i], STATE_STUNNED, 0, 0);
|
|
Camera_RemoveMultiPlayer(*gpCamera, &(Players[i]->position));
|
|
((EnemyMario*)Players[i])->emariohealth = 0;
|
|
}
|
|
|
|
int rstate = Players[i]->status & 0xFFFFFFF0;
|
|
if (Players[0]->flags & MARIOFLAG_HASFLUDD){
|
|
if ((rstate == STATE_IDLE || rstate == STATE_RUNNING || rstate == STATE_JUMP || rstate == STATE_JUMPSIDE) && rstate != STATE_JUMPSPIN){
|
|
WaterSprayTimer[i] = 30;
|
|
if (Controllers[i].buttons & PRESS_R){
|
|
MapObjBase_EmitSplash(Players[i]);
|
|
SetMarioStatus(Players[i], STATE_JUMPSPIN1, 0, 0);
|
|
Players[i]->speedy = 40.0f;
|
|
WaterSprayTimer[i] = 0;
|
|
}
|
|
}
|
|
else if (rstate == STATE_JUMPSPIN && Controllers[i].buttons & PRESS_R){
|
|
MapObjBase_EmitSplash(Players[i]);
|
|
WaterSprayTimer[i] = 0;
|
|
}
|
|
|
|
if (rstate == STATE_JUMPSPIN && WaterSprayTimer[i] < 30){
|
|
ShadowMarioEmitters[i]->position = Players[i]->position;
|
|
ShadowMarioEmitters[i]->u3 = 0.35f;
|
|
ShadowMarioEmitters[i]->u5 = 5.0f;
|
|
ShadowMarioEmitters[i]->u7 = 5.0f;
|
|
ShadowMarioEmitters[i]->u9 = 5.0f;
|
|
ShadowMarioEmitters[i]->u11 = 0;
|
|
ShadowMarioEmitters[i]->count = 16;
|
|
ModelWaterManager_EmitRequest(*gpModelWaterManager, ShadowMarioEmitters[i]);
|
|
|
|
WaterSprayTimer[i]++;
|
|
}
|
|
}
|
|
|
|
if (!(Players[0]->status & STATE_CUTSCENE) && Players[i]->status & STATE_CUTSCENE)
|
|
Players[i]->status = Players[0]->status;
|
|
|
|
if (IsPlaying[i] && LimitDistance && ((EnemyMario*)Players[i])->emariohealth > 0){
|
|
float xdist = Players[i]->position.x - Players[0]->position.x;
|
|
float zdist = Players[i]->position.z - Players[0]->position.z;
|
|
float yplanedist = sqrtf((xdist * xdist) + (zdist * zdist));
|
|
if (yplanedist > MULTI_MAXDISTANCE){
|
|
ParticleManager_EmitWithRotate(*gpMarioParticleManager, 0x15, &Players[i]->position, 0x4000, 0, 0, 0, 0);
|
|
Players[i]->position.y += 60;
|
|
ParticleManager_EmitWithRotate(*gpMarioParticleManager, 0x15, &Players[i]->position, 0x4000, 0, 0, 0, 0);
|
|
Players[i]->position = Players[0]->position;
|
|
ParticleManager_EmitWithRotate(*gpMarioParticleManager, 0x15, &Players[i]->position, 0x4000, 0, 0, 0, 0);
|
|
Players[i]->position.y += 60;
|
|
ParticleManager_EmitWithRotate(*gpMarioParticleManager, 0x15, &Players[i]->position, 0x4000, 0, 0, 0, 0);
|
|
Players[i]->position.y += 240;
|
|
Players[i]->speedy = 0.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
//if (Started)
|
|
//{
|
|
// checkMultiplayerMenu(); // Check if the multiplayer menu is active :)
|
|
//}
|
|
//else { //When not started, add mario and cursor to camera
|
|
if (!Started){
|
|
Camera_AddMultiPlayer(*gpCamera, &(Players[0]->position));
|
|
Camera_AddMultiPlayer(*gpCamera, &cameraGuide);
|
|
}
|
|
|
|
playerAverage.x /= (float)realcount;
|
|
playerAverage.y /= (float)realcount;
|
|
playerAverage.z /= (float)realcount;
|
|
difplayerAverage.x = playerAverage.x - difplayerAverage.x;
|
|
difplayerAverage.y = playerAverage.y - difplayerAverage.y;
|
|
difplayerAverage.z = playerAverage.z - difplayerAverage.z;
|
|
cameraGuide.x = playerAverage.x;
|
|
cameraGuide.y = playerAverage.y;
|
|
cameraGuide.z = playerAverage.z;
|
|
|
|
for (int i = 0; i < NUM_PLAYERS; i++)
|
|
{
|
|
if (Players[i] == 0)
|
|
continue;
|
|
if (!IsPlaying[i])
|
|
continue;
|
|
//Camera controls
|
|
if (Controllers[i].buttons & PRESS_L && (!(Players[i]->status & (STATE_AIRBORN | STATE_DOJUMP))) || (Players[i]->flags & 0x3000))
|
|
CameraPhi = Players[i]->roty + 0x7FFF;
|
|
|
|
if (CameraModeTimer == 0){
|
|
if (Controllers[i].buttons & PRESS_DL){
|
|
CameraMode++;
|
|
CameraModeTimer = 30;
|
|
}
|
|
else if (Controllers[i].buttons & PRESS_DR){
|
|
CameraMode--;
|
|
CameraModeTimer = 30;
|
|
}
|
|
}
|
|
|
|
CameraTheta += Controllers[i].ranalogy * 2;
|
|
CameraPhi += Controllers[i].ranalogx * 4;
|
|
}
|
|
if (CameraTheta > 6000)
|
|
CameraTheta = 6000;
|
|
if (CameraTheta < -1000)
|
|
CameraTheta = -1000;
|
|
if (CameraModeTimer > 0)
|
|
CameraModeTimer--;
|
|
if (CameraMode > 3)
|
|
CameraMode = 0;
|
|
if (CameraMode < 0)
|
|
CameraMode = 2;
|
|
|
|
Started = 1;
|
|
|
|
return GameUpdate(director);
|
|
}
|
|
|
|
/*
|
|
void OnDraw2D(J2DOrthoGraph* graphics)
|
|
{
|
|
snprintf(info, 128, "\33CC[FFFFFFFF]\33GC[FF0000FF]Distance Limit \n%s \n \nCamera Distance \n%f",
|
|
optAry[opt], camDis);
|
|
J2DTextBox_SetString(&textbox, info);
|
|
|
|
J2DGrafContext_Setup2D((J2DGrafContext*)graphics); //Run replaced branch
|
|
|
|
J2DScreen_Draw((J2DScreen*)&textbox, tX, tY, (J2DGrafContext*)graphics, 0x81);
|
|
GXSetScissor(0, 0, 0x0280, 0x01c0);
|
|
|
|
}
|
|
*/
|
|
|
|
void OnSetup(MarDirector* director)
|
|
{
|
|
playerLoopIndex = 0;
|
|
|
|
HeldPlayer[0] = 0;
|
|
IsPlaying[0] = 1;
|
|
LimitDistance = 1;
|
|
|
|
CameraPhi = 0;
|
|
CameraTheta = 0;
|
|
CameraModeTimer = 0;
|
|
CameraMode = 0;
|
|
|
|
cameraGuide.x = cameraGuide.y = cameraGuide.z = 0.0f;
|
|
playerAverage.x = playerAverage.y = playerAverage.z = 0.0f;
|
|
difplayerAverage.x = difplayerAverage.y = difplayerAverage.z = 0.0f;
|
|
|
|
//assembly edit to allow shadow mario to slide on water
|
|
int* address = 0x8024fc98;
|
|
*address = 0x60000000;
|
|
//and to disable loading params that arent needed and crash on console
|
|
address = 0x8004370c;
|
|
*address = 0x60000000;
|
|
|
|
//Run replaced branch
|
|
MarDirector_SetupObjects(director);
|
|
|
|
EMarios[1] = (EMario*)SearchObjByRef("MarMP2");
|
|
if (NUM_PLAYERS > 2)
|
|
EMarios[2] = (EMario*)SearchObjByRef("MarMP3");
|
|
if (NUM_PLAYERS > 3)
|
|
EMarios[3] = (EMario*)SearchObjByRef("MarMP4");
|
|
|
|
MarioOnYoshi[0] = 0;
|
|
for (int i = 1; i < NUM_PLAYERS; i++){
|
|
Players[i] = 0;
|
|
HeldPlayer[i] = 0; //Clear to 0 for real hw
|
|
MarioOnYoshi[i] = 0;
|
|
ShadowMarioEmitters[i] = 0;
|
|
WaterSprayTimer[i] = 0;
|
|
|
|
if (EMarios[i] == 0)
|
|
continue;
|
|
HeldPlayer[i] = 0;
|
|
|
|
Players[i] = (MarioActor*)(EMarios[i]->enemymario); //Get EnemyMario
|
|
Players[i]->Type = ENEMYMARIO_VTABLE;
|
|
EMarios[i]->Type = EMARIO_VTABLE;
|
|
|
|
Players[i]->yoshi = CurrentYoshi; //Add yoshi, prevent some crashes on hw
|
|
|
|
//Mario_InitValues(Players[i]);
|
|
|
|
Players[i]->position.x = 10000;
|
|
Players[i]->position.y = 10000;
|
|
Players[i]->position.y = -10000;
|
|
Players[i]->flags |= MARIOFLAG_INVISIBLE | MARIOFLAG_GONE;
|
|
Players[i]->status = 0x0000133f;
|
|
|
|
((EnemyMario*)Players[i])->emariohealth = 0x40;
|
|
|
|
Players[i]->colarray = malloc(sizeof(void*) * MARIO_COLSIZE);
|
|
|
|
//Give shadow mario fludd to prevent yoshi crash...
|
|
WaterGun* gun = malloc(7632); //fludd is fat
|
|
if (gun != 0){
|
|
WaterGun_Create(gun, Players[i]);
|
|
WaterGun_Init(gun);
|
|
Players[i]->watergun = gun;
|
|
}
|
|
|
|
//Let shadow mario make water
|
|
ShadowMarioEmitters[i] = malloc(sizeof(WaterEmitInfo));
|
|
if (ShadowMarioEmitters[i] != 0)
|
|
WaterEmitInfo_Create(ShadowMarioEmitters[i], MARIO_EMITTER);
|
|
}
|
|
|
|
/*
|
|
int* menuTrans = *TGCCConsole2 + GFX_OFF + 0xCC;
|
|
int* blackTrans = *TGCCConsole2 + GFX_OFF - 0x1B0 + 0xCC;
|
|
int* sMarioCTrans = *TGCCConsole2 + GFX_OFF + 0x1B0 + 0xCC;
|
|
movePane(*TGCCConsole2 + GFX_OFF - 0x1B0, bBx, bBy); // Black background
|
|
movePane(*TGCCConsole2 + GFX_OFF, mX + 170, mY + 50); // MENU Text (Add 0x1B0 for next entry)
|
|
movePane(*TGCCConsole2 + GFX_OFF + 0x1B0, 50, 150); // Shadow Mario Cursor
|
|
*menuTrans = 0x00000000;
|
|
*blackTrans = 0x00000000;
|
|
*sMarioCTrans = 0x00000000;
|
|
|
|
JUTRect rect;
|
|
JUTRect_Set(&rect, 0, 0, 512, 512);
|
|
J2DTextBox_Create(&textbox, 0, &rect, GameFont, GameStrTable, 2, 0);
|
|
|
|
tX = 100;
|
|
tY = -35;
|
|
|
|
boolDis = 1;
|
|
*/
|
|
|
|
Started = 0;
|
|
}
|
|
|
|
//Should only really be used for initializing
|
|
void OnInitApp(void* application){
|
|
Application_Init(application);
|
|
|
|
if (CalcKeyCode(CREDITS) != 9397)
|
|
*(int*)MarDirector_SetupObjects = 0x4e800020;
|
|
|
|
//Disable default multiplayer camera
|
|
*(int*)0x80276b44 = 0x60000000;
|
|
*(int*)0x800397f8 = 0x60000000;
|
|
|
|
//info = (char*)malloc(128);
|
|
|
|
//camDis = 0.667f;
|
|
multimode = 1;
|
|
for (int i = 0; i < NUM_PLAYERS; i++)
|
|
IsPlaying[i] = 0;
|
|
}
|
|
|
|
void SetMarioAddresses(MarioActor* mario){
|
|
*gpMarioAddress = mario;
|
|
*gpMarioPos = (int)mario + 0x10;
|
|
*gpMarioAngleX = (int)mario + 0x94;
|
|
*gpMarioAngleY = (int)mario + 0x96;
|
|
*gpMarioAngleZ = (int)mario + 0x98;
|
|
*gpMarioSpeedX = (int)mario + 0xA4;
|
|
*gpMarioSpeedY = (int)mario + 0xA8;
|
|
*gpMarioSpeedZ = (int)mario + 0xAC;
|
|
}
|
|
|
|
void EMarioControl(MarioActor* enemymario, void* drama, void* graphics){
|
|
for (int i = 0; i < NUM_PLAYERS; i++)
|
|
{
|
|
if (Players[i] == 0)
|
|
continue;
|
|
|
|
if (enemymario == Players[i] && IsPlaying[i])
|
|
return Mario_CheckController(enemymario, drama, graphics);
|
|
}
|
|
}
|
|
|
|
void MarioSendMessage(HitActor* sender, unsigned long message){
|
|
//if (CurrentCollision == 0)
|
|
// return 0;
|
|
|
|
MarioActor* closest = 0;
|
|
float distance = 1000000;
|
|
for (int i = 0; i < NUM_PLAYERS; i++)
|
|
{
|
|
if (Players[i] == 0)
|
|
continue;
|
|
|
|
Vector dif = Players[i]->position;
|
|
Vector_Subtract(&dif, &(sender->position));
|
|
float magnitude = Vector_Magnitude(&dif);
|
|
if (magnitude < distance)
|
|
{
|
|
closest = Players[i];
|
|
distance = magnitude;
|
|
}
|
|
}
|
|
if (closest == 0)
|
|
return;
|
|
|
|
int (*receiveMessage)(HitActor* t, HitActor* s, unsigned long m) = GetObjectFunction(closest, HitActor_ReceiveMessage);
|
|
if (receiveMessage(closest, sender, message) == 0)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
void EMarioDamageExec(HitActor* this, HitActor* other, int u1, int u2, int u3, float f1, int u4, float f2, short u5)
|
|
{
|
|
for (int i = 1; i < NUM_PLAYERS; i++)
|
|
if (Players[i] == this && ((EnemyMario*)Players[i])->emariohealth <= 0)
|
|
return;
|
|
|
|
Mario_DamageExec(this, other, u1, u2, u3, f1, u4, f2, u5);
|
|
}
|
|
|
|
void EMarioReceiveMessage(HitActor* this, HitActor* sender, unsigned long message){
|
|
for (int i = 1; i < NUM_PLAYERS; i++)
|
|
if (Players[i] == this && ((EnemyMario*)Players[i])->emariohealth <= 0)
|
|
return;
|
|
|
|
Mario_ReceiveMessage(this, sender, message);
|
|
}
|
|
|
|
void EMarioPlayerControl(HitActor* this, void* drama, void* graphics){
|
|
Mario_PlayerControl(this, drama, graphics);
|
|
}
|
|
|
|
int IsMultiplayerMap(){
|
|
if (*ChangeScenario == 58 || *ChangeScenario == 30 || (*ChangeScenario == 3 && *ChangeEpisode == 5))
|
|
return 0;
|
|
return multimode;
|
|
}
|
|
|
|
void OnMarioIncHP(HitActor* mario, int n){
|
|
if (mario == Players[0])
|
|
Mario_IncHP(mario, n);
|
|
else
|
|
{
|
|
EnemyMario* enemymario = ((EnemyMario*)mario);
|
|
if (enemymario->emariohealth < 0x40)
|
|
enemymario->emariohealth += 8 * n;
|
|
enemymario->hpmetertimer = 0x400;
|
|
}
|
|
}
|
|
void OnMarioDecHP(HitActor* mario, int n){
|
|
if (mario == Players[0])
|
|
Mario_DecHP(mario, n);
|
|
else
|
|
{
|
|
EnemyMario* enemymario = ((EnemyMario*)mario);
|
|
if (enemymario->emariohealth > 0)
|
|
enemymario->emariohealth -= 8 * n;
|
|
enemymario->hpmetertimer = 0x400;
|
|
}
|
|
}
|
|
|
|
void RemoveObjectFromColArray(HitActor* actor, HitActor* col){
|
|
if (actor->colcount == 0)
|
|
return;
|
|
|
|
int i = actor->colcount - 1;
|
|
while (i >= 0){
|
|
if (actor->colarray[i] == col)
|
|
break;
|
|
i--;
|
|
}
|
|
actor->colcount--;
|
|
if (i == actor->colcount){
|
|
return;
|
|
}
|
|
while (i < actor->colcount)
|
|
actor->colarray[i] = actor->colarray[++i];
|
|
}
|
|
|
|
void BounceMario(MarioActor* mario1, MarioActor* mario2){
|
|
int rstatus = mario1->status & 0xFFFFFFF0;
|
|
if (rstatus != STATE_JUMPSPIN && rstatus != STATE_JUMP && rstatus != STATE_JUMPSIDE && rstatus != 0x02000890)
|
|
{
|
|
if (rstatus == 0x00800230 || rstatus == 0x008008A0){
|
|
//knock over other mario
|
|
if (mario2->status & 0xFFFFFFF0 != 0x000024D0)
|
|
SetMarioStatus(mario2, 0x000208b0, 0, 0);
|
|
}
|
|
return;
|
|
}
|
|
|
|
Vector temp;
|
|
temp.x = 0.5f;
|
|
temp.y = 0.5f;
|
|
temp.z = 0.5f;
|
|
|
|
mario1->speedy = 300.0f;
|
|
Mario_SetAnimation(mario1, 211, 1.0f); //triple trample animation
|
|
SetMarioStatus(mario1, 0x02000890, 0, 0);
|
|
Mario_SetStatusToJumping(mario1, 0x02000890, 0);
|
|
EasyEmitParticle(8, &(mario1->position), mario1, &temp);
|
|
EasyEmitParticle(9, &(mario1->position), mario1, &temp);
|
|
StartSoundActorWithInfo(6168, &(mario1->position), 0, 0.0f, 3, 0, 0, 0, 4);
|
|
|
|
rstatus = mario2->status & 0xFFFFFFF0;
|
|
if (rstatus == STATE_JUMPSPIN || rstatus == STATE_JUMP || rstatus == STATE_JUMPSIDE)
|
|
{
|
|
mario2->speedy = -mario1->speedy;
|
|
}
|
|
|
|
RemoveObjectFromColArray(mario1, mario2);
|
|
}
|
|
|
|
void OnCheckActorsHit(void* hitcheckobj){
|
|
//Set up EMario bounds
|
|
for (int i = 1; i < NUM_PLAYERS; i++){
|
|
if (Players[i] == 0)
|
|
continue;
|
|
|
|
EMarios[i]->bound1 = 80.0f; //Manually
|
|
EMarios[i]->bound2 = 50.0f;
|
|
EMarios[i]->bound3 = 42.0f;
|
|
EMarios[i]->bound4 = 130.0f;
|
|
EMarios[i]->bound5 = 215.880554f;
|
|
EMarios[i]->colflags = 0xFC000000;
|
|
}
|
|
|
|
playerLoopIndex = 0;
|
|
|
|
//Run collision check
|
|
ObjHitCheck_CheckActorsHit(hitcheckobj);
|
|
|
|
for (int i = 1; i < NUM_PLAYERS; i++){
|
|
if (Players[i] == 0)
|
|
continue;
|
|
|
|
//Give EnemyMario EMario's collision data
|
|
Players[i]->colcount = EMarios[i]->colcount;
|
|
Players[i]->colarraysize = EMarios[i]->colarraysize;
|
|
for (int j = 0; j < Players[i]->colcount; j++)
|
|
Players[i]->colarray[j] = EMarios[i]->colarray[j];
|
|
|
|
//Remove held players from collision
|
|
if (HeldPlayer[i])
|
|
RemoveObjectFromColArray(Players[i], HeldPlayer[i]);
|
|
}
|
|
|
|
if (Started){
|
|
//Add in some trample for fun
|
|
for (int i = 0; i < NUM_PLAYERS; i++){
|
|
if (Players[i] == 0)
|
|
continue;
|
|
if (Players[i]->speedy > 0)
|
|
continue;
|
|
if ((int)Players[i]->colarray & 0x80000000 == 0)
|
|
continue;
|
|
for (int j = 0; j < Players[i]->colcount; j++){
|
|
if (GetType((Players[i]->colarray[j])) == ENEMYMARIO_VTABLE || GetType((Players[i]->colarray[j])) == 0x803dd660){
|
|
if (Players[i]->position.y > ((MarioActor*)Players[i]->colarray[j])->position.y + MARIO_TRAMPLEHEIGHT){
|
|
BounceMario(Players[i], (MarioActor*)(Players[i]->colarray[j]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void OnDetectHit(void* hitcheckobj, HitActor* one, HitActor* two){
|
|
EMario* held = 0;
|
|
for (int i = 1; i < NUM_PLAYERS; i++){
|
|
if (Players[i] == 0)
|
|
continue;
|
|
if (Players[i] == HeldPlayer[0])
|
|
held = EMarios[i];
|
|
}
|
|
|
|
if (one == held && two == Players[0])
|
|
return;
|
|
if (two == held && one == Players[0])
|
|
return;
|
|
|
|
ObjHitCheck_DetectHit(hitcheckobj, one, two);
|
|
|
|
if (Started){
|
|
//Hacky have things direct collide with shadowmario
|
|
for (int i = 1; i < NUM_PLAYERS; i++){
|
|
if (Players[i] == 0)
|
|
continue;
|
|
if (one->colcount >= 1 && one->colarray[one->colcount - 1] == EMarios[i])
|
|
one->colarray[one->colcount - 1] = Players[i];
|
|
if (two->colcount >= 1 && two->colarray[two->colcount - 1] == EMarios[i])
|
|
two->colarray[two->colcount - 1] = Players[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
void OnMarioHeld(MarioActor* mario, int a2, int a3, int a4){
|
|
MarioActor* other = 0;
|
|
|
|
__asm__ ("addi %0,29,0" //Forces compiler to stop overwriting r31
|
|
: "=r" (other)
|
|
:
|
|
: "r29");
|
|
|
|
if (Started)
|
|
{
|
|
SetMarioStatus(mario, a2, a3, a4);
|
|
|
|
for (int i = 0; i < NUM_PLAYERS; i++){
|
|
if (Players[i] == 0)
|
|
continue;
|
|
if (Players[i] == other)
|
|
HeldPlayer[i] = mario;
|
|
}
|
|
}
|
|
}
|
|
|
|
//I placed branch right after r3 is set to the thrown object.
|
|
//r31 holds mario's address
|
|
void OnMarioThrow(HitActor* thrownobject){
|
|
MarioActor* mario = 0;
|
|
|
|
__asm__ ("addi %0,31,0" //Forces compiler to stop overwriting r31
|
|
: "=r" (mario)
|
|
:
|
|
: "r31");
|
|
|
|
int (*receiveMessage)(HitActor* t, HitActor* s, unsigned long m) = GetObjectFunction(thrownobject, HitActor_ReceiveMessage);
|
|
|
|
|
|
SetMarioAddresses(mario);
|
|
|
|
receiveMessage(thrownobject, mario, 7);
|
|
|
|
for (int i = 0; i < NUM_PLAYERS; i++){
|
|
if (Players[i] == 0)
|
|
continue;
|
|
if (thrownobject == Players[i]){
|
|
Vector newPos = **gpMarioPos;
|
|
float angle = 2.0f * PI * **gpMarioAngleY / 65535.0f;
|
|
newPos.x += sinf(angle) * 120.0f;
|
|
newPos.z += cosf(angle) * 120.0f;
|
|
Players[i]->position = newPos;
|
|
|
|
Players[i]->status = STATE_DIVE;
|
|
Players[i]->roty = **gpMarioAngleY;
|
|
|
|
float speed = sqrtf((**gpMarioSpeedX * **gpMarioSpeedX) + (**gpMarioSpeedZ * **gpMarioSpeedZ));
|
|
SetMarioVelocity(Players[i], 40.0f + speed);
|
|
Players[i]->speedy = 75.0f;
|
|
}
|
|
if (HeldPlayer[i] == thrownobject)
|
|
HeldPlayer[i] = 0;
|
|
}
|
|
|
|
SetMarioAddresses(Players[0]);
|
|
}
|
|
|
|
//Same for OnMarioDrop
|
|
void OnMarioDrop(HitActor* thrownobject){
|
|
MarioActor* mario = 0;
|
|
|
|
__asm__ ("addi %0,31,0" //Forces compiler to stop overwriting r31
|
|
: "=r" (mario)
|
|
:
|
|
: "r31");
|
|
|
|
int (*receiveMessage)(HitActor* t, HitActor* s, unsigned long m) = GetObjectFunction(thrownobject, HitActor_ReceiveMessage);
|
|
|
|
SetMarioAddresses(mario);
|
|
|
|
receiveMessage(thrownobject, mario, 6);
|
|
|
|
SetMarioAddresses(Players[0]);
|
|
}
|
|
|
|
void OnObjectTouchMario(HitActor* this, HitActor* other)
|
|
{
|
|
//Block certain objects from touching shadow mario
|
|
if ((GetType(other) == TYPE_ENEMYMARIO || GetType(other) == TYPE_EMARIO) &&
|
|
GetType(this) == TYPE_ITEMNOZZLE)
|
|
return;
|
|
|
|
//Run replaced branch
|
|
HitActor* (*TouchPlayer)(void* t, void* o) = GetObjectFunction(this, HitActor_TouchPlayer);
|
|
TouchPlayer((void*)this, (void*)other);
|
|
}
|
|
|
|
void OnChangeNozzle(HitActor* this, int nozzletype, int nozzleslot){
|
|
int (*touchPlayer)(HitActor* t, HitActor* o) = GetObjectFunction(this, HitActor_TouchPlayer);
|
|
|
|
//If this is shadow mario, ignore
|
|
for (int i = 1; i < NUM_PLAYERS; i++){
|
|
if (Players[i] == 0)
|
|
continue;
|
|
if (this == Players[i]->watergun)
|
|
return;
|
|
}
|
|
|
|
WaterGun_ChangeNozzle(this, nozzletype, nozzleslot);
|
|
}
|
|
|
|
void OnRideYoshi(Yoshi* yoshi){
|
|
MarioActor* mario = 0;
|
|
|
|
__asm__ ("addi %0,31,0" //Forces compiler to stop overwriting r31
|
|
: "=r" (mario)
|
|
:
|
|
: "r31");
|
|
|
|
for (int i = 0; i < NUM_PLAYERS; i++){
|
|
if (Players[i] == 0)
|
|
continue;
|
|
if (Players[i] == mario)
|
|
MarioOnYoshi[i] = 1;
|
|
}
|
|
yoshi->mario = mario;
|
|
|
|
SetMarioAddresses(mario);
|
|
|
|
Yoshi_Ride(yoshi);
|
|
|
|
SetMarioAddresses(Players[0]);
|
|
}
|
|
|
|
void OnDismountYoshi(MarioActor* mario){
|
|
for (int i = 0; i < NUM_PLAYERS; i++){
|
|
if (Players[i] == 0)
|
|
continue;
|
|
if (Players[i] == mario)
|
|
MarioOnYoshi[i] = 0;
|
|
}
|
|
|
|
SetMarioAddresses(mario);
|
|
|
|
Mario_GetOffYoshi(mario);
|
|
|
|
SetMarioAddresses(Players[0]);
|
|
}
|
|
|
|
int IsOnYoshi(MarioActor* mario){
|
|
for (int i = 0; i < NUM_PLAYERS; i++){
|
|
if (Players[i] == 0)
|
|
continue;
|
|
if (mario == Players[i] && MarioOnYoshi[i])
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//Doesnt get more hacky than this
|
|
//The way its written: if yoshi has mario on him, then all marios must be on yoshi
|
|
//This should fix that
|
|
int IsOnYoshi2(MarioActor* mario){
|
|
MarioActor* pmario1 = 0;
|
|
MarioActor* pmario2 = 0;
|
|
MarioActor* pmario3 = 0;
|
|
|
|
__asm__ ("addi %0,31,0\n\t" //Forces compiler to stop overwriting possible mario registers
|
|
"addi %1,30,0\n\t"
|
|
"addi %2,29,0"
|
|
: "=r" (pmario1), "=r" (pmario2), "=r" (pmario3)
|
|
:
|
|
: "r29", "r30", "r31");
|
|
|
|
for (int i = 0; i < NUM_PLAYERS; i++){
|
|
if (Players[i] == 0)
|
|
continue;
|
|
if ((pmario1 == Players[i] || pmario2 == Players[i] || pmario3 == Players[i]) && MarioOnYoshi[i])
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void OnWaterGunUpdate(MarioActor* mario){
|
|
if (mario == Players[0])
|
|
Mario_WaterGunUpdate(mario);
|
|
}
|
|
|
|
//Normally converts polar coordinates to cartesian
|
|
void CalcMultiplayerCameraPos(Vector* center, Vector* out, float rho, uint16_t theta ,uint16_t phi){
|
|
switch (CameraMode){
|
|
case 2: rho *= 0.667f; break;
|
|
case 1: rho *= 1.5f; break;
|
|
}
|
|
|
|
CLBPolarToCross(center, out, rho, CameraTheta + theta, CameraPhi + phi);
|
|
}
|
|
|
|
int* CheckOtherObjectCollisions(volatile int* next){ //ugh
|
|
volatile void* res = 0;
|
|
int donext = 0;
|
|
|
|
do {
|
|
res = (void*)EMarios[++playerLoopIndex];
|
|
} while (res == 0 && playerLoopIndex < NUM_PLAYERS); //Loop through players, but dont allow non existent ones to pass
|
|
|
|
if (playerLoopIndex >= NUM_PLAYERS){ //If end of list reached, reset list and continue
|
|
playerLoopIndex = 0;
|
|
donext = 1;
|
|
res = (void*)Players[0];
|
|
|
|
__asm__ volatile ( //continue asm
|
|
"lwz 0,0(%0)\n"
|
|
"\tstw 0,0xa8(1)\n"
|
|
: "=r" (next)
|
|
:
|
|
: "r0", "r3", "r4", "r5", "r30");
|
|
}
|
|
__asm__ volatile ( //set current mario
|
|
"addi 31,%0,0\n"
|
|
:
|
|
: "r" (res)
|
|
: "r0", "r3", "r4", "r5", "r30"); //don't clobber r31 so it stays changed
|
|
return next;
|
|
}
|
|
void CheckOtherObjectCollisions2(){ //ugh
|
|
if (playerLoopIndex == 0)
|
|
{
|
|
__asm__ volatile ( //Only reset count on first loop
|
|
"sth 0, 0x0048(3)\n"
|
|
:
|
|
:
|
|
: "r0", "r1", "r2", "r3", "r4");
|
|
}
|
|
}
|
|
|
|
void ChangeAllPlayerStatus(MarioActor* mario, uint32_t u1, uint32_t u2, int u3){
|
|
for (int i = 1; i < NUM_PLAYERS; i++)
|
|
if (Players[i] != 0)
|
|
SetMarioStatus(Players[i], u1, u2, u3);
|
|
SetMarioStatus(mario, u1, u2, u3);
|
|
}
|
|
Yoshi* CreateYoshi(int size){
|
|
CurrentYoshi = malloc(size);
|
|
return CurrentYoshi;
|
|
}
|
|
|
|
/*
|
|
int checkMultiplayerMenu()
|
|
{
|
|
int* menuTrans = *TGCCConsole2 + GFX_OFF + 0xCC;
|
|
int* blackTrans = *TGCCConsole2 + GFX_OFF - 0x1B0 + 0xCC;
|
|
int* sMarioCTrans = *TGCCConsole2 + GFX_OFF + 0x1B0 + 0xCC;
|
|
//*menuTrans -= 0x10000000;
|
|
//*blackTrans -= 0x10000000;
|
|
|
|
if (isExit) // exiting timer
|
|
{
|
|
exitTimer++;
|
|
}
|
|
|
|
if (!isMenuEnabled) // Controls re-pause timer
|
|
{
|
|
repTimer++;
|
|
tX = 0xffff;
|
|
tY = 0xffff;
|
|
}
|
|
|
|
if (isMenuEnabled)
|
|
{
|
|
if (hasChosen)
|
|
{
|
|
chooseTimer++;
|
|
if (chooseTimer >= 15)
|
|
{
|
|
hasChosen = 0;
|
|
chooseTimer = 0;
|
|
}
|
|
}
|
|
|
|
if (ControllerOne->buttons & PRESS_DD)
|
|
{
|
|
movePane(*TGCCConsole2 + GFX_OFF + 0x1B0, 50, 150 + 60);
|
|
boolDis = 0;
|
|
}
|
|
if (ControllerOne->buttons & PRESS_DU)
|
|
{
|
|
movePane(*TGCCConsole2 + GFX_OFF + 0x1B0, 50, 150);
|
|
boolDis = 1;
|
|
}
|
|
if (ControllerOne->buttons & PRESS_A && boolDis && !hasChosen)
|
|
{
|
|
if (opt == 0)
|
|
{
|
|
opt = 1;
|
|
LimitDistance = 0;
|
|
}
|
|
else
|
|
{
|
|
opt = 0;
|
|
LimitDistance = 1;
|
|
}
|
|
|
|
hasChosen = 1;
|
|
}
|
|
|
|
if (ControllerOne->buttons & PRESS_A && !boolDis && !hasChosen)
|
|
{
|
|
camDis += 0.5f;
|
|
hasChosen = 1;
|
|
}
|
|
|
|
if (ControllerOne->buttons & PRESS_B && !boolDis && !hasChosen)
|
|
{
|
|
camDis -= 0.5f;
|
|
hasChosen = 1;
|
|
}
|
|
tX = 100;
|
|
tY = -35;
|
|
*menuTrans += 0x10000000;
|
|
*blackTrans += 0x10000000;
|
|
*sMarioCTrans += 0x10000000;
|
|
menuTimer++;
|
|
if (menuTimer >= 5) *blackTrans = 0x60000000;
|
|
if (menuTimer >= 10)
|
|
{
|
|
*menuTrans = 0xf0000000;
|
|
*sMarioCTrans = 0xf0000000;
|
|
}
|
|
}
|
|
|
|
if (ControllerOne->buttons & PRESS_Y)
|
|
{
|
|
if (!isMenuEnabled && repTimer >= 15)
|
|
{
|
|
isMenuEnabled = 1;
|
|
}
|
|
if (isMenuEnabled)
|
|
{
|
|
isExit = 1;
|
|
menuTimer = 0;
|
|
tX = 0xffff;
|
|
tY = 0xffff;
|
|
if (isExit && exitTimer >= 15)
|
|
{
|
|
while (*menuTrans > 0)
|
|
{
|
|
*menuTrans -= 0x10000000;
|
|
*blackTrans -= 0x10000000;
|
|
*sMarioCTrans -= 0x10000000;
|
|
}
|
|
|
|
if (*menuTrans <= 0)
|
|
{
|
|
*menuTrans = 0x00000000;
|
|
*blackTrans = 0x00000000;
|
|
*sMarioCTrans = 0x00000000;
|
|
isMenuEnabled = 0;
|
|
isExit = 0;
|
|
exitTimer = 0;
|
|
repTimer = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
// Generated by wxHexEditor //
|
|
const int32_t ENEMYMARIO_VTABLE[0x3b] = {
|
|
0x00000000, 0x00000000,
|
|
0x8003F738, 0x80007D70,
|
|
0x80276BD0, 0x802FA6F4,
|
|
0x80276ADC, 0x802FA6FC,
|
|
0x8003F814, 0x00000000,
|
|
0x00000000, 0x800452D0,
|
|
0x803370C0, 0x8033720C,
|
|
0x80337214, 0x8033721C,
|
|
0x80337220, 0x80337228,
|
|
0x8033722C, 0x80337230,
|
|
0x80337234, 0x80007FA8,
|
|
0x80007FA0, 0x80007F98,
|
|
0x80007F90, 0x80007F88,
|
|
0x80007F80, 0x803370E0,
|
|
0x803370E8, 0x803370EC,
|
|
0x803370F4, 0x803370F8,
|
|
0x80337100, 0x80337104,
|
|
0x802F7004, 0x802F7020,
|
|
0x802F703C, 0x802F7058,
|
|
0x802F7074, 0x802F7090,
|
|
0x80282AF4, 0x80247E50,
|
|
0x80007CFC, 0x80255A8C,
|
|
0x80007D60, 0x00000000,
|
|
0x00000000, 0x800452D8,
|
|
0x80044CA4, 0x800406CC,
|
|
EMarioControl, EMarioPlayerControl,
|
|
0x8004440C, 0x80244520,
|
|
0x80280FE8, EMarioDamageExec,
|
|
0x802853F8, 0x8024D17C,
|
|
0x80042E48 };
|
|
|
|
// Generated by wxHexEditor //
|
|
const int32_t EMARIO_VTABLE[0x45] = {
|
|
0x00000000, 0x00000000,
|
|
0x80039BF4, 0x80007D70,
|
|
0x80039810, 0x802FA6F4,
|
|
0x800397B4, 0x802FA6FC,
|
|
0x80039148, 0x00000000,
|
|
0x00000000, 0x80039F5C,
|
|
0x803370C0, 0x8033720C,
|
|
0x80337214, 0x8033721C,
|
|
0x80337220, 0x80337228,
|
|
0x8033722C, 0x80337230,
|
|
0x80337234, 0x80007FA8,
|
|
0x80007FA0, 0x80007F98,
|
|
0x80007F90, 0x80007F88,
|
|
0x80007F80, 0x803370E0,
|
|
0x803370E8, 0x803370EC,
|
|
0x803370F4, 0x803370F8,
|
|
0x80337100, 0x80337104,
|
|
0x802F7004, 0x802F7020,
|
|
0x802F703C, 0x802F7058,
|
|
0x802F7074, 0x802F7090,
|
|
EMarioReceiveMessage, 0x80217CA8,
|
|
0x80007CFC, 0x80007D40,
|
|
0x80007D60, 0x80218CC0,
|
|
0x80218A48, 0x80039600,
|
|
0x8003BF44, 0x802182F0,
|
|
0x80218414, 0x802184DC,
|
|
0x8021818C, 0x80218020,
|
|
0x80217FC8, 0x80217DE4,
|
|
0x80218344, 0x8003952C,
|
|
0x80217D88, 0x80217D6C,
|
|
0x80217CC8, 0x80217B30,
|
|
0x80217990, 0x8003C460,
|
|
0x8003BE2C, 0x8003BCE0,
|
|
0x8003BF38, 0x80007DFC,
|
|
0x80007E04 };
|