From ceab259f64d6d87804fc5e48e722f5234b0f4719 Mon Sep 17 00:00:00 2001 From: MJ Date: Thu, 27 Nov 2025 15:52:59 +1100 Subject: [PATCH 1/8] Implement missing `JSUList::getNth()` function. This particular piece of code was lifted from an existing implementation of the Legend of Zelda: Skyward Sword decompilation project. This function is just getNthLink() without the need to cast the object's type manually, as it's implied through the data type provided in the declaration via templates. --- include/JSystem/JSupport/JSUList.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/JSystem/JSupport/JSUList.h b/include/JSystem/JSupport/JSUList.h index ab6ad09..303cc71 100644 --- a/include/JSystem/JSupport/JSUList.h +++ b/include/JSystem/JSupport/JSUList.h @@ -72,6 +72,7 @@ class JSUList : public JSUPtrList JSULink *getFirst() const { return (JSULink *)getFirstLink(); } JSULink *getLast() const { return (JSULink *)getLastLink(); } + JSULink *getNth(u32 index) const { return (JSULink *)getNthLink(index); } JSULink *getEnd() const { return nullptr; } u32 getNumLinks() const { return mLinkCount; } From bfb708ff113b01d5527afb96abe539773b7a3428 Mon Sep 17 00:00:00 2001 From: MJ Date: Thu, 27 Nov 2025 18:01:44 +1100 Subject: [PATCH 2/8] Add `CrsArea::getArea()` inline function. --- include/Kaneshige/Course/CrsArea.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/Kaneshige/Course/CrsArea.h b/include/Kaneshige/Course/CrsArea.h index 8c8415e..696460e 100644 --- a/include/Kaneshige/Course/CrsArea.h +++ b/include/Kaneshige/Course/CrsArea.h @@ -28,6 +28,7 @@ class CrsArea void searchLight(const JGeometry::TVec3f &p1) { search(7, p1); } bool isInside() const { return mArea != nullptr; } + Course::Area *getArea() const { return mArea; } f32 getRate() const { return mRate; } f32 getShadowRate() const { return getRate(); } From fb29ecb4e0213f1a3e73b0916fd04c289b5c8df4 Mon Sep 17 00:00:00 2001 From: MJ Date: Thu, 27 Nov 2025 18:03:57 +1100 Subject: [PATCH 3/8] Initial creation of StringObj Header file. --- include/Sato/StringObj.h | 105 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 include/Sato/StringObj.h diff --git a/include/Sato/StringObj.h b/include/Sato/StringObj.h new file mode 100644 index 0000000..7ac109b --- /dev/null +++ b/include/Sato/StringObj.h @@ -0,0 +1,105 @@ +#ifndef STRINGOBJ_H +#define STRINGOBJ_H + +#include "Inagaki/GameSoundMgr.h" +#include "JSystem/J3DU/J3DUClipper.h" +#include "JSystem/JGeometry/Vec.h" +#include "JSystem/JSupport/JSUList.h" +#include "Kaneshige/DarkAnmMgr.h" +#include "Kaneshige/ExModel.h" + +// Forward declarations +class ExStringNodeManager; +class StringNodeManager; + +class StringObj { + StringObj(u8, bool); + virtual ~StringObj() { return; }; + void createModel(JKRSolidHeap *, u32); + void loadmodel(J3DModelData *); + void reset(); + virtual void calc(); + void update(); + void setCurrentViewNo(u32); + void drawSimpleModel(u32, Mtx, J3DUClipper *, Mtx); + +public: + // VTBL: 0x0 + StringNodeManager *mStringNodeMgr; // 0x4 + ExModel *mExModel; // 0x8 + u32 _c; + f32 mScale; // 0x10 + Mtx *_14; + u8 _18; + u8 _19[3]; // Padding? + ItemDarkAnmPlayer **mItemDarkAnmPlayer; // 0x1c +}; + +class StringNode { +public: + StringNode(); + ~StringNode(); + void calc(); + + JGeometry::TVec3f mVel; // 0x0 + JGeometry::TVec3f mPos; // 0xc + JGeometry::TVec3f _18; // 0x18 + JGeometry::TVec3f _24; // 0x24 + u8 _30; // 0x30 + u8 _31; // 0x31 + u8 _32[2]; // Padding? + f32 _34; // 0x34 + JSULink _38; // 0x38 +}; + +class StringNodeManager { +public: + StringNodeManager(u8, f32, bool, bool, u8); + virtual ~StringNodeManager(); + + void setAllNodePos(const JGeometry::TVec3f &); + void calc(); + virtual void calcBetweenNodePosAll(f32); + void calcBetweenNodePos(StringNode *, StringNode *, f32, f32); + void resetNodeAll(JGeometry::TVec3f *); + void moveNodeAll(); + void doAirFricG(f32, f32); + void doHeightCol(); + void getNodeVel(u32, JGeometry::TVec3f *); + void getNodePos(u32, JGeometry::TVec3f *); + void addNodeVel(u32, JGeometry::TVec3f); + void setNodeVel(u32, JGeometry::TVec3f); + void addNodePos(u32, JGeometry::TVec3f); + void setNodePos(u32, JGeometry::TVec3f); + + // VTBL: 0x0 + JSUList mStrNodeList; // 0x4 + f32 _10; + f32 _14; + f32 _18; + f32 _1c; + f32 _20; // Scaling multiplier of some sort. + f32 _24; // Something related to distance calculations...? + Vec _28; + StringNode *_34; + GameAudio::ObjectSoundMgr *mObjSoundMgr; // 0x38 + u32 _3c; + u8 _40; + u8 _41[3]; // Padding? + f32 _44; + CrsGround *mCrsGround; // 0x48 + u8 _4c; + u8 _4d[3]; // Padding? +}; + +class ExStringNodeManager : public StringNodeManager { +public: + ExStringNodeManager(); + ~ExStringNodeManager(); + virtual void calcBetweenNodePosAll(f32); + void setNodeLengthAll(f32); + + f32 _50; +}; + +#endif // STRINGOBJ_H From 7948dec3e21690c358d4051efd892a726b7ffcf3 Mon Sep 17 00:00:00 2001 From: MJ Date: Thu, 27 Nov 2025 18:44:10 +1100 Subject: [PATCH 4/8] Initial Commit of StringObj --- src/Sato/StringObj.cpp | 320 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 320 insertions(+) diff --git a/src/Sato/StringObj.cpp b/src/Sato/StringObj.cpp index e69de29..6c94787 100644 --- a/src/Sato/StringObj.cpp +++ b/src/Sato/StringObj.cpp @@ -0,0 +1,320 @@ +#include "Sato/StringObj.h" +#include "Inagaki/GameSoundMgr.h" +#include "JSystem/JGeometry/Vec.h" +#include "JSystem/JKernel/JKRHeap.h" +#include "JSystem/JMath/JMath.h" +#include "JSystem/JSupport/JSUList.h" +#include "JSystem/JUtility/JUTAssert.h" +#include "Kaneshige/Course/Course.h" +#include "Kaneshige/Course/CrsGround.h" +#include "Kaneshige/DarkAnmMgr.h" +#include "Kaneshige/ExModel.h" +#include "Kaneshige/RaceMgr.h" +#include "Kaneshige/SimpleDrawer.h" +#include "Kaneshige/TexLODControl.h" +#include "Sato/ObjUtility.h" +#include "dolphin/mtx.h" +#include "JSystem/JGeometry/Util.h" +#include "types.h" + + +StringNodeManager::StringNodeManager(u8 count, f32 speed, bool makeSoundMgr, bool makeCrsGround, u8 someFlag) { + mCrsGround = nullptr; + _10 = speed; + + _34 = new StringNode[count]; + StringNode* node = _34; + + for (u8 i = 0; i < count; i++, node++) { + mStrNodeList.append(&node->_38); + } + + _24 = 0.8f; + _20 = 0.97f; + _1c = -1.0f; + _14 = 30.0f; + _18 = 0.0f; + _28.x = 0.0f; + _28.y = 1.0f; + _28.z = 0.0f; + _44 = 100.0f; + _3c = 0; + _40 = 0; + + mObjSoundMgr = makeSoundMgr + ? new GameAudio::ObjectSoundMgr(&mStrNodeList.getNth(_40)->getObject()->mPos, nullptr) + : nullptr; + + if (makeCrsGround) { + mCrsGround = new CrsGround(RaceMgr::sRaceManager->getCourse()); + } + + _4c = someFlag; +} + +StringNode::~StringNode() {} + +StringNode::StringNode() : _38(this) { + mVel.zero(); + mPos.zero(); + _18.zero(); + _24.zero(); + _30 = true; + _31 = false; + _34 = 0.0f; +} + +void StringNodeManager::setAllNodePos(const JGeometry::TVec3f ¶m_1) { + for (u8 i = 0; i < mStrNodeList.getNumLinks(); i++) { + setNodePos(i, param_1); + } +} + +void StringNodeManager::calc() { + doAirFricG(_20, _1c); + moveNodeAll(); + calcBetweenNodePosAll(_24); + doHeightCol(); + if (mObjSoundMgr != nullptr) { + mObjSoundMgr->frameWork(); + } +} + +void StringNodeManager::calcBetweenNodePosAll(f32 param_1) { +} + +void StringNodeManager::calcBetweenNodePos(StringNode *strNodeOne, StringNode *strNodeTwo, f32 f1, f32 f2) { + JGeometry::TVec3f vecInput = strNodeTwo->mPos; // TODO: Declaration is needed, but why is this unused...? + JGeometry::TVec3f vecDiff; + JGeometry::TVec3f vecNormalised; + JGeometry::TVec3f vecScaled; + JGeometry::TVec3f vecFinal; + + PSVECSubtract(&strNodeOne->mPos, &strNodeTwo->mPos, &vecDiff); + f32 magnitude = PSVECMag(&vecDiff); + + if (magnitude > f2) { + PSVECNormalize(&vecDiff, &vecNormalised); + PSVECScale(&vecNormalised, &vecScaled, -f2); + PSVECAdd(&strNodeOne->mPos, &vecScaled, &strNodeTwo->mPos); + + f32 fVar1 = vecDiff.squared(); + if (fVar1 <= JGeometry::TUtilf::epsilon()) { + vecFinal.zero(); + } else { + fVar1 = JGeometry::TUtilf::inv_sqrt(fVar1); + vecFinal.scale(fVar1, vecDiff); + } + + f32 fVar2 = (f1 * (magnitude - f2)); + f32 fVar3 = fVar2 > _44 ? _44 : fVar2; + + vecFinal.scale(fVar3); + strNodeTwo->mVel += vecFinal; + } +} + +void StringNodeManager::resetNodeAll(JGeometry::TVec3f *param_1) { +} + +void StringNodeManager::moveNodeAll() { + for (JSULink *link = mStrNodeList.getFirst()->getNext(); link != nullptr; link = link->getNext()) { + StringNode *stringNode = link->getObject(); + PSVECAdd(&stringNode->mPos, &stringNode->mVel, &stringNode->mPos); + } +} + +void StringNodeManager::doAirFricG(f32 friction, f32 globalScale) { + for (JSULink *link = mStrNodeList.getFirst()->getNext(); link != nullptr; link = link->getNext()) { + link->getObject()->mVel.scale((f32)friction); + JMAVECScaleAdd(&_28, &link->getObject()->mVel, &link->getObject()->mVel, globalScale); + } +} + +void StringNodeManager::doHeightCol() { + CrsArea crsArea; + bool hittingRoof; + u8 someCount = 0; + + for (JSULink *link = mStrNodeList.getFirst()->getNext(); link != nullptr; link = link->getNext()) { + if (link->getObject()->_30 != 0) { + hittingRoof = false; + crsArea.search(2, link->getObject()->mPos); + + if (crsArea.getArea() != nullptr) { + JGeometry::TVec3f roofPos; + crsArea.getRoofPosition(&roofPos); + if ((link->getObject()->mPos.y + 100.0f) > roofPos.y) { + hittingRoof = true; + link->getObject()->mVel.y = 0.0f; + link->getObject()->mVel.x *= _18; + link->getObject()->mVel.z *= _18; + link->getObject()->mPos.y = roofPos.y - 100.0f; + } + } + + CrsGround crsGround2(RaceMgr::sRaceManager->getCourse()); + CrsGround *crsGround = &crsGround2; + if ((mCrsGround != nullptr) && (someCount == _4c)) { + crsGround = mCrsGround; + } + + crsGround->search(link->getObject()->mPos, link->getObject()->_18); + StringNode *stringNode; + if ((crsGround->getAttribute() != CrsGround::Attr_10) && + (crsGround->getHeight() > link->getObject()->mPos.y - _14)) { + + JGeometry::TVec3f crsGroundNormal; + crsGround->getNormal(&crsGroundNormal); + + f32 crsHeight = crsGround->getHeight(); + + f32 yPos = link->getObject()->mPos.y - crsHeight - _14; + crsGroundNormal *= yPos * _18; + link->getObject()->mVel.add(crsGroundNormal); + + stringNode = link->getObject(); + crsHeight = crsGround->getHeight(); + hittingRoof = true; + stringNode->mPos.y = _14 + crsHeight; + } + + if (hittingRoof) { + link->getObject()->_31 = true; + if ((mObjSoundMgr != nullptr) && (someCount == _40)) { + mObjSoundMgr->setSe(_3c); + } + } else { + link->getObject()->_31 = false; + } + crsGround->getNormal(&link->getObject()->_24); + stringNode = link->getObject(); + stringNode->_18 = stringNode->mPos; + } + someCount++; + } +} + +void StringNodeManager::getNodeVel(u32 num, JGeometry::TVec3f *vel) { +#line 421 + JUT_MINMAX_ASSERT(0, num, mStrNodeList.getNumLinks()); + *vel = mStrNodeList.getNth(num)->getObject()->mVel; +} + +void StringNodeManager::getNodePos(u32 num, JGeometry::TVec3f *pos) { +#line 434 + JUT_MINMAX_ASSERT(0, num, mStrNodeList.getNumLinks()); + *pos = mStrNodeList.getNth(num)->getObject()->mPos; +} + +void StringNodeManager::addNodeVel(u32 num, JGeometry::TVec3f newVel) { +#line 448 + JUT_MINMAX_ASSERT(0, num, mStrNodeList.getNumLinks()); + mStrNodeList.getNth(num)->getObject()->mVel += newVel; +} + +void StringNodeManager::setNodeVel(u32 num, JGeometry::TVec3f newVel) { +#line 461 + JUT_MINMAX_ASSERT(0, num, mStrNodeList.getNumLinks()); + mStrNodeList.getNth(num)->getObject()->mVel = newVel; +} + +void StringNodeManager::addNodePos(u32 num, JGeometry::TVec3f newPos) { +#line 474 + JUT_MINMAX_ASSERT(0, num, mStrNodeList.getNumLinks()); + mStrNodeList.getNth(num)->getObject()->mPos += newPos; +} + +void StringNodeManager::setNodePos(u32 num, JGeometry::TVec3f newPos) { +#line 487 + JUT_MINMAX_ASSERT(0, num, mStrNodeList.getNumLinks()); + mStrNodeList.getNth(num)->getObject()->mPos = newPos; +} + +void ExStringNodeManager::calcBetweenNodePosAll(f32 param_1) { +} + +void ExStringNodeManager::setNodeLengthAll(f32 newLength) { + for (JSULink *link = mStrNodeList.getFirst(); link != nullptr; link = link->getNext()) { + link->getObject()->_34 = newLength; + } +} + +StringObj::StringObj(u8 nodeCount, bool someFlag) { +} + +StringNodeManager::~StringNodeManager() { + delete[] _34; +} + +void StringObj::createModel(JKRSolidHeap *, u32) { + return; +} + +void StringObj::loadmodel(J3DModelData *modelData) { + ExModel *exModel = mExModel; + u32 i = 0; + while (i < mStringNodeMgr->mStrNodeList.getNumLinks()) { + exModel->setModelData(modelData); + RaceMgr::sRaceManager->getCourse()->setFogInfo(exModel); + exModel->setLODBias( + TexLODControl::getManager()->getLODBias(TexLODControl::cLODBiasID_3) + ); + i++; + exModel++; + } +} + +void StringObj::reset() { + for (u32 i = 0; i < mStringNodeMgr->mStrNodeList.getNumLinks(); i++) { + mItemDarkAnmPlayer[i]->reset(); + } +} + +void StringObj::calc() { + +} + +void StringObj::update() { + ExModel *exModel = mExModel; + + for (u32 i = 0; i < mStringNodeMgr->mStrNodeList.getNumLinks() - 1; i++, exModel++) { + exModel->update(0); + } +} + +void StringObj::setCurrentViewNo(u32 viewNo) { + ExModel *exModel = mExModel; + for (u32 num = 0; num < mStringNodeMgr->mStrNodeList.getNumLinks() - 1; num++, exModel++) { + exModel->setCurrentViewNo(viewNo); + + JGeometry::TVec3f pos; + mStringNodeMgr->getNodePos(num, &pos); + + Mtx lightMtx; + ObjUtility::getCamDependLightMtx(viewNo, pos, lightMtx); + mExModel[num].setEffectMtx(lightMtx, 0); + } +} + +void StringObj::drawSimpleModel(u32 p1, Mtx mtx1, J3DUClipper *j3duClipper, Mtx mtx2) { + SimpleDrawer simpleDrawer; + + if (!mExModel->getModelData()) return; + + simpleDrawer.drawInit(mExModel); + + while (simpleDrawer.loadPreDrawSetting()) { + for (u32 num = 0; num < mStringNodeMgr->mStrNodeList.getNumLinks() - 1; num++) { + JGeometry::TVec3f pos; + mStringNodeMgr->getNodePos(num, &pos); + f32 radius = pos.x; // FIX: should be `pos.z`, but this reorders instructions weirdly...? + + mExModel[num].clipBySphere(p1, j3duClipper, mtx1, radius); + mItemDarkAnmPlayer[num]->setTevColor(&mExModel[num]); + mExModel[num].simpleDraw(p1, mtx2, 1); + } + } +} + +ExStringNodeManager::~ExStringNodeManager() {} From dfbecca04cdd2e58944577746196c150bb743278 Mon Sep 17 00:00:00 2001 From: MJ Date: Thu, 27 Nov 2025 23:49:35 +1100 Subject: [PATCH 5/8] Add missing Matrix Inlines `getYDirInline()` and `getZDirInline()` to the matrix class. The `getYDirInline()` inline function is used the in `StringObj::calc()` function, and after a quick GitHub search I've seen a similar implementation for these inlines also exist in the [Super Mario Galaxy (Petari) Decomp](https://github.com/SMGCommunity/Petari/blob/33b64d162c02fc6e027c874a3a23216d6fb40376/libs/JSystem/include/JSystem/JGeometry/TMatrix.hpp#L211). --- include/JSystem/JGeometry/Matrix.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/JSystem/JGeometry/Matrix.h b/include/JSystem/JGeometry/Matrix.h index 60cc19f..27d96a3 100644 --- a/include/JSystem/JGeometry/Matrix.h +++ b/include/JSystem/JGeometry/Matrix.h @@ -194,6 +194,22 @@ namespace JGeometry { rDest.set(x, y, z); } + inline void getYDirInline(TVec3f &rDest) const + { + f32 z = this->mMtx[2][1]; + f32 y = this->mMtx[1][1]; + f32 x = this->mMtx[0][1]; + rDest.set(x, y, z); + } + + inline void getZDirInline(TVec3f &rDest) const + { + f32 z = this->mMtx[2][2]; + f32 y = this->mMtx[1][2]; + f32 x = this->mMtx[0][2]; + rDest.set(x, y, z); + } + #ifdef NON_MATCHING inline void mult33Inline(const TVec3f &rSrc, TVec3f &rDest) const { From 9cd1a36a62eec62156f40fc773c852ec0200bd8e Mon Sep 17 00:00:00 2001 From: MJ Date: Fri, 28 Nov 2025 17:00:42 +1100 Subject: [PATCH 6/8] Fix typo in `StringObj::setCurrentViewNo`. --- src/Sato/StringObj.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sato/StringObj.cpp b/src/Sato/StringObj.cpp index 6c94787..ac587e0 100644 --- a/src/Sato/StringObj.cpp +++ b/src/Sato/StringObj.cpp @@ -289,7 +289,7 @@ void StringObj::setCurrentViewNo(u32 viewNo) { exModel->setCurrentViewNo(viewNo); JGeometry::TVec3f pos; - mStringNodeMgr->getNodePos(num, &pos); + mStringNodeMgr->getNodePos(viewNo, &pos); Mtx lightMtx; ObjUtility::getCamDependLightMtx(viewNo, pos, lightMtx); From 5a1a02e3fe2e9d1712b5021b0d3a58194274080c Mon Sep 17 00:00:00 2001 From: MJ Date: Fri, 28 Nov 2025 18:14:38 +1100 Subject: [PATCH 7/8] Restructure `StringObj::setCurrentViewNo` as `while` loop, increasing match accuracy. --- src/Sato/StringObj.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Sato/StringObj.cpp b/src/Sato/StringObj.cpp index ac587e0..82fb6cf 100644 --- a/src/Sato/StringObj.cpp +++ b/src/Sato/StringObj.cpp @@ -284,8 +284,10 @@ void StringObj::update() { } void StringObj::setCurrentViewNo(u32 viewNo) { + u32 num = 0; ExModel *exModel = mExModel; - for (u32 num = 0; num < mStringNodeMgr->mStrNodeList.getNumLinks() - 1; num++, exModel++) { + + while (num < mStringNodeMgr->mStrNodeList.getNumLinks() - 1) { exModel->setCurrentViewNo(viewNo); JGeometry::TVec3f pos; @@ -294,6 +296,8 @@ void StringObj::setCurrentViewNo(u32 viewNo) { Mtx lightMtx; ObjUtility::getCamDependLightMtx(viewNo, pos, lightMtx); mExModel[num].setEffectMtx(lightMtx, 0); + num++; + exModel++; } } From dac0c0e2d830c36e4e60faccab3207e825552470 Mon Sep 17 00:00:00 2001 From: MJ Date: Sat, 29 Nov 2025 15:32:17 +1100 Subject: [PATCH 8/8] Fully matched `StringNodeManager::doHeightCol()` function. --- src/Sato/StringObj.cpp | 81 ++++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/src/Sato/StringObj.cpp b/src/Sato/StringObj.cpp index 82fb6cf..ea0e2cc 100644 --- a/src/Sato/StringObj.cpp +++ b/src/Sato/StringObj.cpp @@ -132,65 +132,68 @@ void StringNodeManager::doAirFricG(f32 friction, f32 globalScale) { } void StringNodeManager::doHeightCol() { + JSULink *node; CrsArea crsArea; - bool hittingRoof; u8 someCount = 0; - - for (JSULink *link = mStrNodeList.getFirst()->getNext(); link != nullptr; link = link->getNext()) { - if (link->getObject()->_30 != 0) { - hittingRoof = false; - crsArea.search(2, link->getObject()->mPos); + + for (node = mStrNodeList.getFirst()->getNext(); node != nullptr; node = node->getNext()) { + if (node->getObject()->_30 != 0) { + bool someCheck = false; + crsArea.search(2, node->getObject()->mPos); if (crsArea.getArea() != nullptr) { JGeometry::TVec3f roofPos; crsArea.getRoofPosition(&roofPos); - if ((link->getObject()->mPos.y + 100.0f) > roofPos.y) { - hittingRoof = true; - link->getObject()->mVel.y = 0.0f; - link->getObject()->mVel.x *= _18; - link->getObject()->mVel.z *= _18; - link->getObject()->mPos.y = roofPos.y - 100.0f; + + if (node->getObject()->mPos.y + 100.0f > roofPos.y) { + someCheck = true; + node->getObject()->mVel.y = 0.0f; + node->getObject()->mVel.x *= _18; + node->getObject()->mVel.z *= _18; + node->getObject()->mPos.y = roofPos.y - 100.0f; } } + + CrsGround crsGroundInit(RaceMgr::sRaceManager->getCourse()); + CrsGround *crsGround = &crsGroundInit; - CrsGround crsGround2(RaceMgr::sRaceManager->getCourse()); - CrsGround *crsGround = &crsGround2; if ((mCrsGround != nullptr) && (someCount == _4c)) { crsGround = mCrsGround; } - crsGround->search(link->getObject()->mPos, link->getObject()->_18); - StringNode *stringNode; - if ((crsGround->getAttribute() != CrsGround::Attr_10) && - (crsGround->getHeight() > link->getObject()->mPos.y - _14)) { - - JGeometry::TVec3f crsGroundNormal; - crsGround->getNormal(&crsGroundNormal); - - f32 crsHeight = crsGround->getHeight(); - - f32 yPos = link->getObject()->mPos.y - crsHeight - _14; - crsGroundNormal *= yPos * _18; - link->getObject()->mVel.add(crsGroundNormal); - - stringNode = link->getObject(); - crsHeight = crsGround->getHeight(); - hittingRoof = true; - stringNode->mPos.y = _14 + crsHeight; + crsGround->search(node->getObject()->mPos, node->getObject()->_18); + + if (crsGround->getAttribute() != 10) { + if (crsGround->getHeight() > (node->getObject()->mPos.y - _14)) { + JGeometry::TVec3f crsGroundNormal; + crsGround->getNormal(&crsGroundNormal); + + f32 crsHeight = crsGround->getHeight(); + f32 newScale = crsHeight - (node->getObject()->mPos.y - _14); + crsGroundNormal.scale(newScale * _18); + node->getObject()->mVel.add(crsGroundNormal); + + StringNode *stringNode = node->getObject(); + crsHeight = crsGround->getHeight(); + someCheck = true; + stringNode->mPos.y = _14 + crsHeight; + } } - - if (hittingRoof) { - link->getObject()->_31 = true; + + if (someCheck) { + node->getObject()->_31 = true; if ((mObjSoundMgr != nullptr) && (someCount == _40)) { mObjSoundMgr->setSe(_3c); } + } else { - link->getObject()->_31 = false; + node->getObject()->_31 = false; } - crsGround->getNormal(&link->getObject()->_24); - stringNode = link->getObject(); - stringNode->_18 = stringNode->mPos; + + crsGround->getNormal(&node->getObject()->_24); + node->getObject()->_18 = node->getObject()->mPos; } + someCount++; } }