#include "z64collision_check.h" #include "stdbool.h" #include "macros.h" #include "sfx.h" #include "sys_matrix.h" #include "z64actor.h" #include "z64effect.h" #include "z64frameadvance.h" #include "zelda_arena.h" #include "z64math.h" typedef s32 (*ColChkResetFunc)(struct PlayState*, Collider*); typedef void (*ColChkBloodFunc)(struct PlayState*, Collider*, Vec3f*); typedef void (*ColChkApplyFunc)(struct PlayState*, CollisionCheckContext*, Collider*); typedef void (*ColChkVsFunc)(struct PlayState*, CollisionCheckContext*, Collider*, Collider*); typedef s32 (*ColChkLineFunc)(struct PlayState*, CollisionCheckContext*, Collider*, Vec3f*, Vec3f*); #pragma increment_block_number "n64-us:192" Vec3f D_801EDE00; Vec3f D_801EDE10; Vec3f D_801EDE20; Vec3f D_801EDE30; TriNorm D_801EDE40; TriNorm D_801EDE78; Linef D_801EDEB0; TriNorm D_801EDEC8; TriNorm D_801EDF00; Vec3f D_801EDF38; Vec3f D_801EDF48; TriNorm D_801EDF58; TriNorm D_801EDF90; Linef D_801EDFC8; Vec3f D_801EDFE0; Vec3f D_801EDFF0; TriNorm D_801EE000; TriNorm D_801EE038; TriNorm D_801EE070[2]; Vec3f D_801EE0D8; TriNorm D_801EE0E8[2]; TriNorm D_801EE150; TriNorm D_801EE188; #pragma increment_block_number "n64-us:128" /** * Gets the damage and effect that should be applied for the collision between * `at` and `ac`, referring to the ac actor's damage chart if applicable. */ f32 CollisionCheck_GetDamageAndEffectOnElementAC(Collider* atCol, ColliderElement* atElem, Collider* acCol, ColliderElement* acElem, u32* effect) { static f32 sDamageMultipliers[] = { 0.0f, 1.0f, 2.0f, 0.5f, 0.25f, 3.0f, 4.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, }; u32 dmgFlags; s32 i; f32 damage; *effect = 0; damage = CollisionCheck_GetElementATDamage(atCol, atElem, acCol, acElem); if (acCol->actor->colChkInfo.damageTable != NULL) { dmgFlags = atElem->atDmgInfo.dmgFlags; for (i = 0; i < ARRAY_COUNT(acCol->actor->colChkInfo.damageTable->attack); i++) { if (dmgFlags == 1) { break; } dmgFlags >>= 1; } damage *= sDamageMultipliers[acCol->actor->colChkInfo.damageTable->attack[i] & 0xF]; *effect = (acCol->actor->colChkInfo.damageTable->attack[i] >> 4) & 0xF; } return damage; } /** * Calculates damage after factoring in the ac collider's defense */ f32 CollisionCheck_ApplyElementATDefense(f32 damage, ColliderElement* acElem) { f32 finalDamage = damage - acElem->acDmgInfo.defense; return finalDamage; } /** * Gets the damage to be inflicted by `at` on `ac`, before applying other * factors such as the ac collider's defense. */ s32 CollisionCheck_GetElementATDamage(Collider* atCol, ColliderElement* atElem, Collider* acCol, ColliderElement* acElem) { if ((atCol->actor != NULL) && (atCol->actor->id == ACTOR_EN_BOM) && (acCol->actor != NULL) && (acCol->actor->id == ACTOR_PLAYER)) { return 8; } return atElem->atDmgInfo.damage; } s32 Collider_InitBase(struct PlayState* play, Collider* col) { static Collider sDefaultCollider = { NULL, NULL, NULL, NULL, AT_NONE, AC_NONE, OC1_NONE, OC2_NONE, COL_MATERIAL_HIT3, COLSHAPE_MAX, }; *col = sDefaultCollider; return 1; } s32 Collider_DestroyBase(struct PlayState* play, Collider* col) { return 1; } /** * Uses default OC2_TYPE_1 and COL_MATERIAL_HIT0 */ s32 Collider_SetBaseToActor(struct PlayState* play, Collider* col, ColliderInitToActor* src) { col->actor = src->actor; col->atFlags = src->atFlags; col->acFlags = src->acFlags; col->ocFlags1 = src->ocFlags1; col->ocFlags2 = OC2_TYPE_1; col->shape = src->shape; return 1; } /** * Uses default OC2_TYPE_1 */ s32 Collider_SetBaseType1(struct PlayState* play, Collider* col, Actor* actor, ColliderInitType1* src) { col->actor = actor; col->colMaterial = src->colMaterial; col->atFlags = src->atFlags; col->acFlags = src->acFlags; col->ocFlags1 = src->ocFlags1; col->ocFlags2 = OC2_TYPE_1; col->shape = src->shape; return 1; } s32 Collider_SetBase(struct PlayState* play, Collider* col, Actor* actor, ColliderInit* src) { col->actor = actor; col->colMaterial = src->colMaterial; col->atFlags = src->atFlags; col->acFlags = src->acFlags; col->ocFlags1 = src->ocFlags1; col->ocFlags2 = src->ocFlags2; col->shape = src->shape; return 1; } void Collider_ResetATBase(struct PlayState* play, Collider* col) { col->at = NULL; col->atFlags &= ~(AT_HIT | AT_BOUNCED); } void Collider_ResetACBase(struct PlayState* play, Collider* col) { col->ac = NULL; col->acFlags &= ~(AC_HIT | AC_BOUNCED); } void Collider_ResetOCBase(struct PlayState* play, Collider* col) { col->oc = NULL; col->ocFlags1 &= ~OC1_HIT; col->ocFlags2 &= ~OC2_HIT_PLAYER; } s32 Collider_InitElementDamageInfoAT(struct PlayState* play, ColliderElementDamageInfoAT* atDmgInfo) { static ColliderElementDamageInfoAT sDefaultDamageInfoAT = { 0x00000000, 0, 0 }; *atDmgInfo = sDefaultDamageInfoAT; return 1; } s32 Collider_DestroyElementDamageInfoAT(struct PlayState* play, ColliderElementDamageInfoAT* atDmgInfo) { return 1; } s32 Collider_SetElementDamageInfoAT(struct PlayState* play, ColliderElementDamageInfoAT* dest, ColliderElementDamageInfoATInit* src) { dest->dmgFlags = src->dmgFlags; dest->effect = src->effect; dest->damage = src->damage; return 1; } void Collider_ResetATElementUnk(struct PlayState* play, ColliderElement* elem) { } s32 Collider_InitElementDamageInfoAC(struct PlayState* play, ColliderElementDamageInfoAC* acDmgInfo) { static ColliderElementDamageInfoAC sDefaultDamageInfoAC = { 0xF7CFFFFF, 0, 0, { 0, 0, 0 } }; *acDmgInfo = sDefaultDamageInfoAC; return 1; } s32 Collider_DestroyElementDamageInfoAC(struct PlayState* play, ColliderElementDamageInfoAC* acDmgInfo) { return 1; } s32 Collider_SetElementDamageInfoAC(struct PlayState* play, ColliderElementDamageInfoAC* dest, ColliderElementDamageInfoACInit* src) { dest->dmgFlags = src->dmgFlags; dest->effect = src->effect; dest->defense = src->defense; return 1; } s32 Collider_InitElement(struct PlayState* play, ColliderElement* elem) { static ColliderElement sDefaultColliderElement = { { 0, 0, 0 }, { 0xF7CFFFFF, 0, 0, { 0, 0, 0 } }, ELEM_MATERIAL_UNK0, ATELEM_NONE, ACELEM_NONE, OCELEM_NONE, NULL, NULL, NULL, NULL, }; *elem = sDefaultColliderElement; Collider_InitElementDamageInfoAT(play, &elem->atDmgInfo); Collider_InitElementDamageInfoAC(play, &elem->acDmgInfo); return 1; } s32 Collider_DestroyElement(struct PlayState* play, ColliderElement* elem) { Collider_DestroyElementDamageInfoAT(play, &elem->atDmgInfo); Collider_DestroyElementDamageInfoAC(play, &elem->acDmgInfo); return 1; } s32 Collider_SetElement(struct PlayState* play, ColliderElement* elem, ColliderElementInit* elemInit) { elem->elemMaterial = elemInit->elemMaterial; Collider_SetElementDamageInfoAT(play, &elem->atDmgInfo, &elemInit->atDmgInfo); Collider_SetElementDamageInfoAC(play, &elem->acDmgInfo, &elemInit->acDmgInfo); elem->atElemFlags = elemInit->atElemFlags; elem->acElemFlags = elemInit->acElemFlags; elem->ocElemFlags = elemInit->ocElemFlags; return 1; } void Collider_ResetATElement(struct PlayState* play, ColliderElement* elem) { elem->atHit = NULL; elem->atHitElem = NULL; elem->atElemFlags &= ~ATELEM_HIT; elem->atElemFlags &= ~ATELEM_DREW_HITMARK; Collider_ResetATElementUnk(play, elem); } void Collider_ResetACElement(struct PlayState* play, ColliderElement* elem) { elem->acDmgInfo.hitPos.x = elem->acDmgInfo.hitPos.y = elem->acDmgInfo.hitPos.z = 0; elem->acElemFlags &= ~ACELEM_HIT; elem->acElemFlags &= ~ACELEM_DRAW_HITMARK; elem->acHit = NULL; elem->acHitElem = NULL; } void Collider_ResetOCElement(struct PlayState* play, ColliderElement* elem) { elem->ocElemFlags &= ~OCELEM_HIT; } s32 Collider_InitJntSphElementDim(struct PlayState* play, ColliderJntSphElementDim* dim) { static ColliderJntSphElementDim sDefaultColliderJntSphElementDim = { { { 0, 0, 0 }, 0 }, { { 0, 0, 0 }, 0 }, 0.0f, 0, }; *dim = sDefaultColliderJntSphElementDim; return 1; } s32 Collider_DestroyJntSphElementDim(struct PlayState* play, ColliderJntSphElementDim* dim) { return 1; } s32 Collider_SetJntSphElementDim(struct PlayState* play, ColliderJntSphElementDim* dest, ColliderJntSphElementDimInit* src) { dest->limb = src->limb; dest->modelSphere = src->modelSphere; dest->scale = src->scale * 0.01f; return 1; } s32 Collider_InitJntSphElement(struct PlayState* play, ColliderJntSphElement* jntSphElem) { Collider_InitElement(play, &jntSphElem->base); Collider_InitJntSphElementDim(play, &jntSphElem->dim); return 1; } s32 Collider_DestroyJntSphElement(struct PlayState* play, ColliderJntSphElement* jntSphElem) { Collider_DestroyElement(play, &jntSphElem->base); Collider_DestroyJntSphElementDim(play, &jntSphElem->dim); return 1; } s32 Collider_SetJntSphElement(struct PlayState* play, ColliderJntSphElement* dest, ColliderJntSphElementInit* src) { Collider_SetElement(play, &dest->base, &src->base); Collider_SetJntSphElementDim(play, &dest->dim, &src->dim); return 1; } s32 Collider_ResetJntSphElementAT(struct PlayState* play, ColliderJntSphElement* jntSphElem) { Collider_ResetATElement(play, &jntSphElem->base); return 1; } s32 Collider_ResetJntSphElementAC(struct PlayState* play, ColliderJntSphElement* jntSphElem) { Collider_ResetACElement(play, &jntSphElem->base); return 1; } s32 Collider_ResetJntSphElementOC(struct PlayState* play, ColliderJntSphElement* jntSphElem) { Collider_ResetOCElement(play, &jntSphElem->base); return 1; } /** * Initializes a ColliderJntSph to default values */ s32 Collider_InitJntSph(struct PlayState* play, ColliderJntSph* jntSph) { Collider_InitBase(play, &jntSph->base); jntSph->count = 0; jntSph->elements = NULL; return 1; } /** * Destroys a dynamically allocated ColliderJntSph */ s32 Collider_FreeJntSph(struct PlayState* play, ColliderJntSph* jntSph) { ColliderJntSphElement* jntSphElem; Collider_DestroyBase(play, &jntSph->base); for (jntSphElem = jntSph->elements; jntSphElem < &jntSph->elements[jntSph->count]; jntSphElem++) { Collider_DestroyJntSphElement(play, jntSphElem); } jntSph->count = 0; if (jntSph->elements != NULL) { ZeldaArena_Free(jntSph->elements); } jntSph->elements = NULL; return 1; } /** * Destroys a preallocated ColliderJntSph */ s32 Collider_DestroyJntSph(struct PlayState* play, ColliderJntSph* jntSph) { ColliderJntSphElement* jntSphElem; Collider_DestroyBase(play, &jntSph->base); for (jntSphElem = jntSph->elements; jntSphElem < &jntSph->elements[jntSph->count]; jntSphElem++) { Collider_DestroyJntSphElement(play, jntSphElem); } jntSph->count = 0; jntSph->elements = NULL; return 1; } /** * Sets up the ColliderJntSph using the values in src, sets it to the actor specified in src, and dynamically allocates * the element array. Uses default OC2_TYPE_1 and COL_MATERIAL_HIT0. */ s32 Collider_SetJntSphToActor(struct PlayState* play, ColliderJntSph* dest, ColliderJntSphInitToActor* src) { ColliderJntSphElement* destElem; ColliderJntSphElementInit* srcElem; Collider_SetBaseToActor(play, &dest->base, &src->base); dest->count = src->count; dest->elements = ZeldaArena_Malloc(src->count * sizeof(ColliderJntSphElement)); if (dest->elements == NULL) { dest->count = 0; return 0; } for (destElem = dest->elements, srcElem = src->elements; destElem < &dest->elements[dest->count]; destElem++, srcElem++) { Collider_InitJntSphElement(play, destElem); Collider_SetJntSphElement(play, destElem, srcElem); } return 1; } /** * Sets up the ColliderJntSph using the values in src and dynamically allocates the element array. Uses default * OC2_TYPE_1. */ s32 Collider_SetJntSphAllocType1(struct PlayState* play, ColliderJntSph* dest, Actor* actor, ColliderJntSphInitType1* src) { ColliderJntSphElement* destElem; ColliderJntSphElementInit* srcElem; Collider_SetBaseType1(play, &dest->base, actor, &src->base); dest->count = src->count; dest->elements = ZeldaArena_Malloc(src->count * sizeof(ColliderJntSphElement)); if (dest->elements == NULL) { dest->count = 0; return 0; } for (destElem = dest->elements, srcElem = src->elements; destElem < &dest->elements[dest->count]; destElem++, srcElem++) { Collider_InitJntSphElement(play, destElem); Collider_SetJntSphElement(play, destElem, srcElem); } return 1; } /** * Sets up the ColliderJntSph using the values in src, placing the element array in elements. */ s32 Collider_SetJntSph(struct PlayState* play, ColliderJntSph* dest, Actor* actor, ColliderJntSphInit* src, ColliderJntSphElement* jntSphElements) { ColliderJntSphElement* destElem; ColliderJntSphElementInit* srcElem; Collider_SetBase(play, &dest->base, actor, &src->base); dest->count = src->count; dest->elements = jntSphElements; for (destElem = dest->elements, srcElem = src->elements; destElem < &dest->elements[dest->count]; destElem++, srcElem++) { Collider_InitJntSphElement(play, destElem); Collider_SetJntSphElement(play, destElem, srcElem); } return 1; } /** * Fully initializes a ColliderJntSph using the values in `src`, placing the element array in elements. */ s32 Collider_InitAndSetJntSph(struct PlayState* play, ColliderJntSph* dest, Actor* actor, ColliderJntSphInit* src, ColliderJntSphElement* jntSphElements) { Collider_InitJntSph(play, dest); Collider_SetJntSph(play, dest, actor, src, jntSphElements); return 0; } /** * Resets the collider's AT collision flags. */ s32 Collider_ResetJntSphAT(struct PlayState* play, Collider* col) { ColliderJntSphElement* jntSphElem; ColliderJntSph* jntSph = (ColliderJntSph*)col; Collider_ResetATBase(play, &jntSph->base); for (jntSphElem = jntSph->elements; jntSphElem < &jntSph->elements[jntSph->count]; jntSphElem++) { Collider_ResetJntSphElementAT(play, jntSphElem); } return 1; } /** * Resets the collider's AC collision flags. */ s32 Collider_ResetJntSphAC(struct PlayState* play, Collider* col) { ColliderJntSphElement* jntSphElem; ColliderJntSph* jntSph = (ColliderJntSph*)col; Collider_ResetACBase(play, &jntSph->base); for (jntSphElem = jntSph->elements; jntSphElem < &jntSph->elements[jntSph->count]; jntSphElem++) { Collider_ResetJntSphElementAC(play, jntSphElem); } return 1; } /** * Resets the collider's OC collision flags. */ s32 Collider_ResetJntSphOC(struct PlayState* play, Collider* col) { ColliderJntSphElement* jntSphElem; ColliderJntSph* jntSph = (ColliderJntSph*)col; Collider_ResetOCBase(play, &jntSph->base); for (jntSphElem = jntSph->elements; jntSphElem < &jntSph->elements[jntSph->count]; jntSphElem++) { Collider_ResetJntSphElementOC(play, jntSphElem); } return 1; } s32 Collider_InitCylinderDim(struct PlayState* play, Cylinder16* dim) { static Cylinder16 sDefaultColliderCylinderDim = { 0, 0, 0, { 0, 0, 0 } }; *dim = sDefaultColliderCylinderDim; return 1; } s32 Collider_DestroyCylinderDim(struct PlayState* play, Cylinder16* dim) { return 1; } s32 Collider_SetCylinderDim(struct PlayState* play, Cylinder16* dim, Cylinder16* src) { *dim = *src; return 1; } /** * Initializes a ColliderCylinder to default values */ s32 Collider_InitCylinder(struct PlayState* play, ColliderCylinder* cyl) { Collider_InitBase(play, &cyl->base); Collider_InitElement(play, &cyl->elem); Collider_InitCylinderDim(play, &cyl->dim); return 1; } /** * Destroys a ColliderCylinder */ s32 Collider_DestroyCylinder(struct PlayState* play, ColliderCylinder* cyl) { Collider_DestroyBase(play, &cyl->base); Collider_DestroyElement(play, &cyl->elem); Collider_DestroyCylinderDim(play, &cyl->dim); return 1; } /** * Sets up the ColliderCylinder using the values in src and sets it to the actor specified in src. Uses default * OC2_TYPE_1 and COL_MATERIAL_0. */ s32 Collider_SetCylinderToActor(struct PlayState* play, ColliderCylinder* dest, ColliderCylinderInitToActor* src) { Collider_SetBaseToActor(play, &dest->base, &src->base); Collider_SetElement(play, &dest->elem, &src->elem); Collider_SetCylinderDim(play, &dest->dim, &src->dim); return 1; } /** * Sets up the ColliderCylinder using the values in src. Uses default OC2_TYPE_1 */ s32 Collider_SetCylinderType1(struct PlayState* play, ColliderCylinder* dest, Actor* actor, ColliderCylinderInitType1* src) { Collider_SetBaseType1(play, &dest->base, actor, &src->base); Collider_SetElement(play, &dest->elem, &src->elem); Collider_SetCylinderDim(play, &dest->dim, &src->dim); return 1; } /** * Sets up the ColliderCylinder using the values in src. */ s32 Collider_SetCylinder(struct PlayState* play, ColliderCylinder* dest, Actor* actor, ColliderCylinderInit* src) { Collider_SetBase(play, &dest->base, actor, &src->base); Collider_SetElement(play, &dest->elem, &src->elem); Collider_SetCylinderDim(play, &dest->dim, &src->dim); return 1; } /** * Fully initializes a ColliderCylinder using the values in src. */ s32 Collider_InitAndSetCylinder(struct PlayState* play, ColliderCylinder* dest, Actor* actor, ColliderCylinderInit* src) { Collider_InitCylinder(play, dest); Collider_SetCylinder(play, dest, actor, src); return 1; } /** * Resets the collider's AT collision flags. */ s32 Collider_ResetCylinderAT(struct PlayState* play, Collider* col) { ColliderCylinder* cyl = (ColliderCylinder*)col; Collider_ResetATBase(play, &cyl->base); Collider_ResetATElement(play, &cyl->elem); return 1; } /** * Resets the collider's AC collision flags. */ s32 Collider_ResetCylinderAC(struct PlayState* play, Collider* col) { ColliderCylinder* cyl = (ColliderCylinder*)col; Collider_ResetACBase(play, &cyl->base); Collider_ResetACElement(play, &cyl->elem); return 1; } /** * Resets the collider's OC collision flags. */ s32 Collider_ResetCylinderOC(struct PlayState* play, Collider* col) { ColliderCylinder* cyl = (ColliderCylinder*)col; Collider_ResetOCBase(play, &cyl->base); Collider_ResetOCElement(play, &cyl->elem); return 1; } s32 Collider_InitTrisElementDim(struct PlayState* play, TriNorm* dim) { static TriNorm sDefaultColliderTrisElementDim = { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }, { { 0.0f, 0.0f, 0.0f }, 0.0f }, }; *dim = sDefaultColliderTrisElementDim; return 1; } s32 Collider_DestroyTrisElementDim(struct PlayState* play, TriNorm* dim) { return 1; } s32 Collider_SetTrisElementDim(struct PlayState* play, TriNorm* dest, ColliderTrisElementDimInit* src) { Vec3f* destVtx; Vec3f* srcVtx; f32 nx; f32 ny; f32 nz; f32 originDist; for (destVtx = dest->vtx, srcVtx = &src->vtx[0]; destVtx < &dest->vtx[3]; destVtx++, srcVtx++) { *destVtx = *srcVtx; } Math3D_DefPlane(&src->vtx[0], &src->vtx[1], &src->vtx[2], &nx, &ny, &nz, &originDist); dest->plane.normal.x = nx; dest->plane.normal.y = ny; dest->plane.normal.z = nz; dest->plane.originDist = originDist; return 1; } s32 Collider_InitTrisElement(struct PlayState* play, ColliderTrisElement* trisElem) { Collider_InitElement(play, &trisElem->base); Collider_InitTrisElementDim(play, &trisElem->dim); return 1; } s32 Collider_DestroyTrisElement(struct PlayState* play, ColliderTrisElement* trisElem) { Collider_DestroyElement(play, &trisElem->base); Collider_DestroyTrisElementDim(play, &trisElem->dim); return 1; } s32 Collider_SetTrisElement(struct PlayState* play, ColliderTrisElement* dest, ColliderTrisElementInit* src) { Collider_SetElement(play, &dest->base, &src->base); Collider_SetTrisElementDim(play, &dest->dim, &src->dim); return 1; } s32 Collider_ResetTrisElementAT(struct PlayState* play, ColliderTrisElement* trisElem) { Collider_ResetATElement(play, &trisElem->base); return 1; } s32 Collider_ResetTrisElementAC(struct PlayState* play, ColliderTrisElement* trisElem) { Collider_ResetACElement(play, &trisElem->base); return 1; } s32 Collider_ResetTrisElementOC(struct PlayState* play, ColliderTrisElement* trisElem) { Collider_ResetOCElement(play, &trisElem->base); return 1; } /** * Initializes a ColliderTris to default values */ s32 Collider_InitTris(struct PlayState* play, ColliderTris* tris) { Collider_InitBase(play, &tris->base); tris->count = 0; tris->elements = NULL; return 1; } /** * Destroys a dynamically allocated ColliderTris */ s32 Collider_FreeTris(struct PlayState* play, ColliderTris* tris) { ColliderTrisElement* trisElem; Collider_DestroyBase(play, &tris->base); for (trisElem = tris->elements; trisElem < &tris->elements[tris->count]; trisElem++) { Collider_DestroyTrisElement(play, trisElem); } tris->count = 0; if (tris->elements != NULL) { ZeldaArena_Free(tris->elements); } tris->elements = NULL; return 1; } /** * Destroys a preallocated ColliderTris */ s32 Collider_DestroyTris(struct PlayState* play, ColliderTris* tris) { ColliderTrisElement* trisElem; Collider_DestroyBase(play, &tris->base); for (trisElem = tris->elements; trisElem < &tris->elements[tris->count]; trisElem++) { Collider_DestroyTrisElement(play, trisElem); } tris->count = 0; tris->elements = NULL; return 1; } /** * Sets up the ColliderTris using the values in src and dynamically allocates the element array. Uses default * OC2_TYPE_1. */ s32 Collider_SetTrisAllocType1(struct PlayState* play, ColliderTris* dest, Actor* actor, ColliderTrisInitType1* src) { ColliderTrisElement* destElem; ColliderTrisElementInit* srcElem; Collider_SetBaseType1(play, &dest->base, actor, &src->base); dest->count = src->count; dest->elements = ZeldaArena_Malloc(dest->count * sizeof(ColliderTrisElement)); if (dest->elements == NULL) { dest->count = 0; return 0; } for (destElem = dest->elements, srcElem = src->elements; destElem < &dest->elements[dest->count]; destElem++, srcElem++) { Collider_InitTrisElement(play, destElem); Collider_SetTrisElement(play, destElem, srcElem); } return 1; } /** * Sets up the ColliderTris using the values in src, placing the element array in elements. */ s32 Collider_SetTris(struct PlayState* play, ColliderTris* dest, Actor* actor, ColliderTrisInit* src, ColliderTrisElement* trisElements) { ColliderTrisElement* destElem; ColliderTrisElementInit* srcElem; Collider_SetBase(play, &dest->base, actor, &src->base); dest->count = src->count; dest->elements = trisElements; for (destElem = dest->elements, srcElem = src->elements; destElem < &dest->elements[dest->count]; destElem++, srcElem++) { Collider_InitTrisElement(play, destElem); Collider_SetTrisElement(play, destElem, srcElem); } return 1; } /** * Fully initializes a ColliderTris using the values in src, placing the element array in elements. */ s32 Collider_InitAndSetTris(struct PlayState* play, ColliderTris* dest, Actor* actor, ColliderTrisInit* src, ColliderTrisElement* trisElements) { Collider_InitTris(play, dest); Collider_SetTris(play, dest, actor, src, trisElements); return 0; } /** * Resets the collider's AT collision flags. */ s32 Collider_ResetTrisAT(struct PlayState* play, Collider* col) { ColliderTrisElement* trisElem; ColliderTris* tris = (ColliderTris*)col; Collider_ResetATBase(play, &tris->base); for (trisElem = tris->elements; trisElem < &tris->elements[tris->count]; trisElem++) { Collider_ResetTrisElementAT(play, trisElem); } return 1; } /** * Resets the collider's AC collision flags. */ s32 Collider_ResetTrisAC(struct PlayState* play, Collider* col) { ColliderTrisElement* trisElem; ColliderTris* tris = (ColliderTris*)col; Collider_ResetACBase(play, &tris->base); for (trisElem = tris->elements; trisElem < &tris->elements[tris->count]; trisElem++) { Collider_ResetTrisElementAC(play, trisElem); } return 1; } /** * Resets the collider's OC collision flags. */ s32 Collider_ResetTrisOC(struct PlayState* play, Collider* col) { ColliderTrisElement* trisElem; ColliderTris* tris = (ColliderTris*)col; Collider_ResetOCBase(play, &tris->base); for (trisElem = tris->elements; trisElem < &tris->elements[tris->count]; trisElem++) { Collider_ResetTrisElementOC(play, trisElem); } return 1; } s32 Collider_InitQuadDim(struct PlayState* play, ColliderQuadDim* dim) { static ColliderQuadDim sDefaultColliderQuadDim = { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }, { 0, 0, 0 }, { 0, 0, 0 }, 1.0E38f, }; *dim = sDefaultColliderQuadDim; return 1; } s32 Collider_DestroyQuadDim(struct PlayState* play, ColliderQuadDim* dim) { return 1; } s32 Collider_ResetQuadACDist(struct PlayState* play, ColliderQuadDim* dim) { dim->acDist = 1.0E38f; return 1; } void Collider_SetQuadMidpoints(ColliderQuadDim* dim) { dim->dcMid.x = (dim->quad[3].x + dim->quad[2].x) * 0.5f; dim->dcMid.y = (dim->quad[3].y + dim->quad[2].y) * 0.5f; dim->dcMid.z = (dim->quad[3].z + dim->quad[2].z) * 0.5f; dim->baMid.x = (dim->quad[1].x + dim->quad[0].x) * 0.5f; dim->baMid.y = (dim->quad[1].y + dim->quad[0].y) * 0.5f; dim->baMid.z = (dim->quad[1].z + dim->quad[0].z) * 0.5f; } s32 Collider_SetQuadDim(struct PlayState* play, ColliderQuadDim* dest, ColliderQuadDimInit* src) { dest->quad[0] = src->quad[0]; dest->quad[1] = src->quad[1]; dest->quad[2] = src->quad[2]; dest->quad[3] = src->quad[3]; Collider_SetQuadMidpoints(dest); return 1; } /** * Initializes a ColliderQuad to default values. */ s32 Collider_InitQuad(struct PlayState* play, ColliderQuad* quad) { Collider_InitBase(play, &quad->base); Collider_InitElement(play, &quad->elem); Collider_InitQuadDim(play, &quad->dim); return 1; } /** * Destroys a ColliderQuad. */ s32 Collider_DestroyQuad(struct PlayState* play, ColliderQuad* quad) { Collider_DestroyBase(play, &quad->base); Collider_DestroyElement(play, &quad->elem); Collider_DestroyQuadDim(play, &quad->dim); return 1; } /** * Sets up the ColliderQuad using the values in src. Uses the default OC2_TYPE_1 */ s32 Collider_SetQuadType1(struct PlayState* play, ColliderQuad* dest, Actor* actor, ColliderQuadInitType1* src) { Collider_SetBaseType1(play, &dest->base, actor, &src->base); Collider_SetElement(play, &dest->elem, &src->elem); Collider_SetQuadDim(play, &dest->dim, &src->dim); return 1; } /** * Sets up the ColliderQuad using the values in src. */ s32 Collider_SetQuad(struct PlayState* play, ColliderQuad* dest, Actor* actor, ColliderQuadInit* src) { Collider_SetBase(play, &dest->base, actor, &src->base); Collider_SetElement(play, &dest->elem, &src->elem); Collider_SetQuadDim(play, &dest->dim, &src->dim); return 1; } /** * Fully initializes a ColliderQuad using the values in src. */ s32 Collider_InitAndSetQuad(struct PlayState* play, ColliderQuad* dest, Actor* actor, ColliderQuadInit* src) { Collider_InitQuad(play, dest); Collider_SetQuad(play, dest, actor, src); return 0; } /** * Resets the collider's AT collision flags. */ s32 Collider_ResetQuadAT(struct PlayState* play, Collider* col) { ColliderQuad* quad = (ColliderQuad*)col; Collider_ResetATBase(play, &quad->base); Collider_ResetATElement(play, &quad->elem); Collider_ResetQuadACDist(play, &quad->dim); return 1; } /** * Resets the collider's AC collision flags. */ s32 Collider_ResetQuadAC(struct PlayState* play, Collider* col) { ColliderQuad* quad = (ColliderQuad*)col; Collider_ResetACBase(play, &quad->base); Collider_ResetACElement(play, &quad->elem); return 1; } /** * Resets the collider's OC collision flags. */ s32 Collider_ResetQuadOC(struct PlayState* play, Collider* col) { ColliderQuad* quad = (ColliderQuad*)col; Collider_ResetOCBase(play, &quad->base); Collider_ResetOCElement(play, &quad->elem); return 1; } /** * For quad colliders with ATELEM_NEAREST, resets the previous AC collider it hit if the current element is closer, * otherwise returns false. Used on player AT colliders to prevent multiple collisions from registering. */ s32 Collider_QuadSetNearestAC(struct PlayState* play, ColliderQuad* quad, Vec3f* hitPos) { f32 acDist; Vec3f dcMid; if (!(quad->elem.atElemFlags & ATELEM_NEAREST)) { return 1; } Math_Vec3s_ToVec3f(&dcMid, &quad->dim.dcMid); acDist = Math3D_Vec3fDistSq(&dcMid, hitPos); if (acDist < quad->dim.acDist) { quad->dim.acDist = acDist; if (quad->elem.atHit != NULL) { Collider_ResetACBase(play, quad->elem.atHit); } if (quad->elem.atHitElem != NULL) { Collider_ResetACElement(play, quad->elem.atHitElem); } return 1; } else { return 0; } } /** * Initializes a ColliderSphere to default values. */ s32 Collider_InitSphere(struct PlayState* play, ColliderSphere* sph) { Collider_InitBase(play, &sph->base); Collider_InitElement(play, &sph->elem); Collider_InitJntSphElementDim(play, &sph->dim); return 1; } /** * Destroys a ColliderSphere. */ s32 Collider_DestroySphere(struct PlayState* play, ColliderSphere* sph) { Collider_DestroyBase(play, &sph->base); Collider_DestroyElement(play, &sph->elem); Collider_DestroyJntSphElementDim(play, &sph->dim); return 1; } /** * Sets up the ColliderSphere using the values in src. */ s32 Collider_SetSphere(struct PlayState* play, ColliderSphere* dest, Actor* actor, ColliderSphereInit* src) { Collider_SetBase(play, &dest->base, actor, &src->base); Collider_SetElement(play, &dest->elem, &src->elem); Collider_SetJntSphElementDim(play, &dest->dim, &src->dim); return 1; } /** * Fully initializes a ColliderSphere using the values in src. */ s32 Collider_InitAndSetSphere(struct PlayState* play, ColliderSphere* dest, Actor* actor, ColliderSphereInit* src) { Collider_InitSphere(play, dest); Collider_SetSphere(play, dest, actor, src); return 0; } /** * Resets the collider's AT collision flags. */ s32 Collider_ResetSphereAT(struct PlayState* play, Collider* col) { ColliderSphere* sph = (ColliderSphere*)col; Collider_ResetATBase(play, &sph->base); Collider_ResetATElement(play, &sph->elem); return 1; } /** * Resets the collider's AC collision flags. */ s32 Collider_ResetSphereAC(struct PlayState* play, Collider* col) { ColliderSphere* sph = (ColliderSphere*)col; Collider_ResetACBase(play, &sph->base); Collider_ResetACElement(play, &sph->elem); return 1; } /** * Resets the collider's OC collision flags. */ s32 Collider_ResetSphereOC(struct PlayState* play, Collider* col) { ColliderSphere* sph = (ColliderSphere*)col; Collider_ResetOCBase(play, &sph->base); Collider_ResetOCElement(play, &sph->elem); return 1; } /** * Initializes an OcLine to default values */ s32 Collider_InitLine(struct PlayState* play, OcLine* line) { static Vec3f sDefaultLinePoint = { 0.0f, 0.0f, 0.0f }; Math_Vec3f_Copy(&line->line.a, &sDefaultLinePoint); Math_Vec3f_Copy(&line->line.b, &sDefaultLinePoint); return 1; } /** * Destroys an OcLine */ s32 Collider_DestroyLine(struct PlayState* play, OcLine* line) { return 1; } /** * Sets up an OcLine with endpoints a and b. */ s32 Collider_SetLinePoints(struct PlayState* play, OcLine* line, Vec3f* a, Vec3f* b) { Math_Vec3f_Copy(&line->line.a, a); Math_Vec3f_Copy(&line->line.b, b); return 1; } /** * Sets up an OcLine using the values in src. */ s32 Collider_SetLine(struct PlayState* play, OcLine* dst, OcLine* src) { dst->ocFlags = src->ocFlags; Collider_SetLinePoints(play, dst, &src->line.a, &src->line.b); return 1; } /** * Resets the OcLine's collision flags. */ s32 Collider_ResetLineOC(struct PlayState* play, OcLine* line) { line->ocFlags &= ~OCLINE_HIT; return 1; } /** * Initializes CollisionCheckContext. * Clears all collider arrays, disables SAC, and sets flags for drawing colliders. */ void CollisionCheck_InitContext(struct PlayState* play, CollisionCheckContext* colChkCtx) { colChkCtx->sacFlags = 0; CollisionCheck_ClearContext(play, colChkCtx); } void CollisionCheck_DestroyContext(struct PlayState* play, CollisionCheckContext* colChkCtx) { } /** * Clears all collider lists in CollisionCheckContext when not in SAC mode. */ void CollisionCheck_ClearContext(struct PlayState* play, CollisionCheckContext* colChkCtx) { Collider** colP; OcLine** lineP; if (colChkCtx->sacFlags & SAC_ON) { return; } colChkCtx->colATCount = 0; colChkCtx->colACCount = 0; colChkCtx->colOCCount = 0; colChkCtx->colLineCount = 0; for (colP = &colChkCtx->colAT[0]; colP < &colChkCtx->colAT[ARRAY_COUNT(colChkCtx->colAT)]; colP++) { *colP = NULL; } for (colP = &colChkCtx->colAC[0]; colP < &colChkCtx->colAC[ARRAY_COUNT(colChkCtx->colAC)]; colP++) { *colP = NULL; } for (colP = &colChkCtx->colOC[0]; colP < &colChkCtx->colOC[ARRAY_COUNT(colChkCtx->colOC)]; colP++) { *colP = NULL; } for (lineP = &colChkCtx->colLine[0]; lineP < &colChkCtx->colLine[ARRAY_COUNT(colChkCtx->colLine)]; lineP++) { *lineP = NULL; } } /** * Enables SAC, an alternate collision check mode that allows direct management of collider lists. */ void CollisionCheck_EnableSAC(struct PlayState* play, CollisionCheckContext* colChkCtx) { colChkCtx->sacFlags |= SAC_ON; } /** * Disables SAC, an alternate collision check mode that allows direct management of collider lists. */ void CollisionCheck_DisableSAC(struct PlayState* play, CollisionCheckContext* colChkCtx) { colChkCtx->sacFlags &= ~SAC_ON; } ColChkResetFunc sATResetFuncs[COLSHAPE_MAX] = { Collider_ResetJntSphAT, // COLSHAPE_JNTSPH Collider_ResetCylinderAT, // COLSHAPE_CYLINDER Collider_ResetTrisAT, // COLSHAPE_TRIS Collider_ResetQuadAT, // COLSHAPE_QUAD Collider_ResetSphereAT, // COLSHAPE_SPHERE }; /** * Sets collider as an AT (attack) for the current frame, which will be checked against ACs (attack colliders) */ s32 CollisionCheck_SetAT(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* col) { s32 index; if (FrameAdvance_IsEnabled(play)) { return -1; } sATResetFuncs[col->shape](play, col); if ((col->actor != NULL) && (col->actor->update == NULL)) { return -1; } if (colChkCtx->colATCount >= ARRAY_COUNT(colChkCtx->colAT)) { return -1; } if (colChkCtx->sacFlags & SAC_ON) { return -1; } index = colChkCtx->colATCount; colChkCtx->colAT[colChkCtx->colATCount++] = col; return index; } /** * Sets collider as an AT (attack) for the current frame, which will be checked against ACs (attack colliders). * If CollisionCheck_SAC is enabled, the collider will be inserted into the list at the specified index, otherwise it * will be inserted into the next slot. */ s32 CollisionCheck_SetAT_SAC(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* col, s32 index) { if (FrameAdvance_IsEnabled(play)) { return -1; } sATResetFuncs[col->shape](play, col); if ((col->actor != NULL) && (col->actor->update == NULL)) { return -1; } if (colChkCtx->sacFlags & SAC_ON) { if (index >= colChkCtx->colATCount) { return -1; } colChkCtx->colAT[index] = col; } else { if (colChkCtx->colATCount >= ARRAY_COUNT(colChkCtx->colAT)) { return -1; } index = colChkCtx->colATCount; colChkCtx->colAT[colChkCtx->colATCount++] = col; } return index; } ColChkResetFunc sACResetFuncs[COLSHAPE_MAX] = { Collider_ResetJntSphAC, // COLSHAPE_JNTSPH Collider_ResetCylinderAC, // COLSHAPE_CYLINDER Collider_ResetTrisAC, // COLSHAPE_TRIS Collider_ResetQuadAC, // COLSHAPE_QUAD Collider_ResetSphereAC, // COLSHAPE_SPHERE }; /** * Sets collider as an AC (attack collider) for the current frame, allowing it to detect ATs (attacks) */ s32 CollisionCheck_SetAC(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* col) { s32 index; if (FrameAdvance_IsEnabled(play)) { return -1; } sACResetFuncs[col->shape](play, col); if ((col->actor != NULL) && (col->actor->update == NULL)) { return -1; } if (colChkCtx->colACCount >= ARRAY_COUNT(colChkCtx->colAC)) { return -1; } if (colChkCtx->sacFlags & SAC_ON) { return -1; } index = colChkCtx->colACCount; colChkCtx->colAC[colChkCtx->colACCount++] = col; return index; } /** * Sets collider as an AC (attack collider) for the current frame, allowing it to detect ATs (attacks). * If CollisionCheck_SAC is enabled, the collider will be inserted into the list at the specified index, otherwise it * will be inserted into the next slot */ s32 CollisionCheck_SetAC_SAC(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* col, s32 index) { if (FrameAdvance_IsEnabled(play)) { return -1; } sACResetFuncs[col->shape](play, col); if ((col->actor != NULL) && (col->actor->update == NULL)) { return -1; } if (colChkCtx->sacFlags & SAC_ON) { if (index >= colChkCtx->colACCount) { return -1; } colChkCtx->colAC[index] = col; } else { if (colChkCtx->colACCount >= ARRAY_COUNT(colChkCtx->colAC)) { return -1; } index = colChkCtx->colACCount; colChkCtx->colAC[colChkCtx->colACCount++] = col; } return index; } static ColChkResetFunc sOCResetFuncs[COLSHAPE_MAX] = { Collider_ResetJntSphOC, // COLSHAPE_JNTSPH Collider_ResetCylinderOC, // COLSHAPE_CYLINDER Collider_ResetTrisOC, // COLSHAPE_TRIS Collider_ResetQuadOC, // COLSHAPE_QUAD Collider_ResetSphereOC, // COLSHAPE_SPHERE }; TriNorm D_801EE6C8; TriNorm D_801EE700; /** * Sets collider as an OC (object collider) for the current frame, allowing it to detect other OCs. */ s32 CollisionCheck_SetOC(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* col) { s32 index; if (FrameAdvance_IsEnabled(play)) { return -1; } sOCResetFuncs[col->shape](play, col); if ((col->actor != NULL) && (col->actor->update == NULL)) { return -1; } if (colChkCtx->colOCCount >= ARRAY_COUNT(colChkCtx->colOC)) { return -1; } if (colChkCtx->sacFlags & SAC_ON) { return -1; } index = colChkCtx->colOCCount; colChkCtx->colOC[colChkCtx->colOCCount++] = col; return index; } /** * Sets collider as an OC (object collider) for the current frame, allowing it to detect other OCs. * If CollisionCheck_SAC is enabled, the collider will be inserted into the list at the specified index, otherwise it * will be inserted into the next slot. */ s32 CollisionCheck_SetOC_SAC(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* col, s32 index) { if (FrameAdvance_IsEnabled(play)) { return -1; } sOCResetFuncs[col->shape](play, col); if ((col->actor != NULL) && (col->actor->update == NULL)) { return -1; } if (colChkCtx->sacFlags & SAC_ON) { if (index >= colChkCtx->colOCCount) { return -1; } //! @bug should be colOC colChkCtx->colAT[index] = col; } else { if (colChkCtx->colOCCount >= ARRAY_COUNT(colChkCtx->colOC)) { return -1; } index = colChkCtx->colOCCount; colChkCtx->colOC[colChkCtx->colOCCount] = col; colChkCtx->colOCCount++; } return index; } /** * Sets a line as an OC collider for this frame. */ s32 CollisionCheck_SetOCLine(struct PlayState* play, CollisionCheckContext* colChkCtx, OcLine* line) { s32 index; if (FrameAdvance_IsEnabled(play)) { return -1; } Collider_ResetLineOC(play, line); if (colChkCtx->colLineCount >= ARRAY_COUNT(colChkCtx->colLine)) { return -1; } index = colChkCtx->colLineCount; colChkCtx->colLine[colChkCtx->colLineCount++] = line; return index; } /** * Skips AT elements that are off. */ s32 CollisionCheck_IsElementNotAT(ColliderElement* elem) { if (!(elem->atElemFlags & ATELEM_ON)) { return true; } return false; } /** * Skips AC elements that are off. */ s32 CollisionCheck_IsElementNotAC(ColliderElement* elem) { if (!(elem->acElemFlags & ACELEM_ON)) { return true; } return false; } /** * If the AT element has no dmgFlags in common with the AC element, no collision happens. */ s32 CollisionCheck_NoSharedFlags(ColliderElement* atElem, ColliderElement* acElem) { if (!(atElem->atDmgInfo.dmgFlags & acElem->acDmgInfo.dmgFlags)) { return true; } return false; } /** * Spawns no blood drops. * Used by collider types HIT1, HIT3, HIT5, METAL, NONE, WOOD, HARD, and TREE */ void CollisionCheck_NoBlood(struct PlayState* play, Collider* col, Vec3f* v) { } /** * Spawns blue blood drops. * Used by collider types HIT0 and HIT8. */ void CollisionCheck_BlueBlood(struct PlayState* play, Collider* col, Vec3f* v) { static EffectSparkInit D_801EEC00; s32 effectIndex; D_801EEC00.position.x = v->x; D_801EEC00.position.y = v->y; D_801EEC00.position.z = v->z; D_801EEC00.uDiv = 5; D_801EEC00.vDiv = 5; D_801EEC00.colorStart[0].r = 10; D_801EEC00.colorStart[0].g = 10; D_801EEC00.colorStart[0].b = 200; D_801EEC00.colorStart[0].a = 255; D_801EEC00.colorStart[1].r = 0; D_801EEC00.colorStart[1].g = 0; D_801EEC00.colorStart[1].b = 128; D_801EEC00.colorStart[1].a = 255; D_801EEC00.colorStart[2].r = 0; D_801EEC00.colorStart[2].g = 0; D_801EEC00.colorStart[2].b = 128; D_801EEC00.colorStart[2].a = 255; D_801EEC00.colorStart[3].r = 0; D_801EEC00.colorStart[3].g = 0; D_801EEC00.colorStart[3].b = 128; D_801EEC00.colorStart[3].a = 255; D_801EEC00.colorEnd[0].r = 0; D_801EEC00.colorEnd[0].g = 0; D_801EEC00.colorEnd[0].b = 32; D_801EEC00.colorEnd[0].a = 0; D_801EEC00.colorEnd[1].r = 0; D_801EEC00.colorEnd[1].g = 0; D_801EEC00.colorEnd[1].b = 32; D_801EEC00.colorEnd[1].a = 0; D_801EEC00.colorEnd[2].r = 0; D_801EEC00.colorEnd[2].g = 0; D_801EEC00.colorEnd[2].b = 64; D_801EEC00.colorEnd[2].a = 0; D_801EEC00.colorEnd[3].r = 0; D_801EEC00.colorEnd[3].g = 0; D_801EEC00.colorEnd[3].b = 64; D_801EEC00.colorEnd[3].a = 0; D_801EEC00.timer = 0; D_801EEC00.duration = 16; D_801EEC00.speed = 8.0f; D_801EEC00.gravity = -1.0f; Effect_Add(play, &effectIndex, EFFECT_SPARK, 0, 1, &D_801EEC00); } /** * Spawns green blood drops. * Used by collider types HIT2 and HIT6. No actor has type HIT2. */ void CollisionCheck_GreenBlood(struct PlayState* play, Collider* col, Vec3f* v) { static EffectSparkInit D_801EF0C8; s32 effectIndex; D_801EF0C8.position.x = v->x; D_801EF0C8.position.y = v->y; D_801EF0C8.position.z = v->z; D_801EF0C8.uDiv = 5; D_801EF0C8.vDiv = 5; D_801EF0C8.colorStart[0].r = 10; D_801EF0C8.colorStart[0].g = 200; D_801EF0C8.colorStart[0].b = 10; D_801EF0C8.colorStart[0].a = 255; D_801EF0C8.colorStart[1].r = 0; D_801EF0C8.colorStart[1].g = 128; D_801EF0C8.colorStart[1].b = 0; D_801EF0C8.colorStart[1].a = 255; D_801EF0C8.colorStart[2].r = 0; D_801EF0C8.colorStart[2].g = 128; D_801EF0C8.colorStart[2].b = 0; D_801EF0C8.colorStart[2].a = 255; D_801EF0C8.colorStart[3].r = 0; D_801EF0C8.colorStart[3].g = 128; D_801EF0C8.colorStart[3].b = 0; D_801EF0C8.colorStart[3].a = 255; D_801EF0C8.colorEnd[0].r = 0; D_801EF0C8.colorEnd[0].g = 32; D_801EF0C8.colorEnd[0].b = 0; D_801EF0C8.colorEnd[0].a = 0; D_801EF0C8.colorEnd[1].r = 0; D_801EF0C8.colorEnd[1].g = 32; D_801EF0C8.colorEnd[1].b = 0; D_801EF0C8.colorEnd[1].a = 0; D_801EF0C8.colorEnd[2].r = 0; D_801EF0C8.colorEnd[2].g = 64; D_801EF0C8.colorEnd[2].b = 0; D_801EF0C8.colorEnd[2].a = 0; D_801EF0C8.colorEnd[3].r = 0; D_801EF0C8.colorEnd[3].g = 64; D_801EF0C8.colorEnd[3].b = 0; D_801EF0C8.colorEnd[3].a = 0; D_801EF0C8.timer = 0; D_801EF0C8.duration = 16; D_801EF0C8.speed = 8.0f; D_801EF0C8.gravity = -1.0f; Effect_Add(play, &effectIndex, EFFECT_SPARK, 0, 1, &D_801EF0C8); } TriNorm D_801EF590; TriNorm D_801EF5C8; /** * Spawns a burst of water. * Used by collider type HIT4. */ void CollisionCheck_WaterBurst(struct PlayState* play, Collider* col, Vec3f* v) { EffectSsSibuki_SpawnBurst(play, v); CollisionCheck_SpawnWaterDroplets(play, v); } /** * Spawns red blood drops. * Used by collider type HIT7. */ void CollisionCheck_RedBlood(struct PlayState* play, Collider* col, Vec3f* v) { CollisionCheck_SpawnRedBlood(play, v); } /** * Spawns red blood drops. */ void CollisionCheck_RedBloodUnused(struct PlayState* play, Collider* col, Vec3f* v) { CollisionCheck_SpawnRedBlood(play, v); } /** * Plays sound effects and displays hitmarks for solid-type AC colliders (METAL, WOOD, HARD, and TREE) */ void CollisionCheck_HitSolid(struct PlayState* play, ColliderElement* elem, Collider* col, Vec3f* hitPos) { s32 flags = elem->atElemFlags & ATELEM_SFX_NONE; if ((flags == ATELEM_SFX_NORMAL) && (col->colMaterial != COL_MATERIAL_METAL)) { EffectSsHitmark_SpawnFixedScale(play, 0, hitPos); if (col->actor == NULL) { Audio_PlaySfx(NA_SE_IT_SHIELD_BOUND); } else { Audio_PlaySfx_AtPos(&col->actor->projectedPos, NA_SE_IT_SHIELD_BOUND); } } else if (flags == ATELEM_SFX_NORMAL) { EffectSsHitmark_SpawnFixedScale(play, 3, hitPos); if (col->actor == NULL) { CollisionCheck_SpawnShieldParticlesMetal(play, hitPos); } else { CollisionCheck_SpawnShieldParticlesMetalSound(play, hitPos, &col->actor->projectedPos); } } else if (flags == ATELEM_SFX_HARD) { EffectSsHitmark_SpawnFixedScale(play, 0, hitPos); if (col->actor == NULL) { Audio_PlaySfx(NA_SE_IT_SHIELD_BOUND); } else { Audio_PlaySfx_AtPos(&col->actor->projectedPos, NA_SE_IT_SHIELD_BOUND); } } else if (flags == ATELEM_SFX_WOOD) { EffectSsHitmark_SpawnFixedScale(play, 1, hitPos); if (col->actor == NULL) { Audio_PlaySfx(NA_SE_IT_REFLECTION_WOOD); } else { Audio_PlaySfx_AtPos(&col->actor->projectedPos, NA_SE_IT_REFLECTION_WOOD); } } } /** * Plays a hit sound effect for AT colliders attached to Player based on the AC element's elemMaterial. */ s32 CollisionCheck_SwordHitAudio(Collider* at, ColliderElement* acElem) { if ((at->actor != NULL) && (at->actor->category == ACTORCAT_PLAYER)) { if (acElem->elemMaterial == ELEM_MATERIAL_UNK0) { Audio_PlaySfx_AtPos(&at->actor->projectedPos, NA_SE_IT_SWORD_STRIKE); } else if (acElem->elemMaterial == ELEM_MATERIAL_UNK1) { Audio_PlaySfx_AtPos(&at->actor->projectedPos, NA_SE_IT_SWORD_STRIKE_HARD); } else if (acElem->elemMaterial == ELEM_MATERIAL_UNK2) { Audio_PlaySfx_AtPos(&at->actor->projectedPos, 0); } else if (acElem->elemMaterial == ELEM_MATERIAL_UNK3) { Audio_PlaySfx_AtPos(&at->actor->projectedPos, 0); } } return 1; } ColChkBloodFunc sBloodFuncs[] = { CollisionCheck_NoBlood, CollisionCheck_BlueBlood, CollisionCheck_GreenBlood, CollisionCheck_WaterBurst, CollisionCheck_RedBlood, CollisionCheck_RedBloodUnused, }; HitInfo sHitInfo[] = { { BLOOD_BLUE, HIT_WHITE }, { BLOOD_NONE, HIT_DUST }, { BLOOD_GREEN, HIT_DUST }, { BLOOD_NONE, HIT_WHITE }, { BLOOD_WATER, HIT_NONE }, { BLOOD_NONE, HIT_RED }, { BLOOD_GREEN, HIT_WHITE }, { BLOOD_RED, HIT_WHITE }, { BLOOD_BLUE, HIT_RED }, { BLOOD_NONE, HIT_SOLID }, { BLOOD_NONE, HIT_NONE }, { BLOOD_NONE, HIT_SOLID }, { BLOOD_NONE, HIT_SOLID }, { BLOOD_NONE, HIT_WOOD }, }; /** * Handles hitmarks, blood, and sound effects for each AC collision, determined by the AC collider's colMaterial */ void CollisionCheck_HitEffects(struct PlayState* play, Collider* at, ColliderElement* atElem, Collider* ac, ColliderElement* acElem, Vec3f* hitPos) { if (acElem->acElemFlags & ACELEM_NO_HITMARK) { return; } if (!(atElem->atElemFlags & ATELEM_AT_HITMARK) && (atElem->atElemFlags & ATELEM_DREW_HITMARK)) { return; } if (ac->actor != NULL) { sBloodFuncs[sHitInfo[ac->colMaterial].blood](play, ac, hitPos); } if (ac->actor != NULL) { if (sHitInfo[ac->colMaterial].effect == HIT_SOLID) { CollisionCheck_HitSolid(play, atElem, ac, hitPos); } else if (sHitInfo[ac->colMaterial].effect == HIT_WOOD) { if (at->actor == NULL) { CollisionCheck_SpawnShieldParticles(play, hitPos); Audio_PlaySfx(NA_SE_IT_REFLECTION_WOOD); } else { CollisionCheck_SpawnShieldParticlesWood(play, hitPos, &at->actor->projectedPos); } } else if (sHitInfo[ac->colMaterial].effect != HIT_NONE) { EffectSsHitmark_SpawnFixedScale(play, sHitInfo[ac->colMaterial].effect, hitPos); if (!(acElem->acElemFlags & ACELEM_NO_SWORD_SFX)) { CollisionCheck_SwordHitAudio(at, acElem); } } } else { EffectSsHitmark_SpawnFixedScale(play, 0, hitPos); if (ac->actor == NULL) { Audio_PlaySfx(NA_SE_IT_SHIELD_BOUND); } else { Audio_PlaySfx_AtPos(&ac->actor->projectedPos, NA_SE_IT_SHIELD_BOUND); } } } /** * Sets the flags to indicate an attack bounced off an AC_HARD collider. */ void CollisionCheck_SetBounce(Collider* at, Collider* ac) { at->atFlags |= AT_BOUNCED; ac->acFlags |= AC_BOUNCED; } /** * Performs the AC collision between the AT element and AC element that collided. */ s32 CollisionCheck_SetATvsAC(struct PlayState* play, Collider* atCol, ColliderElement* atElem, Vec3f* atPos, Collider* acCol, ColliderElement* acElem, Vec3f* acPos, Vec3f* hitPos) { f32 damage; u32 effect; if (CollisionCheck_GetElementATDamage(atCol, atElem, acCol, acElem) != 0) { damage = CollisionCheck_GetDamageAndEffectOnElementAC(atCol, atElem, acCol, acElem, &effect); if (damage < 1.0f) { if (effect == 0) { return 0; } } else if ((CollisionCheck_ApplyElementATDefense(damage, acElem) < 1.0f) && (effect == 0)) { return 0; } } if ((acCol->acFlags & AC_HARD) && (atCol->actor != NULL) && (acCol->actor != NULL)) { CollisionCheck_SetBounce(atCol, acCol); } if (!(acElem->acElemFlags & ACELEM_NO_AT_INFO)) { atCol->atFlags |= AT_HIT; atCol->at = acCol->actor; atElem->atHit = acCol; atElem->atElemFlags |= ATELEM_HIT; atElem->atHitElem = acElem; if (!(atElem->acElemFlags & ACELEM_HIT)) { atElem->acDmgInfo.hitPos.x = hitPos->x; atElem->acDmgInfo.hitPos.y = hitPos->y; atElem->acDmgInfo.hitPos.z = hitPos->z; } if (atCol->actor != NULL) { atCol->actor->colChkInfo.atHitEffect = acElem->acDmgInfo.effect; } } if (!(atElem->ocElemFlags & OCELEM_UNK2)) { acCol->acFlags |= AC_HIT; acCol->ac = atCol->actor; acElem->acHit = atCol; acElem->acHitElem = atElem; acElem->acElemFlags |= ACELEM_HIT; if (acCol->actor != NULL) { acCol->actor->colChkInfo.acHitEffect = atElem->atDmgInfo.effect; } acElem->acDmgInfo.hitPos.x = hitPos->x; acElem->acDmgInfo.hitPos.y = hitPos->y; acElem->acDmgInfo.hitPos.z = hitPos->z; } if (!(atElem->atElemFlags & ATELEM_AT_HITMARK) && (acCol->colMaterial != COL_MATERIAL_METAL) && (acCol->colMaterial != COL_MATERIAL_WOOD) && (acCol->colMaterial != COL_MATERIAL_HARD)) { acElem->acElemFlags |= ACELEM_DRAW_HITMARK; } else { CollisionCheck_HitEffects(play, atCol, atElem, acCol, acElem, hitPos); atElem->atElemFlags |= ATELEM_DREW_HITMARK; } return 1; } void CollisionCheck_TrisAvgPoint(ColliderTrisElement* trisElem, Vec3f* avg) { f32 oneThird = 1.0f / 3; avg->x = (trisElem->dim.vtx[0].x + trisElem->dim.vtx[1].x + trisElem->dim.vtx[2].x) * oneThird; avg->y = (trisElem->dim.vtx[0].y + trisElem->dim.vtx[1].y + trisElem->dim.vtx[2].y) * oneThird; avg->z = (trisElem->dim.vtx[0].z + trisElem->dim.vtx[1].z + trisElem->dim.vtx[2].z) * oneThird; } void CollisionCheck_QuadAvgPoint(ColliderQuad* quad, Vec3f* avg) { avg->x = (quad->dim.quad[0].x + (quad->dim.quad[1].x + (quad->dim.quad[3].x + quad->dim.quad[2].x))) / 4.0f; avg->y = (quad->dim.quad[0].y + (quad->dim.quad[1].y + (quad->dim.quad[3].y + quad->dim.quad[2].y))) / 4.0f; avg->z = (quad->dim.quad[0].z + (quad->dim.quad[1].z + (quad->dim.quad[3].z + quad->dim.quad[2].z))) / 4.0f; } TriNorm D_801EF600; TriNorm D_801EF638; /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_JntSphVsJntSph(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderJntSph* atJntSph = (ColliderJntSph*)atCol; ColliderJntSphElement* atJntSphElem; ColliderJntSph* acJntSph = (ColliderJntSph*)acCol; ColliderJntSphElement* acJntSphElem; f32 overlapSize; f32 centerDist; if ((atJntSph->count > 0) && (atJntSph->elements != NULL) && (acJntSph->count > 0) && (acJntSph->elements != NULL)) { for (atJntSphElem = atJntSph->elements; atJntSphElem < &atJntSph->elements[atJntSph->count]; atJntSphElem++) { if (CollisionCheck_IsElementNotAT(&atJntSphElem->base)) { continue; } for (acJntSphElem = acJntSph->elements; acJntSphElem < &acJntSph->elements[acJntSph->count]; acJntSphElem++) { if (CollisionCheck_IsElementNotAC(&acJntSphElem->base)) { continue; } if (CollisionCheck_NoSharedFlags(&atJntSphElem->base, &acJntSphElem->base)) { continue; } if (Math3D_SphVsSphOverlapCenterDist(&atJntSphElem->dim.worldSphere, &acJntSphElem->dim.worldSphere, &overlapSize, ¢erDist)) { f32 acToHit; Vec3f hitPos; Vec3f atPos; Vec3f acPos; Math_Vec3s_ToVec3f(&atPos, &atJntSphElem->dim.worldSphere.center); Math_Vec3s_ToVec3f(&acPos, &acJntSphElem->dim.worldSphere.center); if (!IS_ZERO(centerDist)) { acToHit = acJntSphElem->dim.worldSphere.radius / centerDist; hitPos.x = ((atPos.x - acPos.x) * acToHit) + acPos.x; hitPos.y = ((atPos.y - acPos.y) * acToHit) + acPos.y; hitPos.z = ((atPos.z - acPos.z) * acToHit) + acPos.z; } else { Math_Vec3f_Copy(&hitPos, &atPos); } CollisionCheck_SetATvsAC(play, &atJntSph->base, &atJntSphElem->base, &atPos, &acJntSph->base, &acJntSphElem->base, &acPos, &hitPos); if (!(acJntSph->base.ocFlags2 & OC2_FIRST_ONLY)) { return; } } } } } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_JntSphVsCyl(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderJntSph* atJntSph = (ColliderJntSph*)atCol; ColliderJntSphElement* atJntSphElem; ColliderCylinder* acCyl = (ColliderCylinder*)acCol; f32 overlapSize; f32 centerDist; if ((atJntSph->count > 0) && (atJntSph->elements != NULL) && (acCyl->dim.radius > 0) && (acCyl->dim.height > 0)) { if (CollisionCheck_IsElementNotAC(&acCyl->elem)) { return; } for (atJntSphElem = atJntSph->elements; atJntSphElem < &atJntSph->elements[atJntSph->count]; atJntSphElem++) { if (CollisionCheck_IsElementNotAT(&atJntSphElem->base)) { continue; } if (CollisionCheck_NoSharedFlags(&atJntSphElem->base, &acCyl->elem)) { continue; } if (Math3D_SphVsCylOverlapCenterDist(&atJntSphElem->dim.worldSphere, &acCyl->dim, &overlapSize, ¢erDist)) { Vec3f hitPos; Vec3f atPos; Vec3f acPos; f32 acToHit; Math_Vec3s_ToVec3f(&atPos, &atJntSphElem->dim.worldSphere.center); Math_Vec3s_ToVec3f(&acPos, &acCyl->dim.pos); if (!IS_ZERO(centerDist)) { acToHit = acCyl->dim.radius / centerDist; if (acToHit <= 1.0f) { hitPos.x = ((atPos.x - acPos.x) * acToHit) + acPos.x; hitPos.y = ((atPos.y - acPos.y) * acToHit) + acPos.y; hitPos.z = ((atPos.z - acPos.z) * acToHit) + acPos.z; } else { Math_Vec3f_Copy(&hitPos, &atPos); } } else { Math_Vec3f_Copy(&hitPos, &atPos); } CollisionCheck_SetATvsAC(play, &atJntSph->base, &atJntSphElem->base, &atPos, &acCyl->base, &acCyl->elem, &acPos, &hitPos); return; } } } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_JntSphVsTris(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderJntSph* atJntSph = (ColliderJntSph*)atCol; ColliderJntSphElement* atJntSphElem; ColliderTris* acTris = (ColliderTris*)acCol; ColliderTrisElement* acTrisElem; Vec3f hitPos; if ((atJntSph->count > 0) && (atJntSph->elements != NULL) && (acTris->count > 0) && (acTris->elements != NULL)) { for (atJntSphElem = atJntSph->elements; atJntSphElem < &atJntSph->elements[atJntSph->count]; atJntSphElem++) { if (CollisionCheck_IsElementNotAT(&atJntSphElem->base)) { continue; } for (acTrisElem = acTris->elements; acTrisElem < &acTris->elements[acTris->count]; acTrisElem++) { if (CollisionCheck_IsElementNotAC(&acTrisElem->base)) { continue; } if (CollisionCheck_NoSharedFlags(&atJntSphElem->base, &acTrisElem->base)) { continue; } if (Math3D_TriVsSphIntersect(&atJntSphElem->dim.worldSphere, &acTrisElem->dim, &hitPos)) { Vec3f atPos; Vec3f acPos; Math_Vec3s_ToVec3f(&atPos, &atJntSphElem->dim.worldSphere.center); CollisionCheck_TrisAvgPoint(acTrisElem, &acPos); CollisionCheck_SetATvsAC(play, &atJntSph->base, &atJntSphElem->base, &atPos, &acTris->base, &acTrisElem->base, &acPos, &hitPos); return; } } } } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_JntSphVsQuad(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderJntSph* atJntSph = (ColliderJntSph*)atCol; ColliderQuad* acQuad = (ColliderQuad*)acCol; Vec3f hitPos; ColliderJntSphElement* atJntSphElem; if ((atJntSph->count > 0) && (atJntSph->elements != NULL)) { if (CollisionCheck_IsElementNotAC(&acQuad->elem)) { return; } Math3D_TriNorm(&D_801EF590, &acQuad->dim.quad[2], &acQuad->dim.quad[3], &acQuad->dim.quad[1]); Math3D_TriNorm(&D_801EF5C8, &acQuad->dim.quad[1], &acQuad->dim.quad[0], &acQuad->dim.quad[2]); for (atJntSphElem = atJntSph->elements; atJntSphElem < &atJntSph->elements[atJntSph->count]; atJntSphElem++) { if (CollisionCheck_IsElementNotAT(&atJntSphElem->base)) { continue; } if (CollisionCheck_NoSharedFlags(&atJntSphElem->base, &acQuad->elem)) { continue; } if (Math3D_TriVsSphIntersect(&atJntSphElem->dim.worldSphere, &D_801EF590, &hitPos) || Math3D_TriVsSphIntersect(&atJntSphElem->dim.worldSphere, &D_801EF5C8, &hitPos)) { Vec3f atPos; Vec3f acPos; Math_Vec3s_ToVec3f(&atPos, &atJntSphElem->dim.worldSphere.center); CollisionCheck_QuadAvgPoint(acQuad, &acPos); CollisionCheck_SetATvsAC(play, &atJntSph->base, &atJntSphElem->base, &atPos, &acQuad->base, &acQuad->elem, &acPos, &hitPos); return; } } } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_JntSphVsSphere(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderJntSph* atJntSph = (ColliderJntSph*)atCol; ColliderSphere* acSph = (ColliderSphere*)acCol; ColliderJntSphElement* atJntSphElem; f32 overlapSize; f32 centerDist; if ((atJntSph->count > 0) && (atJntSph->elements != NULL)) { if (CollisionCheck_IsElementNotAC(&acSph->elem)) { return; } for (atJntSphElem = atJntSph->elements; atJntSphElem < &atJntSph->elements[atJntSph->count]; atJntSphElem++) { if (CollisionCheck_IsElementNotAT(&atJntSphElem->base)) { continue; } if (CollisionCheck_NoSharedFlags(&atJntSphElem->base, &acSph->elem)) { continue; } if (Math3D_SphVsSphOverlapCenterDist(&atJntSphElem->dim.worldSphere, &acSph->dim.worldSphere, &overlapSize, ¢erDist)) { f32 acToHit; Vec3f hitPos; Vec3f atPos; Vec3f acPos; Math_Vec3s_ToVec3f(&atPos, &atJntSphElem->dim.worldSphere.center); Math_Vec3s_ToVec3f(&acPos, &acSph->dim.worldSphere.center); if (!IS_ZERO(centerDist)) { acToHit = acSph->dim.worldSphere.radius / centerDist; hitPos.x = ((atPos.x - acPos.x) * acToHit) + acPos.x; hitPos.y = ((atPos.y - acPos.y) * acToHit) + acPos.y; hitPos.z = ((atPos.z - acPos.z) * acToHit) + acPos.z; } else { Math_Vec3f_Copy(&hitPos, &atPos); } CollisionCheck_SetATvsAC(play, &atJntSph->base, &atJntSphElem->base, &atPos, &acSph->base, &acSph->elem, &acPos, &hitPos); } } } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_CylVsJntSph(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderCylinder* atCyl = (ColliderCylinder*)atCol; ColliderJntSph* acJntSph = (ColliderJntSph*)acCol; f32 overlapSize; f32 centerDist; ColliderJntSphElement* acJntSphElem; if ((acJntSph->count > 0) && (acJntSph->elements != NULL) && (atCyl->dim.radius > 0) && (atCyl->dim.height > 0)) { if (CollisionCheck_IsElementNotAT(&atCyl->elem)) { return; } for (acJntSphElem = acJntSph->elements; acJntSphElem < &acJntSph->elements[acJntSph->count]; acJntSphElem++) { if (CollisionCheck_IsElementNotAC(&acJntSphElem->base)) { continue; } if (CollisionCheck_NoSharedFlags(&atCyl->elem, &acJntSphElem->base)) { continue; } if (Math3D_SphVsCylOverlapCenterDist(&acJntSphElem->dim.worldSphere, &atCyl->dim, &overlapSize, ¢erDist)) { Vec3f hitPos; Vec3f atPos; Vec3f acPos; f32 acToHit; Math_Vec3s_ToVec3f(&atPos, &atCyl->dim.pos); Math_Vec3s_ToVec3f(&acPos, &acJntSphElem->dim.worldSphere.center); if (!IS_ZERO(centerDist)) { acToHit = acJntSphElem->dim.worldSphere.radius / centerDist; if (acToHit <= 1.0f) { hitPos.x = ((atPos.x - acPos.x) * acToHit) + acPos.x; hitPos.y = ((atPos.y - acPos.y) * acToHit) + acPos.y; hitPos.z = ((atPos.z - acPos.z) * acToHit) + acPos.z; } else { Math_Vec3f_Copy(&hitPos, &atPos); } } else { Math_Vec3f_Copy(&hitPos, &atPos); } CollisionCheck_SetATvsAC(play, &atCyl->base, &atCyl->elem, &atPos, &acJntSph->base, &acJntSphElem->base, &acPos, &hitPos); if (!(acJntSph->base.ocFlags2 & OC2_FIRST_ONLY)) { break; } } } } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_CylVsCyl(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderCylinder* atCyl = (ColliderCylinder*)atCol; ColliderCylinder* acCyl = (ColliderCylinder*)acCol; f32 overlapSize; f32 centerDist; if ((atCyl->dim.radius > 0) && (atCyl->dim.height > 0) && (acCyl->dim.radius > 0) && (acCyl->dim.height > 0)) { if (CollisionCheck_IsElementNotAC(&acCyl->elem)) { return; } if (CollisionCheck_IsElementNotAT(&atCyl->elem)) { return; } if (CollisionCheck_NoSharedFlags(&atCyl->elem, &acCyl->elem)) { return; } if (Math3D_CylVsCylOverlapCenterDist(&atCyl->dim, &acCyl->dim, &overlapSize, ¢erDist)) { Vec3f hitPos; Vec3f atPos; Vec3f acPos; Math_Vec3s_ToVec3f(&atPos, &atCyl->dim.pos); Math_Vec3s_ToVec3f(&acPos, &acCyl->dim.pos); if (!IS_ZERO(centerDist)) { f32 acToHit = acCyl->dim.radius / centerDist; f32 height; hitPos.y = (f32)atCyl->dim.pos.y + atCyl->dim.yShift + atCyl->dim.height * 0.5f; height = (f32)acCyl->dim.pos.y + acCyl->dim.yShift; if (hitPos.y < height) { hitPos.y = height; } else { height += acCyl->dim.height; if (hitPos.y > height) { hitPos.y = height; } } hitPos.x = ((f32)atCyl->dim.pos.x - acCyl->dim.pos.x) * acToHit + acCyl->dim.pos.x; hitPos.z = ((f32)atCyl->dim.pos.z - acCyl->dim.pos.z) * acToHit + acCyl->dim.pos.z; } else { Math_Vec3s_ToVec3f(&hitPos, &acCyl->dim.pos); } CollisionCheck_SetATvsAC(play, &atCyl->base, &atCyl->elem, &atPos, &acCyl->base, &acCyl->elem, &acPos, &hitPos); } } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_CylVsTris(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderCylinder* atCyl = (ColliderCylinder*)atCol; ColliderTris* acTris = (ColliderTris*)acCol; ColliderTrisElement* acTrisElem; Vec3f hitPos; if ((atCyl->dim.radius > 0) && (atCyl->dim.height > 0) && (acTris->count > 0) && (acTris->elements != NULL)) { if (CollisionCheck_IsElementNotAT(&atCyl->elem)) { return; } for (acTrisElem = acTris->elements; acTrisElem < &acTris->elements[acTris->count]; acTrisElem++) { if (CollisionCheck_IsElementNotAC(&acTrisElem->base)) { continue; } if (CollisionCheck_NoSharedFlags(&atCyl->elem, &acTrisElem->base)) { continue; } if (Math3D_CylTriVsIntersect(&atCyl->dim, &acTrisElem->dim, &hitPos)) { Vec3f atPos; Vec3f acPos; Math_Vec3s_ToVec3f(&atPos, &atCyl->dim.pos); CollisionCheck_TrisAvgPoint(acTrisElem, &acPos); CollisionCheck_SetATvsAC(play, &atCyl->base, &atCyl->elem, &atPos, &acTris->base, &acTrisElem->base, &acPos, &hitPos); return; } } } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_CylVsQuad(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderCylinder* atCyl = (ColliderCylinder*)atCol; ColliderQuad* acQuad = (ColliderQuad*)acCol; if ((atCyl->dim.height > 0) && (atCyl->dim.radius > 0)) { if (CollisionCheck_IsElementNotAT(&atCyl->elem)) { return; } if (CollisionCheck_IsElementNotAC(&acQuad->elem)) { return; } if (CollisionCheck_NoSharedFlags(&atCyl->elem, &acQuad->elem)) { return; } Math3D_TriNorm(&D_801EF600, &acQuad->dim.quad[2], &acQuad->dim.quad[3], &acQuad->dim.quad[1]); Math3D_TriNorm(&D_801EF638, &acQuad->dim.quad[1], &acQuad->dim.quad[0], &acQuad->dim.quad[2]); if (Math3D_CylTriVsIntersect(&atCyl->dim, &D_801EF600, &D_801EDE00)) { Vec3f atPos; Vec3f acPos; Math_Vec3s_ToVec3f(&atPos, &atCyl->dim.pos); CollisionCheck_QuadAvgPoint(acQuad, &acPos); CollisionCheck_SetATvsAC(play, &atCyl->base, &atCyl->elem, &atPos, &acQuad->base, &acQuad->elem, &acPos, &D_801EDE00); } else if (Math3D_CylTriVsIntersect(&atCyl->dim, &D_801EF638, &D_801EDE00)) { Vec3f atPos; Vec3f acPos; Math_Vec3s_ToVec3f(&atPos, &atCyl->dim.pos); CollisionCheck_QuadAvgPoint(acQuad, &acPos); CollisionCheck_SetATvsAC(play, &atCyl->base, &atCyl->elem, &atPos, &acQuad->base, &acQuad->elem, &acPos, &D_801EDE00); } } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_CylVsSphere(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderCylinder* atCyl = (ColliderCylinder*)atCol; ColliderSphere* acSph = (ColliderSphere*)acCol; f32 overlapSize; f32 centerDist; if ((atCyl->dim.radius > 0) && (atCyl->dim.height > 0)) { if (CollisionCheck_IsElementNotAT(&atCyl->elem)) { return; } if (CollisionCheck_IsElementNotAC(&acSph->elem)) { return; } if (CollisionCheck_NoSharedFlags(&atCyl->elem, &acSph->elem)) { return; } if (Math3D_SphVsCylOverlapCenterDist(&acSph->dim.worldSphere, &atCyl->dim, &overlapSize, ¢erDist)) { Vec3f hitPos; Vec3f atPos; Vec3f acPos; f32 acToHit; Math_Vec3s_ToVec3f(&atPos, &atCyl->dim.pos); Math_Vec3s_ToVec3f(&acPos, &acSph->dim.worldSphere.center); if (!IS_ZERO(centerDist)) { acToHit = acSph->dim.worldSphere.radius / centerDist; if (acToHit <= 1.0f) { hitPos.x = ((atPos.x - acPos.x) * acToHit) + acPos.x; hitPos.y = ((atPos.y - acPos.y) * acToHit) + acPos.y; hitPos.z = ((atPos.z - acPos.z) * acToHit) + acPos.z; } else { Math_Vec3f_Copy(&hitPos, &atPos); } } else { Math_Vec3f_Copy(&hitPos, &atPos); } CollisionCheck_SetATvsAC(play, &atCyl->base, &atCyl->elem, &atPos, &acSph->base, &acSph->elem, &acPos, &hitPos); } } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_TrisVsJntSph(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderTris* atTris = (ColliderTris*)atCol; ColliderJntSphElement* acJntSphElem; ColliderJntSph* acJntSph = (ColliderJntSph*)acCol; ColliderTrisElement* atTrisElem; Vec3f hitPos; if ((acJntSph->count > 0) && (acJntSph->elements != NULL) && (atTris->count > 0) && (atTris->elements != NULL)) { for (acJntSphElem = acJntSph->elements; acJntSphElem < &acJntSph->elements[acJntSph->count]; acJntSphElem++) { if (CollisionCheck_IsElementNotAC(&acJntSphElem->base)) { continue; } for (atTrisElem = atTris->elements; atTrisElem < &atTris->elements[atTris->count]; atTrisElem++) { if (CollisionCheck_IsElementNotAT(&atTrisElem->base)) { continue; } if (CollisionCheck_NoSharedFlags(&atTrisElem->base, &acJntSphElem->base)) { continue; } if (Math3D_TriVsSphIntersect(&acJntSphElem->dim.worldSphere, &atTrisElem->dim, &hitPos)) { Vec3f atPos; Vec3f acPos; Math_Vec3s_ToVec3f(&acPos, &acJntSphElem->dim.worldSphere.center); CollisionCheck_TrisAvgPoint(atTrisElem, &atPos); CollisionCheck_SetATvsAC(play, &atTris->base, &atTrisElem->base, &atPos, &acJntSph->base, &acJntSphElem->base, &acPos, &hitPos); if (!(acJntSph->base.ocFlags2 & OC2_FIRST_ONLY)) { return; } } } } } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_TrisVsCyl(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderTris* atTris = (ColliderTris*)atCol; ColliderCylinder* acCyl = (ColliderCylinder*)acCol; ColliderTrisElement* atTrisElem; if ((acCyl->dim.radius > 0) && (acCyl->dim.height > 0) && (atTris->count > 0) && (atTris->elements != NULL)) { if (CollisionCheck_IsElementNotAC(&acCyl->elem)) { return; } for (atTrisElem = atTris->elements; atTrisElem < &atTris->elements[atTris->count]; atTrisElem++) { if (CollisionCheck_IsElementNotAT(&atTrisElem->base)) { continue; } if (CollisionCheck_NoSharedFlags(&atTrisElem->base, &acCyl->elem)) { continue; } if (Math3D_CylTriVsIntersect(&acCyl->dim, &atTrisElem->dim, &D_801EDE10)) { Vec3f atPos; Vec3f acPos; CollisionCheck_TrisAvgPoint(atTrisElem, &atPos); Math_Vec3s_ToVec3f(&acPos, &acCyl->dim.pos); CollisionCheck_SetATvsAC(play, &atTris->base, &atTrisElem->base, &atPos, &acCyl->base, &acCyl->elem, &acPos, &D_801EDE10); return; } } } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_TrisVsTris(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderTris* atTris = (ColliderTris*)atCol; ColliderTrisElement* atTrisElem; ColliderTris* acTris = (ColliderTris*)acCol; ColliderTrisElement* acTrisElem; if ((acTris->count > 0) && (acTris->elements != NULL) && (atTris->count > 0) && (atTris->elements != NULL)) { for (acTrisElem = acTris->elements; acTrisElem < &acTris->elements[acTris->count]; acTrisElem++) { if (CollisionCheck_IsElementNotAC(&acTrisElem->base)) { continue; } for (atTrisElem = atTris->elements; atTrisElem < &atTris->elements[atTris->count]; atTrisElem++) { if (CollisionCheck_IsElementNotAT(&atTrisElem->base)) { continue; } if (CollisionCheck_NoSharedFlags(&atTrisElem->base, &acTrisElem->base)) { continue; } if (Math3D_TriVsTriIntersect(&atTrisElem->dim, &acTrisElem->dim, &D_801EDE20)) { Vec3f atPos; Vec3f acPos; CollisionCheck_TrisAvgPoint(atTrisElem, &atPos); CollisionCheck_TrisAvgPoint(acTrisElem, &acPos); CollisionCheck_SetATvsAC(play, &atTris->base, &atTrisElem->base, &atPos, &acTris->base, &acTrisElem->base, &acPos, &D_801EDE20); return; } } } } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_TrisVsQuad(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderTris* atTris = (ColliderTris*)atCol; ColliderQuad* acQuad = (ColliderQuad*)acCol; ColliderTrisElement* atTrisElem; if ((atTris->count > 0) && (atTris->elements != NULL)) { if (CollisionCheck_IsElementNotAC(&acQuad->elem)) { return; } Math3D_TriNorm(&D_801EDE40, &acQuad->dim.quad[2], &acQuad->dim.quad[3], &acQuad->dim.quad[1]); Math3D_TriNorm(&D_801EDE78, &acQuad->dim.quad[1], &acQuad->dim.quad[0], &acQuad->dim.quad[2]); for (atTrisElem = atTris->elements; atTrisElem < &atTris->elements[atTris->count]; atTrisElem++) { if (CollisionCheck_IsElementNotAT(&atTrisElem->base)) { continue; } if (CollisionCheck_NoSharedFlags(&atTrisElem->base, &acQuad->elem)) { continue; } if (Math3D_TriVsTriIntersect(&D_801EDE40, &atTrisElem->dim, &D_801EDE30) || Math3D_TriVsTriIntersect(&D_801EDE78, &atTrisElem->dim, &D_801EDE30)) { Vec3f atPos; Vec3f acPos; CollisionCheck_TrisAvgPoint(atTrisElem, &atPos); CollisionCheck_QuadAvgPoint(acQuad, &acPos); CollisionCheck_SetATvsAC(play, &atTris->base, &atTrisElem->base, &atPos, &acQuad->base, &acQuad->elem, &acPos, &D_801EDE30); return; } } } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_TrisVsSphere(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderTris* atTris = (ColliderTris*)atCol; ColliderTrisElement* atTrisElem; ColliderSphere* acSph = (ColliderSphere*)acCol; Vec3f hitPos; if ((atTris->count > 0) && (atTris->elements != NULL)) { if (CollisionCheck_IsElementNotAC(&acSph->elem)) { return; } for (atTrisElem = atTris->elements; atTrisElem < &atTris->elements[atTris->count]; atTrisElem++) { if (CollisionCheck_IsElementNotAT(&atTrisElem->base)) { continue; } if (CollisionCheck_NoSharedFlags(&atTrisElem->base, &acSph->elem)) { continue; } if (Math3D_TriVsSphIntersect(&acSph->dim.worldSphere, &atTrisElem->dim, &hitPos)) { Vec3f atPos; Vec3f acPos; Math_Vec3s_ToVec3f(&acPos, &acSph->dim.worldSphere.center); CollisionCheck_TrisAvgPoint(atTrisElem, &atPos); CollisionCheck_SetATvsAC(play, &atTris->base, &atTrisElem->base, &atPos, &acSph->base, &acSph->elem, &acPos, &hitPos); } } } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_QuadVsJntSph(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderJntSphElement* acJntSphElem; Vec3f hitPos; ColliderQuad* atQuad = (ColliderQuad*)atCol; ColliderJntSph* acJntSph = (ColliderJntSph*)acCol; if ((acJntSph->count > 0) && (acJntSph->elements != NULL)) { if (CollisionCheck_IsElementNotAT(&atQuad->elem)) { return; } Math3D_TriNorm(&D_801EDEC8, &atQuad->dim.quad[2], &atQuad->dim.quad[3], &atQuad->dim.quad[1]); Math3D_TriNorm(&D_801EDF00, &atQuad->dim.quad[2], &atQuad->dim.quad[1], &atQuad->dim.quad[0]); for (acJntSphElem = acJntSph->elements; acJntSphElem < &acJntSph->elements[acJntSph->count]; acJntSphElem++) { if (CollisionCheck_IsElementNotAC(&acJntSphElem->base)) { continue; } if (CollisionCheck_NoSharedFlags(&atQuad->elem, &acJntSphElem->base)) { continue; } if (Math3D_TriVsSphIntersect(&acJntSphElem->dim.worldSphere, &D_801EDEC8, &hitPos) || Math3D_TriVsSphIntersect(&acJntSphElem->dim.worldSphere, &D_801EDF00, &hitPos)) { Vec3f atPos; Vec3f acPos; if (!Collider_QuadSetNearestAC(play, atQuad, &hitPos)) { continue; } Math_Vec3s_ToVec3f(&acPos, &acJntSphElem->dim.worldSphere.center); CollisionCheck_QuadAvgPoint(atQuad, &atPos); CollisionCheck_SetATvsAC(play, &atQuad->base, &atQuad->elem, &atPos, &acJntSph->base, &acJntSphElem->base, &acPos, &hitPos); if (!(acJntSph->base.ocFlags2 & OC2_FIRST_ONLY)) { break; } } } } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_QuadVsCyl(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderQuad* atQuad = (ColliderQuad*)atCol; ColliderCylinder* acCyl = (ColliderCylinder*)acCol; if ((acCyl->dim.height > 0) && (acCyl->dim.radius > 0)) { if (CollisionCheck_IsElementNotAC(&acCyl->elem)) { return; } if (CollisionCheck_IsElementNotAT(&atQuad->elem)) { return; } if (CollisionCheck_NoSharedFlags(&atQuad->elem, &acCyl->elem)) { return; } Math3D_TriNorm(&D_801EDF58, &atQuad->dim.quad[2], &atQuad->dim.quad[3], &atQuad->dim.quad[1]); Math3D_TriNorm(&D_801EDF90, &atQuad->dim.quad[2], &atQuad->dim.quad[1], &atQuad->dim.quad[0]); if (Math3D_CylTriVsIntersect(&acCyl->dim, &D_801EDF58, &D_801EDFE0)) { if (Collider_QuadSetNearestAC(play, atQuad, &D_801EDFE0)) { Vec3f atPos; Vec3f acPos; CollisionCheck_QuadAvgPoint(atQuad, &atPos); Math_Vec3s_ToVec3f(&acPos, &acCyl->dim.pos); CollisionCheck_SetATvsAC(play, &atQuad->base, &atQuad->elem, &atPos, &acCyl->base, &acCyl->elem, &acPos, &D_801EDFE0); return; } } if (Math3D_CylTriVsIntersect(&acCyl->dim, &D_801EDF90, &D_801EDFE0)) { if (Collider_QuadSetNearestAC(play, atQuad, &D_801EDFE0)) { Vec3f atPos; Vec3f acPos; CollisionCheck_QuadAvgPoint(atQuad, &atPos); Math_Vec3s_ToVec3f(&acPos, &acCyl->dim.pos); CollisionCheck_SetATvsAC(play, &atQuad->base, &atQuad->elem, &atPos, &acCyl->base, &acCyl->elem, &acPos, &D_801EDFE0); } } } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_QuadVsTris(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderQuad* atQuad = (ColliderQuad*)atCol; ColliderTris* acTris = (ColliderTris*)acCol; ColliderTrisElement* acTrisElem; if ((acTris->count > 0) && (acTris->elements != NULL)) { if (CollisionCheck_IsElementNotAT(&atQuad->elem)) { return; } Math3D_TriNorm(&D_801EE000, &atQuad->dim.quad[2], &atQuad->dim.quad[3], &atQuad->dim.quad[1]); Math3D_TriNorm(&D_801EE038, &atQuad->dim.quad[1], &atQuad->dim.quad[0], &atQuad->dim.quad[2]); for (acTrisElem = acTris->elements; acTrisElem < &acTris->elements[acTris->count]; acTrisElem++) { if (CollisionCheck_IsElementNotAC(&acTrisElem->base)) { continue; } if (CollisionCheck_NoSharedFlags(&atQuad->elem, &acTrisElem->base)) { continue; } if ((Math3D_TriVsTriIntersect(&D_801EE000, &acTrisElem->dim, &D_801EDFF0)) || (Math3D_TriVsTriIntersect(&D_801EE038, &acTrisElem->dim, &D_801EDFF0))) { if (Collider_QuadSetNearestAC(play, atQuad, &D_801EDFF0)) { Vec3f atPos; Vec3f acPos; CollisionCheck_TrisAvgPoint(acTrisElem, &acPos); CollisionCheck_QuadAvgPoint(atQuad, &atPos); CollisionCheck_SetATvsAC(play, &atQuad->base, &atQuad->elem, &atPos, &acTris->base, &acTrisElem->base, &acPos, &D_801EDFF0); return; } } } } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_QuadVsQuad(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderQuad* atQuad = (ColliderQuad*)atCol; ColliderQuad* acQuad = (ColliderQuad*)acCol; s32 i; s32 j; if (CollisionCheck_IsElementNotAT(&atQuad->elem)) { return; } if (CollisionCheck_IsElementNotAC(&acQuad->elem)) { return; } if (CollisionCheck_NoSharedFlags(&atQuad->elem, &acQuad->elem)) { return; } Math3D_TriNorm(&D_801EE0E8[0], &atQuad->dim.quad[2], &atQuad->dim.quad[3], &atQuad->dim.quad[1]); Math3D_TriNorm(&D_801EE0E8[1], &atQuad->dim.quad[2], &atQuad->dim.quad[1], &atQuad->dim.quad[0]); Math3D_TriNorm(&D_801EE070[0], &acQuad->dim.quad[2], &acQuad->dim.quad[3], &acQuad->dim.quad[1]); Math3D_TriNorm(&D_801EE070[1], &acQuad->dim.quad[2], &acQuad->dim.quad[1], &acQuad->dim.quad[0]); for (i = 0; i < 2; i++) { for (j = 0; j < 2; j++) { if (Math3D_TriVsTriIntersect(&D_801EE0E8[j], &D_801EE070[i], &D_801EE0D8) && Collider_QuadSetNearestAC(play, atQuad, &D_801EE0D8)) { Vec3f atPos; Vec3f acPos; CollisionCheck_QuadAvgPoint(atQuad, &atPos); CollisionCheck_QuadAvgPoint(acQuad, &acPos); CollisionCheck_SetATvsAC(play, &atQuad->base, &atQuad->elem, &atPos, &acQuad->base, &acQuad->elem, &acPos, &D_801EE0D8); return; } } } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_QuadVsSphere(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderQuad* atQuad = (ColliderQuad*)atCol; Vec3f hitPos; ColliderSphere* acSph = (ColliderSphere*)acCol; if (CollisionCheck_IsElementNotAT(&atQuad->elem)) { return; } if (CollisionCheck_IsElementNotAC(&acSph->elem) || CollisionCheck_NoSharedFlags(&atQuad->elem, &acSph->elem)) { return; } Math3D_TriNorm(&D_801EE150, &atQuad->dim.quad[2], &atQuad->dim.quad[3], &atQuad->dim.quad[1]); Math3D_TriNorm(&D_801EE188, &atQuad->dim.quad[2], &atQuad->dim.quad[1], &atQuad->dim.quad[0]); if ((Math3D_TriVsSphIntersect(&acSph->dim.worldSphere, &D_801EE150, &hitPos)) || (Math3D_TriVsSphIntersect(&acSph->dim.worldSphere, &D_801EE188, &hitPos))) { if (Collider_QuadSetNearestAC(play, atQuad, &hitPos)) { Vec3f atPos; Vec3f acPos; Math_Vec3s_ToVec3f(&acPos, &acSph->dim.worldSphere.center); CollisionCheck_QuadAvgPoint(atQuad, &atPos); CollisionCheck_SetATvsAC(play, &atQuad->base, &atQuad->elem, &atPos, &acSph->base, &acSph->elem, &acPos, &hitPos); } } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_SphereVsJntSph(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderSphere* atSph = (ColliderSphere*)atCol; ColliderJntSph* acJntSph = (ColliderJntSph*)acCol; ColliderJntSphElement* acJntSphElem; f32 overlapSize; f32 centerDist; if ((acJntSph->count > 0) && (acJntSph->elements != NULL)) { if (CollisionCheck_IsElementNotAT(&atSph->elem)) { return; } for (acJntSphElem = acJntSph->elements; acJntSphElem < &acJntSph->elements[acJntSph->count]; acJntSphElem++) { if (CollisionCheck_IsElementNotAC(&acJntSphElem->base)) { continue; } if (CollisionCheck_NoSharedFlags(&atSph->elem, &acJntSphElem->base)) { continue; } if (Math3D_SphVsSphOverlapCenterDist(&atSph->dim.worldSphere, &acJntSphElem->dim.worldSphere, &overlapSize, ¢erDist)) { f32 acToHit; Vec3f hitPos; Vec3f atPos; Vec3f acPos; Math_Vec3s_ToVec3f(&atPos, &atSph->dim.worldSphere.center); Math_Vec3s_ToVec3f(&acPos, &acJntSphElem->dim.worldSphere.center); if (!IS_ZERO(centerDist)) { acToHit = acJntSphElem->dim.worldSphere.radius / centerDist; hitPos.x = (atPos.x - acPos.x) * acToHit + acPos.x; hitPos.y = (atPos.y - acPos.y) * acToHit + acPos.y; hitPos.z = (atPos.z - acPos.z) * acToHit + acPos.z; } else { Math_Vec3f_Copy(&hitPos, &atPos); } CollisionCheck_SetATvsAC(play, &atSph->base, &atSph->elem, &atPos, &acJntSph->base, &acJntSphElem->base, &acPos, &hitPos); } } } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_SphereVsCylinder(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderCylinder* acCyl = (ColliderCylinder*)acCol; ColliderSphere* atSph = (ColliderSphere*)atCol; f32 overlapSize; f32 centerDist; if (CollisionCheck_IsElementNotAT(&atSph->elem)) { return; } if (CollisionCheck_IsElementNotAC(&acCyl->elem)) { return; } if (CollisionCheck_NoSharedFlags(&atSph->elem, &acCyl->elem)) { return; } if (Math3D_SphVsCylOverlapCenterDist(&atSph->dim.worldSphere, &acCyl->dim, &overlapSize, ¢erDist)) { Vec3f hitPos; Vec3f atPos; Vec3f acPos; Math_Vec3s_ToVec3f(&atPos, &atSph->dim.worldSphere.center); Math_Vec3s_ToVec3f(&acPos, &acCyl->dim.pos); if (!IS_ZERO(centerDist)) { f32 acToHit = acCyl->dim.radius / centerDist; if (acToHit <= 1.0f) { hitPos.x = (atPos.x - acPos.x) * acToHit + acPos.x; hitPos.y = (atPos.y - acPos.y) * acToHit + acPos.y; hitPos.z = (atPos.z - acPos.z) * acToHit + acPos.z; } else { Math_Vec3f_Copy(&hitPos, &atPos); } } else { Math_Vec3f_Copy(&hitPos, &atPos); } CollisionCheck_SetATvsAC(play, &atSph->base, &atSph->elem, &atPos, &acCyl->base, &acCyl->elem, &acPos, &hitPos); } if (atSph) {} } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_SphereVsTris(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderSphere* atSph = (ColliderSphere*)atCol; ColliderTris* acTris = (ColliderTris*)acCol; ColliderTrisElement* acTrisElem; Vec3f hitPos; if (CollisionCheck_IsElementNotAT(&atSph->elem)) { return; } for (acTrisElem = acTris->elements; acTrisElem < &acTris->elements[acTris->count]; acTrisElem++) { if (CollisionCheck_IsElementNotAC(&acTrisElem->base)) { continue; } if (CollisionCheck_NoSharedFlags(&atSph->elem, &acTrisElem->base)) { continue; } if (Math3D_TriVsSphIntersect(&atSph->dim.worldSphere, &acTrisElem->dim, &hitPos)) { Vec3f atPos; Vec3f acPos; Math_Vec3s_ToVec3f(&atPos, &atSph->dim.worldSphere.center); CollisionCheck_TrisAvgPoint(acTrisElem, &acPos); CollisionCheck_SetATvsAC(play, &atSph->base, &atSph->elem, &atPos, &acTris->base, &acTrisElem->base, &acPos, &hitPos); return; } } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_SphereVsQuad(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderSphere* atSph = (ColliderSphere*)atCol; Vec3f hitPos; ColliderQuad* acQuad = (ColliderQuad*)acCol; if (CollisionCheck_IsElementNotAT(&atSph->elem)) { return; } if (CollisionCheck_IsElementNotAC(&acQuad->elem) || CollisionCheck_NoSharedFlags(&atSph->elem, &acQuad->elem)) { return; } Math3D_TriNorm(&D_801EE6C8, &acQuad->dim.quad[2], &acQuad->dim.quad[3], &acQuad->dim.quad[1]); Math3D_TriNorm(&D_801EE700, &acQuad->dim.quad[1], &acQuad->dim.quad[0], &acQuad->dim.quad[2]); if (Math3D_TriVsSphIntersect(&atSph->dim.worldSphere, &D_801EE6C8, &hitPos) || Math3D_TriVsSphIntersect(&atSph->dim.worldSphere, &D_801EE700, &hitPos)) { Vec3f atPos; Vec3f acPos; Math_Vec3s_ToVec3f(&atPos, &atSph->dim.worldSphere.center); CollisionCheck_QuadAvgPoint(acQuad, &acPos); CollisionCheck_SetATvsAC(play, &atSph->base, &atSph->elem, &atPos, &acQuad->base, &acQuad->elem, &acPos, &hitPos); } } /** * AC overlap check. Calculates the center of each collider element and the point of contact. */ void CollisionCheck_AC_SphereVsSphere(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { ColliderSphere* atSph = (ColliderSphere*)atCol; ColliderSphere* acSph = (ColliderSphere*)acCol; f32 overlapSize; f32 centerDist; if (CollisionCheck_IsElementNotAT(&atSph->elem)) { return; } if (CollisionCheck_IsElementNotAC(&acSph->elem)) { return; } if (CollisionCheck_NoSharedFlags(&atSph->elem, &acSph->elem)) { return; } if (Math3D_SphVsSphOverlapCenterDist(&atSph->dim.worldSphere, &acSph->dim.worldSphere, &overlapSize, ¢erDist)) { f32 acToHit; Vec3f hitPos; Vec3f atPos; Vec3f acPos; Math_Vec3s_ToVec3f(&atPos, &atSph->dim.worldSphere.center); Math_Vec3s_ToVec3f(&acPos, &acSph->dim.worldSphere.center); if (!IS_ZERO(centerDist)) { acToHit = acSph->dim.worldSphere.radius / centerDist; hitPos.x = (atPos.x - acPos.x) * acToHit + acPos.x; hitPos.y = (atPos.y - acPos.y) * acToHit + acPos.y; hitPos.z = (atPos.z - acPos.z) * acToHit + acPos.z; } else { Math_Vec3f_Copy(&hitPos, &atPos); } CollisionCheck_SetATvsAC(play, &atSph->base, &atSph->elem, &atPos, &acSph->base, &acSph->elem, &acPos, &hitPos); } } /** * Sets a ColliderJntSph's hit effects */ void CollisionCheck_SetJntSphHitFX(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* col) { ColliderJntSph* jntSph = (ColliderJntSph*)col; ColliderJntSphElement* jntSphElem; for (jntSphElem = jntSph->elements; jntSphElem < &jntSph->elements[jntSph->count]; jntSphElem++) { if ((jntSphElem->base.acElemFlags & ACELEM_DRAW_HITMARK) && (jntSphElem->base.acHitElem != NULL) && !(jntSphElem->base.acHitElem->atElemFlags & ATELEM_DREW_HITMARK)) { Vec3f hitPos; Math_Vec3s_ToVec3f(&hitPos, &jntSphElem->base.acDmgInfo.hitPos); CollisionCheck_HitEffects(play, jntSphElem->base.acHit, jntSphElem->base.acHitElem, &jntSph->base, &jntSphElem->base, &hitPos); jntSphElem->base.acHitElem->atElemFlags |= ATELEM_DREW_HITMARK; return; } } } /** * Sets a ColliderCylinder's hit effects */ void CollisionCheck_SetCylHitFX(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* col) { ColliderCylinder* cyl = (ColliderCylinder*)col; if ((cyl->elem.acElemFlags & ACELEM_DRAW_HITMARK) && (cyl->elem.acHitElem != NULL) && !(cyl->elem.acHitElem->atElemFlags & ATELEM_DREW_HITMARK)) { Vec3f hitPos; Math_Vec3s_ToVec3f(&hitPos, &cyl->elem.acDmgInfo.hitPos); CollisionCheck_HitEffects(play, cyl->elem.acHit, cyl->elem.acHitElem, &cyl->base, &cyl->elem, &hitPos); cyl->elem.acHitElem->atElemFlags |= ATELEM_DREW_HITMARK; } } /** * Sets a ColliderTris's hit effects */ void CollisionCheck_SetTrisHitFX(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* col) { ColliderTris* tris = (ColliderTris*)col; ColliderTrisElement* trisElem; for (trisElem = tris->elements; trisElem < &tris->elements[tris->count]; trisElem++) { if ((trisElem->base.acElemFlags & ACELEM_DRAW_HITMARK) && (trisElem->base.acHitElem != NULL) && !(trisElem->base.acHitElem->atElemFlags & ATELEM_DREW_HITMARK)) { Vec3f hitPos; Math_Vec3s_ToVec3f(&hitPos, &trisElem->base.acDmgInfo.hitPos); CollisionCheck_HitEffects(play, trisElem->base.acHit, trisElem->base.acHitElem, &tris->base, &trisElem->base, &hitPos); trisElem->base.acHitElem->atElemFlags |= ATELEM_DREW_HITMARK; return; } } } /** * Sets a ColliderQuad's hit effects */ void CollisionCheck_SetQuadHitFX(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* col) { ColliderQuad* quad = (ColliderQuad*)col; if ((quad->elem.acElemFlags & ACELEM_DRAW_HITMARK) && (quad->elem.acHitElem != NULL) && !(quad->elem.acHitElem->atElemFlags & ATELEM_DREW_HITMARK)) { Vec3f hitPos; Math_Vec3s_ToVec3f(&hitPos, &quad->elem.acDmgInfo.hitPos); CollisionCheck_HitEffects(play, quad->elem.acHit, quad->elem.acHitElem, &quad->base, &quad->elem, &hitPos); quad->elem.acHitElem->atElemFlags |= ATELEM_DREW_HITMARK; } } /** * Sets a ColliderSphere's hit effects */ void CollisionCheck_SetSphereHitFX(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* col) { ColliderSphere* sph = (ColliderSphere*)col; if ((sph->elem.acElemFlags & ACELEM_DRAW_HITMARK) && (sph->elem.acHitElem != NULL) && !(sph->elem.acHitElem->atElemFlags & ATELEM_DREW_HITMARK)) { Vec3f hitPos; Math_Vec3s_ToVec3f(&hitPos, &sph->elem.acDmgInfo.hitPos); CollisionCheck_HitEffects(play, sph->elem.acHit, sph->elem.acHitElem, &sph->base, &sph->elem, &hitPos); sph->elem.acHitElem->atElemFlags |= ATELEM_DREW_HITMARK; } } ColChkApplyFunc sColChkApplyFuncs[COLSHAPE_MAX] = { CollisionCheck_SetJntSphHitFX, // COLSHAPE_JNTSPH CollisionCheck_SetCylHitFX, // COLSHAPE_CYLINDER CollisionCheck_SetTrisHitFX, // COLSHAPE_TRIS CollisionCheck_SetQuadHitFX, // COLSHAPE_QUAD CollisionCheck_SetSphereHitFX, // COLSHAPE_SPHERE }; /** * Handles hit effects for each AC collider that had an AC collision. Spawns hitmarks and plays sound effects. */ void CollisionCheck_SetHitEffects(struct PlayState* play, CollisionCheckContext* colChkCtx) { Collider** acColP; for (acColP = &colChkCtx->colAC[0]; acColP < &colChkCtx->colAC[colChkCtx->colACCount]; acColP++) { Collider* acCol = *acColP; if ((acCol != NULL) && (acCol->acFlags & AC_ON)) { if ((acCol->actor != NULL) && (acCol->actor->update == NULL)) { continue; } sColChkApplyFuncs[acCol->shape](play, colChkCtx, acCol); } } } ColChkVsFunc sACVsFuncs[COLSHAPE_MAX][COLSHAPE_MAX] = { // COLSHAPE_JNTSPH { CollisionCheck_AC_JntSphVsJntSph, // COLSHAPE_JNTSPH CollisionCheck_AC_JntSphVsCyl, // COLSHAPE_CYLINDER CollisionCheck_AC_JntSphVsTris, // COLSHAPE_TRIS CollisionCheck_AC_JntSphVsQuad, // COLSHAPE_QUAD CollisionCheck_AC_JntSphVsSphere // COLSHAPE_SPHERE }, // COLSHAPE_CYLINDER { CollisionCheck_AC_CylVsJntSph, // COLSHAPE_JNTSPH CollisionCheck_AC_CylVsCyl, // COLSHAPE_CYLINDER CollisionCheck_AC_CylVsTris, // COLSHAPE_TRIS CollisionCheck_AC_CylVsQuad, // COLSHAPE_QUAD CollisionCheck_AC_CylVsSphere // COLSHAPE_SPHERE }, // COLSHAPE_TRIS { CollisionCheck_AC_TrisVsJntSph, // COLSHAPE_JNTSPH CollisionCheck_AC_TrisVsCyl, // COLSHAPE_CYLINDER CollisionCheck_AC_TrisVsTris, // COLSHAPE_TRIS CollisionCheck_AC_TrisVsQuad, // COLSHAPE_QUAD CollisionCheck_AC_TrisVsSphere // COLSHAPE_SPHERE }, // COLSHAPE_QUAD { CollisionCheck_AC_QuadVsJntSph, // COLSHAPE_JNTSPH CollisionCheck_AC_QuadVsCyl, // COLSHAPE_CYLINDER CollisionCheck_AC_QuadVsTris, // COLSHAPE_TRIS CollisionCheck_AC_QuadVsQuad, // COLSHAPE_QUAD CollisionCheck_AC_QuadVsSphere // COLSHAPE_SPHERE }, // COLSHAPE_SPHERE { CollisionCheck_AC_SphereVsJntSph, // COLSHAPE_JNTSPH CollisionCheck_AC_SphereVsCylinder, // COLSHAPE_CYLINDER CollisionCheck_AC_SphereVsTris, // COLSHAPE_TRIS CollisionCheck_AC_SphereVsQuad, // COLSHAPE_QUAD CollisionCheck_AC_SphereVsSphere // COLSHAPE_SPHERE }, }; /** * Iterates through all AC colliders, performing AC collisions with the AT collider. */ void CollisionCheck_AC(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol) { Collider** acColP; for (acColP = &colChkCtx->colAC[0]; acColP < &colChkCtx->colAC[colChkCtx->colACCount]; acColP++) { Collider* acCol = *acColP; if ((acCol != NULL) && (acCol->acFlags & AC_ON)) { if ((acCol->actor != NULL) && (acCol->actor->update == NULL)) { continue; } if ((acCol->acFlags & atCol->atFlags & AC_TYPE_ALL) && (atCol != acCol)) { if (!(atCol->atFlags & AT_SELF) && (atCol->actor != NULL) && (acCol->actor == atCol->actor)) { continue; } sACVsFuncs[atCol->shape][acCol->shape](play, colChkCtx, atCol, acCol); } } } } /** * Iterates through all AT colliders, testing them for AC collisions with each AC collider, setting the info regarding * the collision for each AC and AT collider that collided. Then spawns hitmarks and plays sound effects for each * successful collision. To collide, an AT collider must share a type (AC_TYPE_PLAYER, AC_TYPE_ENEMY, or AC_TYPE_OTHER) * with the AC collider and the atDmgInfo and acDmgInfo elements that overlapped must share a dmgFlag. */ void CollisionCheck_AT(struct PlayState* play, CollisionCheckContext* colChkCtx) { Collider** acColP; if ((colChkCtx->colATCount == 0) || (colChkCtx->colACCount == 0)) { return; } for (acColP = &colChkCtx->colAT[0]; acColP < &colChkCtx->colAT[colChkCtx->colATCount]; acColP++) { Collider* acCol = *acColP; if ((acCol != NULL) && (acCol->atFlags & AT_ON)) { if ((acCol->actor != NULL) && (acCol->actor->update == NULL)) { continue; } CollisionCheck_AC(play, colChkCtx, acCol); } } CollisionCheck_SetHitEffects(play, colChkCtx); } /** * Get mass type. Immobile colliders cannot be pushed, while heavy colliders can only be pushed by heavy and immobile * colliders. */ s32 CollisionCheck_GetMassType(u8 mass) { if (mass == MASS_IMMOVABLE) { return MASSTYPE_IMMOVABLE; } if (mass == MASS_HEAVY) { return MASSTYPE_HEAVY; } return MASSTYPE_NORMAL; } /** * Sets OC collision flags for OC collider overlaps. If both colliders are attached to actors and can push, * also performs an elastic collision where both colliders are moved apart in proportion to their masses. */ void CollisionCheck_SetOCvsOC(struct PlayState* play, Collider* leftCol, ColliderElement* leftElem, Vec3f* leftPos, Collider* rightCol, ColliderElement* rightElem, Vec3f* rightPos, f32 overlapSize) { f32 pad; f32 leftDispRatio; f32 rightDispRatio; f32 xzDist; f32 leftMass; f32 rightMass; f32 totalMass; f32 inverseTotalMass; f32 xDelta; f32 zDelta; Actor* leftActor = leftCol->actor; Actor* rightActor = rightCol->actor; s32 leftMassType; s32 rightMassType; leftCol->ocFlags1 |= OC1_HIT; leftCol->oc = rightActor; leftElem->ocElemFlags |= OCELEM_HIT; if (rightCol->ocFlags2 & OC2_TYPE_PLAYER) { leftCol->ocFlags2 |= OC2_HIT_PLAYER; } rightCol->ocFlags1 |= OC1_HIT; rightCol->oc = leftActor; rightElem->ocElemFlags |= OCELEM_HIT; if (leftCol->ocFlags2 & OC2_TYPE_PLAYER) { rightCol->ocFlags2 |= OC2_HIT_PLAYER; } if ((leftActor == NULL) || (rightActor == NULL) || (leftCol->ocFlags1 & OC1_NO_PUSH) || (rightCol->ocFlags1 & OC1_NO_PUSH)) { return; } rightMassType = CollisionCheck_GetMassType(leftActor->colChkInfo.mass); leftMassType = CollisionCheck_GetMassType(rightActor->colChkInfo.mass); leftMass = leftActor->colChkInfo.mass; rightMass = rightActor->colChkInfo.mass; totalMass = leftMass + rightMass; if (IS_ZERO(totalMass)) { leftMass = rightMass = 1.0f; totalMass = 2.0f; } xDelta = rightPos->x - leftPos->x; zDelta = rightPos->z - leftPos->z; xzDist = sqrtf(SQ(xDelta) + SQ(zDelta)); if (rightMassType == MASSTYPE_IMMOVABLE) { if (leftMassType == MASSTYPE_IMMOVABLE) { return; } else { leftDispRatio = 0; rightDispRatio = 1; } } else if (rightMassType == MASSTYPE_HEAVY) { if (leftMassType == MASSTYPE_IMMOVABLE) { leftDispRatio = 1; rightDispRatio = 0; } else if (leftMassType == MASSTYPE_HEAVY) { leftDispRatio = 0.5f; rightDispRatio = 0.5f; } else { leftDispRatio = 0; rightDispRatio = 1; } } else { if (leftMassType == MASSTYPE_NORMAL) { inverseTotalMass = 1.0f / totalMass; leftDispRatio = rightMass * inverseTotalMass; rightDispRatio = leftMass * inverseTotalMass; } else { leftDispRatio = 1; rightDispRatio = 0; } } if (!IS_ZERO(xzDist)) { xDelta *= overlapSize / xzDist; zDelta *= overlapSize / xzDist; leftActor->colChkInfo.displacement.x += -xDelta * leftDispRatio; leftActor->colChkInfo.displacement.z += -zDelta * leftDispRatio; rightActor->colChkInfo.displacement.x += xDelta * rightDispRatio; rightActor->colChkInfo.displacement.z += zDelta * rightDispRatio; } else if (overlapSize != 0.0f) { leftActor->colChkInfo.displacement.x += -overlapSize * leftDispRatio; rightActor->colChkInfo.displacement.x += overlapSize * rightDispRatio; } else { leftActor->colChkInfo.displacement.x += -leftDispRatio; rightActor->colChkInfo.displacement.x += rightDispRatio; } } /** * OC overlap check for two JntSphs */ void CollisionCheck_OC_JntSphVsJntSph(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* leftCol, Collider* rightCol) { ColliderJntSph* leftJntSph = (ColliderJntSph*)leftCol; ColliderJntSph* rightJntSph = (ColliderJntSph*)rightCol; ColliderJntSphElement* leftJntSphElem; ColliderJntSphElement* rightJntSphElem; f32 overlapSize; if ((leftJntSph->count > 0) && (leftJntSph->elements != NULL) && (rightJntSph->count > 0) && (rightJntSph->elements != NULL) && (leftJntSph->base.ocFlags1 & OCELEM_ON) && (rightJntSph->base.ocFlags1 & OCELEM_ON)) { for (leftJntSphElem = leftJntSph->elements; leftJntSphElem < &leftJntSph->elements[leftJntSph->count]; leftJntSphElem++) { if (!(leftJntSphElem->base.ocElemFlags & OCELEM_ON)) { continue; } for (rightJntSphElem = rightJntSph->elements; rightJntSphElem < &rightJntSph->elements[rightJntSph->count]; rightJntSphElem++) { if (!(rightJntSphElem->base.ocElemFlags & OCELEM_ON)) { continue; } if (Math3D_SphVsSphOverlap(&leftJntSphElem->dim.worldSphere, &rightJntSphElem->dim.worldSphere, &overlapSize)) { Vec3f leftPos; Vec3f rightPos; Math_Vec3s_ToVec3f(&leftPos, &leftJntSphElem->dim.worldSphere.center); Math_Vec3s_ToVec3f(&rightPos, &rightJntSphElem->dim.worldSphere.center); CollisionCheck_SetOCvsOC(play, &leftJntSph->base, &leftJntSphElem->base, &leftPos, &rightJntSph->base, &rightJntSphElem->base, &rightPos, overlapSize); } } } } } /** * OC overlap check for a JntSph and Cylinder */ void CollisionCheck_OC_JntSphVsCyl(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* leftCol, Collider* rightCol) { ColliderJntSph* leftJntSph = (ColliderJntSph*)leftCol; ColliderCylinder* rightCyl = (ColliderCylinder*)rightCol; ColliderJntSphElement* leftJntSphElem; f32 overlapSize; if ((leftJntSph->count > 0) && (leftJntSph->elements != NULL) && (leftJntSph->base.ocFlags1 & OCELEM_ON) && (rightCyl->base.ocFlags1 & OCELEM_ON) && (rightCyl->elem.ocElemFlags & OCELEM_ON)) { for (leftJntSphElem = leftJntSph->elements; leftJntSphElem < &leftJntSph->elements[leftJntSph->count]; leftJntSphElem++) { if (!(leftJntSphElem->base.ocElemFlags & OCELEM_ON)) { continue; } if (Math3D_SphVsCylOverlap(&leftJntSphElem->dim.worldSphere, &rightCyl->dim, &overlapSize)) { Vec3f leftPos; Vec3f rightPos; Math_Vec3s_ToVec3f(&leftPos, &leftJntSphElem->dim.worldSphere.center); Math_Vec3s_ToVec3f(&rightPos, &rightCyl->dim.pos); CollisionCheck_SetOCvsOC(play, &leftJntSph->base, &leftJntSphElem->base, &leftPos, &rightCyl->base, &rightCyl->elem, &rightPos, overlapSize); } } } } /** * OC overlap check for a JntSph and Sphere */ void CollisionCheck_OC_JntSphVsSphere(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* leftCol, Collider* rightCol) { ColliderJntSph* leftJntSph = (ColliderJntSph*)leftCol; ColliderSphere* rightSph = (ColliderSphere*)rightCol; ColliderJntSphElement* leftJntSphElem; f32 overlapSize; if ((leftJntSph->count > 0) && (leftJntSph->elements != NULL) && (leftJntSph->base.ocFlags1 & OCELEM_ON) && (rightSph->base.ocFlags1 & OCELEM_ON) && (rightSph->elem.ocElemFlags & OCELEM_ON)) { for (leftJntSphElem = leftJntSph->elements; leftJntSphElem < &leftJntSph->elements[leftJntSph->count]; leftJntSphElem++) { if (!(leftJntSphElem->base.ocElemFlags & OCELEM_ON)) { continue; } if (Math3D_SphVsSphOverlap(&leftJntSphElem->dim.worldSphere, &rightSph->dim.worldSphere, &overlapSize)) { Vec3f leftPos; Vec3f rightPos; Math_Vec3s_ToVec3f(&leftPos, &leftJntSphElem->dim.worldSphere.center); Math_Vec3s_ToVec3f(&rightPos, &rightSph->dim.worldSphere.center); CollisionCheck_SetOCvsOC(play, &leftJntSph->base, &leftJntSphElem->base, &leftPos, &rightSph->base, &rightSph->elem, &rightPos, overlapSize); } } } } /** * OC overlap check for a Cylinder and JntSph */ void CollisionCheck_OC_CylVsJntSph(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* leftCol, Collider* rightCol) { CollisionCheck_OC_JntSphVsCyl(play, colChkCtx, rightCol, leftCol); } /** * OC overlap check for two Cylinders */ void CollisionCheck_OC_CylVsCyl(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* leftCol, Collider* rightCol) { ColliderCylinder* leftCyl = (ColliderCylinder*)leftCol; ColliderCylinder* rightCyl = (ColliderCylinder*)rightCol; f32 overlapSize; if ((leftCyl->base.ocFlags1 & OCELEM_ON) && (rightCyl->base.ocFlags1 & OCELEM_ON) && (leftCyl->elem.ocElemFlags & OCELEM_ON) && (rightCyl->elem.ocElemFlags & OCELEM_ON)) { if (Math3D_CylVsCylOverlap(&leftCyl->dim, &rightCyl->dim, &overlapSize)) { Vec3f leftPos; Vec3f rightPos; Math_Vec3s_ToVec3f(&leftPos, &leftCyl->dim.pos); Math_Vec3s_ToVec3f(&rightPos, &rightCyl->dim.pos); CollisionCheck_SetOCvsOC(play, &leftCyl->base, &leftCyl->elem, &leftPos, &rightCyl->base, &rightCyl->elem, &rightPos, overlapSize); } } } /** * OC overlap check for a Cylinder and Sphere */ void CollisionCheck_OC_CylVsSphere(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* leftCol, Collider* rightCol) { ColliderCylinder* leftCyl = (ColliderCylinder*)leftCol; ColliderSphere* rightSph = (ColliderSphere*)rightCol; f32 overlapSize; if ((leftCyl->base.ocFlags1 & OCELEM_ON) && (leftCyl->elem.ocElemFlags & OCELEM_ON) && (rightSph->base.ocFlags1 & OCELEM_ON) && (rightSph->elem.ocElemFlags & OCELEM_ON)) { if (Math3D_SphVsCylOverlap(&rightSph->dim.worldSphere, &leftCyl->dim, &overlapSize)) { Vec3f leftPos; Vec3f rightPos; Math_Vec3s_ToVec3f(&leftPos, &leftCyl->dim.pos); Math_Vec3s_ToVec3f(&rightPos, &rightSph->dim.worldSphere.center); CollisionCheck_SetOCvsOC(play, &leftCyl->base, &leftCyl->elem, &leftPos, &rightSph->base, &rightSph->elem, &rightPos, overlapSize); } } } /** * OC overlap check for a Sphere and JntSph */ void CollisionCheck_OC_SphereVsJntSph(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* leftCol, Collider* rightCol) { CollisionCheck_OC_JntSphVsSphere(play, colChkCtx, rightCol, leftCol); } /** * OC overlap check for a Sphere and Cylinder */ void CollisionCheck_OC_SphereVsCyl(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* leftCol, Collider* rightCol) { CollisionCheck_OC_CylVsSphere(play, colChkCtx, rightCol, leftCol); } /** * OC overlap check for two Spheres */ void CollisionCheck_OC_SphereVsSphere(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* leftCol, Collider* rightCol) { ColliderSphere* leftSph = (ColliderSphere*)leftCol; ColliderSphere* rightSph = (ColliderSphere*)rightCol; f32 overlapSize; if ((leftSph->base.ocFlags1 & OCELEM_ON) && (leftSph->elem.ocElemFlags & OCELEM_ON) && (rightSph->base.ocFlags1 & OCELEM_ON) && (rightSph->elem.ocElemFlags & OCELEM_ON)) { if (Math3D_SphVsSphOverlap(&leftSph->dim.worldSphere, &rightSph->dim.worldSphere, &overlapSize)) { Vec3f leftPos; Vec3f rightPos; Math_Vec3s_ToVec3f(&leftPos, &leftSph->dim.worldSphere.center); Math_Vec3s_ToVec3f(&rightPos, &rightSph->dim.worldSphere.center); CollisionCheck_SetOCvsOC(play, &leftSph->base, &leftSph->elem, &leftPos, &rightSph->base, &rightSph->elem, &rightPos, overlapSize); } } } /** * Skip any OC colliders that are off */ s32 CollisionCheck_SkipOC(Collider* col) { if (!(col->ocFlags1 & OCELEM_ON)) { return 1; } return 0; } /** * Checks for OC compatibility. There are three conditions: * First, each collider must have an OC flag corresponding to the other's OC type. * Second, OC2_UNK1 and OC2_UNK2 can't collide with each other (has something to do with horses?) * Third, the colliders can't collide if they belong to the same actor */ s32 CollisionCheck_Incompatible(Collider* leftCol, Collider* rightCol) { if (!(leftCol->ocFlags1 & rightCol->ocFlags2 & OC1_TYPE_ALL) || !(leftCol->ocFlags2 & rightCol->ocFlags1 & OC1_TYPE_ALL) || ((leftCol->ocFlags2 & OC2_UNK1) && (rightCol->ocFlags2 & OC2_UNK2)) || ((rightCol->ocFlags2 & OC2_UNK1) && (leftCol->ocFlags2 & OC2_UNK2))) { return 1; } if (leftCol->actor == rightCol->actor) { return 1; } return 0; } ColChkVsFunc sOCVsFuncs[COLSHAPE_MAX][COLSHAPE_MAX] = { // COLSHAPE_JNTSPH { CollisionCheck_OC_JntSphVsJntSph, // COLSHAPE_JNTSPH CollisionCheck_OC_JntSphVsCyl, // COLSHAPE_CYLINDER NULL, // COLSHAPE_TRIS NULL, // COLSHAPE_QUAD CollisionCheck_OC_JntSphVsSphere // COLSHAPE_SPHERE }, // COLSHAPE_CYLINDER { CollisionCheck_OC_CylVsJntSph, // COLSHAPE_JNTSPH CollisionCheck_OC_CylVsCyl, // COLSHAPE_CYLINDER NULL, // COLSHAPE_TRIS NULL, // COLSHAPE_QUAD CollisionCheck_OC_CylVsSphere // COLSHAPE_SPHERE }, // COLSHAPE_TRIS { NULL, // COLSHAPE_JNTSPH NULL, // COLSHAPE_CYLINDER NULL, // COLSHAPE_TRIS NULL, // COLSHAPE_QUAD NULL // COLSHAPE_SPHERE }, // COLSHAPE_QUAD { NULL, // COLSHAPE_JNTSPH NULL, // COLSHAPE_CYLINDER NULL, // COLSHAPE_TRIS NULL, // COLSHAPE_QUAD NULL // COLSHAPE_SPHERE }, // COLSHAPE_SPHERE { CollisionCheck_OC_SphereVsJntSph, // COLSHAPE_JNTSPH CollisionCheck_OC_SphereVsCyl, // COLSHAPE_CYLINDER NULL, // COLSHAPE_TRIS NULL, // COLSHAPE_QUAD CollisionCheck_OC_SphereVsSphere // COLSHAPE_SPHERE }, }; /** * Iterates through all OC colliders and collides them with all subsequent OC colliders on the list. During an OC * collision, colliders with overlapping elements move away from each other so that their elements no longer overlap. * The relative amount each collider is pushed is determined by the collider's mass. Only JntSph, Cylinder and Sphere * colliders can collide, and each collider must have the OC flag corresponding to the other's OC type. Additionally, * OC2_UNK1 cannot collide with OC2_UNK2, nor can two colliders that share an actor. */ void CollisionCheck_OC(struct PlayState* play, CollisionCheckContext* colChkCtx) { Collider** leftColP; Collider** rightColP; ColChkVsFunc vsFunc; for (leftColP = colChkCtx->colOC; leftColP < colChkCtx->colOC + colChkCtx->colOCCount; leftColP++) { if ((*leftColP == NULL) || CollisionCheck_SkipOC(*leftColP)) { continue; } for (rightColP = leftColP + 1; rightColP < colChkCtx->colOC + colChkCtx->colOCCount; rightColP++) { if ((*rightColP == NULL) || CollisionCheck_SkipOC(*rightColP) || CollisionCheck_Incompatible(*leftColP, *rightColP)) { continue; } vsFunc = sOCVsFuncs[(*leftColP)->shape][(*rightColP)->shape]; if (vsFunc == NULL) { continue; } vsFunc(play, colChkCtx, *leftColP, *rightColP); } } } /** * Initializes CollisionCheckInfo to default values */ void CollisionCheck_InitInfo(CollisionCheckInfo* info) { static CollisionCheckInfo sDefaultColChkInfo = { NULL, { 0.0f, 0.0f, 0.0f }, 10, 10, 0, MASS_IMMOVABLE, 8, 0, 0, 0, 0, }; *info = sDefaultColChkInfo; } /** * Resets ColisionCheckInfo fields other than DamageTable, mass, and dim. */ void CollisionCheck_ResetDamage(CollisionCheckInfo* info) { info->damage = 0; info->damageEffect = 0; info->atHitEffect = 0; info->acHitEffect = 0; info->displacement.x = info->displacement.y = info->displacement.z = 0.0f; } /** * Sets up CollisionCheckInfo using the values in init. Does not set a damage table or the unused unk_14. */ void CollisionCheck_SetInfoNoDamageTable(CollisionCheckInfo* info, CollisionCheckInfoInit* init) { info->health = init->health; info->cylRadius = init->cylRadius; info->cylHeight = init->cylHeight; info->mass = init->mass; } /** * Sets up CollisionCheckInfo using the values in init. Does not set the unused unk_14 */ void CollisionCheck_SetInfo(CollisionCheckInfo* info, DamageTable* damageTable, CollisionCheckInfoInit* init) { info->health = init->health; info->damageTable = damageTable; info->cylRadius = init->cylRadius; info->cylHeight = init->cylHeight; info->mass = init->mass; } /** * Sets up CollisionCheckInfo using the values in init. Sets the unused unk_14 */ void CollisionCheck_SetInfo2(CollisionCheckInfo* info, DamageTable* damageTable, CollisionCheckInfoInit2* init) { info->health = init->health; info->damageTable = damageTable; info->cylRadius = init->cylRadius; info->cylHeight = init->cylHeight; info->cylYShift = init->cylYShift; info->mass = init->mass; } /** * Sets up CollisionCheckInfo using the values in Init and a preset damage table. Sets the unused unk_14. */ void CollisionCheck_SetInfoGetDamageTable(CollisionCheckInfo* info, s32 index, CollisionCheckInfoInit2* init) { CollisionCheck_SetInfo2(info, DamageTable_Get(index), init); } /** * Apply AC damage effect */ void CollisionCheck_ApplyDamage(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* col, ColliderElement* elem) { f32 damage; f32 finalDamage = 0.0f; s32 pad; Collider* atCol; ColliderElement* atElem; s32 pad1; u32 effect; if ((col->actor == NULL) || !(col->acFlags & AC_HIT)) { return; } if (!(elem->acElemFlags & ACELEM_HIT) || (elem->acElemFlags & ACELEM_NO_DAMAGE)) { return; } atCol = elem->acHit; atElem = elem->acHitElem; if ((atCol != NULL) && (atElem != NULL) && (col != NULL) && (elem != NULL)) { damage = CollisionCheck_GetDamageAndEffectOnElementAC(atCol, atElem, col, elem, &effect); if (CollisionCheck_GetElementATDamage(atCol, atElem, col, elem) != 0) { if (damage < 1.0f) { finalDamage = 0.0f; if (effect == 0) { return; } } else { finalDamage = CollisionCheck_ApplyElementATDefense(damage, elem); if ((finalDamage < 1.0f) && (effect == 0)) { return; } } } if (col->actor->colChkInfo.damageTable != NULL) { col->actor->colChkInfo.damageEffect = effect; } if (!(col->acFlags & AC_HARD) || ((col->acFlags & AC_HARD) && (atElem->atDmgInfo.dmgFlags == 0x20000000))) { if (col->actor->colChkInfo.damage < finalDamage) { col->actor->colChkInfo.damage = finalDamage; } } } } /** * Apply ColliderJntSph AC damage effect */ void CollisionCheck_ApplyDamageJntSph(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* col) { ColliderJntSph* jntSph = (ColliderJntSph*)col; s32 i; if ((jntSph->count > 0) && (jntSph->elements != NULL)) { for (i = 0; i < jntSph->count; i++) { CollisionCheck_ApplyDamage(play, colChkCtx, &jntSph->base, &jntSph->elements[i].base); } } } /** * Apply ColliderCylinder AC damage effect */ void CollisionCheck_ApplyDamageCyl(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* col) { ColliderCylinder* cyl = (ColliderCylinder*)col; CollisionCheck_ApplyDamage(play, colChkCtx, &cyl->base, &cyl->elem); } /** * Apply ColliderTris AC damage effect */ void CollisionCheck_ApplyDamageTris(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* col) { ColliderTris* tris = (ColliderTris*)col; s32 i; // unlike sphere groups above, tri groups are not guarded against // tris->elements being NULL for (i = 0; i < tris->count; i++) { CollisionCheck_ApplyDamage(play, colChkCtx, &tris->base, &tris->elements[i].base); } } /** * Apply ColliderQuad AC damage effect */ void CollisionCheck_ApplyDamageQuad(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* col) { ColliderQuad* quad = (ColliderQuad*)col; CollisionCheck_ApplyDamage(play, colChkCtx, &quad->base, &quad->elem); } /** * Apply ColliderSphere AC damage effect */ void CollisionCheck_ApplyDamageSphere(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* col) { ColliderSphere* sph = (ColliderSphere*)col; CollisionCheck_ApplyDamage(play, colChkCtx, &sph->base, &sph->elem); } ColChkApplyFunc sApplyDamageFuncs[COLSHAPE_MAX] = { CollisionCheck_ApplyDamageJntSph, // COLSHAPE_JNTSPH CollisionCheck_ApplyDamageCyl, // COLSHAPE_CYLINDER CollisionCheck_ApplyDamageTris, // COLSHAPE_TRIS CollisionCheck_ApplyDamageQuad, // COLSHAPE_QUAD CollisionCheck_ApplyDamageSphere, // COLSHAPE_SPHERE }; /** * For all AC colliders, sets any damage effects from collisions with AT colliders to their corresponding actor's * CollisionCheckInfo. */ void CollisionCheck_Damage(struct PlayState* play, CollisionCheckContext* colChkCtx) { s32 i; for (i = 0; i < colChkCtx->colACCount; i++) { Collider* col = colChkCtx->colAC[i]; if (col == NULL) { continue; } if (col->acFlags & AC_NO_DAMAGE) { continue; } sApplyDamageFuncs[col->shape](play, colChkCtx, col); } } /** * Checks if the line segment ab intersects any of the ColliderJntSph's elements */ s32 CollisionCheck_LineOC_JntSph(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* col, Vec3f* a, Vec3f* b) { ColliderJntSph* jntSph = (ColliderJntSph*)col; s32 i; for (i = 0; i < jntSph->count; i++) { ColliderJntSphElement* jntSphElem = &jntSph->elements[i]; if (!(jntSphElem->base.ocElemFlags & OCELEM_ON)) { continue; } D_801EDEB0.a = *a; D_801EDEB0.b = *b; if (Math3D_LineVsSph(&jntSphElem->dim.worldSphere, &D_801EDEB0)) { return true; } } return false; } /** * Checks if the line segment ab intersects the ColliderCylinder */ s32 CollisionCheck_LineOC_Cyl(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* col, Vec3f* a, Vec3f* b) { ColliderCylinder* cyl = (ColliderCylinder*)col; if (!(cyl->elem.ocElemFlags & OCELEM_ON)) { return false; } if (Math3D_CylVsLineSeg(&cyl->dim, a, b, &D_801EDF38, &D_801EDF48)) { return true; } return false; } /** * Checks if the line segment ab intersects the ColliderSphere */ s32 CollisionCheck_LineOC_Sphere(struct PlayState* play, CollisionCheckContext* colChkCtx, Collider* col, Vec3f* a, Vec3f* b) { ColliderSphere* sph = (ColliderSphere*)col; if (!(sph->elem.ocElemFlags & OCELEM_ON)) { return false; } D_801EDFC8.a = *a; D_801EDFC8.b = *b; if (Math3D_LineVsSph(&sph->dim.worldSphere, &D_801EDFC8)) { return true; } return false; } static ColChkLineFunc sOCLineCheckFuncs[COLSHAPE_MAX] = { CollisionCheck_LineOC_JntSph, // COLSHAPE_JNTSPH CollisionCheck_LineOC_Cyl, // COLSHAPE_CYLINDER NULL, // COLSHAPE_TRIS NULL, // COLSHAPE_QUAD CollisionCheck_LineOC_Sphere, // COLSHAPE_SPHERE }; /** * Checks if the line segment ab intersects any OC colliders, excluding those attached to actors * on the exclusion list. Returns true if there are any intersections and false otherwise. */ s32 CollisionCheck_LineOC(struct PlayState* play, CollisionCheckContext* colChkCtx, Vec3f* a, Vec3f* b, Actor** exclusions, s32 numExclusions) { ColChkLineFunc lineCheck; Collider** colP; s32 i; s32 exclude; s32 result = false; for (colP = colChkCtx->colOC; colP < &colChkCtx->colOC[colChkCtx->colOCCount]; colP++) { if (CollisionCheck_SkipOC(*colP)) { continue; } exclude = false; for (i = 0; i < numExclusions; i++) { if ((*colP)->actor == exclusions[i]) { exclude = 1; break; } } if (exclude) { continue; } lineCheck = sOCLineCheckFuncs[(*colP)->shape]; if (lineCheck == NULL) { continue; } result = lineCheck(play, colChkCtx, *colP, a, b); if (result) { break; } } return result; } /** * Checks if the line segment ab intersects any OC colliders. Returns true if there are any intersections and false * otherwise. */ s32 CollisionCheck_LineOCCheckAll(struct PlayState* play, CollisionCheckContext* colChkCtx, Vec3f* a, Vec3f* b) { return CollisionCheck_LineOC(play, colChkCtx, a, b, NULL, 0); } /** * Checks if the line segment ab intersects any OC colliders, excluding those attached to actors on the exclusion list. * Returns true if there are any intersections and false otherwise. */ s32 CollisionCheck_LineOCCheck(struct PlayState* play, CollisionCheckContext* colChkCtx, Vec3f* a, Vec3f* b, Actor** exclusions, s32 numExclusions) { return CollisionCheck_LineOC(play, colChkCtx, a, b, exclusions, numExclusions); } /** * Moves the ColliderCylinder's position to the actor's position */ void Collider_UpdateCylinder(Actor* actor, ColliderCylinder* cyl) { cyl->dim.pos.x = actor->world.pos.x; cyl->dim.pos.y = actor->world.pos.y; cyl->dim.pos.z = actor->world.pos.z; } /** * Sets the ColliderCylinder's position */ void Collider_SetCylinderPosition(ColliderCylinder* cyl, Vec3s* pos) { cyl->dim.pos.x = pos->x; cyl->dim.pos.y = pos->y; cyl->dim.pos.z = pos->z; } /** * Sets the ColliderQuad's vertices */ void Collider_SetQuadVertices(ColliderQuad* quad, Vec3f* a, Vec3f* b, Vec3f* c, Vec3f* d) { Math_Vec3f_Copy(&quad->dim.quad[2], c); Math_Vec3f_Copy(&quad->dim.quad[3], d); Math_Vec3f_Copy(&quad->dim.quad[0], a); Math_Vec3f_Copy(&quad->dim.quad[1], b); Collider_SetQuadMidpoints(&quad->dim); } /** * Sets the specified ColliderTrisElement's vertices */ void Collider_SetTrisVertices(ColliderTris* tris, s32 elemIndex, Vec3f* a, Vec3f* b, Vec3f* c) { ColliderTrisElement* trisElem = &tris->elements[elemIndex]; f32 nx; f32 ny; f32 nz; f32 originDist; Math_Vec3f_Copy(&trisElem->dim.vtx[0], a); Math_Vec3f_Copy(&trisElem->dim.vtx[1], b); Math_Vec3f_Copy(&trisElem->dim.vtx[2], c); Math3D_DefPlane(a, b, c, &nx, &ny, &nz, &originDist); trisElem->dim.plane.normal.x = nx; trisElem->dim.plane.normal.y = ny; trisElem->dim.plane.normal.z = nz; trisElem->dim.plane.originDist = originDist; } /** * Sets the specified ColliderTrisElement's dim using the values in src */ void Collider_SetTrisDim(struct PlayState* play, ColliderTris* dest, s32 elemIndex, ColliderTrisElementDimInit* src) { ColliderTrisElement* trisElem = &dest->elements[elemIndex]; Collider_SetTrisElementDim(play, &trisElem->dim, src); } /** * Updates the world spheres for all of the collider's JntSph elements attached to the specified limb */ void Collider_UpdateSpheres(s32 limb, ColliderJntSph* jntSph) { static Vec3f D_801EE1C0; static Vec3f D_801EE1D0; s32 i; for (i = 0; i < jntSph->count; i++) { if (limb == jntSph->elements[i].dim.limb) { D_801EE1C0.x = jntSph->elements[i].dim.modelSphere.center.x; D_801EE1C0.y = jntSph->elements[i].dim.modelSphere.center.y; D_801EE1C0.z = jntSph->elements[i].dim.modelSphere.center.z; Matrix_MultVec3f(&D_801EE1C0, &D_801EE1D0); jntSph->elements[i].dim.worldSphere.center.x = D_801EE1D0.x; jntSph->elements[i].dim.worldSphere.center.y = D_801EE1D0.y; jntSph->elements[i].dim.worldSphere.center.z = D_801EE1D0.z; jntSph->elements[i].dim.worldSphere.radius = jntSph->elements[i].dim.modelSphere.radius * jntSph->elements[i].dim.scale; } } } /** * Updates the world spheres for the specified ColliderJntSph element */ void Collider_UpdateSpheresElement(ColliderJntSph* jntSph, s32 elemIndex, Actor* actor) { if (elemIndex < jntSph->count) { jntSph->elements[elemIndex].dim.worldSphere.center.x = jntSph->elements[elemIndex].dim.modelSphere.center.x + actor->world.pos.x; jntSph->elements[elemIndex].dim.worldSphere.center.y = jntSph->elements[elemIndex].dim.modelSphere.center.y + actor->world.pos.y; jntSph->elements[elemIndex].dim.worldSphere.center.z = jntSph->elements[elemIndex].dim.modelSphere.center.z + actor->world.pos.z; jntSph->elements[elemIndex].dim.worldSphere.radius = jntSph->elements[elemIndex].dim.modelSphere.radius * jntSph->elements[elemIndex].dim.scale; } } /** * Updates the world sphere for the ColliderSphere if it is attached to the specified limb */ void Collider_UpdateSphere(s32 limb, ColliderSphere* sph) { static Vec3f D_801EE1E0; static Vec3f D_801EE1F0; if (limb == sph->dim.limb) { D_801EE1E0.x = sph->dim.modelSphere.center.x; D_801EE1E0.y = sph->dim.modelSphere.center.y; D_801EE1E0.z = sph->dim.modelSphere.center.z; Matrix_MultVec3f(&D_801EE1E0, &D_801EE1F0); sph->dim.worldSphere.center.x = D_801EE1F0.x; sph->dim.worldSphere.center.y = D_801EE1F0.y; sph->dim.worldSphere.center.z = D_801EE1F0.z; sph->dim.worldSphere.radius = sph->dim.modelSphere.radius * sph->dim.scale; } } /** * Spawns red blood droplets. * No actor has a collision type that spawns red blood. */ void CollisionCheck_SpawnRedBlood(struct PlayState* play, Vec3f* v) { static EffectSparkInit D_801EE200; s32 effectIndex; D_801EE200.position.x = v->x; D_801EE200.position.y = v->y; D_801EE200.position.z = v->z; D_801EE200.uDiv = 5; D_801EE200.vDiv = 5; D_801EE200.colorStart[0].r = 128; D_801EE200.colorStart[0].g = 0; D_801EE200.colorStart[0].b = 64; D_801EE200.colorStart[0].a = 255; D_801EE200.colorStart[1].r = 128; D_801EE200.colorStart[1].g = 0; D_801EE200.colorStart[1].b = 64; D_801EE200.colorStart[1].a = 255; D_801EE200.colorStart[2].r = 255; D_801EE200.colorStart[2].g = 128; D_801EE200.colorStart[2].b = 0; D_801EE200.colorStart[2].a = 255; D_801EE200.colorStart[3].r = 255; D_801EE200.colorStart[3].g = 128; D_801EE200.colorStart[3].b = 0; D_801EE200.colorStart[3].a = 255; D_801EE200.colorEnd[0].r = 64; D_801EE200.colorEnd[0].g = 0; D_801EE200.colorEnd[0].b = 32; D_801EE200.colorEnd[0].a = 0; D_801EE200.colorEnd[1].r = 64; D_801EE200.colorEnd[1].g = 0; D_801EE200.colorEnd[1].b = 32; D_801EE200.colorEnd[1].a = 0; D_801EE200.colorEnd[2].r = 128; D_801EE200.colorEnd[2].g = 0; D_801EE200.colorEnd[2].b = 64; D_801EE200.colorEnd[2].a = 0; D_801EE200.colorEnd[3].r = 128; D_801EE200.colorEnd[3].g = 0; D_801EE200.colorEnd[3].b = 64; D_801EE200.colorEnd[3].a = 0; D_801EE200.timer = 0; D_801EE200.duration = 16; D_801EE200.speed = 8.0f; D_801EE200.gravity = -1.0f; Effect_Add(play, &effectIndex, EFFECT_SPARK, 0, 1, &D_801EE200); } /** * Spawns water droplets. * No actor has a collision type that spawns water droplets. */ void CollisionCheck_SpawnWaterDroplets(struct PlayState* play, Vec3f* v) { static EffectSparkInit D_801EE738; s32 effectIndex; D_801EE738.position.x = v->x; D_801EE738.position.y = v->y; D_801EE738.position.z = v->z; D_801EE738.uDiv = 5; D_801EE738.vDiv = 5; D_801EE738.colorStart[0].r = 255; D_801EE738.colorStart[0].g = 255; D_801EE738.colorStart[0].b = 255; D_801EE738.colorStart[0].a = 255; D_801EE738.colorStart[1].r = 100; D_801EE738.colorStart[1].g = 100; D_801EE738.colorStart[1].b = 100; D_801EE738.colorStart[1].a = 100; D_801EE738.colorStart[2].r = 100; D_801EE738.colorStart[2].g = 100; D_801EE738.colorStart[2].b = 100; D_801EE738.colorStart[2].a = 100; D_801EE738.colorStart[3].r = 100; D_801EE738.colorStart[3].g = 100; D_801EE738.colorStart[3].b = 100; D_801EE738.colorStart[3].a = 100; D_801EE738.colorEnd[0].r = 50; D_801EE738.colorEnd[0].g = 50; D_801EE738.colorEnd[0].b = 50; D_801EE738.colorEnd[0].a = 50; D_801EE738.colorEnd[1].r = 50; D_801EE738.colorEnd[1].g = 50; D_801EE738.colorEnd[1].b = 50; D_801EE738.colorEnd[1].a = 50; D_801EE738.colorEnd[2].r = 50; D_801EE738.colorEnd[2].g = 50; D_801EE738.colorEnd[2].b = 50; D_801EE738.colorEnd[2].a = 50; D_801EE738.colorEnd[3].r = 0; D_801EE738.colorEnd[3].g = 0; D_801EE738.colorEnd[3].b = 0; D_801EE738.colorEnd[3].a = 0; D_801EE738.timer = 0; D_801EE738.duration = 16; D_801EE738.speed = 8.0f; D_801EE738.gravity = -1.0f; Effect_Add(play, &effectIndex, EFFECT_SPARK, 0, 1, &D_801EE738); } /** * Spawns streaks of light from hits against solid objects */ void CollisionCheck_SpawnShieldParticles(struct PlayState* play, Vec3f* v) { static EffectShieldParticleInit sShieldParticleInitMetal = { 16, { 0, 0, 0 }, { 0, 200, 255, 255 }, { 255, 255, 255, 255 }, { 255, 255, 128, 255 }, { 255, 255, 0, 255 }, { 255, 64, 0, 200 }, { 255, 0, 0, 255 }, 2.1f, 35.0f, 30.0f, 8, { 0, 0, 0, { 0, 128, 255 }, 0, 300 }, 1, }; s32 effectIndex; sShieldParticleInitMetal.position.x = v->x; sShieldParticleInitMetal.position.y = v->y; sShieldParticleInitMetal.position.z = v->z; sShieldParticleInitMetal.lightPoint.x = sShieldParticleInitMetal.position.x; sShieldParticleInitMetal.lightPoint.y = sShieldParticleInitMetal.position.y; sShieldParticleInitMetal.lightPoint.z = sShieldParticleInitMetal.position.z; Effect_Add(play, &effectIndex, EFFECT_SHIELD_PARTICLE, 0, 1, &sShieldParticleInitMetal); } /** * Spawns streaks of light and makes a metallic sound */ void CollisionCheck_SpawnShieldParticlesMetal(struct PlayState* play, Vec3f* v) { CollisionCheck_SpawnShieldParticles(play, v); Audio_PlaySfx(NA_SE_IT_SHIELD_REFLECT_SW); } /** * Spawns streaks of light and makes a metallic sound at the specified position */ void CollisionCheck_SpawnShieldParticlesMetalSound(struct PlayState* play, Vec3f* v, Vec3f* pos) { CollisionCheck_SpawnShieldParticles(play, v); Audio_PlaySfx_AtPos(pos, NA_SE_IT_SHIELD_REFLECT_SW); } /** * Spawns streaks of light and makes a metallic sound */ void CollisionCheck_SpawnShieldParticlesMetal2(struct PlayState* play, Vec3f* v) { CollisionCheck_SpawnShieldParticlesMetal(play, v); } /** * Spawns streaks of light and makes a wooden sound */ void CollisionCheck_SpawnShieldParticlesWood(struct PlayState* play, Vec3f* v, Vec3f* pos) { static EffectShieldParticleInit sShieldParticleInitWood = { 16, { 0, 0, 0 }, { 0, 200, 255, 255 }, { 255, 255, 255, 255 }, { 255, 255, 128, 255 }, { 255, 255, 0, 255 }, { 255, 64, 0, 200 }, { 255, 0, 0, 255 }, 2.1f, 35.0f, 30.0f, 8, { 0, 0, 0, { 0, 128, 255 }, 0, 300 }, 0, }; s32 effectIndex; sShieldParticleInitWood.position.x = v->x; sShieldParticleInitWood.position.y = v->y; sShieldParticleInitWood.position.z = v->z; sShieldParticleInitWood.lightPoint.x = sShieldParticleInitWood.position.x; sShieldParticleInitWood.lightPoint.y = sShieldParticleInitWood.position.y; sShieldParticleInitWood.lightPoint.z = sShieldParticleInitWood.position.z; Effect_Add(play, &effectIndex, EFFECT_SHIELD_PARTICLE, 0, 1, &sShieldParticleInitWood); Audio_PlaySfx_AtPos(pos, NA_SE_IT_REFLECTION_WOOD); } /** * Determines if the line segment connecting `itemPos` and `itemProjPos` intersects the side of a cylinder with the * given `radius`, `height`, and `offset` at `actorPos`. Returns 3 if either endpoint is inside the cylinder, otherwise * returns the number of points of intersection with the side of the cylinder. The locations of those points are put in * `out1` and `out2`, with `out1` being closer to `itemPos`. Line segments that pass through both bases of the cylinder * are not detected. */ s32 CollisionCheck_CylSideVsLineSeg(f32 radius, f32 height, f32 offset, Vec3f* actorPos, Vec3f* itemPos, Vec3f* itemProjPos, Vec3f* out1, Vec3f* out2) { Vec3f actorToItem; Vec3f actorToItemProj; Vec3f itemStep; f32 frac1 = 0.0f; f32 frac2 = 0.0f; u32 intersect2; u32 intersect1; u32 test1; u32 test2; f32 radSqDiff; f32 actorDotItemXZ; f32 zero = 0.0f; f32 closeDist; s32 pad1; s32 pad2; actorToItem.x = itemPos->x - actorPos->x; actorToItem.y = itemPos->y - actorPos->y - offset; actorToItem.z = itemPos->z - actorPos->z; actorToItemProj.x = itemProjPos->x - actorPos->x; actorToItemProj.y = itemProjPos->y - actorPos->y - offset; actorToItemProj.z = itemProjPos->z - actorPos->z; itemStep.x = actorToItemProj.x - actorToItem.x; itemStep.y = actorToItemProj.y - actorToItem.y; itemStep.z = actorToItemProj.z - actorToItem.z; if ((actorToItem.y > 0.0f) && (actorToItem.y < height) && (sqrtf(SQXZ(actorToItem)) < radius)) { return 3; } if ((actorToItemProj.y > 0.0f) && (actorToItemProj.y < height) && (sqrtf(SQXZ(actorToItemProj)) < radius)) { return 3; } radSqDiff = SQXZ(actorToItem) - SQ(radius); if (!IS_ZERO(SQXZ(itemStep))) { actorDotItemXZ = DOTXZ(2.0f * itemStep, actorToItem); if (SQ(actorDotItemXZ) < (4.0f * SQXZ(itemStep) * radSqDiff)) { return 0; } if ((SQ(actorDotItemXZ) - (4.0f * SQXZ(itemStep) * radSqDiff)) > zero) { intersect1 = intersect2 = true; } else { intersect1 = true; intersect2 = false; } closeDist = sqrtf(SQ(actorDotItemXZ) - (4.0f * SQXZ(itemStep) * radSqDiff)); if (intersect1) { frac1 = (closeDist - actorDotItemXZ) / (2.0f * SQXZ(itemStep)); } if (intersect2) { frac2 = (-actorDotItemXZ - closeDist) / (2.0f * SQXZ(itemStep)); } } else if (!IS_ZERO(DOTXZ(2.0f * itemStep, actorToItem))) { intersect1 = true; intersect2 = false; frac1 = -radSqDiff / DOTXZ(2.0f * itemStep, actorToItem); } else { if (radSqDiff <= 0.0f) { test1 = (0.0f < actorToItem.y) && (actorToItem.y < height); test2 = (0.0f < actorToItemProj.y) && (actorToItemProj.y < height); if (test1 && test2) { *out1 = actorToItem; *out2 = actorToItemProj; return 2; } if (test1) { *out1 = actorToItem; return 1; } if (test2) { *out1 = actorToItemProj; return 1; } } return 0; } if (!intersect2) { if ((frac1 < 0.0f) || (1.0f < frac1)) { return 0; } } else { test1 = (frac1 < 0.0f) || (1.0f < frac1); test2 = (frac2 < 0.0f) || (1.0f < frac2); if (test1 && test2) { return 0; } if (test1) { intersect1 = false; } if (test2) { intersect2 = false; } } if (intersect1 && (((frac1 * itemStep.y + actorToItem.y) < 0.0f) || (height < (frac1 * itemStep.y + actorToItem.y)))) { intersect1 = false; } if (intersect2 && (((frac2 * itemStep.y + actorToItem.y) < 0.0f) || (height < (frac2 * itemStep.y + actorToItem.y)))) { intersect2 = false; } if (!intersect1 && !intersect2) { return 0; } else if (intersect1 && intersect2) { out1->x = frac1 * itemStep.x + actorToItem.x + actorPos->x; out1->y = frac1 * itemStep.y + actorToItem.y + actorPos->y; out1->z = frac1 * itemStep.z + actorToItem.z + actorPos->z; out2->x = frac2 * itemStep.x + actorToItem.x + actorPos->x; out2->y = frac2 * itemStep.y + actorToItem.y + actorPos->y; out2->z = frac2 * itemStep.z + actorToItem.z + actorPos->z; return 2; } else if (intersect1) { out1->x = frac1 * itemStep.x + actorToItem.x + actorPos->x; out1->y = frac1 * itemStep.y + actorToItem.y + actorPos->y; out1->z = frac1 * itemStep.z + actorToItem.z + actorPos->z; return 1; } else if (intersect2) { out1->x = frac2 * itemStep.x + actorToItem.x + actorPos->x; out1->y = frac2 * itemStep.y + actorToItem.y + actorPos->y; out1->z = frac2 * itemStep.z + actorToItem.z + actorPos->z; return 1; } return 1; }