Skip to content

Commit

Permalink
Multiplayer: Show up to 3 other players as Link (gamestabled#725)
Browse files Browse the repository at this point in the history
* Multiplayer: Show up to 3 other players as Link

* Workaround child y pos + Skip posing on console

* Fix custom tunic + adjust limb assumptions

* Fix missing puppets + Separate joint packet
Also add the missing parameter to Actor_Spawn

* Add collision + shadow
  • Loading branch information
Kewlan authored Apr 24, 2024
1 parent ca479cd commit 01793f2
Show file tree
Hide file tree
Showing 17 changed files with 540 additions and 90 deletions.
12 changes: 9 additions & 3 deletions code/include/z3D/z3D.h
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ typedef struct OcLine OcLine; // TODO
#define COLLISION_CHECK_OC_MAX 50
#define COLLISION_CHECK_OC_LINE_MAX 3

typedef struct {
typedef struct CollisionCheckContext {
/* 0x000 */ s16 colAtCount;
/* 0x002 */ u16 sacFlags;
/* 0x004 */ Collider* colAt[COLLISION_CHECK_AT_MAX];
Expand Down Expand Up @@ -780,8 +780,8 @@ typedef void (*PlaySound_proc)(u32);
#define PlaySound ((PlaySound_proc)PlaySound_addr)

typedef Actor* (*Actor_Spawn_proc)(ActorContext* actorCtx, GlobalContext* globalCtx, s16 actorId, float posX,
float posY, float posZ, s16 rotX, s16 rotY, s16 rotZ, s16 params)
__attribute__((pcs("aapcs-vfp")));
float posY, float posZ, s16 rotX, s16 rotY, s16 rotZ, s16 params,
s32 initImmediately) __attribute__((pcs("aapcs-vfp")));
#define Actor_Spawn_addr 0x3738D0
#define Actor_Spawn ((Actor_Spawn_proc)Actor_Spawn_addr)

Expand Down Expand Up @@ -846,6 +846,12 @@ typedef s32 (*Audio_PlayActorSfx2_proc)(Actor* actor, s32 sfxID);
#define Audio_PlayActorSfx2_addr 0x375BCC
#define Audio_PlayActorSfx2 ((Audio_PlayActorSfx2_proc)Audio_PlayActorSfx2_addr)

typedef s32 (*Model_GetMeshGroupCount_proc)(SkeletonAnimationModel* skelAnimeModel);
#define Model_GetMeshGroupCount ((Model_GetMeshGroupCount_proc)0x2BB71C)

typedef s32 (*Model_IsMeshGroupUsed_proc)(SkeletonAnimationModel* skelAnimeModel, s32 param);
#define Model_IsMeshGroupUsed ((Model_IsMeshGroupUsed_proc)0x4C6880)

typedef void (*Model_EnableMeshGroupByIndex_proc)(SkeletonAnimationModel* skel, u32 index);
#define Model_EnableMeshGroupByIndex ((Model_EnableMeshGroupByIndex_proc)0x37266C)

Expand Down
73 changes: 43 additions & 30 deletions code/include/z3D/z3Dactor.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define _Z3DACTOR_H_

#include "z3Dvec.h"
#include "z3Dcollision_check.h"

struct Actor;
struct GlobalContext;
Expand Down Expand Up @@ -29,7 +30,11 @@ typedef struct {
} SkeletonAnimationModel_VTable;

typedef struct SkeletonAnimationModel_unk_10 {
/* 0x00 */ char unk_00[0x14];
/* 0x00 */ void* unk_00;
/* 0x04 */ void* unk_04;
/* 0x08 */ s32 unk_08;
/* 0x0C */ s32 unk_0C;
/* 0x10 */ s32 unk_10;
} SkeletonAnimationModel_unk_10; // size = 0x14

typedef struct SkeletonAnimationModel_unk_0C {
Expand Down Expand Up @@ -133,34 +138,18 @@ typedef struct {
/* 0x1B */ u8 acHitEffect;
} CollisionCheckInfo; // size = 0x1C

typedef struct {
/* 0x00 */ struct Actor* actor; // Attached actor
/* 0x04 */ struct Actor* at; // Actor attached to what it collided with as an AT collider.
/* 0x08 */ struct Actor* ac; // Actor attached to what it collided with as an AC collider.
/* 0x0C */ struct Actor* oc; // Actor attached to what it collided with as an OC collider.
/* 0x10 */ u8 atFlags; // Information flags for AT collisions.
/* 0x11 */ u8 acFlags; // Information flags for AC collisions.
/* 0x12 */ u8 ocFlags1; // Information flags for OC collisions.
/* 0x13 */ u8 ocFlags2; // Flags related to which colliders it can OC collide with.
/* 0x14 */ u8 colType; // Determines hitmarks and sound effects during AC collisions.
/* 0x15 */ u8 shape; // JntSph, Cylinder, Tris, or Quad
} Collider; // size = 0x18

typedef struct {
/* 0x00 */ Collider base;
/* 0x18 */ char unk_18[0x28]; // ColliderInfo info;
/* 0x40 */ char unk_40[0x18]; // Cylinderf dim;
} ColliderCylinder; // size = 0x58

typedef struct {
/* 0x00 */ Vec3s rot; // Current actor shape rotation
/* 0x06 */ u8 unk_06;
/* 0x08 */ f32 unk_08; // Model y axis offset. Represents model space units. collision mesh related
/* 0x06 */ s16 face;
/* 0x08 */ f32 yOffset; // Model y axis offset. Represents model space units. collision mesh related
/* 0x0C */ void (*shadowDrawFunc)(struct Actor*, struct LightMapper*, struct GlobalContext*);
/* 0x10 */ f32 unk_10;
/* 0x14 */ u8 unk_14;
/* 0x15 */ u8 unk_15;
} ActorShape; // size = 0x18
/* 0x10 */ f32 shadowScale;
/* 0x14 */ u8 shadowAlpha;
/* 0x15 */ u8 feetFloorFlag;
/* 0x16 */ char unk_16[2]; // Required padding?
/* 0x18 */ Vec3f feetPos[2];
} ActorShape; // size = 0x30
_Static_assert(sizeof(ActorShape) == 0x30, "ActorShape size");

typedef struct Actor {
/* 0x000 */ s16 id; // Actor Id
Expand Down Expand Up @@ -197,7 +186,6 @@ typedef struct Actor {
/* 0x09C */ f32 yDistToPlayer; // Dist is negative if the actor is above the player
/* 0x0A0 */ CollisionCheckInfo colChkInfo; // Variables related to the Collision Check system
/* 0x0BC */ ActorShape shape; // Variables related to the physical shape of the actor
/* 0x0D4 */ Vec3f unk_D4[2];
/* 0x0EC */ Vec3f unk_EC; // Stores result of some vector transformation involving actor xyz vector, and a matrix at
// Global Context + 11D60
/* 0x0F8 */ f32 unk_F8; // Related to above
Expand Down Expand Up @@ -235,6 +223,7 @@ typedef struct Actor {
/* 0x1A0 */ f32 unk_1A0;
/* From here on, the structure and size varies for each actor */
} Actor; // size = 0x1A4
_Static_assert(sizeof(Actor) == 0x1A4, "Actor size");

typedef struct DynaPolyActor {
/* 0x000 */ struct Actor actor;
Expand All @@ -250,7 +239,11 @@ typedef struct DynaPolyActor {

typedef struct {
/* 0x0000 */ Actor actor;
/* 0x01A4 */ char unk_148[0x0005];
/* 0x01A4 */ s8 currentTunic;
/* 0x01A5 */ s8 currentSword;
/* 0x01A6 */ s8 currentShield;
/* 0x01A7 */ s8 currentBoots;
/* 0x01A8 */ s8 heldItemButton;
/* 0x01A9 */ s8 heldItemActionParam;
/* 0x01AA */ u8 heldItemId;
/* 0x01AB */ char unk_1AB[0x1];
Expand All @@ -275,7 +268,9 @@ typedef struct {
/* 0x024C */ void* giDrawSpace;
/* 0x0250 */ char unk_250[0x0004];
/* 0x0254 */ struct SkelAnime skelAnime;
/* 0x02D8 */ char unk_2D8[0x0F4C];
/* 0x02D8 */ char jointTable[0x514];
/* 0x07EC */ char morphTable[0x514];
/* 0x0D00 */ char unk_2D8[0x0524];
/* 0x1224 */ Actor* heldActor;
/* 0x1228 */ char unk_1228[0x84];
/* 0x12AC */ u8 getItemId;
Expand Down Expand Up @@ -305,9 +300,14 @@ typedef struct {
/* 0x2250 */ char unk_2250[0x0238];
/* 0x2488 */ s8 invincibilityTimer; // prevents damage when nonzero
// (positive = visible, counts towards zero each frame)
/* 0x2489 */ char unk_2489[0x27B];
/* 0x2489 */ char unk_2489[0x0053];
/* 0x24DC */ void* cmbMan;
/* 0x24E0 */ void* zarInfo;
/* 0x24E4 */ char unk_24E4[0x0220];
/* 0x2704 */ struct SkeletonAnimationModel_unk_0C* bodyTexAnim;
/* 0x2708 */ char unk_2708[0x344];
} Player; // total size (from init vars): 2A4C
_Static_assert(sizeof(Player) == 0x2A4C, "Player size");

typedef enum {
/* 0x00 */ ACTORTYPE_SWITCH,
Expand Down Expand Up @@ -342,4 +342,17 @@ typedef u32 (*Actor_HasParent_proc)(Actor* actor, struct GlobalContext* globalCt
typedef f32 (*Actor_WorldDistXYZToActor_proc)(Actor* a, Actor* b) __attribute__((pcs("aapcs-vfp")));
#define Actor_WorldDistXYZToActor ((Actor_WorldDistXYZToActor_proc)0x3306C4)

typedef void (*ActorShape_Init_proc)(ActorShape* shape, f32 yOffset, void* shadowDrawFunc, f32 shadowScale)
__attribute__((pcs("aapcs-vfp")));
#define ActorShape_Init ((ActorShape_Init_proc)0x372D4C)

typedef void (*Actor_SetFeetPos_proc)(Actor* actor, nn_math_MTX34* mtx, int param_3, int param_4, Vec3f* param_5,
int param_6, Vec3f* param_7);
#define Actor_SetFeetPos ((Actor_SetFeetPos_proc)0x34CBB4)

typedef void (*Actor_UpdateBgCheckInfo_proc)(struct GlobalContext* globalCtx, Actor* actor, f32 wallCheckHeight,
f32 wallCheckRadius, f32 ceilingCheckHeight, s32 flags)
__attribute__((pcs("aapcs-vfp")));
#define Actor_UpdateBgCheckInfo ((Actor_UpdateBgCheckInfo_proc)0x376340)

#endif
119 changes: 119 additions & 0 deletions code/include/z3D/z3Dcollision_check.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#ifndef _Z3DCOLLISION_CHECK_H_
#define _Z3DCOLLISION_CHECK_H_

#include "z3Dvec.h"

struct GlobalContext;
struct CollisionCheckContext;

typedef struct {
/* 0x00 */ struct Actor* actor; // Attached actor
/* 0x04 */ struct Actor* at; // Actor attached to what it collided with as an AT collider.
/* 0x08 */ struct Actor* ac; // Actor attached to what it collided with as an AC collider.
/* 0x0C */ struct Actor* oc; // Actor attached to what it collided with as an OC collider.
/* 0x10 */ u8 atFlags; // Information flags for AT collisions.
/* 0x11 */ u8 acFlags; // Information flags for AC collisions.
/* 0x12 */ u8 ocFlags1; // Information flags for OC collisions.
/* 0x13 */ u8 ocFlags2; // Flags related to which colliders it can OC collide with.
/* 0x14 */ u8 colType; // Determines hitmarks and sound effects during AC collisions.
/* 0x15 */ u8 shape; // JntSph, Cylinder, Tris, or Quad
} Collider; // size = 0x18
_Static_assert(sizeof(Collider) == 0x18, "Collider size");

typedef struct {
/* 0x00 */ u32 damageFlags;
/* 0x04 */ u8 effect;
/* 0x05 */ u8 damage;
} ColliderTouch; // size = 0x8
_Static_assert(sizeof(ColliderTouch) == 0x8, "ColliderTouch size");

typedef struct {
/* 0x00 */ u32 damageFlags;
/* 0x04 */ u8 effect;
/* 0x05 */ u8 defense;
/* 0x06 */ Vec3s hitPos;
} ColliderBump; // size = 0xC
_Static_assert(sizeof(ColliderBump) == 0xC, "ColliderBump size");

typedef struct {
/* 0x00 */ ColliderTouch toucher;
/* 0x08 */ ColliderBump bumper;
/* 0x14 */ u8 elementType;
/* 0x15 */ u8 toucherFlags;
/* 0x16 */ u8 bumperFlags;
/* 0x17 */ u8 ocElementFlags;
/* 0x18 */ Collider* at_hit;
/* 0x1C */ Collider* ac_hit;
/* 0x20 */ Collider* at_hit_info;
/* 0x24 */ Collider* ac_hit_info;
} ColliderInfo; // size = 0x28
_Static_assert(sizeof(ColliderInfo) == 0x28, "ColliderInfo size");

typedef struct {
/* 0x00 */ f32 radius;
/* 0x04 */ f32 height;
/* 0x08 */ f32 yShift;
/* 0x0C */ Vec3f position;
} Cylinderf; // size = 0x18
_Static_assert(sizeof(Cylinderf) == 0x18, "Cylinderf size");

typedef struct {
/* 0x00 */ Collider base;
/* 0x18 */ ColliderInfo info;
/* 0x40 */ Cylinderf dim;
} ColliderCylinder; // size = 0x58
_Static_assert(sizeof(ColliderCylinder) == 0x58, "ColliderCylinder size");

typedef struct {
/* 0x00 */ u8 type;
/* 0x01 */ u8 atFlags;
/* 0x02 */ u8 acFlags;
/* 0x03 */ u8 maskA;
/* 0x04 */ u8 maskB;
/* 0x05 */ u8 shape;
} ColliderInit; // size = 0x6
_Static_assert(sizeof(ColliderInit) == 0x6, "ColliderInit size");

typedef struct {
/* 0x00 */ u32 damageFlags;
/* 0x04 */ u8 effect;
/* 0x05 */ u8 defense;
} ColliderBumpInit; // size = 0x8
_Static_assert(sizeof(ColliderBumpInit) == 0x8, "ColliderBumpInit size");

typedef struct {
/* 0x00 */ u8 elementType;
/* 0x04 */ ColliderTouch toucher;
/* 0x0C */ ColliderBumpInit bumper;
/* 0x14 */ u8 toucherFlags;
/* 0x15 */ u8 bumperFlags;
/* 0x16 */ u8 ocElementFlags;
} ColliderInfoInit; // size = 0x18
_Static_assert(sizeof(ColliderInfoInit) == 0x18, "ColliderInfoInit size");

typedef struct {
/* 0x00 */ ColliderInit base;
/* 0x08 */ ColliderInfoInit info;
/* 0x20 */ Cylinderf dim;
} ColliderCylinderInit; // size = 0x58
_Static_assert(sizeof(ColliderCylinderInit) == 0x38, "ColliderCylinderInit size");

typedef void (*Collider_InitCylinder_proc)(struct GlobalContext* globalCtx, ColliderCylinder* collider);
#define Collider_InitCylinder ((Collider_InitCylinder_proc)0x353DD0)

typedef void (*Collider_SetCylinder_proc)(struct GlobalContext* globalCtx, ColliderCylinder* collider,
struct Actor* actor, void* cylinderInitData);
#define Collider_SetCylinder ((Collider_SetCylinder_proc)0x353D24)

typedef void (*Collider_UpdateCylinder_proc)(struct Actor* actor, ColliderCylinder* collider);
#define Collider_UpdateCylinder ((Collider_UpdateCylinder_proc)0x37632C)

typedef void (*CollisionCheck_SetOC_proc)(struct GlobalContext* globalCtx, struct CollisionCheckContext* colChkCtx,
void* collider);
#define CollisionCheck_SetOC ((CollisionCheck_SetOC_proc)0x3762A4)

typedef void (*CollisionCheck_SetAC_proc)(struct GlobalContext* globalCtx, struct CollisionCheckContext* colChkCtx,
void* collider);
#define CollisionCheck_SetAC ((CollisionCheck_SetAC_proc)0x376168)

#endif //_Z3DCOLLISION_CHECK_H_
3 changes: 3 additions & 0 deletions code/src/actor.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
#include "red_ice.h"
#include "shabom.h"
#include "anubis.h"
#include "link_puppet.h"

#define OBJECT_GI_KEY 170
#define OBJECT_GI_BOSSKEY 185
Expand All @@ -85,6 +86,8 @@ void Actor_Init() {
gActorOverlayTable[0x0].initInfo->destroy = PlayerActor_rDestroy;
gActorOverlayTable[0x0].initInfo->draw = PlayerActor_rDraw;

gActorOverlayTable[0x1].initInfo = &EnLinkPuppet_InitVars;

gActorOverlayTable[0x4].initInfo->init = ShopsanityItem_Init;
gActorOverlayTable[0x4].initInfo->instanceSize = sizeof(ShopsanityItem);

Expand Down
6 changes: 3 additions & 3 deletions code/src/actors/chest.c
Original file line number Diff line number Diff line change
Expand Up @@ -246,16 +246,16 @@ u8 Chest_OverrideIceSmoke(Actor* thisx) {
case ICETRAP_BOMB_SIMPLE:
case ICETRAP_BOMB_KNOCKDOWN:
sBomb = Actor_Spawn(&gGlobalContext->actorCtx, gGlobalContext, 0x10, thisx->world.pos.x,
thisx->world.pos.y, thisx->world.pos.z, 0, 0, 0, 0);
thisx->world.pos.y, thisx->world.pos.z, 0, 0, 0, 0, FALSE);
break;
case ICETRAP_ANTIFAIRY:
sFairy = (EnElf*)Actor_Spawn(&gGlobalContext->actorCtx, gGlobalContext, 0x18, thisx->world.pos.x,
thisx->world.pos.y, thisx->world.pos.z, 0, 0, 0, 0x5);
thisx->world.pos.y, thisx->world.pos.z, 0, 0, 0, 0x5, FALSE);
PLAYER->actor.home.pos.y = -5000; // Make Link airborne for a frame to cancel the get item event
break;
case ICETRAP_RUPPY:
Actor_Spawn(&gGlobalContext->actorCtx, gGlobalContext, 0x131, thisx->world.pos.x,
thisx->world.pos.y + 30, thisx->world.pos.z, 0, 0, 0, 0x2);
thisx->world.pos.y + 30, thisx->world.pos.z, 0, 0, 0, 0x2, FALSE);
PLAYER->actor.home.pos.y = -5000; // Make Link airborne for a frame to cancel the get item event
break;
case ICETRAP_FIRE:
Expand Down
2 changes: 1 addition & 1 deletion code/src/actors/gerudos.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ void EnGe1_rInit(Actor* thisx, GlobalContext* globalCtx) {
if ((self->actor.params & 0xFF) == GE1_TYPE_GATE_OPERATOR) {
if (gSettingsContext.shuffleGerudoToken || gSettingsContext.shuffleOverworldEntrances
/* || gSettingsContext.shuffleInteriorEntrances || gSettingsContexts.shuffleSpawnPositions*/) {
Actor_Spawn(&globalCtx->actorCtx, globalCtx, 0x138, -1358.0f, 88.0f, -3018.0f, 0, 0x95B0, 0, 0x0302);
Actor_Spawn(&globalCtx->actorCtx, globalCtx, 0x138, -1358.0f, 88.0f, -3018.0f, 0, 0x95B0, 0, 0x0302, FALSE);
}
} else if ((self->actor.params & 0xFF) == GE1_TYPE_EXTRA_GATE_OPERATOR) {
self->actor.params &= ~0xFF;
Expand Down
2 changes: 1 addition & 1 deletion code/src/actors/jabu.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ void BgBdanSwitch_rInit(Actor* thisx, GlobalContext* globalCtx) {
if ((gSaveContext.infTable[0x14] & 0x20 || gSaveContext.eventChkInf[3] & 0x0080) &&
gSettingsContext.jabuJabusBellyDungeonMode == DUNGEONMODE_MQ && globalCtx->sceneNum == 2 && thisx->room == 3 &&
((thisx->params & 0x000F) == 0)) {
Actor_Spawn(&globalCtx->actorCtx, globalCtx, 0x110, 222.0f, -1113.0f, -3270.0f, 0, 0, 0, 0);
Actor_Spawn(&globalCtx->actorCtx, globalCtx, 0x110, 222.0f, -1113.0f, -3270.0f, 0, 0, 0, 0, FALSE);
}
BgBdanSwitch_Init(thisx, globalCtx);
}
8 changes: 4 additions & 4 deletions code/src/actors/lake_hylia_objects.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ void BgSpot06Objects_rUpdate(Actor* thisx, GlobalContext* globalCtx) {
}

// Spawn a floor switch
floorSwitch =
Actor_Spawn(&globalCtx->actorCtx, globalCtx, 0x12A, -896.0f, -1243.0f, 6953.0f, 0, 0, 0, switchParams);
floorSwitch = Actor_Spawn(&globalCtx->actorCtx, globalCtx, 0x12A, -896.0f, -1243.0f, 6953.0f, 0, 0, 0,
switchParams, FALSE);

// Spawn a sign
Actor_Spawn(&globalCtx->actorCtx, globalCtx, 0x141, -970.0f, -1242.0f, 6954.0f, 0, 0, 0, 0x0046);
Actor_Spawn(&globalCtx->actorCtx, globalCtx, 0x141, -970.0f, -1242.0f, 6954.0f, 0, 0, 0, 0x0046, FALSE);

// Spawn a Navi check spot
if (!(gSaveContext.eventChkInf[4] & 0x0400)) { // Water Temple blue warp not cleared
Actor_Spawn(&globalCtx->actorCtx, globalCtx, 0x173, -896.0f, -1243.0f, 6953.0f, 0, 0, 0, 0x3DB3);
Actor_Spawn(&globalCtx->actorCtx, globalCtx, 0x173, -896.0f, -1243.0f, 6953.0f, 0, 0, 0, 0x3DB3, FALSE);
}

actionCounter++;
Expand Down
Loading

0 comments on commit 01793f2

Please sign in to comment.