#include "c/c_counter.h" #include "c/c_math.h" #include "common.h" #include "d/a/d_a_base.h" #include "d/a/d_a_player.h" #include "d/a/obj/d_a_obj_base.h" #include "d/col/bg/d_bg_s.h" #include "d/col/bg/d_bg_s_gnd_chk.h" #include "d/col/c/c_cc_d.h" #include "d/col/cc/d_cc_mass_s.h" #include "d/col/cc/d_cc_s.h" #include "d/d_camera.h" #include "d/d_heap.h" #include "d/d_heap_alloc.h" #include "d/d_light_env.h" #include "d/d_player_act.h" #include "d/d_sc_game.h" #include "d/d_stage.h" #include "d/d_stage_mgr.h" #include "d/snd/d_snd_small_effect_mgr.h" #include "d/t/d_t_mass_obj.h" #include "egg/core/eggHeap.h" #include "egg/math/eggQuat.h" #include "f/f_base.h" #include "m/m3d/m3d.h" #include "m/m3d/m_scnleaf.h" #include "m/m_allocator.h" #include "m/m_angle.h" #include "m/m_color.h" #include "m/m_heap.h" #include "m/m_mtx.h" #include "m/m_quat.h" #include "m/m_vec.h" #include "nw4r/g3d/g3d_draw1mat1shp.h" #include "nw4r/g3d/g3d_scnobj.h" #include "nw4r/g3d/g3d_state.h" #include "nw4r/g3d/platform/g3d_gpu.h" #include "nw4r/g3d/res/g3d_resfile.h" #include "nw4r/g3d/res/g3d_resmat.h" #include "nw4r/g3d/res/g3d_resmdl.h" #include "nw4r/g3d/res/g3d_resshp.h" #include "nw4r/math/math_types.h" #include "rvl/GX/GXTev.h" #include "rvl/GX/GXTransform.h" #include "rvl/GX/GXTypes.h" #include "rvl/MTX/mtx.h" #include "s/s_Math.h" #include "toBeSorted/d_emitter.h" #include "toBeSorted/special_item_drop_mgr.h" #include "toBeSorted/time_area_mgr.h" SPECIAL_ACTOR_PROFILE(MASS_OBJ_TAG, dTgMassObj_c, fProfile::MASS_OBJ_TAG, 0x28A, 0, 4); GrassModel *dTgMassObj_c::sGrassModels[5] = {}; dTgMassObj_c *dTgMassObj_c::sInstance = nullptr; mHeapAllocator_c *dTgMassObj_c::sAllocator = nullptr; u8 dTgMassObj_c::getCurrentStageGrassSubtype() { u8 ret = 0; if (dStageMgr_c::GetInstance()->isSTIFAreaFaron()) { if (dScGame_c::isCurrentStage("D100") || dScGame_c::isCurrentStage("B100")) { ret = 4; } else { ret = 3; } } else if (dStageMgr_c::GetInstance()->isSTIFAreaEldin()) { ret = 2; } else if (dStageMgr_c::GetInstance()->isSTIFAreaLanayru()) { ret = 1; } if (dScGame_c::isCurrentStage("D003")) { ret = 0; } else if (dScGame_c::isCurrentStage("D003_1") || dScGame_c::isCurrentStage("D003_2")) { ret = 2; } else if (dScGame_c::isCurrentStage("D003_3") || dScGame_c::isCurrentStage("D003_4")) { ret = 1; } else if (dScGame_c::isCurrentStage("D003_5") || dScGame_c::isCurrentStage("D003_6")) { ret = 3; } return ret; } int dTgMassObj_c::actorCreate() { return SUCCEEDED; } static const GrassModelInfo GRASS_A_TYPES[5] = { { "GrassA", "GrassA", "GrassACut", 0x1770, 0x64, 0x2, 0x7D0, 0x8, 0x80, 0, 0, }, { "GrassA", "GrassA", "GrassACut", 0x1770, 0x64, 0x2, 0x3E8, 0x8, 0x80, 0, 0, }, { "FlowerA00", "FlowerA00", "FlowerA00Cut", 0x1770, 0x64, 0x2, 0x12C, 0x8, 0x20, 0x2, 0, }, { "FlowerB00", "FlowerB00", "FlowerB00Cut", 0x1770, 0x64, 0x2, 0x12C, 0x8, 0x20, 0x2, 0, }, { "FlowerB01", "FlowerB01", "FlowerB01Cut", 0x1770, 0x64, 0x2, 0x12C, 0x8, 0x20, 0x2, 0, }, }; static const GrassModelNames GRASS_MODEL_NAMES[5] = { { "GrassA", "GrassA", "GrassACut", }, { "GrassB", "GrassB", "GrassBCut", }, { "GrassC", "GrassC", "GrassCCut", }, { "GrassD", "GrassD", "GrassDCut", }, { "GrassE", "GrassE", "GrassECut", }, }; static const Vec GRASS_VECS[4] = { { -0.5f, 0.5f,-0.5f, }, { 0.5f, 0.5f, -0.5f, }, { 0.5f, -0.5f, 0.5f, }, { -0.5f, -0.5f, 0.5f, }, }; int dTgMassObj_c::actorPostCreate() { s32 grassModelIndex = getGrassTypeFromParams(); s32 retVar = SUCCEEDED; if (sInstance == nullptr) { sInstance = this; toGlobalRoom(); sAllocator = new (dHeap::work1Heap.heap) mHeapAllocator_c(); for (s32 i = 0; i < 5; i++) { sGrassModels[i] = new (dHeap::work1Heap.heap) GrassModel(); } if (!sAllocator->createFrmHeap( 0xFFFFFFFF, dHeap::work1Heap.heap, "dTgMassObj_c::m_allocator", 0x20, mHeap::OPT_NONE )) { return FAILED; } u8 grassSubtype = getCurrentStageGrassSubtype(); for (s32 i = 0; i < 5; i++) { const GrassModelInfo *modelInfo; modelInfo = &GRASS_A_TYPES[i]; sGrassModels[i]->setModelInfo(modelInfo, sAllocator); if (i == 0) { void *file = dAcObjBase_c::getOarcResFile(GRASS_MODEL_NAMES[grassSubtype].mArcName); nw4r::g3d::ResFile res(file); nw4r::g3d::ResMdl mdl = res.GetResMdl(GRASS_MODEL_NAMES[grassSubtype].mModelName); sGrassModels[i]->initResForModel(0, mdl.GetResMat(0), mdl.GetResShp(0)); mdl = res.GetResMdl(GRASS_MODEL_NAMES[grassSubtype].mCutModelName); sGrassModels[i]->initResForModel(1, mdl.GetResMat(0), mdl.GetResShp(0)); } else { void *file = dAcObjBase_c::getOarcResFile(modelInfo->mArcName); nw4r::g3d::ResFile res(file); nw4r::g3d::ResMdl mdl = res.GetResMdl(modelInfo->mModelName); sGrassModels[i]->initResForModel(0, mdl.GetResMat(0), mdl.GetResShp(0)); mdl = res.GetResMdl(modelInfo->mCutModelName); sGrassModels[i]->initResForModel(1, mdl.GetResMat(0), mdl.GetResShp(0)); } } sAllocator->adjustFrmHeap(); } else { retVar = 2; } if (grassModelIndex == 0) { grassModelIndex = 0; mMassSubtype = getCurrentStageGrassSubtype(); if (getFromParams(3, 1) != 0) { if (getFromParams(0x12, 1) != 0) { mMassSubtype = 1; grassModelIndex = 0; } else { mMassSubtype = 0; grassModelIndex = 1; } } } else if (grassModelIndex == 1) { mMassSubtype = 9; grassModelIndex = 4; } else if (grassModelIndex == 2) { mMassSubtype = 7; grassModelIndex = 2; } else if (grassModelIndex == 3) { if (s32(mParams >> 0x19) == 1) { mMassSubtype = 9; grassModelIndex = 4; } else { mMassSubtype = 8; grassModelIndex = 3; } } s32 fromParam4 = getFromParams(4, 0x7F); s32 fromParamB = getFromParams(0xB, 0x7F); s32 affectedByTimeshift = getFromParams(3, 1); s32 activeInPresent = getFromParams(0x12, 1); s32 uVar6 = fromParam4 * 10; f32 xzStep = uVar6; s32 xzDisplacement = xzStep * (fromParamB / 100.f); GrassModel **tmpGrassModel = &sGrassModels[grassModelIndex]; mMtx_c mtx; mtx.transS(mPosition); mtx.YrotM(mRotation.y); mtx.scaleM(mScale); mMtx_c boundsMtx = mtx.copyInverse(); mVec3_c minVec(FLOAT_MAX, FLOAT_MAX, FLOAT_MAX); mVec3_c maxVec(FLOAT_MIN, FLOAT_MIN, FLOAT_MIN); const Vec *loop = &GRASS_VECS[0]; for (s32 i = 0; i < ARRAY_LENGTH(GRASS_VECS); i++) { mVec3_c mult = mtx * *(loop++); if (mult.x < minVec.x) { minVec.x = mult.x; } if (mult.x > maxVec.x) { maxVec.x = mult.x; } if (mult.y < minVec.y) { minVec.y = mult.y; } if (mult.y > maxVec.y) { maxVec.y = mult.y; } if (mult.z < minVec.z) { minVec.z = mult.z; } if (mult.z > maxVec.z) { maxVec.z = mult.z; } } minVec.x += uVar6 - ((s32)minVec.x % uVar6); minVec.z += uVar6 - ((s32)minVec.z % uVar6); maxVec.x -= (s32)maxVec.x % uVar6; maxVec.z -= (s32)maxVec.z % uVar6; s32 specialItemDropId = getParams2UpperByte(); fillUpperParams2Byte(); mVec3_c groundCheckPos = mPosition; groundCheckPos.y += 200; u8 lightingCode = dBgS::GetInstance()->GetLightingCode(&groundCheckPos); s32 rnd = 0; // special item drop for farore's tear, unused if (specialItemDropId == 10) { if (getFromParams(0x15, 0xF) == 0) { s32 tmp = initializeCircle( *tmpGrassModel, &minVec, &maxVec, xzDisplacement, &boundsMtx, specialItemDropId, affectedByTimeshift, activeInPresent, -1, mMassSubtype, lightingCode, xzStep ); rnd = cM::rndInt(tmp); } else { s32 tmp = initializeBox( *tmpGrassModel, &minVec, &maxVec, xzDisplacement, &boundsMtx, specialItemDropId, affectedByTimeshift, activeInPresent, -1, mMassSubtype, lightingCode, xzStep ); rnd = cM::rndInt(tmp); } } if (getFromParams(0x15, 0xF) == 0) { initializeCircle( *tmpGrassModel, &minVec, &maxVec, xzDisplacement, &boundsMtx, specialItemDropId, affectedByTimeshift, activeInPresent, rnd, mMassSubtype, lightingCode, xzStep ); } else { initializeBox( *tmpGrassModel, &minVec, &maxVec, xzDisplacement, &boundsMtx, specialItemDropId, affectedByTimeshift, activeInPresent, rnd, mMassSubtype, lightingCode, xzStep ); } return retVar; } int dTgMassObj_c::doDelete() { if (sInstance == this) { GrassModel **modelP = sGrassModels; for (s32 i = 0; i < 5; i++, modelP++) { delete *modelP; *modelP = nullptr; } delete sAllocator; sAllocator = nullptr; sInstance = nullptr; } return SUCCEEDED; } int dTgMassObj_c::actorExecute() { dLightEnv_c &lightEnv = dLightEnv_c::GetInstance(); if (sInstance == nullptr) { return NOT_READY; } else { dCcS::GetInstance()->GetMassMng().Prepare(); GrassModel **modelP = sGrassModels; for (s32 i = 0; i < 5; i++) { if ((cCounter_c::GetGameFrame() & 1) == 0) { (*modelP)->calcCutCounter(); } if (lightEnv.getfield_0x5D44() > 0.f) { (*modelP)->setPriorityDraw(0x1C, 0); } else { (*modelP)->setPriorityDraw(0x7F, 0); } (*modelP++)->update(); } return SUCCEEDED; } } int dTgMassObj_c::draw() { if (sInstance == nullptr) { return NOT_READY; } else { GrassModel **modelP = sGrassModels; for (s32 i = 0; i < 5; i++) { (*modelP++)->entry(); } return SUCCEEDED; } } void dTgMassObj_c::unloadRoom(u16 roomid) { GrassModel **modelP = sGrassModels; for (s32 i = 0; i < 5; i++) { (*modelP++)->unloadRoom(roomid); } } int dTgMassObj_c::initializeCircle( GrassModel *grassModel, mVec3_c *bbStart, mVec3_c *bbEnd, s32 xzDisplacement, mMtx_c *param_7, s32 specialItemDropIdParam, undefined4 affectedByTimeshift, u8 activeInPresent, int randInt, s32 massObjSubtype, u8 lightingCode, f32 fParam ) { f32 p4z = bbStart->z; s32 iVar3 = 0; while (p4z < bbEnd->z) { f32 p4x = bbStart->x; while (p4x < bbEnd->x) { mVec3_c position(p4x + cM::rndFX(xzDisplacement), bbEnd->y, p4z + cM::rndFX(xzDisplacement)); mVec3_c multiplied; param_7->multVec(position, multiplied); mVec3_c nul(0, 0, 0); multiplied.y = 0.f; f32 length = multiplied.distance(nul); if (length < 0.5f && dBgS_ObjGndChk::CheckPos(position) && dBgS_ObjGndChk::GetGroundHeight() >= (bbStart->y - 100.f)) { if (randInt >= 0) { position.y = dBgS_ObjGndChk::GetGroundHeight(); s32 specialItemDropId = specialItemDropIdParam; if (specialItemDropId == 10) { if (randInt == iVar3) { specialItemDropIdParam = 0xFF; } else { specialItemDropId = 0xFF; } } if (!grassModel->spawnSingleGrass( 0, getRoomId(), &position, 0, specialItemDropId, affectedByTimeshift, activeInPresent, massObjSubtype, lightingCode )) { return 0; } } iVar3++; } p4x += fParam; } p4z += fParam; } return iVar3; } int dTgMassObj_c::initializeBox( GrassModel *grassModel, mVec3_c *minVec, mVec3_c *maxVec, int xzDisplacement, mMtx_c *boundsMtx, int specialItemDropIdParam, undefined4 affectedByTimeshift, u8 activeInPresent, int randInt, s32 massObjSubtype, u8 lightingCode, f32 fParam ) { f32 p4z = minVec->z; s32 iVar3 = 0; while (p4z < maxVec->z) { f32 p4x = minVec->x; while (p4x < maxVec->x) { mVec3_c position(p4x + cM::rndFX(xzDisplacement), maxVec->y, p4z + cM::rndFX(xzDisplacement)); mVec3_c multiplied; boundsMtx->multVec(position, multiplied); if (-0.5f <= multiplied.x && multiplied.x <= 0.5f && -0.5f <= multiplied.z && multiplied.z <= 0.5f && dBgS_ObjGndChk::CheckPos(position)) { if (dBgS_ObjGndChk::GetGroundHeight() >= (minVec->y - 100.f)) { if (randInt >= 0) { position.y = dBgS_ObjGndChk::GetGroundHeight(); s32 specialItemDropId = specialItemDropIdParam; if (specialItemDropId == 10) { if (randInt == iVar3) { specialItemDropIdParam = 0xFF; } else { specialItemDropId = 0xFF; } } if (!grassModel->spawnSingleGrass( 0, getRoomId(), &position, 0, specialItemDropId, affectedByTimeshift, activeInPresent, massObjSubtype, lightingCode )) { return 0; } } } iVar3++; } p4x += fParam; } p4z += fParam; } return iVar3; } void GrassModel::remove() { if (mpModelData != nullptr) { delete[] mpModelData; mpModelData = nullptr; } if (mInstanceList != nullptr) { delete[] mInstanceList; mInstanceList = nullptr; } if (mStaticTransformationList != nullptr) { delete[] mStaticTransformationList; mStaticTransformationList = nullptr; } if (mDynamicTransformationList != nullptr) { delete[] mDynamicTransformationList; mDynamicTransformationList = nullptr; } m3d::scnLeaf_c::remove(); } bool GrassModel::setModelInfo( f32 radius, f32 param_2, int param_4, s32 roomCount, u16 instanceListLength, u16 staticTransformationListLength, int dynamicTransformationListLength, undefined1 param_9, s32 opaDrawPrio, u32 xluDrawPrio, mHeapAllocator_c *allocator ) { if (!create(allocator, nullptr)) { return false; } EGG::Heap *heap = allocator->mHeap; mpModelData = new (heap, 4) GrassModelData[param_4]; if (!mpModelData) { remove(); return false; } GrassModelData *modelData = &mpModelData[0]; for (s32 i = 0; i < param_4; i++) { if (!(modelData++)->tryCreateLinkedLists(roomCount, heap)) { remove(); return false; } } mInstanceList = new (heap, 4) dTgMassObjInstance[instanceListLength]; if (!mInstanceList) { remove(); return false; } mStaticTransformationList = new (heap, 4) dTgMassObjTransform[staticTransformationListLength]; if (!mStaticTransformationList) { remove(); return false; } mDynamicTransformationList = new (heap, 4) dTgMassObjTransform[dynamicTransformationListLength]; if (!mDynamicTransformationList) { remove(); return false; } field_0x58 = param_4; mRoomCount = roomCount; mInstanceListLength = instanceListLength; mStaticTransformationListLength = staticTransformationListLength; mDynamicTransformationListLength = dynamicTransformationListLength; mRadius = radius; mRadiusSquared = radius * radius; field_0x54 = param_2; field_0x62 = param_9; { dTgMassObjInstance *itr1 = mInstanceList; s32 i = 0; if (getInstanceListLength() > 0) { for (; i < getInstanceListLength(); i++) { // regswap mFreeInstances.append(itr1++); } } } { dTgMassObjTransform *itr2 = mDynamicTransformationList; s32 i = 0; if (getDynamicTransformListLength() > 0) { for (; i < getDynamicTransformListLength(); i++) { // regswap mAvailableTransforms.append(itr2++); } } } if (opaDrawPrio >= 0) { setPriorityDraw(opaDrawPrio, 0); setOption(nw4r::g3d::ScnObj::OPTION_DISABLE_DRAW_XLU, 1); } else { setPriorityDraw(0, xluDrawPrio); setOption(nw4r::g3d::ScnObj::OPTION_DISABLE_DRAW_OPA, 1); } return true; } void GrassModel::initResForModel(s32 room, nw4r::g3d::ResMat pResMat, nw4r::g3d::ResShp pResShp) { mpModelData[room].initRes(pResMat, pResShp); } // non matching bool GrassModel::spawnSingleGrass( int modelSubtype, u16 roomid, mVec3_c *groundHeight, u16 yRotation, s32 specialItemDropId, int affectedByTimeshift, int activeInPresent, s32 massObjSubtype, u8 lightingCode ) { const dLightEnv_c &lightEnv = dLightEnv_c::GetInstance(); if (mFreeInstances.mCount == 0) { return false; } // almost dTgMassObjInstance &first = *mFreeInstances.GetBeginIter(); mFreeInstances.remove(&first); first.reset(); first.mGroundHeight.set(*groundHeight); first.yRotation = yRotation; first.mSpecialItemDropId = specialItemDropId; first.mDynamicTransform = nullptr; s32 chosen = cM::rndInt(mStaticTransformationListLength); first.mInitPosTransform = &mStaticTransformationList[chosen]; first.field_0x24 = cM::rndFX(0.2f) + 1.f; first.mMassObjSubtype = massObjSubtype; first.mLightingCode = lightingCode; first.mTevColor = lightEnv.GetCurrentSpf().mActorPalette.field_0x02C; if (first.mLightingCode == 0) { first.mTevColor.r = 0.7f * first.mTevColor.r; first.mTevColor.r &= 0xFF; first.mTevColor.g = 0.6f * first.mTevColor.g; first.mTevColor.g &= 0xFF; first.mTevColor.b = 0.7f * first.mTevColor.b; first.mTevColor.b &= 0xFF; } if (affectedByTimeshift) { first.mGrassFlags |= dTgMassObjInstance::TG_MASS_UNK2_TIMESHIFT_RELATED; if (activeInPresent) { first.mActiveInPresent = true; } else { first.mScale = 0.f; } } mpModelData[modelSubtype].addToRoom(roomid, &first); return true; } bool GrassModel::addToRoom(u32 modelSubtype, s32 roomid, dTgMassObjInstance *pInstance) { mpModelData[modelSubtype].addToRoom(roomid, pInstance); if (modelSubtype == 1) { SpecialItemDropMgr::GetInstance()->giveSpecialDropItem( pInstance->mSpecialItemDropId, roomid, &pInstance->mGroundHeight, 0, 0, -1 ); pInstance->mSpecialItemDropId = 0xFF; } return true; } void GrassModel::addToFreeInstances(dTgMassObjInstance *param_2) { mFreeInstances.append(param_2); } void GrassModel::unloadRoom(u16 roomid) { GrassModelData *modelData = mpModelData; for (s32 i = 0; i < field_0x58; i++) { (modelData++)->unloadRoom(this, roomid); } } inline void SetMassMngAttr(f32 radius, f32 height, u8 param_2, u8 param_3) { dCcS::GetInstance()->GetMassMng().SetAttr(radius, height, param_2, param_3); } void GrassModel::update() { dTgMassObjTransform *obj = mStaticTransformationList; for (s32 i = 0; i < mStaticTransformationListLength; obj++, i++) { obj->update(); } SetMassMngAttr(mRadius, mRadius * 2, 0xB, field_0x62); GrassModelData *modelData = mpModelData; for (s32 i = 0; i < field_0x58; i++) { (modelData++)->update(this); } } void GrassModel::draw() { nw4r::math::MTX34 cameraMtx; m3d::getCurrentCamera().GetCameraMtx(&cameraMtx); GrassModelData *data = mpModelData; for (s32 i = 0; i < field_0x58; data++, i++) { data->draw(mRadius, field_0x54, &cameraMtx); } } // non matching dTgMassObjTransform *GrassModel::aquireTransform() { if (mAvailableTransforms.mCount == 0) { return nullptr; } // not exactly, this produces an additional instruction dTgMassObjTransform &first = *mAvailableTransforms.GetBeginIter(); mAvailableTransforms.remove(&first); mAquiredTransforms.append(&first); return &first; } // regalloc, should be equivalent void GrassModel::releaseTransform(dTgMassObjTransform *param2) { mAquiredTransforms.remove(param2); mAvailableTransforms.append(param2); } dTgMassObjTransform::dTgMassObjTransform() { field_0x00 = 0; field_0x04 = 0; mRotXSpeed = 0; mRotY = cM::rndInt(0x10000); mRotX = 0x1000; mQuat.setUnit(); s32 rnd = cM::rndInt(100); for (s32 i = 0; i < rnd; i++) { update(); } } void dTgMassObjTransform::update() { mRotXSpeed -= (s16)(mRotX * 0.005f); mRotXSpeed = cM::minMaxLimit(mRotXSpeed, -0x4B, 0x4B); mRotX += mRotXSpeed; mVec3_c tmp1(0, 1, 0); mVec3_c tmp2(0, 1, 0); tmp2.rotX(mRotX); tmp2.rotY(mRotY); EGG::Quatf quat; quat.makeVectorRotation(tmp1, tmp2); mQuat.slerpTo(quat, 0.5f, mQuat); mQuat.normalise(); mQuat.makeWPositive(); mMtx.fromQuat(mQuat); } void dTgMassObjInstance::releaseDynamicTransform(GrassModel *param_2) { if (mDynamicTransform != nullptr) { param_2->releaseTransform(mDynamicTransform); mDynamicTransform = nullptr; } } void dTgMassObjInstance::reset() { mGrassFlags = 0; mTevColor.a = 0; mTevColor.b = 0; mTevColor.g = 0; mTevColor.r = 0; mActiveInPresent = false; mScale = 1; } // matches besides data void dTgMassObjInstance::getDrawMatrix(mMtx_c *pOut) { if (mDynamicTransform != nullptr) { *pOut = mDynamicTransform->getMtx(); } else if (mInitPosTransform != nullptr) { *pOut = mInitPosTransform->getMtx(); } else { pOut->YrotS(yRotation); } pOut->setBase(3, mGroundHeight); pOut->scaleM(mScale, mScale, mScale); s32 sure = ((s16)mGroundHeight.x >> 1) & 7; pOut->scaleM(sure * 0.015f + 1.f, sure * 0.015f + 1.f, sure * 0.015f + 1.f); } bool dTgMassObjInstance::checkForHit(GrassModel *param_2, GrassModelData *param_3, u16 roomid) { dCcMassS_HitInf massHitInf; dAcObjBase_c *actor; u32 chk = dCcS::GetInstance()->GetMassMng().Chk(&mGroundHeight, &actor, &massHitInf); if (!checkForHit(chk, massHitInf, actor, param_2, param_3, roomid) && !FUN_80278c70(chk, massHitInf, actor, param_2) && !handleLinkSpinAttack(param_2)) { if (mDynamicTransform != nullptr) { EGG::Quatf &dynQuat = mDynamicTransform->mQuat; f32 fVar2 = (mInitPosTransform->mQuat.dot(dynQuat)); fVar2 *= 0.1f; if (fVar2 < 0.1f) { fVar2 = 0.1f; } dynQuat.slerpTo(mInitPosTransform->mQuat, fVar2, dynQuat); mDynamicTransform->mQuat.normalise(); mDynamicTransform->mQuat.makeWPositive(); mDynamicTransform->mMtx.fromQuat(mDynamicTransform->mQuat); if (mInitPosTransform->mQuat.dot(mDynamicTransform->mQuat) >= 1.f) { param_2->releaseTransform(mDynamicTransform); mDynamicTransform = nullptr; } } return false; } else { return true; } } bool dTgMassObjInstance::checkForHit( u32 param_2, dCcMassS_HitInf ¶m_3, dAcObjBase_c *param_4, GrassModel *param_5, GrassModelData *param_6, undefined4 roomid ) { dAcPy_c *link = dAcPy_c::GetLinkM(); if (link == nullptr) { return false; } if ((param_2 & 1) == 0 || param_4 == nullptr) { return false; } f32 impactDistanceFactor; f32 impactFactor; f32 maybeMaxImpactDistance = 0.f; f32 distance; s32 isNotCut; f32 fVar20; f32 fVar5; f32 fVar4; f32 fVar2; f32 fVar21; mVec3_c localB4(0.f, 1.f, 0.f); cCcD_Obj *hitObj = param_3.GetAtHitObj(); if (hitObj != nullptr) { mVec3_c &p4Pos = param_4->getPosition(); mVec3_c hitPosition = param_4->mPosition; s32 needsLightingRelated = 0; impactFactor = 0.f; if (hitObj->ChkAtType(AT_TYPE_BELLOWS)) { hitPosition = link->mPosition; needsLightingRelated = 1; maybeMaxImpactDistance = 1000.f; impactFactor = cM::rndF(0.5f) + 0.5f; isNotCut = 1; } else if (hitObj->ChkAtType(AT_TYPE_SLINGSHOT)) { maybeMaxImpactDistance = 120.f; impactFactor = cM::rndF(0.06f) + 0.6f; isNotCut = 1; } else if (hitObj->ChkAtType(AT_TYPE_0x200000) || hitObj->ChkAtType(AT_TYPE_BUGNET)) { maybeMaxImpactDistance = 1000.f; impactFactor = cM::rndF(0.2f) + 0.6f; isNotCut = 1; } else if (hitObj->ChkAtType(AT_TYPE_0x40)) { mVec3_c localCC = mGroundHeight; isNotCut = 1; if (std::fabsf(localCC.y - param_4->mPosition.y) < 60.f) { localCC.y = param_4->mPosition.y; f32 dist = localCC.distance(p4Pos); if (dist < 160.f) { isNotCut = 0; } } if (isNotCut == 1) { maybeMaxImpactDistance = 600.f; impactFactor = cM::rndF(0.3f) + 0.3f; } needsLightingRelated = 1; } else if (hitObj->ChkAtType(AT_TYPE_BEETLE)) { mVec3_c localD8 = mGroundHeight; isNotCut = 1; if (std::fabsf(localD8.y - param_4->mPosition.y) < 80.f) { localD8.y = param_4->mPosition.y; f32 dist = localD8.distance(param_4->mPosition); if (dist < 160.f) { isNotCut = 0; } } if (isNotCut == 1) { maybeMaxImpactDistance = 800.f; impactFactor = cM::rndF(0.2f) + 0.5f; } } else if (hitObj->ChkAtType(AT_TYPE_WHIP)) { mVec3_c localE4 = mGroundHeight; hitPosition = *link->anotherThingWithWhip(); if (localE4.distance(hitPosition) < 120.f) { isNotCut = 0; } else { maybeMaxImpactDistance = 300.f; impactFactor = cM::rndF(0.05f) + 0.5f; isNotCut = 1; } } else if (hitObj->ChkAtType(AT_TYPE_0x800000)) { isNotCut = 0; } else if (hitObj->ChkAtType(AT_TYPE_ARROW)) { mVec3_c localE4 = mGroundHeight; if (localE4.distance(param_4->mPosition) < 80.f) { isNotCut = 0; } else { maybeMaxImpactDistance = 200.f; impactFactor = cM::rndF(0.04f) + 0.4f; isNotCut = 1; } } else if (hitObj->ChkAtType(AT_TYPE_BOMB)) { mVec3_c localE4 = mGroundHeight; if (localE4.distance(param_4->mPosition) < 300.f) { isNotCut = 0; } else { maybeMaxImpactDistance = 5000.f; impactFactor = cM::rndF(0.09f) + 0.9f; isNotCut = 1; } } else if (hitObj->ChkAtType(AT_TYPE_SWORD)) { s32 currentSword = daPlayerActBase_c::getCurrentSwordTypeInline(); fVar20 = 120.f; fVar21 = 260.f; fVar5 = 430.f; fVar4 = 305.f; fVar2 = 1000.f; if (currentSword == 0) { fVar5 = 200.f; fVar21 = 150.f; fVar2 = 180.f; fVar4 = 200.f; } else if (currentSword == 1) { fVar21 = 170.f; fVar5 = 400.f; fVar4 = 250.f; fVar2 = 200.f; } else if (currentSword == 2 || currentSword == 3) { fVar20 = 130.f; fVar21 = 200.f; fVar5 = 420.f; fVar4 = 290.f; fVar2 = 250.f; } hitPosition = link->mPosition; distance = mGroundHeight.distance(hitPosition); if ((s32)link->getSpecificAttackDirection() == dAcPy_c::ATTACK_DIRECTION_DOWN || (s32)link->getSpecificAttackDirection() == dAcPy_c::ATTACK_DIRECTION_UP || (s32)link->getSpecificAttackDirection() == dAcPy_c::ATTACK_DIRECTION_STAB) { if (distance < fVar20) { isNotCut = 0; } else { maybeMaxImpactDistance = 300.f; impactFactor = cM::rndF(0.05f) + 0.5f; isNotCut = 1; if (distance >= fVar20 + 60.f) { return true; } } } else { if (mMassObjSubtype == 8 || mMassObjSubtype == 9) { fVar21 = fVar20 * 0.8f; hitPosition = dAcPy_c::GetLink()->getSwordPos(); distance = mGroundHeight.distance(hitPosition); if ((s32)link->getSpecificAttackDirection() != dAcPy_c::ATTACK_DIRECTION_DOWNRIGHT && (s32)link->getSpecificAttackDirection() != dAcPy_c::ATTACK_DIRECTION_DOWNLEFT) { return true; } if (distance >= fVar21) { return true; } } if (link->isAttackingSpin()) { if (link->checkSwordAndMoreStates(daPlayerActBase_c::SKYWARD_STRIKE_ACTIVE)) { fVar21 = fVar5; } else { fVar21 = fVar4; } } if (distance < fVar21) { isNotCut = 0; } else if (distance < fVar2) { maybeMaxImpactDistance = 5000.f; impactFactor = cM::rndF(0.5f) + 0.5f; isNotCut = 1; } else { return true; } } } else { isNotCut = 0; } if (isNotCut == 1) { // grass is not cut, just move f32 fVar20 = cM::rndFX(0.025f) + 0.05f; if (!hitObj->ChkAtType(AT_TYPE_WHIP) && !hitObj->ChkAtType(AT_TYPE_BELLOWS)) { if ((!link->isAttackingSpin())) { hitPosition = param_4->mPosition; } } f32 tempDistance = mGroundHeight.distance(hitPosition); if (tempDistance > maybeMaxImpactDistance) { tempDistance = maybeMaxImpactDistance; } // check this for regalloc impactDistanceFactor = (1.f - (tempDistance / maybeMaxImpactDistance) * (tempDistance / maybeMaxImpactDistance)); if (mDynamicTransform == nullptr) { mDynamicTransform = param_5->aquireTransform(); if (mDynamicTransform == nullptr) { return false; } mDynamicTransform->mQuat.set(mInitPosTransform->mQuat); mDynamicTransform->mMtx.fromQuat(mInitPosTransform->mQuat); } if (param_4 == nullptr) { return false; } mVec3_c local108 = mGroundHeight - hitPosition; local108.rotY(0x4000); local108.normalize(); mVec3_c local114 = hitObj->mAt.mVec; mVec3_c local120 = local114; if (needsLightingRelated) { fVar20 = cM::rndF(0.5f) + 0.15f; hitPosition.y += 50.f; dLightEnv_c::GetPInstance()->get_vectle_calc(&hitPosition, &mGroundHeight, &local120); if (!local120.normalizeRS()) { local120.x = 1.f; local120.z = 0.f; } if (!local120.normalizeRS()) { local120 = localB4; } // regalloc } else if (cM::isZero(local114.getSquareMag())) { local120 = mGroundHeight - hitPosition; if (!local120.normalizeRS()) { local120.x = 1.f; local120.z = 0.f; } local120.y = 0.5f; if (!local120.normalizeRS()) { local120 = localB4; } fVar20 = cM::rndF(0.5f) + 0.5f; } else { local114.y = 0.f; local114.normalize(); f32 fVar2 = local108.inprodXZ(local114); if (fVar2 != 0.f) { local120.y = 2.f / fVar2; } if (local108.inprodXZ(local114) >= 0.f) { local120.rotY(-0x4000); } else { local120.rotY(0x4000); } if (!local120.normalizeRS()) { local120 = localB4; } } mQuat_c local130; local130.makeVectorRotation(localB4, local120); local130.slerpTo(mInitPosTransform->mQuat, 1.f - impactFactor * impactDistanceFactor, local130); local130.normalise(); local130.makeWPositive(); mQuat_c local140; mDynamicTransform->mMtx.toQuat(local140); local140.slerpTo(local130, fVar20, local140); local140.normalise(); local140.makeWPositive(); mDynamicTransform->mQuat.set(local130); mDynamicTransform->mMtx.fromQuat(local140); } else if (hitObj->ChkAtType(AT_TYPE_WIND)) { if (mDynamicTransform == nullptr) { mDynamicTransform = param_5->aquireTransform(); if (mDynamicTransform == nullptr) { return false; } mDynamicTransform->mQuat.set(mInitPosTransform->mQuat); mDynamicTransform->mMtx.fromQuat(mInitPosTransform->mQuat); } if (param_4 == nullptr) { return false; } mVec3_c local150 = mGroundHeight - param_4->mPosition; mQuat_c local160; local150.y = 0.f; local150.x *= (cM::rndFX(0.5f) + 0.5f); local150.z *= (cM::rndFX(0.5f) + 0.5f); if (!local150.normalizeRS()) { local150 = localB4; } local160.makeVectorRotation(localB4, local150); // float regalloc f32 fVar2 = (local160.dot(mDynamicTransform->mQuat)); fVar2 *= 0.1f; if (fVar2 < 0.1f) { fVar2 = 0.1f; } local160.slerpTo2(cM::rndF(0.4f) + 0.3f, mInitPosTransform->mQuat, local160); local160.normalise(); local160.makeWPositive(); mDynamicTransform->mQuat.slerpTo(local160, fVar2, mDynamicTransform->mQuat); mDynamicTransform->mQuat.normalise(); if (mDynamicTransform->mQuat.w < 0) { mDynamicTransform->mQuat.multScalar(-1); } mDynamicTransform->mMtx.fromQuat(mDynamicTransform->mQuat); } else { // grass is cut param_6->removeFromRoom(roomid, this); param_5->addToRoom(1, roomid, this); mGrassFlags |= TG_MASS_UNK2_IS_CUT; if (mDynamicTransform != nullptr) { // this is wrong... yRotation = *(u16 *)mDynamicTransform->mRotY.ref(); param_5->releaseTransform(mDynamicTransform); mDynamicTransform = nullptr; } yRotation = *(u16 *)mInitPosTransform->mRotY.ref(); mInitPosTransform = nullptr; if (param_5->mCutCounter < 0x19) { param_5->mCutCounter++; mVec3_c local16C(mGroundHeight.x, mGroundHeight.y, mGroundHeight.z); mColor local17C = 0xFFFFFFFF; f32 waterHeight = dScGame_c::getCamera()->getWaterHeight(); local17C.r = mTevColor.r; local17C.g = mTevColor.g; local17C.b = mTevColor.b; local17C.a = mTevColor.a; if (local16C.y + 100.f < waterHeight && mMassObjSubtype <= 5) { if (dScGame_c::isCurrentStage("D100")) { mMassObjSubtype = 6; } else { mMassObjSubtype = 5; } } dEmitterBase_c *ret; switch (mMassObjSubtype) { case 0: dJEffManager_c::createMassObjEffect( PARTICLE_RESOURCE_ID_MAPPING_120_, local16C, nullptr, &local17C ); break; case 1: dJEffManager_c::createMassObjEffect( PARTICLE_RESOURCE_ID_MAPPING_121_, local16C, nullptr, &local17C ); break; case 2: dJEffManager_c::createMassObjEffect( PARTICLE_RESOURCE_ID_MAPPING_122_, local16C, nullptr, &local17C ); break; case 3: dJEffManager_c::createMassObjEffect( PARTICLE_RESOURCE_ID_MAPPING_123_, local16C, nullptr, &local17C ); break; case 4: dJEffManager_c::createMassObjEffect( PARTICLE_RESOURCE_ID_MAPPING_124_, local16C, nullptr, &local17C ); break; case 5: dJEffManager_c::createMassObjEffect( PARTICLE_RESOURCE_ID_MAPPING_687_, local16C, nullptr, &local17C ); break; case 6: dJEffManager_c::createMassObjEffect( PARTICLE_RESOURCE_ID_MAPPING_800_, local16C, nullptr, &local17C ); break; case 7: ret = dJEffManager_c::spawnEffect( PARTICLE_RESOURCE_ID_MAPPING_838_, local16C, nullptr, nullptr, &local17C, nullptr, 0, 0 ); if (ret != nullptr) { ret->bindShpEmitter(dJEffManager_c::FlowerA00F, true); } ret = dJEffManager_c::spawnEffect( PARTICLE_RESOURCE_ID_MAPPING_839_, local16C, nullptr, nullptr, &local17C, nullptr, 0, 0 ); if (ret != nullptr) { ret->bindShpEmitter(dJEffManager_c::FlowerA00L, true); } break; case 8: ret = dJEffManager_c::spawnEffect( PARTICLE_RESOURCE_ID_MAPPING_840_, local16C, nullptr, nullptr, &local17C, nullptr, 0, 0 ); if (ret != nullptr) { ret->bindShpEmitter(dJEffManager_c::FlowerB00, true); } break; case 9: ret = dJEffManager_c::spawnEffect( PARTICLE_RESOURCE_ID_MAPPING_841_, local16C, nullptr, nullptr, &local17C, nullptr, 0, 0 ); if (ret != nullptr) { ret->bindShpEmitter(dJEffManager_c::FlowerB01, true); } break; } dSndSmallEffectMgr_c::GetInstance()->playSoundAtPosition(SE_O_GRASS_CUT, mGroundHeight); } } } return true; } // non matching bool dTgMassObjInstance::FUN_80278c70(u32 param_2, dCcMassS_HitInf ¶m_3, dAcBase_c *param_4, GrassModel *param_5) { mVec3_c local54 = mGroundHeight; f32 fVar12 = 1000.f; s32 iVar11 = 0; f32 fVar1 = 0.55f; if ((param_2 & 2) == 0) { return false; } if (param_4 == nullptr) { param_4 = dAcPy_c::GetLinkM(); } if (param_3.GetCoHitObj()->ChkCo_0x10() && dAcPy_c::GetLink() != nullptr) { mVec3_c local60 = mGroundHeight; mVec3_c local6C; if (dAcPy_c::GetLink()->isUsingBugnet()) { local6C = dAcPy_c::GetLink()->getBugNetPos(); } else { local6C = dAcPy_c::GetLink()->vt_0x278(); local6C += (dAcPy_c::GetLink()->getSwordPos() - dAcPy_c::GetLink()->vt_0x278()) * 0.5f; } local60.y += 60.f; local54 = local60 - local6C; fVar12 = local60.distance(local6C); if (fVar12 > 40.f) { iVar11 = 1; } else { fVar12 = 1 - fVar12 / 250.f; } } else { iVar11 = param_3.GetCoHitObj()->ChkCo_0x2() ? 1 : 2; } if (iVar11 != 0) { f32 fVar13; f32 fVar12_2 = 100.f; if (iVar11 == 2) { fVar12_2 = 200.f; fVar1 = 0.8f; } f32 radSq = param_5->mRadiusSquared; local54 = mGroundHeight - param_4->mPosition; if (local54.squareMagXZ() > radSq) { return false; } f32 distance = mGroundHeight.distance(param_4->getPosition()); fVar13 = distance; if (distance > fVar12_2) { fVar13 = fVar12_2; } else { if (iVar11 == 1) { param_4->getSoundSource()->holdSound(SE_L_GRASS_RUSTLE_LV); } } fVar12 = 1 - fVar13 / fVar12_2; } mVec3_c local78(0.f, 1.f, 0.f); if (mDynamicTransform == nullptr) { mDynamicTransform = param_5->aquireTransform(); if (mDynamicTransform == nullptr) { return false; } mDynamicTransform->mQuat.set(mInitPosTransform->mQuat); mDynamicTransform->mMtx.fromQuat(mInitPosTransform->mQuat); } // regalloc if (cM::isZero(local54.getSquareMag())) { local54.set(1.f, 0.5f, 0.f); } mQuat_c local88; mVec3_c local98 = local54; if (!local98.normalizeRS()) { local98.x = 1.f; local98.z = 0.f; } local98.y = 0.5f; if (!local98.normalizeRS()) { local98 = local78; } local88.makeVectorRotation(local78, local98); local88.slerpTo(mInitPosTransform->mQuat, 1 - fVar1 * fVar12, local88); local88.normalise(); local88.makeWPositive(); mQuat_c localA8; mDynamicTransform->mMtx.toQuat(localA8); localA8.slerpTo(local88, cM::rndF(0.2f) + 0.2f, localA8); localA8.normalise(); localA8.makeWPositive(); mDynamicTransform->setQuat(local88); mDynamicTransform->setMtxFromQuat(localA8); return true; } // non matching bool dTgMassObjInstance::handleLinkSpinAttack(GrassModel *param_2) { const dAcPy_c *link = dAcPy_c::GetLink(); if (link == nullptr) { return false; } if (!link->isAttackingSpin()) { return false; } mVec3_c local3C; dAcPy_c::GetLink()->getPostionDifferenceOut(mGroundHeight, local3C); f32 dist = mGroundHeight.distance(dAcPy_c::GetLinkM()->getPosition()); f32 comparison = 350.f; if (link->isAttackingDown()) { comparison = 150.f; } else if (link->checkSwordAndMoreStates(daPlayerActBase_c::SKYWARD_STRIKE_ACTIVE)) { comparison = 450.f; } if (dist > comparison) { return false; } mVec3_c local48(0.f, 1.f, 0.f); if (mDynamicTransform == nullptr) { mDynamicTransform = param_2->aquireTransform(); if (mDynamicTransform == nullptr) { return false; } mDynamicTransform->mQuat.set(mInitPosTransform->mQuat); mDynamicTransform->mMtx.fromQuat(mInitPosTransform->mQuat); } // regalloc if (cM::isZero(local3C.getSquareMag())) { local3C.set(1.f, 0.5f, 0.f); } mQuat_c local58; mVec3_c local68 = local3C; if (!local68.normalizeRS()) { local68.x = 1.f; local68.z = 0.f; } local68.y = 0.5f; if (!local68.normalizeRS()) { local68 = local48; } local58.makeVectorRotation(local48, local68); local58.slerpTo2(cM::rndF(0.5f), mInitPosTransform->mQuat, local58); local58.normalise(); local58.makeWPositive(); mQuat_c local78; mDynamicTransform->mMtx.toQuat(local78); local78.slerpTo(local58, cM::rndF(0.3f) + 0.5f, local78); local78.normalise(); local78.makeWPositive(); mDynamicTransform->mQuat.set(local58); mDynamicTransform->setMtxFromQuat(local78); return true; } extern "C" bool fn_801BB700(EGG::Quatf *, f32); bool dTgMassObjInstance::isHidden(f32 param2, f32 param3) { bool uVar1 = mSpecialItemDropId != 10; if (uVar1) { mVec3_c tmp2(mGroundHeight.x, mGroundHeight.y + param2, mGroundHeight.z); EGG::Quatf tmp(param2 + 700.f, tmp2); uVar1 = fn_801BB700(&tmp, param3); } if (uVar1) { mGrassFlags |= dTgMassObjInstance::TG_MASS_UNK2_IS_HIDDEN; return true; } else { mGrassFlags &= ~dTgMassObjInstance::TG_MASS_UNK2_IS_HIDDEN; return false; } } bool dTgMassObjInstance::handleTimeshiftZone() { bool ret = true; f32 fVar2 = dTimeAreaMgr_c::GetInstance()->checkPositionIsInPastState(-1, mGroundHeight, nullptr, 8.f); if (mActiveInPresent == false) { sLib::addCalc(&mScale, fVar2, 0.5f, 0.2f, 0.01f); if (mScale <= 0) { ret = false; } } else if (mActiveInPresent == true) { sLib::addCalc(&mScale, 1.f - fVar2, 0.5f, 0.2f, 0.01f); if (mScale <= 0) { ret = false; } } return ret; } bool GrassModelData::tryCreateLinkedLists(s32 entrycount, EGG::Heap *heap) { mLinkedLists = new (heap, 4) dTgMassObjInstanceList[entrycount]; if (mLinkedLists == nullptr) { destroyLinkedLists(); return false; } else { mLinkedListsCount = entrycount; return true; } } void GrassModelData::destroyLinkedLists() { if (mLinkedLists != nullptr) { delete[] mLinkedLists; mLinkedLists = nullptr; } } void GrassModelData::unloadRoom(GrassModel *param_2, int roomid) { dTgMassObjInstanceList &gll = mLinkedLists[roomid]; dTgMassObjInstanceList::Iterator it = gll.GetBeginIter(); while (it != gll.GetEndIter()) { dTgMassObjInstance &lst = *it; ++it; lst.releaseDynamicTransform(param_2); gll.remove(&lst); param_2->addToFreeInstances(&lst); } } void GrassModelData::initRes(nw4r::g3d::ResMat pResMat, nw4r::g3d::ResShp pResShp) { mResMat = pResMat; mResShp = pResShp; nw4r::g3d::ResMatChan chan = mResMat.GetResMatChan(); chan.GXSetChanAmbColor(GX_COLOR0, mColor(0xFF, 0xFF, 0xFF, 0xFF)); } void GrassModelData::addToRoom(s32 room, dTgMassObjInstance *p3) { mLinkedLists[room].append(p3); } // regalloc void GrassModelData::removeFromRoom(s32 room, dTgMassObjInstance *p3) { mLinkedLists[room].remove(p3); } void GrassModelData::update(GrassModel *param2) { mVec3_c groundHeight(999999.f, 999999.f, 999999.f); dLightEnv_c &lightEnv = dLightEnv_c::GetInstance(); bool bVar1 = false; s32 count = 0; f32 minDist = lightEnv.getfield_0x38C0(); dTgMassObjInstanceList *grassList = mLinkedLists; for (s32 roomid = 0; roomid < mLinkedListsCount; roomid++, grassList++) { if (grassList->mCount == 0 || dStage_c::GetInstance()->getRoom(roomid)->checkFlag(4)) { continue; } dTgMassObjInstanceList::Iterator it = grassList->GetBeginIter(); while (it != grassList->GetEndIter()) { dTgMassObjInstance &lst = *it; ++it; if ((lst.mGrassFlags & dTgMassObjInstance::TG_MASS_UNK2_TIMESHIFT_RELATED) && !lst.handleTimeshiftZone()) { lst.mGrassFlags |= dTgMassObjInstance::TG_MASS_UNK2_IS_HIDDEN; } else { if ((lst.mGrassFlags & dTgMassObjInstance::TG_MASS_UNK2_IS_HIDDEN) == 0 && (lst.mGrassFlags & dTgMassObjInstance::TG_MASS_UNK2_IS_CUT) == 0) { lst.checkForHit(param2, this, roomid); } if (lightEnv.getfield_0x38B0() == 1) { if (lightEnv.getfield_0x38B4().distance(lst.mGroundHeight) < minDist) { minDist = lightEnv.getfield_0x38B4().distance(lst.mGroundHeight); groundHeight = lst.mGroundHeight; } if (lightEnv.getfield_0x38B4().distance(lst.mGroundHeight) < lightEnv.getfield_0x38C0()) { bVar1 = true; count++; } } } } } if (lightEnv.getfield_0x38B0() == 1) { lightEnv.setfield_0x38B0(0); if (bVar1) { lightEnv.getfield_0x38C8() = groundHeight; } lightEnv.setfield_0x38C4(count); } } extern void LoadMaterial( nw4r::g3d::ResMat mat, u32 ctrl, nw4r::g3d::Draw1Mat1ShpSwap *pSwap, nw4r::g3d::G3DState::IndMtxOp *pIndMtxOp, bool bIgnoreMaterial ); void GrassModelData::draw(f32 param_1, f32 param_2, nw4r::math::MTX34 *pMtx) { mVec3_c cameraPosition = dScGame_c::getCamera()->getPosition(); bool isInFaronWoods = dScGame_c::isCurrentStage("F100"); nw4r::g3d::ResMatMisc miscData(mResMat.GetResMatMisc()); miscData.SetLightSetIdx(1); miscData.SetFogIdx(0); LoadMaterial(mResMat, 0, nullptr, nullptr, false); nw4r::g3d::G3DState::LoadResShpPrePrimitive(mResShp); const static u32 fifoMtx[8] = { GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, }; nw4r::g3d::fifo::GDSetCurrentMtx(fifoMtx); GXSetCurrentMtx(0); dTgMassObjInstanceList *grassList = mLinkedLists; for (s32 roomid = 0; roomid < mLinkedListsCount; roomid++, grassList++) { if (grassList->mCount == 0 || dStage_c::GetInstance()->getRoom(roomid)->checkFlag(4)) { continue; } dTgMassObjInstanceList::Iterator it = grassList->GetBeginIter(); for (; it != grassList->GetEndIter(); ++it) { dTgMassObjInstance &lst = *it; if (!lst.isHidden(param_1, param_2)) { mMtx_c local160; lst.getDrawMatrix(&local160); f32 cameraDist = lst.mGroundHeight.distance(cameraPosition); f32 fVar6 = param_2 * 0.6f; if (cameraDist > fVar6) { f32 fVar7 = (cameraDist - fVar6) / (param_2 - fVar6); if (fVar7 > 1.f) { fVar7 = 1.f; } if (fVar7 < 0.f) { fVar7 = 0.f; continue; } else { f32 scale = 1. - fVar7; // double local160.scaleM(scale, scale, scale); } } if (isInFaronWoods && lst.mGroundHeight.x <= -5200.f && lst.mGroundHeight.x >= -5500.f && lst.mGroundHeight.z <= -5900.f && lst.mGroundHeight.z >= -6100.f) { local160.scaleM(1.f, 1.5f, 1.f); } f32 fVar7 = ((s32)(lst.mGroundHeight.x + lst.mGroundHeight.z) & 0x1F) / 31.f; f32 scale = fVar7 * 0.1f + 1.f; local160.scaleM(scale, scale, scale); local160.YrotM(fVar7 * 65535.f); MTXConcat(*pMtx, local160, local160); GXLoadPosMtxImm(local160, 0); GXLoadNrmMtxImm(local160, 0); GXSetTevColorS10(GX_TEVREG1, lst.mTevColor); mResShp.CallPrimitiveDisplayList(false); } } } }