mirror of https://github.com/zeldaret/mm.git
4181 lines
147 KiB
C
4181 lines
147 KiB
C
#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;
|
|
}
|