diff --git a/src/Common/CompressionUtil.cpp b/src/Common/CompressionUtil.cpp index ba529d23..1cabb355 100644 --- a/src/Common/CompressionUtil.cpp +++ b/src/Common/CompressionUtil.cpp @@ -233,7 +233,7 @@ namespace CompressionUtil TotalOut = Size; } - // If it's not smaller, write the compressed data + // If it IS smaller, write the compressed data else { // Write new compressed size + data to destination diff --git a/src/Core/CAreaAttributes.cpp b/src/Core/CAreaAttributes.cpp index ee9681d9..97865738 100644 --- a/src/Core/CAreaAttributes.cpp +++ b/src/Core/CAreaAttributes.cpp @@ -33,6 +33,7 @@ bool CAreaAttributes::IsSkyEnabled() const case eEchoes: case eCorruptionProto: case eCorruption: + case eReturns: return static_cast(pBaseStruct->PropertyByIndex(1))->Get(); default: return false; @@ -51,6 +52,7 @@ CModel* CAreaAttributes::SkyModel() const case eEchoes: case eCorruptionProto: case eCorruption: + case eReturns: return (CModel*) static_cast(pBaseStruct->PropertyByID(0xD208C9FA))->Get().Load(); default: return nullptr; diff --git a/src/Core/Resource/Factory/CTemplateLoader.cpp b/src/Core/Resource/Factory/CTemplateLoader.cpp index 2faa7f9b..0d628a70 100644 --- a/src/Core/Resource/Factory/CTemplateLoader.cpp +++ b/src/Core/Resource/Factory/CTemplateLoader.cpp @@ -165,7 +165,9 @@ IPropertyTemplate* CTemplateLoader::LoadProperty(XMLElement *pElem, CScriptTempl LoadProperties(pProperties, pScript, pStruct, rkTemplateName); } - CMasterTemplate::AddProperty(pProp, mMasterDir + rkTemplateName); + if (IsNewProperty) + CMasterTemplate::AddProperty(pProp, mMasterDir + rkTemplateName); + return pProp; } diff --git a/src/Core/Resource/Script/CScriptObject.cpp b/src/Core/Resource/Script/CScriptObject.cpp index c47e18b4..96544e4f 100644 --- a/src/Core/Resource/Script/CScriptObject.cpp +++ b/src/Core/Resource/Script/CScriptObject.cpp @@ -80,6 +80,54 @@ void CScriptObject::SetLayer(CScriptLayer *pLayer) } } +bool CScriptObject::HasNearVisibleActivation() const +{ + /* This function is used to check whether an inactive DKCR object should render in game mode. DKCR deactivates a lot of + * decorative actors when the player isn't close to them as an optimization. This means a lot of them are inactive by + * default but should render in game mode anyway. To get around this, we'll check the links to find out whether this + * instance has a "Near Visible" activation, which is typically done via a trigger that activates the object on + * InternalState04 (usually through a relay). */ + std::list Relays; + + for (u32 iLink = 0; iLink < mInConnections.size(); iLink++) + { + const SLink& rkLink = mInConnections[iLink]; + + // Check for trigger activation + if (rkLink.State == 0x49533034) // "IS04" + { + if ( (rkLink.Message == 0x41435456) || // "ACTV" + (ObjectTypeID() == 0x53524C59 && rkLink.Message == 0x4143544E) ) // If type is "SRLY" and message is "ACTN" + { + CScriptObject *pObj = mpArea->GetInstanceByID(rkLink.ObjectID); + + if (pObj->ObjectTypeID() == 0x54524752) // "TRGR" + return true; + } + } + + // Check for relay activation + else if (rkLink.State == 0x524C4159) // "RLAY" + { + if ( (rkLink.Message == 0x41435456) || // "ACTV" + (ObjectTypeID() == 0x53524C59 && rkLink.Message == 0x4143544E) ) // If type is "SRLY" and message is "ACTN" + { + CScriptObject *pObj = mpArea->GetInstanceByID(rkLink.ObjectID); + + if (pObj->ObjectTypeID() == 0x53524C59) // "SRLY" + Relays.push_back(pObj); + } + } + } + + // Check whether any of the relays have a near visible activation + for (auto it = Relays.begin(); it != Relays.end(); it++) + if ((*it)->HasNearVisibleActivation()) + return true; + + return false; +} + // ************ GETTERS ************ IProperty* CScriptObject::PropertyByIndex(u32 index) const { diff --git a/src/Core/Resource/Script/CScriptObject.h b/src/Core/Resource/Script/CScriptObject.h index 44cd563f..1a5d302d 100644 --- a/src/Core/Resource/Script/CScriptObject.h +++ b/src/Core/Resource/Script/CScriptObject.h @@ -51,6 +51,7 @@ public: void EvaluateVolume(); bool IsEditorProperty(IProperty *pProp); void SetLayer(CScriptLayer *pLayer); + bool HasNearVisibleActivation() const; CScriptTemplate* Template() const; CMasterTemplate* MasterTemplate() const; diff --git a/src/Core/Scene/CScriptNode.cpp b/src/Core/Scene/CScriptNode.cpp index 008e1fd8..c7901aa1 100644 --- a/src/Core/Scene/CScriptNode.cpp +++ b/src/Core/Scene/CScriptNode.cpp @@ -12,6 +12,7 @@ CScriptNode::CScriptNode(CScene *pScene, CSceneNode *pParent, CScriptObject *pObject) : CSceneNode(pScene, pParent) + , mGameModeVisibility(eUntested) { mpVolumePreviewNode = nullptr; mHasVolumePreview = false; @@ -113,7 +114,10 @@ void CScriptNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo) // If we're in game mode, then override other visibility settings. if (ViewInfo.GameMode) { - if ( (!mpInstance->IsActive() && Template()->Game() != eReturns) || !mpInstance->HasInGameModel()) + if (mGameModeVisibility == eUntested) + TestGameModeVisibility(); + + if (mGameModeVisibility != eVisible) return; } @@ -568,6 +572,17 @@ void CScriptNode::GeneratePosition() } } +void CScriptNode::TestGameModeVisibility() +{ + // Don't render if we don't have an ingame model, or if this is the Prime series and the instance is not active. + if ((Template()->Game() < eReturns && !mpInstance->IsActive()) || !mpInstance->HasInGameModel()) + mGameModeVisibility = eNotVisible; + + // If this is Returns, only render if the instance is active OR if it has a near visible activation. + else + mGameModeVisibility = (mpInstance->IsActive() || mpInstance->HasNearVisibleActivation()) ? eVisible : eNotVisible; +} + CColor CScriptNode::WireframeColor() const { return CColor::Integral(12, 135, 194); diff --git a/src/Core/Scene/CScriptNode.h b/src/Core/Scene/CScriptNode.h index aff1bdf8..03a5aa80 100644 --- a/src/Core/Scene/CScriptNode.h +++ b/src/Core/Scene/CScriptNode.h @@ -24,6 +24,11 @@ class CScriptNode : public CSceneNode CLightParameters *mpLightParameters; + enum EGameModeVisibility + { + eVisible, eNotVisible, eUntested + } mGameModeVisibility; + public: CScriptNode(CScene *pScene, CSceneNode *pParent = 0, CScriptObject *pObject = 0); ENodeType NodeType(); @@ -43,6 +48,7 @@ public: void PropertyModified(IProperty *pProp); void UpdatePreviewVolume(); void GeneratePosition(); + void TestGameModeVisibility(); CScriptObject* Object() const; CScriptTemplate* Template() const; CScriptExtra* Extra() const; diff --git a/src/Editor/CStartWindow.cpp b/src/Editor/CStartWindow.cpp index 5e5da516..d863e569 100644 --- a/src/Editor/CStartWindow.cpp +++ b/src/Editor/CStartWindow.cpp @@ -185,6 +185,8 @@ void CStartWindow::on_LaunchWorldEditorButton_clicked() { mpWorld->SetAreaLayerInfo(pArea, mSelectedAreaIndex); mpWorldEditor->SetArea(mpWorld, pArea, mSelectedAreaIndex); + gResCache.Clean(); + mpWorldEditor->setWindowModality(Qt::WindowModal); mpWorldEditor->showMaximized(); @@ -194,8 +196,6 @@ void CStartWindow::on_LaunchWorldEditorButton_clicked() if (HasErrors) ErrorDialog.exec(); - - gResCache.Clean(); } } } diff --git a/templates/Properties.xml b/templates/Properties.xml index 2f550221..aba3f270 100644 --- a/templates/Properties.xml +++ b/templates/Properties.xml @@ -624,7 +624,7 @@ - + diff --git a/templates/mp1/Script/Timer.xml b/templates/mp1/Script/Timer.xml index 8f9ad78e..59213735 100644 --- a/templates/mp1/Script/Timer.xml +++ b/templates/mp1/Script/Timer.xml @@ -3,10 +3,16 @@ Timer - - - - + + + A random value between 0 and this number will be added to the timer's start time. + + + When the timer reaches 0, it will reset back to its start time and start ticking again. + + + The timer will start ticking immediately on load, without waiting for a script message to start it. +