mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-12 07:04:36 +00:00
Merge branch 'TorqueGameEngines:development' into Sir-Skurpsalot-player_fixes_&_tweaks
This commit is contained in:
commit
80eaaff7fa
27 changed files with 2723 additions and 2305 deletions
|
|
@ -186,6 +186,13 @@ void LevelAsset::loadAsset()
|
||||||
{
|
{
|
||||||
// Ensure the image-file is expanded.
|
// Ensure the image-file is expanded.
|
||||||
mLevelPath = getOwned() ? expandAssetFilePath(mLevelFile) : mLevelPath;
|
mLevelPath = getOwned() ? expandAssetFilePath(mLevelFile) : mLevelPath;
|
||||||
|
|
||||||
|
if (mLevelPath == StringTable->EmptyString())
|
||||||
|
{
|
||||||
|
mLoadedState = AssetErrCode::BadFileReference;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mPostFXPresetPath = getOwned() ? expandAssetFilePath(mPostFXPresetFile) : mPostFXPresetPath;
|
mPostFXPresetPath = getOwned() ? expandAssetFilePath(mPostFXPresetFile) : mPostFXPresetPath;
|
||||||
mDecalsPath = getOwned() ? expandAssetFilePath(mDecalsFile) : mDecalsPath;
|
mDecalsPath = getOwned() ? expandAssetFilePath(mDecalsFile) : mDecalsPath;
|
||||||
mForestPath = getOwned() ? expandAssetFilePath(mForestFile) : mForestPath;
|
mForestPath = getOwned() ? expandAssetFilePath(mForestFile) : mForestPath;
|
||||||
|
|
@ -198,6 +205,8 @@ void LevelAsset::loadAsset()
|
||||||
mPreviewImageAssetId = previewImageAssetId;
|
mPreviewImageAssetId = previewImageAssetId;
|
||||||
mPreviewImageAsset = mPreviewImageAssetId;
|
mPreviewImageAsset = mPreviewImageAssetId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mLoadedState = AssetErrCode::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
@ -248,7 +257,7 @@ void LevelAsset::setEditorFile(const char* pEditorFile)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Update.
|
// Update.
|
||||||
mEditorFile = getOwned() ? expandAssetFilePath(pEditorFile) : pEditorFile;
|
mEditorFile = pEditorFile;
|
||||||
|
|
||||||
// Refresh the asset.
|
// Refresh the asset.
|
||||||
refreshAsset();
|
refreshAsset();
|
||||||
|
|
@ -267,7 +276,7 @@ void LevelAsset::setBakedSceneFile(const char* pBakedSceneFile)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Update.
|
// Update.
|
||||||
mBakedSceneFile = getOwned() ? expandAssetFilePath(pBakedSceneFile) : pBakedSceneFile;
|
mBakedSceneFile = pBakedSceneFile;
|
||||||
|
|
||||||
// Refresh the asset.
|
// Refresh the asset.
|
||||||
refreshAsset();
|
refreshAsset();
|
||||||
|
|
@ -286,7 +295,7 @@ void LevelAsset::setPostFXPresetFile(const char* pPostFXPresetFile)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Update.
|
// Update.
|
||||||
mPostFXPresetFile = getOwned() ? expandAssetFilePath(pPostFXPresetFile) : pPostFXPresetFile;
|
mPostFXPresetFile = pPostFXPresetFile;
|
||||||
|
|
||||||
// Refresh the asset.
|
// Refresh the asset.
|
||||||
refreshAsset();
|
refreshAsset();
|
||||||
|
|
@ -305,7 +314,7 @@ void LevelAsset::setDecalsFile(const char* pDecalsFile)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Update.
|
// Update.
|
||||||
mDecalsFile = getOwned() ? expandAssetFilePath(pDecalsFile) : pDecalsFile;
|
mDecalsFile = pDecalsFile;
|
||||||
|
|
||||||
// Refresh the asset.
|
// Refresh the asset.
|
||||||
refreshAsset();
|
refreshAsset();
|
||||||
|
|
@ -324,7 +333,7 @@ void LevelAsset::setForestFile(const char* pForestFile)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Update.
|
// Update.
|
||||||
mForestFile = getOwned() ? expandAssetFilePath(pForestFile) : pForestFile;
|
mForestFile = pForestFile;
|
||||||
|
|
||||||
// Refresh the asset.
|
// Refresh the asset.
|
||||||
refreshAsset();
|
refreshAsset();
|
||||||
|
|
@ -343,7 +352,7 @@ void LevelAsset::setNavmeshFile(const char* pNavmeshFile)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Update.
|
// Update.
|
||||||
mNavmeshFile = getOwned() ? expandAssetFilePath(pNavmeshFile) : pNavmeshFile;
|
mNavmeshFile = pNavmeshFile;
|
||||||
|
|
||||||
// Refresh the asset.
|
// Refresh the asset.
|
||||||
refreshAsset();
|
refreshAsset();
|
||||||
|
|
@ -398,35 +407,88 @@ DefineEngineMethod(LevelAsset, getPreviewImagePath, const char*, (), ,
|
||||||
"Gets the full path of the asset's defined preview image file.\n"
|
"Gets the full path of the asset's defined preview image file.\n"
|
||||||
"@return The string result of the level preview image path")
|
"@return The string result of the level preview image path")
|
||||||
{
|
{
|
||||||
return object->getPreviewImagePath();
|
String previewPath = object->getPreviewImagePath();
|
||||||
|
if (previewPath.isEmpty() || !Torque::FS::IsFile(previewPath))
|
||||||
|
{
|
||||||
|
Torque::Path levelPath = object->getLevelPath();
|
||||||
|
previewPath = String(levelPath.getPath() + "/" + levelPath.getFileName()) + ".png";
|
||||||
|
}
|
||||||
|
|
||||||
|
char* returnBuffer = Con::getReturnBuffer(previewPath.size());
|
||||||
|
dSprintf(returnBuffer, 256, "%s", previewPath.c_str());
|
||||||
|
|
||||||
|
return returnBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
DefineEngineMethod(LevelAsset, getPostFXPresetPath, const char*, (), ,
|
DefineEngineMethod(LevelAsset, getPostFXPresetPath, const char*, (), ,
|
||||||
"Gets the full path of the asset's defined postFX preset file.\n"
|
"Gets the full path of the asset's defined postFX preset file.\n"
|
||||||
"@return The string result of the postFX preset path")
|
"@return The string result of the postFX preset path")
|
||||||
{
|
{
|
||||||
return object->getPostFXPresetPath();
|
String pfxPath = object->getPostFXPresetPath();
|
||||||
|
if (pfxPath.isEmpty() || !Torque::FS::IsFile(pfxPath))
|
||||||
|
{
|
||||||
|
Torque::Path levelPath = object->getLevelPath();
|
||||||
|
pfxPath = String(levelPath.getPath() + "/" + levelPath.getFileName()) + ".postfxpreset.tscript";
|
||||||
|
}
|
||||||
|
|
||||||
|
char* returnBuffer = Con::getReturnBuffer(pfxPath.size());
|
||||||
|
dSprintf(returnBuffer, 256, "%s", pfxPath.c_str());
|
||||||
|
|
||||||
|
return returnBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
DefineEngineMethod(LevelAsset, getDecalsPath, const char*, (), ,
|
DefineEngineMethod(LevelAsset, getDecalsPath, const char*, (), ,
|
||||||
"Gets the full path of the asset's defined decal file.\n"
|
"Gets the full path of the asset's defined decal file.\n"
|
||||||
"@return The string result of the decal path")
|
"@return The string result of the decal path")
|
||||||
{
|
{
|
||||||
return object->getDecalsPath();
|
String decalPath = object->getDecalsPath();
|
||||||
|
if (decalPath.isEmpty() || !Torque::FS::IsFile(decalPath))
|
||||||
|
{
|
||||||
|
Torque::Path levelPath = object->getLevelPath();
|
||||||
|
decalPath = String(levelPath.getPath() + levelPath.getFileName()) + ".decals";
|
||||||
|
|
||||||
|
if (!Torque::FS::IsFile(decalPath)) //Legacy pattern support if it kept the '.mis' sub extension in there
|
||||||
|
decalPath = String(levelPath.getFullPath()) + ".decals";
|
||||||
|
}
|
||||||
|
|
||||||
|
char* returnBuffer = Con::getReturnBuffer(decalPath.size());
|
||||||
|
dSprintf(returnBuffer, 256, "%s", decalPath.c_str());
|
||||||
|
|
||||||
|
return returnBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
DefineEngineMethod(LevelAsset, getForestPath, const char*, (), ,
|
DefineEngineMethod(LevelAsset, getForestPath, const char*, (), ,
|
||||||
"Gets the full path of the asset's defined forest file.\n"
|
"Gets the full path of the asset's defined forest file.\n"
|
||||||
"@return The string result of the forest path")
|
"@return The string result of the forest path")
|
||||||
{
|
{
|
||||||
return object->getForestPath();
|
String forestPath = object->getForestPath();
|
||||||
|
if (forestPath.isEmpty() || !Torque::FS::IsFile(forestPath))
|
||||||
|
{
|
||||||
|
Torque::Path levelPath = object->getLevelPath();
|
||||||
|
forestPath = String(levelPath.getPath() + "/" + levelPath.getFileName()) + ".forest";
|
||||||
|
}
|
||||||
|
|
||||||
|
char* returnBuffer = Con::getReturnBuffer(forestPath.size());
|
||||||
|
dSprintf(returnBuffer, 256, "%s", forestPath.c_str());
|
||||||
|
|
||||||
|
return returnBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
DefineEngineMethod(LevelAsset, getNavmeshPath, const char*, (), ,
|
DefineEngineMethod(LevelAsset, getNavmeshPath, const char*, (), ,
|
||||||
"Gets the full path of the asset's defined navmesh file.\n"
|
"Gets the full path of the asset's defined navmesh file.\n"
|
||||||
"@return The string result of the navmesh path")
|
"@return The string result of the navmesh path")
|
||||||
{
|
{
|
||||||
return object->getNavmeshPath();
|
String navPath = object->getNavmeshPath();
|
||||||
|
if (navPath.isEmpty() || !Torque::FS::IsFile(navPath))
|
||||||
|
{
|
||||||
|
Torque::Path levelPath = object->getLevelPath();
|
||||||
|
navPath = String(levelPath.getPath() + "/" + levelPath.getFileName()) + ".nav";
|
||||||
|
}
|
||||||
|
|
||||||
|
char* returnBuffer = Con::getReturnBuffer(navPath.size());
|
||||||
|
dSprintf(returnBuffer, 256, "%s", navPath.c_str());
|
||||||
|
|
||||||
|
return returnBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
DefineEngineMethod(LevelAsset, loadDependencies, void, (), ,
|
DefineEngineMethod(LevelAsset, loadDependencies, void, (), ,
|
||||||
|
|
|
||||||
|
|
@ -238,6 +238,11 @@ void AssetImportConfig::initPersistFields()
|
||||||
|
|
||||||
void AssetImportConfig::loadImportConfig(Settings* configSettings, String configName)
|
void AssetImportConfig::loadImportConfig(Settings* configSettings, String configName)
|
||||||
{
|
{
|
||||||
|
if (!configSettings)
|
||||||
|
{
|
||||||
|
Con::errorf("AssetImportConfig::loadImportConfig - No config settings!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
//General
|
//General
|
||||||
DuplicateAutoResolution = configSettings->value(String(configName + "/General/DuplicateAutoResolution").c_str());
|
DuplicateAutoResolution = configSettings->value(String(configName + "/General/DuplicateAutoResolution").c_str());
|
||||||
WarningsAsErrors = dAtob(configSettings->value(String(configName + "/General/WarningsAsErrors").c_str()));
|
WarningsAsErrors = dAtob(configSettings->value(String(configName + "/General/WarningsAsErrors").c_str()));
|
||||||
|
|
|
||||||
|
|
@ -1457,7 +1457,7 @@ bool DecalManager::_createDataFile()
|
||||||
if(dot)
|
if(dot)
|
||||||
*dot = '\0';
|
*dot = '\0';
|
||||||
|
|
||||||
dSprintf( fileName, sizeof(fileName), "%s.mis.decals", missionName );
|
dSprintf( fileName, sizeof(fileName), "%s.decals", missionName );
|
||||||
|
|
||||||
mDataFileName = StringTable->insert( fileName );
|
mDataFileName = StringTable->insert( fileName );
|
||||||
|
|
||||||
|
|
@ -1572,8 +1572,8 @@ DefineEngineFunction( decalManagerSave, void, ( String decalSaveFile ), ( "" ),
|
||||||
"@param decalSaveFile Filename to save the decals to.\n"
|
"@param decalSaveFile Filename to save the decals to.\n"
|
||||||
"@tsexample\n"
|
"@tsexample\n"
|
||||||
"// Set the filename to save the decals in. If no filename is set, then the\n"
|
"// Set the filename to save the decals in. If no filename is set, then the\n"
|
||||||
"// decals will default to <activeMissionName>.mis.decals\n"
|
"// decals will default to <activeMissionName>.decals\n"
|
||||||
"%fileName = \"./missionDecals.mis.decals\";\n"
|
"%fileName = \"./missionDecals.decals\";\n"
|
||||||
"// Inform the decal manager to save the decals for the active mission.\n"
|
"// Inform the decal manager to save the decals for the active mission.\n"
|
||||||
"decalManagerSave( %fileName );\n"
|
"decalManagerSave( %fileName );\n"
|
||||||
"@endtsexample\n"
|
"@endtsexample\n"
|
||||||
|
|
@ -1603,7 +1603,7 @@ DefineEngineFunction( decalManagerLoad, bool, ( const char* fileName ),,
|
||||||
"false if it could not.\n"
|
"false if it could not.\n"
|
||||||
"@tsexample\n"
|
"@tsexample\n"
|
||||||
"// Set the filename to load the decals from.\n"
|
"// Set the filename to load the decals from.\n"
|
||||||
"%fileName = \"./missionDecals.mis.decals\";\n"
|
"%fileName = \"./missionDecals.decals\";\n"
|
||||||
"// Inform the decal manager to load the decals from the entered filename.\n"
|
"// Inform the decal manager to load the decals from the entered filename.\n"
|
||||||
"decalManagerLoad( %fileName );\n"
|
"decalManagerLoad( %fileName );\n"
|
||||||
"@endtsexample\n"
|
"@endtsexample\n"
|
||||||
|
|
|
||||||
|
|
@ -907,6 +907,9 @@ GuiControl* GuiInspectorTypeParticleDataList::constructEditControl()
|
||||||
mNewParticleBtn->registerObject();
|
mNewParticleBtn->registerObject();
|
||||||
mNewParticleBtn->_setBitmap(StringTable->insert("ToolsModule:iconAdd_image"));
|
mNewParticleBtn->_setBitmap(StringTable->insert("ToolsModule:iconAdd_image"));
|
||||||
mNewParticleBtn->setDataField(StringTable->insert("profile"), NULL, "ToolsGuiDefaultProfile");
|
mNewParticleBtn->setDataField(StringTable->insert("profile"), NULL, "ToolsGuiDefaultProfile");
|
||||||
|
mNewParticleBtn->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");
|
||||||
|
mNewParticleBtn->setDataField(StringTable->insert("hovertime"), NULL, "1000");
|
||||||
|
mNewParticleBtn->setDataField(StringTable->insert("tooltip"), NULL, "Add new particle slot");
|
||||||
mNewParticleBtn->setHorizSizing(horizResizeRight);
|
mNewParticleBtn->setHorizSizing(horizResizeRight);
|
||||||
mNewParticleBtn->mMakeIconSquare = true;
|
mNewParticleBtn->mMakeIconSquare = true;
|
||||||
mNewParticleBtn->mFitBitmapToButton = true;
|
mNewParticleBtn->mFitBitmapToButton = true;
|
||||||
|
|
@ -914,7 +917,7 @@ GuiControl* GuiInspectorTypeParticleDataList::constructEditControl()
|
||||||
|
|
||||||
char szBuffer[512];
|
char szBuffer[512];
|
||||||
dSprintf(szBuffer, sizeof(szBuffer), "ParticleEditor.addParticleSlot(%s, %s);",
|
dSprintf(szBuffer, sizeof(szBuffer), "ParticleEditor.addParticleSlot(%s, %s);",
|
||||||
mNewParticleBtn->getIdString(), mInspector->getInspectObject()->getIdString());
|
this->getIdString(), mInspector->getInspectObject()->getIdString());
|
||||||
mNewParticleBtn->setField("Command", szBuffer);
|
mNewParticleBtn->setField("Command", szBuffer);
|
||||||
|
|
||||||
GuiContainer* newBtnCtnr = new GuiContainer();
|
GuiContainer* newBtnCtnr = new GuiContainer();
|
||||||
|
|
@ -924,39 +927,16 @@ GuiControl* GuiInspectorTypeParticleDataList::constructEditControl()
|
||||||
|
|
||||||
mStack->addObject(newBtnCtnr);
|
mStack->addObject(newBtnCtnr);
|
||||||
|
|
||||||
//Particle 0
|
_rebuildParticleEntryList();
|
||||||
mParticleSlot0Ctrl = _buildParticleEntryField(0);
|
|
||||||
|
|
||||||
mStack->addObject(mParticleSlot0Ctrl);
|
|
||||||
|
|
||||||
//Now the non-default entries if we already have some
|
|
||||||
Parent::updateValue();
|
|
||||||
const char* data = getData();
|
|
||||||
|
|
||||||
if (data != NULL && !String::isEmpty(data))
|
|
||||||
{
|
|
||||||
U32 particlesCount = StringUnit::getUnitCount(data, " ");
|
|
||||||
for (U32 i=1; i < particlesCount; i++)
|
|
||||||
{
|
|
||||||
GuiControl* particleSlotCtrl = _buildParticleEntryField(i);
|
|
||||||
mStack->addObject(particleSlotCtrl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_registerEditControl(mStack);
|
_registerEditControl(mStack);
|
||||||
|
|
||||||
//constructEditControlChildren(retCtrl, getWidth());
|
|
||||||
|
|
||||||
//retCtrl->addObject(mScriptValue);
|
|
||||||
|
|
||||||
/*char szBuffer[512];
|
|
||||||
dSprintf(szBuffer, 512, "setClipboard(%d.getText());", mScriptValue->getId());
|
|
||||||
mCopyButton->setField("Command", szBuffer);
|
|
||||||
addObject(mCopyButton);*/
|
|
||||||
|
|
||||||
mUseHeightOverride = true;
|
mUseHeightOverride = true;
|
||||||
mHeightOverride = (mStack->getCount() * 23) + 6;
|
mHeightOverride = (mStack->getCount() * 23) + 6;
|
||||||
|
|
||||||
|
//Now the non-default entries if we already have some
|
||||||
|
//Parent::updateValue();
|
||||||
|
|
||||||
return mStack;
|
return mStack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -980,7 +960,7 @@ GuiControl* GuiInspectorTypeParticleDataList::_buildParticleEntryField(const S32
|
||||||
|
|
||||||
char szBuffer[512];
|
char szBuffer[512];
|
||||||
dSprintf(szBuffer, sizeof(szBuffer), "ParticleEditor.changeParticleSlot(%s, %s, %d);",
|
dSprintf(szBuffer, sizeof(szBuffer), "ParticleEditor.changeParticleSlot(%s, %s, %d);",
|
||||||
listBtn->getIdString(), mInspector->getInspectObject()->getIdString(), index);
|
this->getIdString(), mInspector->getInspectObject()->getIdString(), index);
|
||||||
listBtn->setField("Command", szBuffer);
|
listBtn->setField("Command", szBuffer);
|
||||||
|
|
||||||
if (mField && index != -1)
|
if (mField && index != -1)
|
||||||
|
|
@ -1001,6 +981,9 @@ GuiControl* GuiInspectorTypeParticleDataList::_buildParticleEntryField(const S32
|
||||||
editSlotBtn->registerObject();
|
editSlotBtn->registerObject();
|
||||||
editSlotBtn->setText(StringTable->insert("..."));
|
editSlotBtn->setText(StringTable->insert("..."));
|
||||||
editSlotBtn->setDataField(StringTable->insert("profile"), NULL, "ToolsGuiButtonProfile");
|
editSlotBtn->setDataField(StringTable->insert("profile"), NULL, "ToolsGuiButtonProfile");
|
||||||
|
editSlotBtn->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");
|
||||||
|
editSlotBtn->setDataField(StringTable->insert("hovertime"), NULL, "1000");
|
||||||
|
editSlotBtn->setDataField(StringTable->insert("tooltip"), NULL, "Edit this particle");
|
||||||
editSlotBtn->setHorizSizing(horizResizeRight);
|
editSlotBtn->setHorizSizing(horizResizeRight);
|
||||||
editSlotBtn->setInternalName("editBtn");
|
editSlotBtn->setInternalName("editBtn");
|
||||||
editSlotBtn->setPosition(editExtent.x - 40, 0);
|
editSlotBtn->setPosition(editExtent.x - 40, 0);
|
||||||
|
|
@ -1019,6 +1002,9 @@ GuiControl* GuiInspectorTypeParticleDataList::_buildParticleEntryField(const S32
|
||||||
deleteSlotBtn->registerObject();
|
deleteSlotBtn->registerObject();
|
||||||
deleteSlotBtn->_setBitmap(StringTable->insert("ToolsModule:iconCancel_image"));
|
deleteSlotBtn->_setBitmap(StringTable->insert("ToolsModule:iconCancel_image"));
|
||||||
deleteSlotBtn->setDataField(StringTable->insert("profile"), NULL, "ToolsGuiDefaultProfile");
|
deleteSlotBtn->setDataField(StringTable->insert("profile"), NULL, "ToolsGuiDefaultProfile");
|
||||||
|
deleteSlotBtn->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");
|
||||||
|
deleteSlotBtn->setDataField(StringTable->insert("hovertime"), NULL, "1000");
|
||||||
|
deleteSlotBtn->setDataField(StringTable->insert("tooltip"), NULL, "Delete this particle slot");
|
||||||
deleteSlotBtn->setHorizSizing(horizResizeRight);
|
deleteSlotBtn->setHorizSizing(horizResizeRight);
|
||||||
deleteSlotBtn->setInternalName("deleteBtn");
|
deleteSlotBtn->setInternalName("deleteBtn");
|
||||||
deleteSlotBtn->mMakeIconSquare = true;
|
deleteSlotBtn->mMakeIconSquare = true;
|
||||||
|
|
@ -1062,6 +1048,38 @@ void GuiInspectorTypeParticleDataList::_populateMenu(GuiPopUpMenuCtrlEx* menu)
|
||||||
menu->sort();
|
menu->sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GuiInspectorTypeParticleDataList::_rebuildParticleEntryList()
|
||||||
|
{
|
||||||
|
const char* data = getData();
|
||||||
|
|
||||||
|
//whoops it's misaligned, force a rebuild
|
||||||
|
mParticleSlot0Ctrl = NULL;
|
||||||
|
|
||||||
|
for (U32 i = 0; i < mParticleSlotList.size(); i++)
|
||||||
|
{
|
||||||
|
mStack->removeObject(mParticleSlotList[i]);
|
||||||
|
mParticleSlotList[i]->deleteObject();
|
||||||
|
}
|
||||||
|
mParticleSlotList.clear();
|
||||||
|
|
||||||
|
//Particle 0
|
||||||
|
mParticleSlot0Ctrl = _buildParticleEntryField(0);
|
||||||
|
mStack->addObject(mParticleSlot0Ctrl);
|
||||||
|
mParticleSlotList.push_back(mParticleSlot0Ctrl);
|
||||||
|
|
||||||
|
if (data != NULL && !String::isEmpty(data))
|
||||||
|
{
|
||||||
|
U32 particlesCount = StringUnit::getUnitCount(data, " ");
|
||||||
|
|
||||||
|
for (U32 i = 1; i < particlesCount; i++)
|
||||||
|
{
|
||||||
|
GuiControl* particleSlotCtrl = _buildParticleEntryField(i);
|
||||||
|
mStack->addObject(particleSlotCtrl);
|
||||||
|
mParticleSlotList.push_back(particleSlotCtrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool GuiInspectorTypeParticleDataList::updateRects()
|
bool GuiInspectorTypeParticleDataList::updateRects()
|
||||||
{
|
{
|
||||||
S32 rowSize = 18;
|
S32 rowSize = 18;
|
||||||
|
|
@ -1109,14 +1127,43 @@ bool GuiInspectorTypeParticleDataList::updateRects()
|
||||||
|
|
||||||
mEdit->resize(mEditCtrlRect.point, mEditCtrlRect.extent);
|
mEdit->resize(mEditCtrlRect.point, mEditCtrlRect.extent);
|
||||||
|
|
||||||
|
mUseHeightOverride = true;
|
||||||
mHeightOverride = (mStack->getCount() * 23) + 6;
|
mHeightOverride = (mStack->getCount() * 23) + 6;
|
||||||
|
|
||||||
//mCopyButton->resize(Point2I(mProfile->mTextOffset.x, rowSize + 3), Point2I(45, 15));
|
RectI bnds = getBounds();
|
||||||
//mPasteButton->resize(Point2I(mProfile->mTextOffset.x, rowSize + rowSize + 6), Point2I(45, 15));
|
setBounds(bnds.point.x, bnds.point.y, bnds.extent.x, mHeightOverride);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GuiInspectorTypeParticleDataList::updateValue()
|
||||||
|
{
|
||||||
|
const char* data = getData();
|
||||||
|
|
||||||
|
if (data != NULL && !String::isEmpty(data))
|
||||||
|
{
|
||||||
|
U32 particlesCount = StringUnit::getUnitCount(data, " ");
|
||||||
|
|
||||||
|
if (particlesCount != mParticleSlotList.size())
|
||||||
|
{
|
||||||
|
_rebuildParticleEntryList();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (U32 i = 0; i < particlesCount; i++)
|
||||||
|
{
|
||||||
|
GuiButtonCtrl* listBtn = dynamic_cast<GuiButtonCtrl*>(mParticleSlotList[i]->getObject(0));
|
||||||
|
if (!listBtn) //This *really* shouldn't happen
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const char* particleName = StringUnit::getUnit(data, i, " ");
|
||||||
|
listBtn->setText(particleName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateRects();
|
||||||
|
}
|
||||||
|
|
||||||
void GuiInspectorTypeParticleDataList::consoleInit()
|
void GuiInspectorTypeParticleDataList::consoleInit()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,10 @@ public:
|
||||||
|
|
||||||
GuiControl* constructEditControl() override;
|
GuiControl* constructEditControl() override;
|
||||||
bool updateRects() override;
|
bool updateRects() override;
|
||||||
|
void updateValue() override;
|
||||||
|
|
||||||
void _populateMenu(GuiPopUpMenuCtrlEx* menu);
|
void _populateMenu(GuiPopUpMenuCtrlEx* menu);
|
||||||
GuiControl* _buildParticleEntryField(const S32& index);
|
GuiControl* _buildParticleEntryField(const S32& index);
|
||||||
|
void _rebuildParticleEntryList();
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -465,6 +465,7 @@ PlayerData::PlayerData()
|
||||||
|
|
||||||
physicsPlayerType = StringTable->EmptyString();
|
physicsPlayerType = StringTable->EmptyString();
|
||||||
mControlMap = StringTable->EmptyString();
|
mControlMap = StringTable->EmptyString();
|
||||||
|
mDynamicAnimsStart = NumTableActionAnims;
|
||||||
dMemset( actionList, 0, sizeof(actionList) );
|
dMemset( actionList, 0, sizeof(actionList) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -516,7 +517,7 @@ bool PlayerData::preload(bool server, String &errorStr)
|
||||||
// Extract ground transform velocity from animations
|
// Extract ground transform velocity from animations
|
||||||
// Get the named ones first so they can be indexed directly.
|
// Get the named ones first so they can be indexed directly.
|
||||||
ActionAnimation *dp = &actionList[0];
|
ActionAnimation *dp = &actionList[0];
|
||||||
for (S32 i = 0; i < NumTableActionAnims; i++,dp++)
|
for (S32 i = 0; i < mDynamicAnimsStart; i++,dp++)
|
||||||
{
|
{
|
||||||
ActionAnimationDef *sp = &ActionAnimationList[i];
|
ActionAnimationDef *sp = &ActionAnimationList[i];
|
||||||
dp->name = sp->name;
|
dp->name = sp->name;
|
||||||
|
|
@ -694,7 +695,7 @@ bool PlayerData::isTableSequence(S32 seq)
|
||||||
{
|
{
|
||||||
// The sequences from the table must already have
|
// The sequences from the table must already have
|
||||||
// been loaded for this to work.
|
// been loaded for this to work.
|
||||||
for (S32 i = 0; i < NumTableActionAnims; i++)
|
for (S32 i = 0; i < mDynamicAnimsStart; i++)
|
||||||
if (actionList[i].sequence == seq)
|
if (actionList[i].sequence == seq)
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -2834,7 +2835,7 @@ void Player::updateMove(const Move* move)
|
||||||
|
|
||||||
// Cancel any script driven animations if we are going to move.
|
// Cancel any script driven animations if we are going to move.
|
||||||
if (moveVec.x + moveVec.y + moveVec.z != 0.0f &&
|
if (moveVec.x + moveVec.y + moveVec.z != 0.0f &&
|
||||||
(mActionAnimation.action >= PlayerData::NumTableActionAnims
|
(mActionAnimation.action >= mDataBlock->mDynamicAnimsStart
|
||||||
|| mActionAnimation.action == PlayerData::LandAnim))
|
|| mActionAnimation.action == PlayerData::LandAnim))
|
||||||
mActionAnimation.action = PlayerData::NullAnimation;
|
mActionAnimation.action = PlayerData::NullAnimation;
|
||||||
}
|
}
|
||||||
|
|
@ -3729,7 +3730,7 @@ bool Player::inSittingAnim()
|
||||||
U32 action = mActionAnimation.action;
|
U32 action = mActionAnimation.action;
|
||||||
if (mActionAnimation.thread && action < mDataBlock->actionCount) {
|
if (mActionAnimation.thread && action < mDataBlock->actionCount) {
|
||||||
const char * name = mDataBlock->actionList[action].name;
|
const char * name = mDataBlock->actionList[action].name;
|
||||||
if (!dStricmp(name, "Sitting") || !dStricmp(name, "Scoutroot"))
|
if (name && (!dStricmp(name, "Sitting") || !dStricmp(name, "Scoutroot")))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -3979,7 +3980,7 @@ void Player::updateActionThread()
|
||||||
if (mMountPending)
|
if (mMountPending)
|
||||||
mMountPending = (isMounted() ? 0 : (mMountPending - 1));
|
mMountPending = (isMounted() ? 0 : (mMountPending - 1));
|
||||||
|
|
||||||
if (isServerObject() && (mActionAnimation.action >= PlayerData::NumTableActionAnims) && mActionAnimation.atEnd)
|
if (isServerObject() && (mActionAnimation.action >= mDataBlock->mDynamicAnimsStart) && mActionAnimation.atEnd)
|
||||||
{
|
{
|
||||||
//The scripting language will get a call back when a script animation has finished...
|
//The scripting language will get a call back when a script animation has finished...
|
||||||
// example: When the chat menu animations are done playing...
|
// example: When the chat menu animations are done playing...
|
||||||
|
|
@ -4080,7 +4081,7 @@ void Player::pickActionAnimation()
|
||||||
// Go into root position unless something was set explicitly
|
// Go into root position unless something was set explicitly
|
||||||
// from a script.
|
// from a script.
|
||||||
if (mActionAnimation.action != PlayerData::RootAnim &&
|
if (mActionAnimation.action != PlayerData::RootAnim &&
|
||||||
mActionAnimation.action < PlayerData::NumTableActionAnims)
|
mActionAnimation.action < mDataBlock->mDynamicAnimsStart)
|
||||||
setActionThread(PlayerData::RootAnim,true,false,false);
|
setActionThread(PlayerData::RootAnim,true,false,false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -5978,7 +5979,7 @@ void Player::getMuzzlePointAI(U32 imageSlot, Point3F* point)
|
||||||
|
|
||||||
// If we are in one of the standard player animations, adjust the
|
// If we are in one of the standard player animations, adjust the
|
||||||
// muzzle to point in the direction we are looking.
|
// muzzle to point in the direction we are looking.
|
||||||
if (mActionAnimation.action < PlayerData::NumTableActionAnims)
|
if (mActionAnimation.action < mDataBlock->mDynamicAnimsStart)
|
||||||
{
|
{
|
||||||
MatrixF xmat;
|
MatrixF xmat;
|
||||||
xmat.set(EulerF(mHead.x, 0, 0));
|
xmat.set(EulerF(mHead.x, 0, 0));
|
||||||
|
|
@ -6362,7 +6363,7 @@ U32 Player::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||||
|
|
||||||
if (stream->writeFlag(mask & ActionMask &&
|
if (stream->writeFlag(mask & ActionMask &&
|
||||||
mActionAnimation.action != PlayerData::NullAnimation &&
|
mActionAnimation.action != PlayerData::NullAnimation &&
|
||||||
mActionAnimation.action >= PlayerData::NumTableActionAnims)) {
|
mActionAnimation.action >= mDataBlock->mDynamicAnimsStart)) {
|
||||||
stream->writeInt(mActionAnimation.action,PlayerData::ActionAnimBits);
|
stream->writeInt(mActionAnimation.action,PlayerData::ActionAnimBits);
|
||||||
stream->writeFlag(mActionAnimation.holdAtEnd);
|
stream->writeFlag(mActionAnimation.holdAtEnd);
|
||||||
stream->writeFlag(mActionAnimation.atEnd);
|
stream->writeFlag(mActionAnimation.atEnd);
|
||||||
|
|
|
||||||
|
|
@ -303,7 +303,7 @@ struct PlayerData: public ShapeBaseData /*protected AssetPtrCallback < already i
|
||||||
ActionAnimBits = 9,
|
ActionAnimBits = 9,
|
||||||
NullAnimation = (1 << ActionAnimBits) - 1
|
NullAnimation = (1 << ActionAnimBits) - 1
|
||||||
};
|
};
|
||||||
|
int mDynamicAnimsStart;
|
||||||
static ActionAnimationDef ActionAnimationList[NumTableActionAnims];
|
static ActionAnimationDef ActionAnimationList[NumTableActionAnims];
|
||||||
ActionAnimation actionList[NumActionAnims];
|
ActionAnimation actionList[NumActionAnims];
|
||||||
U32 actionCount;
|
U32 actionCount;
|
||||||
|
|
@ -410,9 +410,9 @@ protected:
|
||||||
|
|
||||||
class Player: public ShapeBase
|
class Player: public ShapeBase
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
typedef ShapeBase Parent;
|
typedef ShapeBase Parent;
|
||||||
|
|
||||||
public:
|
|
||||||
enum Pose {
|
enum Pose {
|
||||||
StandPose = 0,
|
StandPose = 0,
|
||||||
SprintPose,
|
SprintPose,
|
||||||
|
|
|
||||||
|
|
@ -120,12 +120,12 @@ typedef const char *StringTableEntry;
|
||||||
|
|
||||||
enum ConsoleValueType
|
enum ConsoleValueType
|
||||||
{
|
{
|
||||||
cvNULL = -5,
|
cvNULL = -5,
|
||||||
cvInteger = -4,
|
cvInteger = -4,
|
||||||
cvFloat = -3,
|
cvFloat = -3,
|
||||||
cvString = -2,
|
cvString = -2, ///< Heap-allocated, owned (dMalloc/dFree)
|
||||||
cvSTEntry = -1,
|
cvSTEntry = -1, ///< StringTable pointer, NOT owned
|
||||||
cvConsoleValueType = 0
|
cvConsoleValueType = 0 ///< First valid engine console type ID
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConsoleValue
|
class ConsoleValue
|
||||||
|
|
@ -148,6 +148,7 @@ public:
|
||||||
EnumTable* enumTable;
|
EnumTable* enumTable;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
#pragma warning(pop)
|
||||||
|
|
||||||
S32 type;
|
S32 type;
|
||||||
U32 bufferLen;
|
U32 bufferLen;
|
||||||
|
|
@ -160,7 +161,10 @@ public:
|
||||||
|
|
||||||
TORQUE_FORCEINLINE void cleanupData()
|
TORQUE_FORCEINLINE void cleanupData()
|
||||||
{
|
{
|
||||||
if (type <= cvString && bufferLen > 0)
|
// Only cvString strings are heap-allocated and owned by this value.
|
||||||
|
// cvSTEntry points into the StringTable (managed externally).
|
||||||
|
// Numeric types use the f/i union fields — s is not valid for them.
|
||||||
|
if (type == ConsoleValueType::cvString && bufferLen > 0)
|
||||||
{
|
{
|
||||||
dFree(s);
|
dFree(s);
|
||||||
bufferLen = 0;
|
bufferLen = 0;
|
||||||
|
|
@ -176,57 +180,38 @@ public:
|
||||||
bufferLen = 0;
|
bufferLen = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleValue(const ConsoleValue& ref)
|
ConsoleValue(const ConsoleValue& other)
|
||||||
|
: type(ConsoleValueType::cvSTEntry)
|
||||||
|
, bufferLen(0)
|
||||||
{
|
{
|
||||||
type = ConsoleValueType::cvSTEntry;
|
|
||||||
s = const_cast<char*>(StringTable->EmptyString());
|
s = const_cast<char*>(StringTable->EmptyString());
|
||||||
bufferLen = 0;
|
copyFrom(other);
|
||||||
|
|
||||||
switch (ref.type)
|
|
||||||
{
|
|
||||||
case cvNULL:
|
|
||||||
std::cout << "Ref already cleared!";
|
|
||||||
break;
|
|
||||||
case cvInteger:
|
|
||||||
setInt(ref.i);
|
|
||||||
break;
|
|
||||||
case cvFloat:
|
|
||||||
setFloat(ref.f);
|
|
||||||
break;
|
|
||||||
case cvSTEntry:
|
|
||||||
setStringTableEntry(ref.s);
|
|
||||||
break;
|
|
||||||
case cvString:
|
|
||||||
setString(ref.s);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
setConsoleData(ref.type, ref.dataPtr, ref.enumTable);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleValue& operator=(const ConsoleValue& ref)
|
/// Move constructor — steals the heap buffer rather than copying it.
|
||||||
|
/// After the move, `other` is left as an empty-string value.
|
||||||
|
ConsoleValue(ConsoleValue&& other) noexcept
|
||||||
|
: type(other.type)
|
||||||
|
, bufferLen(other.bufferLen)
|
||||||
{
|
{
|
||||||
switch (ref.type)
|
transferFrom(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleValue& operator=(const ConsoleValue& other)
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
copyFrom(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleValue& operator=(ConsoleValue&& other) noexcept
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
{
|
{
|
||||||
case cvNULL:
|
cleanupData();
|
||||||
std::cout << "Ref already cleared!";
|
type = other.type;
|
||||||
break;
|
bufferLen = other.bufferLen;
|
||||||
case cvInteger:
|
transferFrom(other);
|
||||||
setInt(ref.i);
|
|
||||||
break;
|
|
||||||
case cvFloat:
|
|
||||||
setFloat(ref.f);
|
|
||||||
break;
|
|
||||||
case cvSTEntry:
|
|
||||||
setStringTableEntry(ref.s);
|
|
||||||
break;
|
|
||||||
case cvString:
|
|
||||||
setString(ref.s);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
setConsoleData(ref.type, ref.dataPtr, ref.enumTable);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
@ -243,75 +228,105 @@ public:
|
||||||
|
|
||||||
TORQUE_FORCEINLINE F64 getFloat() const
|
TORQUE_FORCEINLINE F64 getFloat() const
|
||||||
{
|
{
|
||||||
if (type == ConsoleValueType::cvFloat)
|
switch (type)
|
||||||
|
{
|
||||||
|
case ConsoleValueType::cvFloat:
|
||||||
return f;
|
return f;
|
||||||
if (type == ConsoleValueType::cvInteger)
|
case ConsoleValueType::cvInteger:
|
||||||
return i;
|
return static_cast<F64>(i);
|
||||||
if (type == ConsoleValueType::cvSTEntry)
|
case ConsoleValueType::cvSTEntry:
|
||||||
return s == StringTable->EmptyString() ? 0.0f : dAtof(s);
|
return (s == StringTable->EmptyString()) ? 0.0 : dAtof(s);
|
||||||
if (type == ConsoleValueType::cvString)
|
case ConsoleValueType::cvString:
|
||||||
return dStrcmp(s, "") == 0 ? 0.0f : dAtof(s);
|
return (s[0] == '\0') ? 0.0 : dAtof(s);
|
||||||
return dAtof(getConsoleData());
|
case ConsoleValueType::cvNULL:
|
||||||
|
return 0.0;
|
||||||
|
default:
|
||||||
|
return dAtof(getConsoleData());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TORQUE_FORCEINLINE S64 getInt() const
|
TORQUE_FORCEINLINE S64 getInt() const
|
||||||
{
|
{
|
||||||
if (type == ConsoleValueType::cvInteger)
|
switch (type)
|
||||||
|
{
|
||||||
|
case ConsoleValueType::cvInteger:
|
||||||
return i;
|
return i;
|
||||||
if (type == ConsoleValueType::cvFloat)
|
case ConsoleValueType::cvFloat:
|
||||||
return f;
|
return static_cast<S64>(f);
|
||||||
if (type == ConsoleValueType::cvSTEntry)
|
case ConsoleValueType::cvSTEntry:
|
||||||
return s == StringTable->EmptyString() ? 0 : dAtoi(s);
|
return (s == StringTable->EmptyString()) ? S64(0) : static_cast<S64>(dAtoi(s));
|
||||||
if (type == ConsoleValueType::cvString)
|
case ConsoleValueType::cvString:
|
||||||
return dStrcmp(s, "") == 0 ? 0 : dAtoi(s);
|
return (s[0] == '\0') ? S64(0) : static_cast<S64>(dAtoi(s));
|
||||||
|
case ConsoleValueType::cvNULL:
|
||||||
return dAtoi(getConsoleData());
|
return 0;
|
||||||
}
|
default:
|
||||||
|
return static_cast<S64>(dAtoi(getConsoleData()));
|
||||||
TORQUE_FORCEINLINE const char* getString() const
|
}
|
||||||
{
|
|
||||||
if (isStringType())
|
|
||||||
return s;
|
|
||||||
if (isNumberType())
|
|
||||||
return convertToBuffer();
|
|
||||||
return getConsoleData();
|
|
||||||
}
|
|
||||||
|
|
||||||
TORQUE_FORCEINLINE operator const char* () const
|
|
||||||
{
|
|
||||||
return getString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TORQUE_FORCEINLINE bool getBool() const
|
TORQUE_FORCEINLINE bool getBool() const
|
||||||
{
|
{
|
||||||
if (type == ConsoleValueType::cvInteger)
|
switch (type)
|
||||||
return (bool)i;
|
{
|
||||||
if (type == ConsoleValueType::cvFloat)
|
case ConsoleValueType::cvInteger:
|
||||||
return (bool)f;
|
return (i != 0);
|
||||||
if (type == ConsoleValueType::cvSTEntry)
|
case ConsoleValueType::cvFloat:
|
||||||
return s == StringTable->EmptyString() ? false : dAtob(s);
|
return (f != 0.0);
|
||||||
if (type == ConsoleValueType::cvString)
|
case ConsoleValueType::cvSTEntry:
|
||||||
return dStrcmp(s, "") == 0 ? false : dAtob(s);
|
return (s != StringTable->EmptyString()) && dAtob(s);
|
||||||
return dAtob(getConsoleData());
|
case ConsoleValueType::cvString:
|
||||||
|
return (s[0] != '\0') && dAtob(s);
|
||||||
|
case ConsoleValueType::cvNULL:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return dAtob(getConsoleData());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TORQUE_FORCEINLINE void setFloat(const F64 val)
|
TORQUE_FORCEINLINE const char* getString() const
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case ConsoleValueType::cvSTEntry:
|
||||||
|
case ConsoleValueType::cvString:
|
||||||
|
return s;
|
||||||
|
case ConsoleValueType::cvNULL:
|
||||||
|
return StringTable->EmptyString();
|
||||||
|
case ConsoleValueType::cvFloat:
|
||||||
|
case ConsoleValueType::cvInteger:
|
||||||
|
return convertToBuffer();
|
||||||
|
default:
|
||||||
|
return getConsoleData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TORQUE_FORCEINLINE operator const char* () const { return getString(); }
|
||||||
|
|
||||||
|
TORQUE_FORCEINLINE void setFloat(F64 val)
|
||||||
{
|
{
|
||||||
cleanupData();
|
cleanupData();
|
||||||
type = ConsoleValueType::cvFloat;
|
type = ConsoleValueType::cvFloat;
|
||||||
f = val;
|
f = val;
|
||||||
|
// bufferLen is already 0 after cleanupData — correct for non-string types
|
||||||
}
|
}
|
||||||
|
|
||||||
TORQUE_FORCEINLINE void setInt(const S64 val)
|
TORQUE_FORCEINLINE void setInt(S64 val)
|
||||||
{
|
{
|
||||||
cleanupData();
|
cleanupData();
|
||||||
type = ConsoleValueType::cvInteger;
|
type = ConsoleValueType::cvInteger;
|
||||||
i = val;
|
i = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TORQUE_FORCEINLINE void setBool(bool val)
|
||||||
|
{
|
||||||
|
cleanupData();
|
||||||
|
type = ConsoleValueType::cvInteger;
|
||||||
|
i = val ? S64(1) : S64(0);
|
||||||
|
}
|
||||||
|
|
||||||
TORQUE_FORCEINLINE void setString(const char* val)
|
TORQUE_FORCEINLINE void setString(const char* val)
|
||||||
{
|
{
|
||||||
setString(val, val != NULL ? dStrlen(val) : 0);
|
setString(val, val ? static_cast<S32>(dStrlen(val)) : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TORQUE_FORCEINLINE void setString(const char* val, S32 len)
|
TORQUE_FORCEINLINE void setString(const char* val, S32 len)
|
||||||
|
|
@ -321,67 +336,83 @@ public:
|
||||||
setEmptyString();
|
setEmptyString();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanupData();
|
cleanupData();
|
||||||
|
|
||||||
type = ConsoleValueType::cvString;
|
type = ConsoleValueType::cvString;
|
||||||
|
bufferLen = static_cast<U32>(len) + 1u; // allocation size, always > 0
|
||||||
s = (char*)dMalloc(len + 1);
|
s = static_cast<char*>(dMalloc(bufferLen));
|
||||||
|
|
||||||
bufferLen = len + 1;
|
|
||||||
s[len] = '\0';
|
s[len] = '\0';
|
||||||
dStrcpy(s, val, len + 1);
|
dMemcpy(s, val, static_cast<dsize_t>(len));
|
||||||
}
|
}
|
||||||
|
|
||||||
TORQUE_FORCEINLINE void setStringRef(const char* ref, S32 len)
|
/// Transfer ownership of a dMalloc'd buffer to this value.
|
||||||
|
///
|
||||||
|
/// @param ownedBuf Buffer allocated with dMalloc. Must have a null
|
||||||
|
/// terminator at ownedBuf[len]. This value will call
|
||||||
|
/// dFree(ownedBuf) when it is cleaned up.
|
||||||
|
/// @param len String length NOT including the null terminator.
|
||||||
|
/// If len == 0 the buffer still gets freed correctly
|
||||||
|
/// because bufferLen is stored as len+1.
|
||||||
|
TORQUE_FORCEINLINE void setStringOwned(char* ownedBuf, S32 len)
|
||||||
{
|
{
|
||||||
cleanupData();
|
cleanupData();
|
||||||
type = ConsoleValueType::cvString;
|
type = ConsoleValueType::cvString;
|
||||||
s = (char*)std::move(ref);
|
bufferLen = static_cast<U32>(len) + 1; // always > 0 → cleanupData will free
|
||||||
bufferLen = len;
|
s = ownedBuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
TORQUE_FORCEINLINE void setBool(const bool val)
|
/// @deprecated Use setStringOwned(). Kept so existing call sites compile.
|
||||||
|
/// The old name "Ref" implied a non-owning borrow, which was
|
||||||
|
/// the opposite of the actual semantics.
|
||||||
|
TORQUE_FORCEINLINE void setStringRef(const char* ownedBuf, S32 len)
|
||||||
{
|
{
|
||||||
cleanupData();
|
setStringOwned(const_cast<char*>(ownedBuf), len);
|
||||||
type = ConsoleValueType::cvInteger;
|
|
||||||
i = (int)val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TORQUE_FORCEINLINE void setStringTableEntry(StringTableEntry val)
|
TORQUE_FORCEINLINE void setStringTableEntry(StringTableEntry val)
|
||||||
{
|
{
|
||||||
cleanupData();
|
cleanupData();
|
||||||
type = ConsoleValueType::cvSTEntry;
|
type = ConsoleValueType::cvSTEntry;
|
||||||
s = (char*)(StringTable->insert(val));
|
// StringTable::insert accepts NULL and returns EmptyString
|
||||||
bufferLen = 0;
|
s = const_cast<char*>(StringTable->insert(val ? val : ""));
|
||||||
|
bufferLen = 0; // NOT owned — StringTable manages this memory
|
||||||
}
|
}
|
||||||
|
|
||||||
TORQUE_FORCEINLINE void setEmptyString()
|
TORQUE_FORCEINLINE void setEmptyString()
|
||||||
{
|
{
|
||||||
setStringTableEntry(StringTable->EmptyString());
|
// cleanupData already sets s = EmptyString and type = cvNULL.
|
||||||
|
// We then promote the type to cvSTEntry so queries return a valid
|
||||||
|
// empty string rather than having to special-case cvNULL everywhere.
|
||||||
|
cleanupData();
|
||||||
|
type = ConsoleValueType::cvSTEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
TORQUE_FORCEINLINE void setConsoleData(S32 inConsoleType, void* inDataPtr, const EnumTable* inEnumTable)
|
TORQUE_FORCEINLINE void setConsoleData(S32 inType, void* inDataPtr, const EnumTable* inEnumTable)
|
||||||
{
|
{
|
||||||
cleanupData();
|
cleanupData();
|
||||||
type = inConsoleType;
|
type = inType;
|
||||||
dataPtr = inDataPtr;
|
dataPtr = inDataPtr;
|
||||||
enumTable = const_cast<EnumTable*>(inEnumTable);
|
enumTable = const_cast<EnumTable*>(inEnumTable);
|
||||||
};
|
bufferLen = 0;
|
||||||
|
|
||||||
TORQUE_FORCEINLINE S32 getType() const
|
|
||||||
{
|
|
||||||
return type;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TORQUE_FORCEINLINE bool isStringType() const
|
TORQUE_FORCEINLINE void setFastFloat(F64 val) { type = ConsoleValueType::cvFloat; f = val; }
|
||||||
|
TORQUE_FORCEINLINE F64 getFastFloat() const { return f; }
|
||||||
|
|
||||||
|
TORQUE_FORCEINLINE void setFastInt(S64 val) { type = ConsoleValueType::cvInteger; i = val; }
|
||||||
|
TORQUE_FORCEINLINE S64 getFastInt() const { return i; }
|
||||||
|
|
||||||
|
TORQUE_FORCEINLINE S32 getType() const { return type; }
|
||||||
|
|
||||||
|
TORQUE_FORCEINLINE bool isStringType() const
|
||||||
{
|
{
|
||||||
return type == ConsoleValueType::cvString || type == ConsoleValueType::cvSTEntry;
|
return type == ConsoleValueType::cvString
|
||||||
|
|| type == ConsoleValueType::cvSTEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
TORQUE_FORCEINLINE bool isNumberType() const
|
TORQUE_FORCEINLINE bool isNumberType() const
|
||||||
{
|
{
|
||||||
return type == ConsoleValueType::cvFloat || type == ConsoleValueType::cvInteger;
|
return type == ConsoleValueType::cvFloat
|
||||||
|
|| type == ConsoleValueType::cvInteger;
|
||||||
}
|
}
|
||||||
|
|
||||||
TORQUE_FORCEINLINE bool isConsoleType() const
|
TORQUE_FORCEINLINE bool isConsoleType() const
|
||||||
|
|
@ -391,40 +422,89 @@ public:
|
||||||
|
|
||||||
TORQUE_FORCEINLINE S32 getConsoleType() const
|
TORQUE_FORCEINLINE S32 getConsoleType() const
|
||||||
{
|
{
|
||||||
if(type >= ConsoleValueType::cvConsoleValueType)
|
return (type >= ConsoleValueType::cvConsoleValueType) ? type : 0;
|
||||||
{
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TORQUE_FORCEINLINE void setFastFloat(F64 flt)
|
|
||||||
{
|
|
||||||
type = ConsoleValueType::cvFloat;
|
|
||||||
f = flt;
|
|
||||||
}
|
|
||||||
|
|
||||||
TORQUE_FORCEINLINE F64 getFastFloat() const
|
|
||||||
{
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
TORQUE_FORCEINLINE void setFastInt(S64 flt)
|
|
||||||
{
|
|
||||||
type = ConsoleValueType::cvInteger;
|
|
||||||
i = flt;
|
|
||||||
}
|
|
||||||
|
|
||||||
TORQUE_FORCEINLINE S64 getFastInt() const
|
|
||||||
{
|
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void init();
|
static void init();
|
||||||
static void resetConversionBuffer();
|
static void resetConversionBuffer();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Deep-copy from `other` into `this` (assumes `this` has already been
|
||||||
|
/// cleaned up or is freshly constructed).
|
||||||
|
void copyFrom(const ConsoleValue& other)
|
||||||
|
{
|
||||||
|
switch (other.type)
|
||||||
|
{
|
||||||
|
case ConsoleValueType::cvNULL:
|
||||||
|
// Another value was already cleaned up. Treat as empty string.
|
||||||
|
// Do NOT assert here — cvNULL is a valid transient state that can
|
||||||
|
// appear e.g. when an entry is moved out of.
|
||||||
|
setEmptyString();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ConsoleValueType::cvInteger:
|
||||||
|
setInt(other.i);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ConsoleValueType::cvFloat:
|
||||||
|
setFloat(other.f);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ConsoleValueType::cvSTEntry:
|
||||||
|
// s already points into StringTable — just share the pointer.
|
||||||
|
setStringTableEntry(other.s);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ConsoleValueType::cvString:
|
||||||
|
{
|
||||||
|
// bufferLen == allocation size (len+1), so string length == bufferLen-1.
|
||||||
|
// Guard defensively: if somehow bufferLen is 0 (pre-fix bug state),
|
||||||
|
// fall back to dStrlen.
|
||||||
|
S32 strLen = (other.bufferLen > 0)
|
||||||
|
? static_cast<S32>(other.bufferLen) - 1
|
||||||
|
: static_cast<S32>(dStrlen(other.s));
|
||||||
|
setString(other.s, strLen);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
setConsoleData(other.type, other.dataPtr, other.enumTable);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Steal the payload from `other` (which must already have its type and
|
||||||
|
/// bufferLen copied into `this`), then leave `other` in a safe empty state.
|
||||||
|
/// Called only from move constructor / move assignment after copying type.
|
||||||
|
TORQUE_FORCEINLINE void transferFrom(ConsoleValue& other) noexcept
|
||||||
|
{
|
||||||
|
// Copy the right union field based on the type we already copied.
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case ConsoleValueType::cvFloat:
|
||||||
|
f = other.f;
|
||||||
|
break;
|
||||||
|
case ConsoleValueType::cvInteger:
|
||||||
|
i = other.i;
|
||||||
|
break;
|
||||||
|
case ConsoleValueType::cvString:
|
||||||
|
case ConsoleValueType::cvSTEntry:
|
||||||
|
case ConsoleValueType::cvNULL:
|
||||||
|
s = other.s;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dataPtr = other.dataPtr;
|
||||||
|
enumTable = other.enumTable;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leave `other` as a valid empty-string value.
|
||||||
|
// Critically: if we stole a cvString buffer, other must NOT keep a
|
||||||
|
// non-zero bufferLen, or its destructor will double-free.
|
||||||
|
other.s = const_cast<char*>(StringTable->EmptyString());
|
||||||
|
other.type = ConsoleValueType::cvSTEntry;
|
||||||
|
other.bufferLen = 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Transparently converts ConsoleValue[] to const char**
|
// Transparently converts ConsoleValue[] to const char**
|
||||||
|
|
|
||||||
|
|
@ -672,13 +672,13 @@ Namespace::Entry::Entry()
|
||||||
mPackage = StringTable->EmptyString();
|
mPackage = StringTable->EmptyString();
|
||||||
mToolOnly = false;
|
mToolOnly = false;
|
||||||
VECTOR_SET_ASSOCIATION(mArgFlags);
|
VECTOR_SET_ASSOCIATION(mArgFlags);
|
||||||
VECTOR_SET_ASSOCIATION(mDefaultValues);
|
VECTOR_SET_ASSOCIATION(mDefaultOffsets);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Namespace::Entry::clear()
|
void Namespace::Entry::clear()
|
||||||
{
|
{
|
||||||
mArgFlags.clear();
|
mArgFlags.clear();
|
||||||
mDefaultValues.clear();
|
mDefaultOffsets.clear();
|
||||||
|
|
||||||
if (mModule)
|
if (mModule)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ public:
|
||||||
|
|
||||||
// Offsets to get default values for arguments.
|
// Offsets to get default values for arguments.
|
||||||
Vector<U32> mArgFlags;
|
Vector<U32> mArgFlags;
|
||||||
Vector<ConsoleValue> mDefaultValues;
|
Vector<U32> mDefaultOffsets;
|
||||||
|
|
||||||
/// If it's a script function, this is the line of the declaration in code.
|
/// If it's a script function, this is the line of the declaration in code.
|
||||||
/// @note 0 for functions read from legacy DSOs that have no line number information.
|
/// @note 0 for functions read from legacy DSOs that have no line number information.
|
||||||
|
|
|
||||||
|
|
@ -81,51 +81,45 @@ extern int CMDdebug;
|
||||||
rwSWITCHSTR = 282, /* rwSWITCHSTR */
|
rwSWITCHSTR = 282, /* rwSWITCHSTR */
|
||||||
rwCASEOR = 283, /* rwCASEOR */
|
rwCASEOR = 283, /* rwCASEOR */
|
||||||
rwPACKAGE = 284, /* rwPACKAGE */
|
rwPACKAGE = 284, /* rwPACKAGE */
|
||||||
rwNAMESPACE = 285, /* rwNAMESPACE */
|
rwASSERT = 285, /* rwASSERT */
|
||||||
rwCLASS = 286, /* rwCLASS */
|
ILLEGAL_TOKEN = 286, /* ILLEGAL_TOKEN */
|
||||||
rwASSERT = 287, /* rwASSERT */
|
CHRCONST = 287, /* CHRCONST */
|
||||||
ILLEGAL_TOKEN = 288, /* ILLEGAL_TOKEN */
|
INTCONST = 288, /* INTCONST */
|
||||||
CHRCONST = 289, /* CHRCONST */
|
TTAG = 289, /* TTAG */
|
||||||
INTCONST = 290, /* INTCONST */
|
VAR = 290, /* VAR */
|
||||||
TTAG = 291, /* TTAG */
|
IDENT = 291, /* IDENT */
|
||||||
VAR = 292, /* VAR */
|
TYPEIDENT = 292, /* TYPEIDENT */
|
||||||
IDENT = 293, /* IDENT */
|
DOCBLOCK = 293, /* DOCBLOCK */
|
||||||
TYPEIDENT = 294, /* TYPEIDENT */
|
STRATOM = 294, /* STRATOM */
|
||||||
DOCBLOCK = 295, /* DOCBLOCK */
|
TAGATOM = 295, /* TAGATOM */
|
||||||
STRATOM = 296, /* STRATOM */
|
FLTCONST = 296, /* FLTCONST */
|
||||||
TAGATOM = 297, /* TAGATOM */
|
opINTNAME = 297, /* opINTNAME */
|
||||||
FLTCONST = 298, /* FLTCONST */
|
opINTNAMER = 298, /* opINTNAMER */
|
||||||
opINTNAME = 299, /* opINTNAME */
|
opMINUSMINUS = 299, /* opMINUSMINUS */
|
||||||
opINTNAMER = 300, /* opINTNAMER */
|
opPLUSPLUS = 300, /* opPLUSPLUS */
|
||||||
opMINUSMINUS = 301, /* opMINUSMINUS */
|
opSHL = 301, /* opSHL */
|
||||||
opPLUSPLUS = 302, /* opPLUSPLUS */
|
opSHR = 302, /* opSHR */
|
||||||
STMT_SEP = 303, /* STMT_SEP */
|
opPLASN = 303, /* opPLASN */
|
||||||
opSHL = 304, /* opSHL */
|
opMIASN = 304, /* opMIASN */
|
||||||
opSHR = 305, /* opSHR */
|
opMLASN = 305, /* opMLASN */
|
||||||
opPLASN = 306, /* opPLASN */
|
opDVASN = 306, /* opDVASN */
|
||||||
opMIASN = 307, /* opMIASN */
|
opMODASN = 307, /* opMODASN */
|
||||||
opMLASN = 308, /* opMLASN */
|
opANDASN = 308, /* opANDASN */
|
||||||
opDVASN = 309, /* opDVASN */
|
opXORASN = 309, /* opXORASN */
|
||||||
opMODASN = 310, /* opMODASN */
|
opORASN = 310, /* opORASN */
|
||||||
opANDASN = 311, /* opANDASN */
|
opSLASN = 311, /* opSLASN */
|
||||||
opXORASN = 312, /* opXORASN */
|
opSRASN = 312, /* opSRASN */
|
||||||
opORASN = 313, /* opORASN */
|
opCAT = 313, /* opCAT */
|
||||||
opSLASN = 314, /* opSLASN */
|
opEQ = 314, /* opEQ */
|
||||||
opSRASN = 315, /* opSRASN */
|
opNE = 315, /* opNE */
|
||||||
opCAT = 316, /* opCAT */
|
opGE = 316, /* opGE */
|
||||||
opEQ = 317, /* opEQ */
|
opLE = 317, /* opLE */
|
||||||
opNE = 318, /* opNE */
|
opAND = 318, /* opAND */
|
||||||
opGE = 319, /* opGE */
|
opOR = 319, /* opOR */
|
||||||
opLE = 320, /* opLE */
|
opSTREQ = 320, /* opSTREQ */
|
||||||
opAND = 321, /* opAND */
|
opSTRNE = 321, /* opSTRNE */
|
||||||
opOR = 322, /* opOR */
|
opCOLONCOLON = 322, /* opCOLONCOLON */
|
||||||
opSTREQ = 323, /* opSTREQ */
|
UNARY = 323 /* UNARY */
|
||||||
opCOLONCOLON = 324, /* opCOLONCOLON */
|
|
||||||
opMDASN = 325, /* opMDASN */
|
|
||||||
opNDASN = 326, /* opNDASN */
|
|
||||||
opNTASN = 327, /* opNTASN */
|
|
||||||
opSTRNE = 328, /* opSTRNE */
|
|
||||||
UNARY = 329 /* UNARY */
|
|
||||||
};
|
};
|
||||||
typedef enum yytokentype yytoken_kind_t;
|
typedef enum yytokentype yytoken_kind_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -134,7 +128,7 @@ extern int CMDdebug;
|
||||||
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
|
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
|
||||||
union YYSTYPE
|
union YYSTYPE
|
||||||
{
|
{
|
||||||
#line 87 "CMDgram.y"
|
#line 107 "CMDgram.y"
|
||||||
|
|
||||||
Token< char > c;
|
Token< char > c;
|
||||||
Token< int > i;
|
Token< int > i;
|
||||||
|
|
@ -152,7 +146,7 @@ union YYSTYPE
|
||||||
AssignDecl asn;
|
AssignDecl asn;
|
||||||
IfStmtNode* ifnode;
|
IfStmtNode* ifnode;
|
||||||
|
|
||||||
#line 156 "CMDgram.h"
|
#line 150 "CMDgram.h"
|
||||||
|
|
||||||
};
|
};
|
||||||
typedef union YYSTYPE YYSTYPE;
|
typedef union YYSTYPE YYSTYPE;
|
||||||
|
|
|
||||||
|
|
@ -46,20 +46,28 @@ struct Token
|
||||||
};
|
};
|
||||||
|
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%{
|
%{
|
||||||
/* Reserved Word Definitions */
|
/* Reserved word token definitions */
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%token <i> rwDEFINE rwENDDEF rwDECLARE rwDECLARESINGLETON
|
%token <i> rwDEFINE rwENDDEF rwDECLARE rwDECLARESINGLETON
|
||||||
%token <i> rwBREAK rwELSE rwCONTINUE rwGLOBAL
|
%token <i> rwBREAK rwELSE rwCONTINUE rwGLOBAL
|
||||||
%token <i> rwIF rwNIL rwRETURN rwWHILE rwDO
|
%token <i> rwIF rwNIL rwRETURN rwWHILE rwDO
|
||||||
%token <i> rwENDIF rwENDWHILE rwENDFOR rwDEFAULT
|
%token <i> rwENDIF rwENDWHILE rwENDFOR rwDEFAULT
|
||||||
%token <i> rwFOR rwFOREACH rwFOREACHSTR rwIN rwDATABLOCK rwSWITCH rwCASE rwSWITCHSTR
|
%token <i> rwFOR rwFOREACH rwFOREACHSTR rwIN rwDATABLOCK rwSWITCH rwCASE rwSWITCHSTR
|
||||||
%token <i> rwCASEOR rwPACKAGE rwNAMESPACE rwCLASS
|
%token <i> rwCASEOR rwPACKAGE
|
||||||
%token <i> rwASSERT
|
%token <i> rwASSERT
|
||||||
%token ILLEGAL_TOKEN
|
%token ILLEGAL_TOKEN
|
||||||
|
// NOTE: rwNAMESPACE and rwCLASS were declared here previously but had no
|
||||||
|
// lexer rules and appeared in no grammar productions. They have been
|
||||||
|
// removed. If namespace/class syntax is added in future, add both the
|
||||||
|
// token declaration AND the lexer rule at the same time.
|
||||||
|
|
||||||
%{
|
%{
|
||||||
/* Constants and Identifier Definitions */
|
/* Constant and identifier token definitions */
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%token <c> CHRCONST
|
%token <c> CHRCONST
|
||||||
%token <i> INTCONST
|
%token <i> INTCONST
|
||||||
%token <s> TTAG
|
%token <s> TTAG
|
||||||
|
|
@ -72,16 +80,28 @@ struct Token
|
||||||
%token <f> FLTCONST
|
%token <f> FLTCONST
|
||||||
|
|
||||||
%{
|
%{
|
||||||
/* Operator Definitions */
|
/* Operator token definitions */
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%token <i> '+' '-' '*' '/' '<' '>' '=' '.' '|' '&' '%'
|
%token <i> '+' '-' '*' '/' '<' '>' '=' '.' '|' '&' '%'
|
||||||
%token <i> '(' ')' ',' ':' ';' '{' '}' '^' '~' '!' '@'
|
%token <i> '(' ')' ',' ':' ';' '{' '}' '^' '~' '!' '@'
|
||||||
%token <i> opINTNAME opINTNAMER
|
%token <i> opINTNAME opINTNAMER
|
||||||
%token <i> opMINUSMINUS opPLUSPLUS
|
%token <i> opMINUSMINUS opPLUSPLUS
|
||||||
%token <i> STMT_SEP
|
|
||||||
|
// NOTE: STMT_SEP was declared here but never returned by the lexer and never
|
||||||
|
// used in any grammar production. Removed to prevent confusion.
|
||||||
|
|
||||||
%token <i> opSHL opSHR opPLASN opMIASN opMLASN opDVASN opMODASN opANDASN
|
%token <i> opSHL opSHR opPLASN opMIASN opMLASN opDVASN opMODASN opANDASN
|
||||||
%token <i> opXORASN opORASN opSLASN opSRASN opCAT
|
%token <i> opXORASN opORASN opSLASN opSRASN opCAT
|
||||||
%token <i> opEQ opNE opGE opLE opAND opOR opSTREQ
|
%token <i> opEQ opNE opGE opLE opAND opOR
|
||||||
|
|
||||||
|
// FIX: opSTREQ and opSTRNE must be declared with their semantic type <i>.
|
||||||
|
// Previously opSTRNE was only mentioned in the %left precedence line, which
|
||||||
|
// does declare it as a token but gives it no type — causing a silent type
|
||||||
|
// mismatch when used in grammar rules (even if $2 isn't used in the action,
|
||||||
|
// the generated parser code is technically undefined behaviour).
|
||||||
|
%token <i> opSTREQ opSTRNE
|
||||||
|
|
||||||
%token <i> opCOLONCOLON
|
%token <i> opCOLONCOLON
|
||||||
|
|
||||||
%union {
|
%union {
|
||||||
|
|
@ -143,8 +163,13 @@ struct Token
|
||||||
%type <var> var_list_decl
|
%type <var> var_list_decl
|
||||||
%type <asn> assign_op_struct
|
%type <asn> assign_op_struct
|
||||||
|
|
||||||
|
// Operator precedence — lowest to highest.
|
||||||
|
// FIX: opMDASN, opNDASN, opNTASN were listed here but were never defined
|
||||||
|
// as tokens anywhere and were never returned by the lexer. They appear to
|
||||||
|
// be leftovers from an earlier revision. Removed to prevent compiler
|
||||||
|
// warnings about undeclared token names.
|
||||||
%left '['
|
%left '['
|
||||||
%right opMODASN opANDASN opXORASN opPLASN opMIASN opMLASN opDVASN opMDASN opNDASN opNTASN opORASN opSLASN opSRASN '='
|
%right opMODASN opANDASN opXORASN opPLASN opMIASN opMLASN opDVASN opORASN opSLASN opSRASN '='
|
||||||
%left '?' ':'
|
%left '?' ':'
|
||||||
%left opOR
|
%left opOR
|
||||||
%left opAND
|
%left opAND
|
||||||
|
|
@ -229,17 +254,19 @@ stmt
|
||||||
;
|
;
|
||||||
|
|
||||||
fn_decl_stmt
|
fn_decl_stmt
|
||||||
|
// Global function
|
||||||
: rwDEFINE IDENT '(' var_list_decl ')' '{' statement_list '}'
|
: rwDEFINE IDENT '(' var_list_decl ')' '{' statement_list '}'
|
||||||
{ $$ = FunctionDeclStmtNode::alloc( $1.lineNumber, $2.value, NULL, $4, $7 ); }
|
{ $$ = FunctionDeclStmtNode::alloc( $1.lineNumber, $2.value, NULL, $4, $7 ); }
|
||||||
| rwDEFINE IDENT opCOLONCOLON IDENT '(' var_list_decl ')' '{' statement_list '}'
|
// Namespaced method: function Namespace::name(...) { }
|
||||||
{ $$ = FunctionDeclStmtNode::alloc( $1.lineNumber, $4.value, $2.value, $6, $9 ); }
|
| rwDEFINE IDENT opCOLONCOLON IDENT '(' var_list_decl ')' '{' statement_list '}'
|
||||||
|
{ $$ = FunctionDeclStmtNode::alloc( $1.lineNumber, $4.value, $2.value, $6, $9 ); }
|
||||||
;
|
;
|
||||||
|
|
||||||
var_list_decl
|
var_list_decl
|
||||||
:
|
:
|
||||||
{ $$ = NULL; }
|
{ $$ = NULL; }
|
||||||
| var_list
|
| var_list
|
||||||
{ $$ = $1; }
|
{ $$ = $1; }
|
||||||
;
|
;
|
||||||
|
|
||||||
var_list
|
var_list
|
||||||
|
|
@ -249,27 +276,31 @@ var_list
|
||||||
{ $$ = $1; ((StmtNode*)($1))->append((StmtNode*)$3 ); }
|
{ $$ = $1; ((StmtNode*)($1))->append((StmtNode*)$3 ); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
// Parameter declaration forms:
|
||||||
|
//
|
||||||
|
// %var — required parameter
|
||||||
|
// %var ? — optional parameter, evaluates to "" / 0 when absent
|
||||||
|
// %var = expr — optional parameter with default value
|
||||||
|
// %var ? = expr — same as above; the '?' makes the optionality explicit
|
||||||
|
//
|
||||||
|
// NOTE: the default `expr` can be any valid expression, including function
|
||||||
|
// calls and variable references. At present these are evaluated once at
|
||||||
|
// declaration time (global scope). The planned codelet change (see
|
||||||
|
// FunctionDeclStmtNode::compileStmt in ast.cpp) will evaluate them at
|
||||||
|
// each call site instead — no grammar change is required for that fix.
|
||||||
param
|
param
|
||||||
: VAR
|
: VAR
|
||||||
{
|
{ $$ = VarNode::allocParam($1.lineNumber, $1.value, NULL); }
|
||||||
$$ = VarNode::allocParam($1.lineNumber, $1.value, NULL);
|
| VAR '?'
|
||||||
}
|
{ $$ = VarNode::allocParam($1.lineNumber, $1.value, NULL); }
|
||||||
| VAR '?'
|
| VAR '=' expr
|
||||||
{
|
{ $$ = VarNode::allocParam($1.lineNumber, $1.value, $3); }
|
||||||
$$ = VarNode::allocParam($1.lineNumber, $1.value, NULL);
|
| VAR '?' '=' expr
|
||||||
}
|
{ $$ = VarNode::allocParam($1.lineNumber, $1.value, $4); }
|
||||||
| VAR '=' expr
|
;
|
||||||
{
|
|
||||||
$$ = VarNode::allocParam($1.lineNumber, $1.value, $3);
|
|
||||||
}
|
|
||||||
| VAR '?' '=' expr
|
|
||||||
{
|
|
||||||
$$ = VarNode::allocParam($1.lineNumber, $1.value, $4);
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
datablock_decl
|
datablock_decl
|
||||||
: rwDATABLOCK class_name_expr '(' expr parent_block ')' '{' slot_assign_list_opt '}' ';'
|
: rwDATABLOCK class_name_expr '(' expr parent_block ')' '{' slot_assign_list_opt '}' ';'
|
||||||
{ $$ = ObjectDeclNode::alloc( $1.lineNumber, $2, $4, NULL, $5.value, $8, NULL, true, false, false); }
|
{ $$ = ObjectDeclNode::alloc( $1.lineNumber, $2, $4, NULL, $5.value, $8, NULL, true, false, false); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -341,6 +372,9 @@ switch_stmt
|
||||||
{ $$ = $6; $6->propagateSwitchExpr($3, true); }
|
{ $$ = $6; $6->propagateSwitchExpr($3, true); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
// NOTE: propagateSwitchExpr builds a recursive OR expression tree that is
|
||||||
|
// O(n) deep for n cases. Large switch statements (100+ cases) can overflow
|
||||||
|
// the compiler stack.
|
||||||
case_block
|
case_block
|
||||||
: rwCASE case_expr ':' statement_list
|
: rwCASE case_expr ':' statement_list
|
||||||
{ $$ = IfStmtNode::alloc( $1.lineNumber, $2, $4, NULL, false); }
|
{ $$ = IfStmtNode::alloc( $1.lineNumber, $2, $4, NULL, false); }
|
||||||
|
|
@ -352,9 +386,9 @@ case_block
|
||||||
|
|
||||||
case_expr
|
case_expr
|
||||||
: expr
|
: expr
|
||||||
{ $$ = $1;}
|
{ $$ = $1; }
|
||||||
| case_expr rwCASEOR expr
|
| case_expr rwCASEOR expr
|
||||||
{ ($1)->append($3); $$=$1; }
|
{ ($1)->append($3); $$ = $1; }
|
||||||
;
|
;
|
||||||
|
|
||||||
if_stmt
|
if_stmt
|
||||||
|
|
@ -455,6 +489,12 @@ expr
|
||||||
{ $$ = StreqExprNode::alloc( $1->dbgLineNumber, $1, $3, true); }
|
{ $$ = StreqExprNode::alloc( $1->dbgLineNumber, $1, $3, true); }
|
||||||
| expr opSTRNE expr
|
| expr opSTRNE expr
|
||||||
{ $$ = StreqExprNode::alloc( $1->dbgLineNumber, $1, $3, false); }
|
{ $$ = StreqExprNode::alloc( $1->dbgLineNumber, $1, $3, false); }
|
||||||
|
// The '@' operator covers four cases via token value encoding in the lexer:
|
||||||
|
// '@' → value 0 (plain concatenation)
|
||||||
|
// NL → value '\n'
|
||||||
|
// TAB → value '\t'
|
||||||
|
// SPC → value ' '
|
||||||
|
// The appendChar is stored in $2.value and forwarded to StrcatExprNode.
|
||||||
| expr '@' expr
|
| expr '@' expr
|
||||||
{ $$ = StrcatExprNode::alloc( $1->dbgLineNumber, $1, $3, $2.value); }
|
{ $$ = StrcatExprNode::alloc( $1->dbgLineNumber, $1, $3, $2.value); }
|
||||||
| '!' expr
|
| '!' expr
|
||||||
|
|
@ -482,23 +522,6 @@ expr
|
||||||
| VAR '[' aidx_expr ']'
|
| VAR '[' aidx_expr ']'
|
||||||
{ $$ = (ExprNode*)VarNode::alloc( $1.lineNumber, $1.value, $3 ); }
|
{ $$ = (ExprNode*)VarNode::alloc( $1.lineNumber, $1.value, $3 ); }
|
||||||
;
|
;
|
||||||
/*
|
|
||||||
| rwDEFINE '(' var_list_decl ')' '{' statement_list '}'
|
|
||||||
{
|
|
||||||
const U32 bufLen = 64;
|
|
||||||
UTF8 buffer[bufLen];
|
|
||||||
dSprintf(buffer, bufLen, "__anonymous_function%d", gAnonFunctionID++);
|
|
||||||
StringTableEntry fName = StringTable->insert(buffer);
|
|
||||||
StmtNode *fndef = FunctionDeclStmtNode::alloc($1.lineNumber, fName, NULL, $3, $6);
|
|
||||||
|
|
||||||
if(!gAnonFunctionList)
|
|
||||||
gAnonFunctionList = fndef;
|
|
||||||
else
|
|
||||||
gAnonFunctionList->append(fndef);
|
|
||||||
|
|
||||||
$$ = StrConstNode::alloc( $1.lineNumber, (UTF8*)fName, false );
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
slot_acc
|
slot_acc
|
||||||
: expr '.' IDENT
|
: expr '.' IDENT
|
||||||
|
|
@ -509,9 +532,9 @@ slot_acc
|
||||||
|
|
||||||
intslot_acc
|
intslot_acc
|
||||||
: expr opINTNAME class_name_expr
|
: expr opINTNAME class_name_expr
|
||||||
{ $$.lineNumber = $1->dbgLineNumber; $$.object = $1; $$.slotExpr = $3; $$.recurse = false; }
|
{ $$.lineNumber = $1->dbgLineNumber; $$.object = $1; $$.slotExpr = $3; $$.recurse = false; }
|
||||||
| expr opINTNAMER class_name_expr
|
| expr opINTNAMER class_name_expr
|
||||||
{ $$.lineNumber = $1->dbgLineNumber; $$.object = $1; $$.slotExpr = $3; $$.recurse = true; }
|
{ $$.lineNumber = $1->dbgLineNumber; $$.object = $1; $$.slotExpr = $3; $$.recurse = true; }
|
||||||
;
|
;
|
||||||
|
|
||||||
class_name_expr
|
class_name_expr
|
||||||
|
|
@ -572,18 +595,18 @@ stmt_expr
|
||||||
;
|
;
|
||||||
|
|
||||||
funcall_expr
|
funcall_expr
|
||||||
|
// Global function call: name(args)
|
||||||
: IDENT '(' expr_list_decl ')'
|
: IDENT '(' expr_list_decl ')'
|
||||||
{ $$ = FuncCallExprNode::alloc( $1.lineNumber, $1.value, NULL, $3, false); }
|
{ $$ = FuncCallExprNode::alloc( $1.lineNumber, $1.value, NULL, $3, false); }
|
||||||
|
// Static/namespace call: Namespace::name(args)
|
||||||
| IDENT opCOLONCOLON IDENT '(' expr_list_decl ')'
|
| IDENT opCOLONCOLON IDENT '(' expr_list_decl ')'
|
||||||
{ $$ = FuncCallExprNode::alloc( $1.lineNumber, $3.value, $1.value, $5, false); }
|
{ $$ = FuncCallExprNode::alloc( $1.lineNumber, $3.value, $1.value, $5, false); }
|
||||||
|
// Method call: object.method(args)
|
||||||
|
// The object expression is prepended to the arg list so that exec() can
|
||||||
|
// find it as callArgv[1] (the implicit 'this').
|
||||||
| expr '.' IDENT '(' expr_list_decl ')'
|
| expr '.' IDENT '(' expr_list_decl ')'
|
||||||
{ $1->append($5); $$ = FuncCallExprNode::alloc( $1->dbgLineNumber, $3.value, NULL, $1, true); }
|
{ $1->append($5); $$ = FuncCallExprNode::alloc( $1->dbgLineNumber, $3.value, NULL, $1, true); }
|
||||||
;
|
;
|
||||||
/*
|
|
||||||
| expr '(' expr_list_decl ')'
|
|
||||||
{ $$ = FuncPointerCallExprNode::alloc( $1->dbgLineNumber, $1, $3); }
|
|
||||||
;
|
|
||||||
*/
|
|
||||||
|
|
||||||
assert_expr
|
assert_expr
|
||||||
: rwASSERT '(' expr ')'
|
: rwASSERT '(' expr ')'
|
||||||
|
|
@ -633,36 +656,44 @@ slot_assign
|
||||||
{ $$ = SlotAssignNode::alloc( $1.lineNumber, NULL, $4, $2.value, $7, $1.value); }
|
{ $$ = SlotAssignNode::alloc( $1.lineNumber, NULL, $4, $2.value, $7, $1.value); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
// Array index expressions. Multiple comma-separated indices get
|
||||||
|
// concatenated with '_' separators at runtime (e.g. arr[1,2] → "arr_1_2").
|
||||||
aidx_expr
|
aidx_expr
|
||||||
: expr
|
: expr
|
||||||
{ $$ = $1; }
|
{ $$ = $1; }
|
||||||
| aidx_expr ',' expr
|
| aidx_expr ',' expr
|
||||||
{ $$ = CommaCatExprNode::alloc( $1->dbgLineNumber, $1, $3); }
|
{ $$ = CommaCatExprNode::alloc( $1->dbgLineNumber, $1, $3); }
|
||||||
;
|
;
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
int
|
int
|
||||||
yyreport_syntax_error (const yypcontext_t *ctx)
|
yyreport_syntax_error(const yypcontext_t *ctx)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
String output;
|
String output;
|
||||||
const YYLTYPE *loc = yypcontext_location (ctx);
|
const YYLTYPE *loc = yypcontext_location(ctx);
|
||||||
output += "syntax error: ";
|
output += "syntax error: ";
|
||||||
|
|
||||||
yysymbol_kind_t nxt = yypcontext_token(ctx);
|
yysymbol_kind_t nxt = yypcontext_token(ctx);
|
||||||
if (nxt != YYSYMBOL_YYEMPTY)
|
if (nxt != YYSYMBOL_YYEMPTY)
|
||||||
output += String::ToString("unexpected: %s at column: %d", yysymbol_name(nxt), loc->first_column);
|
output += String::ToString("unexpected: %s at column: %d",
|
||||||
|
yysymbol_name(nxt), loc->first_column);
|
||||||
|
|
||||||
enum { TOKENMAX = 10 };
|
enum { TOKENMAX = 10 };
|
||||||
yysymbol_kind_t expected[TOKENMAX];
|
yysymbol_kind_t expected[TOKENMAX];
|
||||||
|
|
||||||
int exp = yypcontext_expected_tokens(ctx, expected, TOKENMAX);
|
int exp = yypcontext_expected_tokens(ctx, expected, TOKENMAX);
|
||||||
if (exp < 0)
|
if (exp < 0)
|
||||||
|
{
|
||||||
ret = exp;
|
ret = exp;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = 0; i < exp; ++i)
|
for (int i = 0; i < exp; ++i)
|
||||||
output += String::ToString("%s %s", i == 0 ? ": expected" : "or", yysymbol_name(expected[i]));
|
output += String::ToString("%s %s",
|
||||||
|
i == 0 ? ": expected" : "or",
|
||||||
|
yysymbol_name(expected[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lines.size() > 0)
|
if (lines.size() > 0)
|
||||||
|
|
@ -671,12 +702,12 @@ yyreport_syntax_error (const yypcontext_t *ctx)
|
||||||
for (int i = 0; i < lines.size(); i++)
|
for (int i = 0; i < lines.size(); i++)
|
||||||
{
|
{
|
||||||
int line = lines.size() - i;
|
int line = lines.size() - i;
|
||||||
output += String::ToString("%5d | ", loc->first_line - (line-1)) + lines[i] + "\n";
|
output += String::ToString("%5d | ", loc->first_line - (line - 1))
|
||||||
|
+ lines[i] + "\n";
|
||||||
}
|
}
|
||||||
output += String::ToString("%5s | %*s", "", loc->first_column, "^");
|
output += String::ToString("%5s | %*s", "", loc->first_column, "^");
|
||||||
}
|
}
|
||||||
|
|
||||||
yyerror("%s", output.c_str());
|
yyerror("%s", output.c_str());
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -56,6 +56,19 @@ static int Sc_ScanIdent();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Vector<String> lines;
|
Vector<String> lines;
|
||||||
|
static S32 gCachedLineContextCount = -1; // -1 = needs refresh
|
||||||
|
|
||||||
|
static S32 getLineContextCount()
|
||||||
|
{
|
||||||
|
if (gCachedLineContextCount < 0)
|
||||||
|
gCachedLineContextCount = Con::getIntVariable("$scriptErrorLineCount", 10);
|
||||||
|
return gCachedLineContextCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMDFlushLineContextCache()
|
||||||
|
{
|
||||||
|
gCachedLineContextCount = -1;
|
||||||
|
}
|
||||||
|
|
||||||
// Install our own input code...
|
// Install our own input code...
|
||||||
#undef CMDgetc
|
#undef CMDgetc
|
||||||
|
|
@ -65,24 +78,26 @@ int CMDgetc();
|
||||||
#ifndef isatty
|
#ifndef isatty
|
||||||
inline int isatty(int) { return 0; }
|
inline int isatty(int) { return 0; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int yycolumn = 1;
|
static int yycolumn = 1;
|
||||||
// Wrap our getc, so that lex doesn't try to do its own buffering/file IO.
|
|
||||||
#define YY_INPUT(buf,result,max_size) \
|
#define YY_INPUT(buf, result, max_size) \
|
||||||
{ \
|
{ \
|
||||||
int c = '*', n; \
|
int c = '*', n; \
|
||||||
for ( n = 0; n < max_size && \
|
for (n = 0; n < max_size && \
|
||||||
(c = CMDgetc()) != EOF && c != '\n'; ++n ) \
|
(c = CMDgetc()) != EOF && c != '\n'; ++n) \
|
||||||
buf[n] = (char) c; \
|
buf[n] = (char)c; \
|
||||||
if ( c == '\n' ) \
|
if (c == '\n') { buf[n++] = (char)c; yycolumn = 1; } \
|
||||||
buf[n++] = (char) c; yycolumn = 1;\
|
result = n; \
|
||||||
result = n; \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define YY_USER_ACTION do { \
|
#define YY_USER_ACTION \
|
||||||
CMDlloc.first_line = CMDlloc.last_line = yylineno; \
|
do { \
|
||||||
CMDlloc.first_column = yycolumn; CMDlloc.last_column = yycolumn + yyleng - 1; \
|
CMDlloc.first_line = CMDlloc.last_line = yylineno; \
|
||||||
yycolumn += yyleng; \
|
CMDlloc.first_column = yycolumn; \
|
||||||
} while(0);
|
CMDlloc.last_column = yycolumn + yyleng - 1; \
|
||||||
|
yycolumn += yyleng; \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
// File state
|
// File state
|
||||||
void CMDSetScanBuffer(const char *sb, const char *fn);
|
void CMDSetScanBuffer(const char *sb, const char *fn);
|
||||||
|
|
@ -111,69 +126,85 @@ SPACE [ \t\v\f]
|
||||||
HEXDIGIT [a-fA-F0-9]
|
HEXDIGIT [a-fA-F0-9]
|
||||||
|
|
||||||
%%
|
%%
|
||||||
;
|
|
||||||
{SPACE}+ { }
|
{SPACE}+ { /* consume whitespace */ }
|
||||||
("///"([^/\n\r][^\n\r]*)?[\n\r]+)+ { return(Sc_ScanDocBlock()); }
|
|
||||||
"//"[^\n\r]* ;
|
("///"([^/\n\r][^\n\r]*)?[\n\r]+)+ { return Sc_ScanDocBlock(); }
|
||||||
[\r] ;
|
"//"[^\n\r]* { /* line comment — discard */ }
|
||||||
\n.* {
|
[\r] { /* bare CR — discard */ }
|
||||||
|
|
||||||
|
\n.* {
|
||||||
yycolumn = 1;
|
yycolumn = 1;
|
||||||
lines.push_back(String::ToString("%s", yytext+1));
|
|
||||||
if (lines.size() > Con::getIntVariable("$scriptErrorLineCount", 10))
|
// Push the line text (everything after the newline) into the error
|
||||||
|
// context buffer, then trim to the configured maximum.
|
||||||
|
lines.push_back(String::ToString("%s", yytext + 1));
|
||||||
|
S32 maxLines = getLineContextCount();
|
||||||
|
while (lines.size() > maxLines)
|
||||||
lines.erase(lines.begin());
|
lines.erase(lines.begin());
|
||||||
|
|
||||||
yyless(1);
|
yyless(1);
|
||||||
}
|
}
|
||||||
\"(\\.|[^\\"\n\r])*\" { return(Sc_ScanString(STRATOM)); }
|
|
||||||
\'(\\.|[^\\'\n\r])*\' { return(Sc_ScanString(TAGATOM)); }
|
|
||||||
"==" { CMDlval.i = MakeToken< int >( opEQ, yylineno ); return opEQ; }
|
|
||||||
"!=" { CMDlval.i = MakeToken< int >( opNE, yylineno ); return opNE; }
|
|
||||||
">=" { CMDlval.i = MakeToken< int >( opGE, yylineno ); return opGE; }
|
|
||||||
"<=" { CMDlval.i = MakeToken< int >( opLE, yylineno ); return opLE; }
|
|
||||||
"&&" { CMDlval.i = MakeToken< int >( opAND, yylineno ); return opAND; }
|
|
||||||
"||" { CMDlval.i = MakeToken< int >( opOR, yylineno ); return opOR; }
|
|
||||||
"::" { CMDlval.i = MakeToken< int >( opCOLONCOLON, yylineno ); return opCOLONCOLON; }
|
|
||||||
"--" { CMDlval.i = MakeToken< int >( opMINUSMINUS, yylineno ); return opMINUSMINUS; }
|
|
||||||
"++" { CMDlval.i = MakeToken< int >( opPLUSPLUS, yylineno ); return opPLUSPLUS; }
|
|
||||||
"$=" { CMDlval.i = MakeToken< int >( opSTREQ, yylineno ); return opSTREQ; }
|
|
||||||
"!$=" { CMDlval.i = MakeToken< int >( opSTRNE, yylineno ); return opSTRNE; }
|
|
||||||
"<<" { CMDlval.i = MakeToken< int >( opSHL, yylineno ); return opSHL; }
|
|
||||||
">>" { CMDlval.i = MakeToken< int >( opSHR, yylineno ); return opSHR; }
|
|
||||||
"+=" { CMDlval.i = MakeToken< int >( opPLASN, yylineno ); return opPLASN; }
|
|
||||||
"-=" { CMDlval.i = MakeToken< int >( opMIASN, yylineno ); return opMIASN; }
|
|
||||||
"*=" { CMDlval.i = MakeToken< int >( opMLASN, yylineno ); return opMLASN; }
|
|
||||||
"/=" { CMDlval.i = MakeToken< int >( opDVASN, yylineno ); return opDVASN; }
|
|
||||||
"%=" { CMDlval.i = MakeToken< int >( opMODASN, yylineno ); return opMODASN; }
|
|
||||||
"&=" { CMDlval.i = MakeToken< int >( opANDASN, yylineno ); return opANDASN; }
|
|
||||||
"^=" { CMDlval.i = MakeToken< int >( opXORASN, yylineno ); return opXORASN; }
|
|
||||||
"|=" { CMDlval.i = MakeToken< int >( opORASN, yylineno ); return opORASN; }
|
|
||||||
"<<=" { CMDlval.i = MakeToken< int >( opSLASN, yylineno ); return opSLASN; }
|
|
||||||
">>=" { CMDlval.i = MakeToken< int >( opSRASN, yylineno ); return opSRASN; }
|
|
||||||
"->" { CMDlval.i = MakeToken< int >( opINTNAME, yylineno ); return opINTNAME; }
|
|
||||||
"-->" { CMDlval.i = MakeToken< int >( opINTNAMER, yylineno ); return opINTNAMER; }
|
|
||||||
"NL" { CMDlval.i = MakeToken< int >( '\n', yylineno ); return '@'; }
|
|
||||||
"TAB" { CMDlval.i = MakeToken< int >( '\t', yylineno ); return '@'; }
|
|
||||||
"SPC" { CMDlval.i = MakeToken< int >( ' ', yylineno ); return '@'; }
|
|
||||||
"@" { CMDlval.i = MakeToken< int >( 0, yylineno ); return '@'; }
|
|
||||||
"/*" { /* this comment stops syntax highlighting from getting messed up when editing the lexer in TextPad */
|
|
||||||
int c = 0, l;
|
|
||||||
for ( ; ; )
|
|
||||||
{
|
|
||||||
l = c;
|
|
||||||
c = yyinput();
|
|
||||||
|
|
||||||
// Is this an open comment?
|
\"(\\.|[^\\"\n\r])*\" { return Sc_ScanString(STRATOM); }
|
||||||
if ( c == EOF )
|
\'(\\.|[^\\'\n\r])*\' { return Sc_ScanString(TAGATOM); }
|
||||||
{
|
|
||||||
CMDerror( "unexpected end of file found in comment" );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Did we find the end of the comment?
|
"==" { CMDlval.i = MakeToken<int>(opEQ, yylineno); return opEQ; }
|
||||||
else if ( l == '*' && c == '/' )
|
"!=" { CMDlval.i = MakeToken<int>(opNE, yylineno); return opNE; }
|
||||||
break;
|
">=" { CMDlval.i = MakeToken<int>(opGE, yylineno); return opGE; }
|
||||||
}
|
"<=" { CMDlval.i = MakeToken<int>(opLE, yylineno); return opLE; }
|
||||||
|
"&&" { CMDlval.i = MakeToken<int>(opAND, yylineno); return opAND; }
|
||||||
|
"||" { CMDlval.i = MakeToken<int>(opOR, yylineno); return opOR; }
|
||||||
|
"::" { CMDlval.i = MakeToken<int>(opCOLONCOLON, yylineno); return opCOLONCOLON; }
|
||||||
|
"--" { CMDlval.i = MakeToken<int>(opMINUSMINUS, yylineno); return opMINUSMINUS; }
|
||||||
|
"++" { CMDlval.i = MakeToken<int>(opPLUSPLUS, yylineno); return opPLUSPLUS; }
|
||||||
|
"$=" { CMDlval.i = MakeToken<int>(opSTREQ, yylineno); return opSTREQ; }
|
||||||
|
"!$=" { CMDlval.i = MakeToken<int>(opSTRNE, yylineno); return opSTRNE; }
|
||||||
|
"<<" { CMDlval.i = MakeToken<int>(opSHL, yylineno); return opSHL; }
|
||||||
|
">>" { CMDlval.i = MakeToken<int>(opSHR, yylineno); return opSHR; }
|
||||||
|
"+=" { CMDlval.i = MakeToken<int>(opPLASN, yylineno); return opPLASN; }
|
||||||
|
"-=" { CMDlval.i = MakeToken<int>(opMIASN, yylineno); return opMIASN; }
|
||||||
|
"*=" { CMDlval.i = MakeToken<int>(opMLASN, yylineno); return opMLASN; }
|
||||||
|
"/=" { CMDlval.i = MakeToken<int>(opDVASN, yylineno); return opDVASN; }
|
||||||
|
"%=" { CMDlval.i = MakeToken<int>(opMODASN, yylineno); return opMODASN; }
|
||||||
|
"&=" { CMDlval.i = MakeToken<int>(opANDASN, yylineno); return opANDASN; }
|
||||||
|
"^=" { CMDlval.i = MakeToken<int>(opXORASN, yylineno); return opXORASN; }
|
||||||
|
"|=" { CMDlval.i = MakeToken<int>(opORASN, yylineno); return opORASN; }
|
||||||
|
"<<=" { CMDlval.i = MakeToken<int>(opSLASN, yylineno); return opSLASN; }
|
||||||
|
">>=" { CMDlval.i = MakeToken<int>(opSRASN, yylineno); return opSRASN; }
|
||||||
|
"->" { CMDlval.i = MakeToken<int>(opINTNAME, yylineno); return opINTNAME; }
|
||||||
|
"-->" { CMDlval.i = MakeToken<int>(opINTNAMER, yylineno); return opINTNAMER; }
|
||||||
|
%{
|
||||||
|
// String concatenation operators. All four return the '@' token; the
|
||||||
|
// distinguishing data is the separator character stored in the token value.
|
||||||
|
// The grammar rule expr '@' expr uses $2.value as the appendChar
|
||||||
|
// argument to StrcatExprNode — so plain '@' gets 0 (no separator),
|
||||||
|
// NL/TAB/SPC get their respective ASCII codes.
|
||||||
|
%}
|
||||||
|
"NL" { CMDlval.i = MakeToken<int>('\n', yylineno); return '@'; }
|
||||||
|
"TAB" { CMDlval.i = MakeToken<int>('\t', yylineno); return '@'; }
|
||||||
|
"SPC" { CMDlval.i = MakeToken<int>(' ', yylineno); return '@'; }
|
||||||
|
"@" { CMDlval.i = MakeToken<int>(0, yylineno); return '@'; }
|
||||||
|
|
||||||
|
"/*" {
|
||||||
|
// Block comment — consume until '*/'
|
||||||
|
int c = 0, prev = 0;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
prev = c;
|
||||||
|
c = yyinput();
|
||||||
|
if (c == EOF)
|
||||||
|
{
|
||||||
|
CMDerror("unexpected end of file inside block comment");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
if (prev == '*' && c == '/')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
%{
|
||||||
|
// Single-character punctuation tokens.
|
||||||
|
%}
|
||||||
"?" |
|
"?" |
|
||||||
"[" |
|
"[" |
|
||||||
"]" |
|
"]" |
|
||||||
|
|
@ -197,40 +228,55 @@ HEXDIGIT [a-fA-F0-9]
|
||||||
"%" |
|
"%" |
|
||||||
"^" |
|
"^" |
|
||||||
"~" |
|
"~" |
|
||||||
"=" { CMDlval.i = MakeToken< int >( CMDtext[ 0 ], yylineno ); return CMDtext[ 0 ]; }
|
"=" { CMDlval.i = MakeToken<int>(CMDtext[0], yylineno); return CMDtext[0]; }
|
||||||
"in" { CMDlval.i = MakeToken< int >( rwIN, yylineno ); return(rwIN); }
|
%{
|
||||||
"or" { CMDlval.i = MakeToken< int >( rwCASEOR, yylineno ); return(rwCASEOR); }
|
// Reserved words — must be listed before {ID} to take priority.
|
||||||
"break" { CMDlval.i = MakeToken< int >( rwBREAK, yylineno ); return(rwBREAK); }
|
// NOTE: "namespace" and "class" are intentionally NOT listed here.
|
||||||
"return" { CMDlval.i = MakeToken< int >( rwRETURN, yylineno ); return(rwRETURN); }
|
// rwNAMESPACE and rwCLASS were previously declared as grammar tokens but
|
||||||
"else" { CMDlval.i = MakeToken< int >( rwELSE, yylineno ); return(rwELSE); }
|
// had no productions that used them and no lexer rules that produced them.
|
||||||
"assert" { CMDlval.i = MakeToken< int >( rwASSERT, yylineno ); return(rwASSERT); }
|
// They have been removed from the grammar. The words "namespace" and
|
||||||
"while" { CMDlval.i = MakeToken< int >( rwWHILE, yylineno ); return(rwWHILE); }
|
// "class" therefore lex as plain IDENT tokens and can be used as object
|
||||||
"do" { CMDlval.i = MakeToken< int >( rwDO, yylineno ); return(rwDO); }
|
// names or field names in script without causing parse errors. If you add
|
||||||
"if" { CMDlval.i = MakeToken< int >( rwIF, yylineno ); return(rwIF); }
|
// syntax that consumes those keywords, add both the lexer rule and the
|
||||||
"foreach$" { CMDlval.i = MakeToken< int >( rwFOREACHSTR, yylineno ); return(rwFOREACHSTR); }
|
// grammar token declaration at the same time.
|
||||||
"foreach" { CMDlval.i = MakeToken< int >( rwFOREACH, yylineno ); return(rwFOREACH); }
|
%}
|
||||||
"for" { CMDlval.i = MakeToken< int >( rwFOR, yylineno ); return(rwFOR); }
|
"in" { CMDlval.i = MakeToken<int>(rwIN, yylineno); return rwIN; }
|
||||||
"continue" { CMDlval.i = MakeToken< int >( rwCONTINUE, yylineno ); return(rwCONTINUE); }
|
"or" { CMDlval.i = MakeToken<int>(rwCASEOR, yylineno); return rwCASEOR; }
|
||||||
"function" { CMDlval.i = MakeToken< int >( rwDEFINE, yylineno ); return(rwDEFINE); }
|
"break" { CMDlval.i = MakeToken<int>(rwBREAK, yylineno); return rwBREAK; }
|
||||||
"new" { CMDlval.i = MakeToken< int >( rwDECLARE, yylineno ); return(rwDECLARE); }
|
"return" { CMDlval.i = MakeToken<int>(rwRETURN, yylineno); return rwRETURN; }
|
||||||
"singleton" { CMDlval.i = MakeToken< int >( rwDECLARESINGLETON, yylineno ); return(rwDECLARESINGLETON); }
|
"else" { CMDlval.i = MakeToken<int>(rwELSE, yylineno); return rwELSE; }
|
||||||
"datablock" { CMDlval.i = MakeToken< int >( rwDATABLOCK, yylineno ); return(rwDATABLOCK); }
|
"assert" { CMDlval.i = MakeToken<int>(rwASSERT, yylineno); return rwASSERT; }
|
||||||
"case" { CMDlval.i = MakeToken< int >( rwCASE, yylineno ); return(rwCASE); }
|
"while" { CMDlval.i = MakeToken<int>(rwWHILE, yylineno); return rwWHILE; }
|
||||||
"switch$" { CMDlval.i = MakeToken< int >( rwSWITCHSTR, yylineno ); return(rwSWITCHSTR); }
|
"do" { CMDlval.i = MakeToken<int>(rwDO, yylineno); return rwDO; }
|
||||||
"switch" { CMDlval.i = MakeToken< int >( rwSWITCH, yylineno ); return(rwSWITCH); }
|
"if" { CMDlval.i = MakeToken<int>(rwIF, yylineno); return rwIF; }
|
||||||
"default" { CMDlval.i = MakeToken< int >( rwDEFAULT, yylineno ); return(rwDEFAULT); }
|
"foreach$" { CMDlval.i = MakeToken<int>(rwFOREACHSTR, yylineno); return rwFOREACHSTR; }
|
||||||
"package" { CMDlval.i = MakeToken< int >( rwPACKAGE, yylineno ); return(rwPACKAGE); }
|
"foreach" { CMDlval.i = MakeToken<int>(rwFOREACH, yylineno); return rwFOREACH; }
|
||||||
"namespace" { CMDlval.i = MakeToken< int >( rwNAMESPACE, yylineno ); return(rwNAMESPACE); }
|
"for" { CMDlval.i = MakeToken<int>(rwFOR, yylineno); return rwFOR; }
|
||||||
"true" { CMDlval.i = MakeToken< int >( 1, yylineno ); return INTCONST; }
|
"continue" { CMDlval.i = MakeToken<int>(rwCONTINUE, yylineno); return rwCONTINUE; }
|
||||||
"false" { CMDlval.i = MakeToken< int >( 0, yylineno ); return INTCONST; }
|
"function" { CMDlval.i = MakeToken<int>(rwDEFINE, yylineno); return rwDEFINE; }
|
||||||
{VAR} { return(Sc_ScanVar()); }
|
"new" { CMDlval.i = MakeToken<int>(rwDECLARE, yylineno); return rwDECLARE; }
|
||||||
|
"singleton" { CMDlval.i = MakeToken<int>(rwDECLARESINGLETON, yylineno); return rwDECLARESINGLETON; }
|
||||||
|
"datablock" { CMDlval.i = MakeToken<int>(rwDATABLOCK, yylineno); return rwDATABLOCK; }
|
||||||
|
"case" { CMDlval.i = MakeToken<int>(rwCASE, yylineno); return rwCASE; }
|
||||||
|
"switch$" { CMDlval.i = MakeToken<int>(rwSWITCHSTR, yylineno); return rwSWITCHSTR; }
|
||||||
|
"switch" { CMDlval.i = MakeToken<int>(rwSWITCH, yylineno); return rwSWITCH; }
|
||||||
|
"default" { CMDlval.i = MakeToken<int>(rwDEFAULT, yylineno); return rwDEFAULT; }
|
||||||
|
"package" { CMDlval.i = MakeToken<int>(rwPACKAGE, yylineno); return rwPACKAGE; }
|
||||||
|
%{
|
||||||
|
// Boolean literals — return INTCONST so the parser treats them as integers.
|
||||||
|
%}
|
||||||
|
"true" { CMDlval.i = MakeToken<int>(1, yylineno); return INTCONST; }
|
||||||
|
"false" { CMDlval.i = MakeToken<int>(0, yylineno); return INTCONST; }
|
||||||
|
|
||||||
{ID} { return Sc_ScanIdent(); }
|
{VAR} { return Sc_ScanVar(); }
|
||||||
0[xX]{HEXDIGIT}+ return(Sc_ScanHex());
|
{ID} { return Sc_ScanIdent(); }
|
||||||
{INTEGER} { CMDtext[CMDleng] = 0; CMDlval.i = MakeToken< int >( dAtoi(CMDtext), yylineno ); return INTCONST; }
|
0[xX]{HEXDIGIT}+ { return Sc_ScanHex(); }
|
||||||
{FLOAT} return Sc_ScanNum();
|
{INTEGER} { CMDtext[CMDleng] = 0;
|
||||||
{ILID} return(ILLEGAL_TOKEN);
|
CMDlval.i = MakeToken<int>(dAtoi(CMDtext), yylineno);
|
||||||
. return(ILLEGAL_TOKEN);
|
return INTCONST; }
|
||||||
|
{FLOAT} { return Sc_ScanNum(); }
|
||||||
|
{ILID} { return ILLEGAL_TOKEN; }
|
||||||
|
. { return ILLEGAL_TOKEN; }
|
||||||
%%
|
%%
|
||||||
|
|
||||||
static const char *scanBuffer;
|
static const char *scanBuffer;
|
||||||
|
|
@ -238,48 +284,69 @@ static const char *fileName;
|
||||||
static int scanIndex;
|
static int scanIndex;
|
||||||
extern YYLTYPE CMDlloc;
|
extern YYLTYPE CMDlloc;
|
||||||
|
|
||||||
const char * CMDGetCurrentFile()
|
const char* CMDGetCurrentFile() { return fileName; }
|
||||||
|
int CMDGetCurrentLine() { return yylineno; }
|
||||||
|
|
||||||
|
void CMDSetScanBuffer(const char* sb, const char* fn)
|
||||||
{
|
{
|
||||||
return fileName;
|
scanBuffer = sb;
|
||||||
|
fileName = fn;
|
||||||
|
scanIndex = 0;
|
||||||
|
yylineno = 1;
|
||||||
|
gCachedLineContextCount = -1; // re-read $scriptErrorLineCount for each file
|
||||||
|
lines.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
int CMDGetCurrentLine()
|
int CMDgetc()
|
||||||
{
|
{
|
||||||
return yylineno;
|
int c = scanBuffer[scanIndex];
|
||||||
|
if (c)
|
||||||
|
scanIndex++;
|
||||||
|
else
|
||||||
|
c = -1; // EOF sentinel expected by YY_INPUT
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CMDwrap()
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern bool gConsoleSyntaxError;
|
extern bool gConsoleSyntaxError;
|
||||||
|
|
||||||
void CMDerror(const char *format, ...)
|
void CMDerror(const char* format, ...)
|
||||||
{
|
{
|
||||||
Compiler::gSyntaxError = true;
|
Compiler::gSyntaxError = true;
|
||||||
|
|
||||||
const int BUFMAX = 1024;
|
const int BUFMAX = 1024;
|
||||||
char tempBuf[BUFMAX];
|
char tempBuf[BUFMAX];
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start( args, format );
|
va_start(args, format);
|
||||||
#ifdef TORQUE_OS_WIN
|
#ifdef TORQUE_OS_WIN
|
||||||
_vsnprintf( tempBuf, BUFMAX, format, args );
|
_vsnprintf(tempBuf, BUFMAX, format, args);
|
||||||
#else
|
#else
|
||||||
vsnprintf( tempBuf, BUFMAX, format, args );
|
vsnprintf(tempBuf, BUFMAX, format, args);
|
||||||
#endif
|
#endif
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
if(fileName)
|
if (fileName)
|
||||||
{
|
{
|
||||||
Con::errorf(ConsoleLogEntry::Script, "%s Line: %d - %s", fileName, yylineno, tempBuf);
|
Con::errorf(ConsoleLogEntry::Script, "%s Line: %d - %s",
|
||||||
// Update the script-visible error buffer.
|
fileName, yylineno, tempBuf);
|
||||||
const char *prevStr = Con::getVariable("$ScriptError");
|
|
||||||
|
// Append to the script-visible error string and bump the hash so
|
||||||
|
// listeners know a new error arrived.
|
||||||
|
const char* prevStr = Con::getVariable("$ScriptError");
|
||||||
if (prevStr[0])
|
if (prevStr[0])
|
||||||
dSprintf(tempBuf, sizeof(tempBuf), "%s\n%s Line: %d - Syntax error.", prevStr, fileName, yylineno);
|
dSprintf(tempBuf, sizeof(tempBuf), "%s\n%s Line: %d - Syntax error.",
|
||||||
|
prevStr, fileName, yylineno);
|
||||||
else
|
else
|
||||||
dSprintf(tempBuf, sizeof(tempBuf), "%s Line: %d - Syntax error.", fileName, yylineno);
|
dSprintf(tempBuf, sizeof(tempBuf), "%s Line: %d - Syntax error.",
|
||||||
|
fileName, yylineno);
|
||||||
Con::setVariable("$ScriptError", tempBuf);
|
Con::setVariable("$ScriptError", tempBuf);
|
||||||
|
|
||||||
// We also need to mark that we came up with a new error.
|
static S32 sScriptErrorHash = 1000;
|
||||||
static S32 sScriptErrorHash=1000;
|
|
||||||
Con::setIntVariable("$ScriptErrorHash", sScriptErrorHash++);
|
Con::setIntVariable("$ScriptErrorHash", sScriptErrorHash++);
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -287,30 +354,6 @@ void CMDerror(const char *format, ...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMDSetScanBuffer(const char *sb, const char *fn)
|
|
||||||
{
|
|
||||||
scanBuffer = sb;
|
|
||||||
fileName = fn;
|
|
||||||
scanIndex = 0;
|
|
||||||
yylineno = 1;
|
|
||||||
lines.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
int CMDgetc()
|
|
||||||
{
|
|
||||||
int ret = scanBuffer[scanIndex];
|
|
||||||
if(ret)
|
|
||||||
scanIndex++;
|
|
||||||
else
|
|
||||||
ret = -1;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CMDwrap()
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Sc_ScanVar()
|
static int Sc_ScanVar()
|
||||||
{
|
{
|
||||||
// Truncate the temp buffer...
|
// Truncate the temp buffer...
|
||||||
|
|
@ -323,63 +366,60 @@ static int Sc_ScanVar()
|
||||||
|
|
||||||
static int charConv(int in)
|
static int charConv(int in)
|
||||||
{
|
{
|
||||||
switch(in)
|
switch (in)
|
||||||
{
|
{
|
||||||
case 'r':
|
case 'r': return '\r';
|
||||||
return '\r';
|
case 'n': return '\n';
|
||||||
case 'n':
|
case 't': return '\t';
|
||||||
return '\n';
|
default: return in;
|
||||||
case 't':
|
|
||||||
return '\t';
|
|
||||||
default:
|
|
||||||
return in;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getHexDigit(char c)
|
static int getHexDigit(char c)
|
||||||
{
|
{
|
||||||
if(c >= '0' && c <= '9')
|
if (c >= '0' && c <= '9') return c - '0';
|
||||||
return c - '0';
|
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
|
||||||
if(c >= 'A' && c <= 'F')
|
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
||||||
return c - 'A' + 10;
|
|
||||||
if(c >= 'a' && c <= 'f')
|
|
||||||
return c - 'a' + 10;
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Sc_ScanDocBlock()
|
static int Sc_ScanDocBlock()
|
||||||
{
|
{
|
||||||
S32 len = dStrlen(CMDtext);
|
S32 len = dStrlen(CMDtext);
|
||||||
char* text = (char *) consoleAlloc(len + 1);
|
char* text = (char*)consoleAlloc(len + 1);
|
||||||
S32 line = yylineno;
|
S32 line = yylineno;
|
||||||
|
|
||||||
for( S32 i = 0, j = 0; j <= len; j++ )
|
for (S32 i = 0, j = 0; j <= len; j++)
|
||||||
{
|
{
|
||||||
if( ( j <= (len - 2) ) && ( CMDtext[j] == '/' ) && ( CMDtext[j + 1] == '/' ) && ( CMDtext[j + 2] == '/' ) )
|
// Strip leading '///' on each doc line.
|
||||||
|
if ((j <= (len - 2)) &&
|
||||||
|
CMDtext[j] == '/' &&
|
||||||
|
CMDtext[j + 1] == '/' &&
|
||||||
|
CMDtext[j + 2] == '/')
|
||||||
{
|
{
|
||||||
j += 2;
|
j += 2;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (CMDtext[j] == '\r') continue;
|
||||||
if( CMDtext[j] == '\r' )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
text[i++] = CMDtext[j];
|
text[i++] = CMDtext[j];
|
||||||
}
|
}
|
||||||
|
|
||||||
CMDlval.str = MakeToken< char* >( text, line );
|
CMDlval.str = MakeToken<char*>(text, line);
|
||||||
return(DOCBLOCK);
|
return DOCBLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Sc_ScanString(int ret)
|
static int Sc_ScanString(int ret)
|
||||||
{
|
{
|
||||||
CMDtext[CMDleng - 1] = 0;
|
// CMDtext arrives as "content" or 'content' (with quotes).
|
||||||
if(!collapseEscape(CMDtext+1))
|
// Replace the closing quote with a null terminator so collapseEscape
|
||||||
|
// can work on the interior without seeing the delimiter.
|
||||||
|
CMDtext[CMDleng - 1] = '\0';
|
||||||
|
if (!collapseEscape(CMDtext + 1))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
dsize_t bufferLen = dStrlen( CMDtext );
|
dsize_t allocSize = dStrlen(CMDtext); // = 1 + content_len (see above)
|
||||||
char* buffer = ( char* ) consoleAlloc( bufferLen );
|
char* buffer = (char*)consoleAlloc(allocSize);
|
||||||
dStrcpy( buffer, CMDtext + 1, bufferLen );
|
dStrcpy(buffer, CMDtext + 1, allocSize); // skip the opening quote
|
||||||
|
|
||||||
CMDlval.str = MakeToken< char* >( buffer, yylineno );
|
CMDlval.str = MakeToken< char* >( buffer, yylineno );
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -387,22 +427,35 @@ static int Sc_ScanString(int ret)
|
||||||
|
|
||||||
static int Sc_ScanIdent()
|
static int Sc_ScanIdent()
|
||||||
{
|
{
|
||||||
ConsoleBaseType *type;
|
|
||||||
|
|
||||||
CMDtext[CMDleng] = 0;
|
CMDtext[CMDleng] = 0;
|
||||||
|
|
||||||
if((type = ConsoleBaseType::getTypeByName(CMDtext)) != NULL)
|
// Check if the identifier is a registered engine type name (e.g. "Point3F").
|
||||||
|
ConsoleBaseType* type = ConsoleBaseType::getTypeByName(CMDtext);
|
||||||
|
if (type)
|
||||||
{
|
{
|
||||||
/* It's a type */
|
CMDlval.i = MakeToken<int>(type->getTypeID(), yylineno);
|
||||||
CMDlval.i = MakeToken< int >( type->getTypeID(), yylineno );
|
|
||||||
return TYPEIDENT;
|
return TYPEIDENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* It's an identifier */
|
CMDlval.s = MakeToken<StringTableEntry>(StringTable->insert(CMDtext), yylineno);
|
||||||
CMDlval.s = MakeToken< StringTableEntry >( StringTable->insert(CMDtext), yylineno );
|
|
||||||
return IDENT;
|
return IDENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int Sc_ScanNum()
|
||||||
|
{
|
||||||
|
CMDtext[CMDleng] = 0;
|
||||||
|
CMDlval.f = MakeToken<double>(dAtof(CMDtext), yylineno);
|
||||||
|
return FLTCONST;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Sc_ScanHex()
|
||||||
|
{
|
||||||
|
S32 val = 0;
|
||||||
|
dSscanf(CMDtext, "%x", &val);
|
||||||
|
CMDlval.i = MakeToken<int>(val, yylineno);
|
||||||
|
return INTCONST;
|
||||||
|
}
|
||||||
|
|
||||||
void expandEscape(char *dest, const char *src)
|
void expandEscape(char *dest, const char *src)
|
||||||
{
|
{
|
||||||
U8 c;
|
U8 c;
|
||||||
|
|
@ -570,21 +623,6 @@ bool collapseEscape(char *buf)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Sc_ScanNum()
|
|
||||||
{
|
|
||||||
CMDtext[CMDleng] = 0;
|
|
||||||
CMDlval.f = MakeToken< double >( dAtof(CMDtext), yylineno );
|
|
||||||
return(FLTCONST);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Sc_ScanHex()
|
|
||||||
{
|
|
||||||
S32 val = 0;
|
|
||||||
dSscanf(CMDtext, "%x", &val);
|
|
||||||
CMDlval.i = MakeToken< int >( val, yylineno );
|
|
||||||
return INTCONST;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CMD_reset()
|
void CMD_reset()
|
||||||
{
|
{
|
||||||
CMDrestart(NULL);
|
CMDrestart(NULL);
|
||||||
|
|
|
||||||
|
|
@ -178,15 +178,23 @@ U32 ReturnStmtNode::compileStmt(CodeStream& codeStream, U32 ip)
|
||||||
|
|
||||||
ExprNode* IfStmtNode::getSwitchOR(ExprNode* left, ExprNode* list, bool string)
|
ExprNode* IfStmtNode::getSwitchOR(ExprNode* left, ExprNode* list, bool string)
|
||||||
{
|
{
|
||||||
ExprNode* nextExpr = (ExprNode*)list->getNext();
|
ExprNode* result;
|
||||||
ExprNode* test;
|
|
||||||
if (string)
|
if (string)
|
||||||
test = StreqExprNode::alloc(left->dbgLineNumber, left, list, true);
|
result = StreqExprNode::alloc(left->dbgLineNumber, left, list, true);
|
||||||
else
|
else
|
||||||
test = IntBinaryExprNode::alloc(left->dbgLineNumber, opEQ, left, list);
|
result = IntBinaryExprNode::alloc(left->dbgLineNumber, opEQ, left, list);
|
||||||
if (!nextExpr)
|
|
||||||
return test;
|
for (ExprNode* walk = (ExprNode*)list->getNext(); walk; walk = (ExprNode*)walk->getNext())
|
||||||
return IntBinaryExprNode::alloc(test->dbgLineNumber, opOR, test, getSwitchOR(left, nextExpr, string));
|
{
|
||||||
|
ExprNode* nextExpr;
|
||||||
|
if (string)
|
||||||
|
nextExpr = StreqExprNode::alloc(left->dbgLineNumber, left, list, true);
|
||||||
|
else
|
||||||
|
nextExpr = IntBinaryExprNode::alloc(left->dbgLineNumber, opEQ, left, list);
|
||||||
|
|
||||||
|
result = IntBinaryExprNode::alloc(result->dbgLineNumber, opOR, result, nextExpr);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IfStmtNode::propagateSwitchExpr(ExprNode* left, bool string)
|
void IfStmtNode::propagateSwitchExpr(ExprNode* left, bool string)
|
||||||
|
|
@ -405,11 +413,14 @@ U32 ConditionalExprNode::compile(CodeStream& codeStream, U32 ip, TypeReq type)
|
||||||
|
|
||||||
TypeReq ConditionalExprNode::getPreferredType()
|
TypeReq ConditionalExprNode::getPreferredType()
|
||||||
{
|
{
|
||||||
// We can't make it calculate a type based on subsequent expressions as the expression
|
TypeReq trueType = trueExpr->getPreferredType();
|
||||||
// could be a string, or just numbers. To play it safe, stringify anything that deals with
|
TypeReq falseType = falseExpr->getPreferredType();
|
||||||
// a conditional, and let the interpreter cast as needed to other types safely.
|
|
||||||
//
|
// Both numeric and the same → keep numeric
|
||||||
// See: Regression Test 7 in ScriptTest. It has a string result in the else portion of the ?: ternary.
|
if (trueType == falseType && trueType != TypeReqNone)
|
||||||
|
return trueType;
|
||||||
|
|
||||||
|
// One is numeric, other is string/none → string (can't avoid conversion)
|
||||||
return TypeReqString;
|
return TypeReqString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -874,7 +885,7 @@ U32 ConstantNode::compile(CodeStream& codeStream, U32 ip, TypeReq type)
|
||||||
case TypeReqNone:
|
case TypeReqNone:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return ip;
|
return codeStream.tell();
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeReq ConstantNode::getPreferredType()
|
TypeReq ConstantNode::getPreferredType()
|
||||||
|
|
@ -1498,16 +1509,6 @@ TypeReq ObjectDeclNode::getPreferredType()
|
||||||
|
|
||||||
U32 FunctionDeclStmtNode::compileStmt(CodeStream& codeStream, U32 ip)
|
U32 FunctionDeclStmtNode::compileStmt(CodeStream& codeStream, U32 ip)
|
||||||
{
|
{
|
||||||
// OP_FUNC_DECL
|
|
||||||
// func name
|
|
||||||
// namespace
|
|
||||||
// package
|
|
||||||
// hasBody?
|
|
||||||
// func end ip
|
|
||||||
// argc
|
|
||||||
// ident array[argc]
|
|
||||||
// code
|
|
||||||
// OP_RETURN_VOID
|
|
||||||
setCurrentStringTable(&getFunctionStringTable());
|
setCurrentStringTable(&getFunctionStringTable());
|
||||||
setCurrentFloatTable(&getFunctionFloatTable());
|
setCurrentFloatTable(&getFunctionFloatTable());
|
||||||
|
|
||||||
|
|
@ -1523,53 +1524,52 @@ U32 FunctionDeclStmtNode::compileStmt(CodeStream& codeStream, U32 ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeBlock::smInFunction = true;
|
CodeBlock::smInFunction = true;
|
||||||
|
|
||||||
precompileIdent(fnName);
|
precompileIdent(fnName);
|
||||||
precompileIdent(nameSpace);
|
precompileIdent(nameSpace);
|
||||||
precompileIdent(package);
|
precompileIdent(package);
|
||||||
|
|
||||||
CodeBlock::smInFunction = false;
|
CodeBlock::smInFunction = false;
|
||||||
|
|
||||||
setCurrentStringTable(&getGlobalStringTable());
|
// -------------------------------------------------------------------------
|
||||||
// check for argument setup
|
// Layout (all relative to the first word after OP_FUNC_DECL):
|
||||||
for (VarNode* walk = args; walk; walk = (VarNode*)((StmtNode*)walk)->getNext())
|
// +0,+1 fnName STE
|
||||||
{
|
// +2,+3 nameSpace STE
|
||||||
if (walk->defaultValue)
|
// +4,+5 package STE
|
||||||
{
|
// +6 hasBody | (lineNumber << 1)
|
||||||
TypeReq walkType = walk->defaultValue->getPreferredType();
|
// +7 endIp (patched after codelets)
|
||||||
if (walkType == TypeReqNone)
|
// +8 argc
|
||||||
walkType = TypeReqString;
|
// +9 local variable count (patched after body)
|
||||||
|
// +10 .. +10+argc-1 register mappings
|
||||||
ip = walk->defaultValue->compile(codeStream, ip, walkType);
|
// +10+argc .. +10+2*argc-1 arg flags
|
||||||
}
|
// +10+2*argc .. +10+3*argc-1 default codelet IPs (patched below)
|
||||||
}
|
// -------------------------------------------------------------------------
|
||||||
setCurrentStringTable(&getFunctionStringTable());
|
|
||||||
|
|
||||||
codeStream.emit(OP_FUNC_DECL);
|
codeStream.emit(OP_FUNC_DECL);
|
||||||
codeStream.emitSTE(fnName);
|
codeStream.emitSTE(fnName);
|
||||||
codeStream.emitSTE(nameSpace);
|
codeStream.emitSTE(nameSpace);
|
||||||
codeStream.emitSTE(package);
|
codeStream.emitSTE(package);
|
||||||
|
|
||||||
codeStream.emit(U32(bool(stmts != NULL) ? 1 : 0) + U32(dbgLineNumber << 1));
|
codeStream.emit(U32(bool(stmts != NULL) ? 1 : 0) + U32(dbgLineNumber << 1));
|
||||||
const U32 endIp = codeStream.emit(0);
|
const U32 endIpSlot = codeStream.emit(0); // patched after codelets
|
||||||
codeStream.emit(argc);
|
codeStream.emit(argc);
|
||||||
const U32 localNumVarsIP = codeStream.emit(0);
|
const U32 localVarCountSlot = codeStream.emit(0); // patched after body
|
||||||
|
|
||||||
|
// Register mappings (one per arg, in declaration order).
|
||||||
for (VarNode* walk = args; walk; walk = (VarNode*)((StmtNode*)walk)->getNext())
|
for (VarNode* walk = args; walk; walk = (VarNode*)((StmtNode*)walk)->getNext())
|
||||||
{
|
{
|
||||||
StringTableEntry name = walk->varName;
|
codeStream.emit(getFuncVars(dbgLineNumber)->lookup(walk->varName, dbgLineNumber));
|
||||||
codeStream.emit(getFuncVars(dbgLineNumber)->lookup(name, dbgLineNumber));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for argument setup
|
// Arg flags (bit 0x1 = this argument has a default expression).
|
||||||
for (VarNode* walk = args; walk; walk = (VarNode*)((StmtNode*)walk)->getNext())
|
for (VarNode* walk = args; walk; walk = (VarNode*)((StmtNode*)walk)->getNext())
|
||||||
{
|
{
|
||||||
U32 flags = 0;
|
codeStream.emit(walk->defaultValue ? 1 : 0);
|
||||||
if (walk->defaultValue) flags |= 0x1;
|
|
||||||
|
|
||||||
codeStream.emit(flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default codelet IP slots — emit 0 placeholders, patched after the body
|
||||||
|
Vector<U32> defaultOffsetSlots;
|
||||||
|
defaultOffsetSlots.setSize(argc);
|
||||||
|
for (S32 i = 0; i < argc; i++)
|
||||||
|
defaultOffsetSlots[i] = codeStream.emit(0);
|
||||||
|
|
||||||
CodeBlock::smInFunction = true;
|
CodeBlock::smInFunction = true;
|
||||||
ip = compileBlock(stmts, codeStream, ip);
|
ip = compileBlock(stmts, codeStream, ip);
|
||||||
|
|
||||||
|
|
@ -1579,9 +1579,32 @@ U32 FunctionDeclStmtNode::compileStmt(CodeStream& codeStream, U32 ip)
|
||||||
|
|
||||||
CodeBlock::smInFunction = false;
|
CodeBlock::smInFunction = false;
|
||||||
codeStream.emit(OP_RETURN_VOID);
|
codeStream.emit(OP_RETURN_VOID);
|
||||||
|
codeStream.patch(localVarCountSlot, getFuncVars(dbgLineNumber)->count());
|
||||||
|
|
||||||
codeStream.patch(localNumVarsIP, getFuncVars(dbgLineNumber)->count());
|
S32 argIdx = 0;
|
||||||
codeStream.patch(endIp, codeStream.tell());
|
for (VarNode* walk = args; walk; walk = (VarNode*)((StmtNode*)walk)->getNext(), ++argIdx)
|
||||||
|
{
|
||||||
|
if (walk->defaultValue)
|
||||||
|
{
|
||||||
|
// Record where this codelet begins and patch the header slot.
|
||||||
|
const U32 codeletStart = codeStream.tell();
|
||||||
|
codeStream.patch(defaultOffsetSlots[argIdx], codeletStart);
|
||||||
|
|
||||||
|
// Compile the default expression, preferring its natural type.
|
||||||
|
// Fall back to string if the type is indeterminate.
|
||||||
|
TypeReq walkType = walk->defaultValue->getPreferredType();
|
||||||
|
if (walkType == TypeReqNone)
|
||||||
|
walkType = TypeReqString;
|
||||||
|
|
||||||
|
ip = walk->defaultValue->compile(codeStream, ip, walkType);
|
||||||
|
|
||||||
|
// Terminate the codelet. exec() handles this by taking the
|
||||||
|
// stack top as its return value and exiting the interpreter loop.
|
||||||
|
codeStream.emit(OP_DEFAULT_END);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
codeStream.patch(endIpSlot, codeStream.tell());
|
||||||
|
|
||||||
setCurrentStringTable(&getGlobalStringTable());
|
setCurrentStringTable(&getGlobalStringTable());
|
||||||
setCurrentFloatTable(&getGlobalFloatTable());
|
setCurrentFloatTable(&getGlobalFloatTable());
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -562,6 +562,7 @@ Con::EvalResult CodeBlock::exec(U32 ip, const char* functionName, Namespace* thi
|
||||||
|
|
||||||
U32 iterDepth = 0;
|
U32 iterDepth = 0;
|
||||||
ConsoleValue returnValue;
|
ConsoleValue returnValue;
|
||||||
|
const bool isCodelet = (!argv && setFrame == -2);
|
||||||
|
|
||||||
incRefCount();
|
incRefCount();
|
||||||
F64* curFloatTable;
|
F64* curFloatTable;
|
||||||
|
|
@ -615,24 +616,102 @@ Con::EvalResult CodeBlock::exec(U32 ip, const char* functionName, Namespace* thi
|
||||||
Script::gEvalState.moveConsoleValue(reg, (value));
|
Script::gEvalState.moveConsoleValue(reg, (value));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wantedArgc < fnArgc)
|
// -----------------------------------------------------------------------
|
||||||
|
// Handle missing arguments.
|
||||||
|
//
|
||||||
|
// For each absent arg that carries a default (argFlags bit 0x1), we
|
||||||
|
// execute its codelet — a small bytecode expression compiled after the
|
||||||
|
// function body that ends with OP_DEFAULT_END.
|
||||||
|
//
|
||||||
|
// The codelet is run in its own minimal frame via a nested exec() call.
|
||||||
|
//
|
||||||
|
// If the default offset is 0, the argument had no default expression and
|
||||||
|
// the register keeps its zero-initialised value.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
if (wantedArgc < S32(fnArgc))
|
||||||
{
|
{
|
||||||
Namespace::Entry* temp = thisNamespace->lookup(thisFunctionName);
|
Namespace::Entry* temp = thisNamespace->lookup(thisFunctionName);
|
||||||
for (; i < fnArgc; i++)
|
|
||||||
|
// Offset into the header where arg flags begin.
|
||||||
|
const U32 flagBase = ip + 10 + fnArgc;
|
||||||
|
// Offset into the header where default codelet IPs begin.
|
||||||
|
const U32 offsetBase = ip + 10 + 2 * fnArgc;
|
||||||
|
|
||||||
|
for (; i < S32(fnArgc); i++)
|
||||||
{
|
{
|
||||||
S32 reg = code[ip + (2 + 6 + 1 + 1) + i];
|
const S32 reg = code[ip + 10 + i];
|
||||||
if (temp->mArgFlags[i] & 0x1)
|
const U32 argFlags = code[flagBase + i];
|
||||||
|
|
||||||
|
if (argFlags & 0x1) // argument has a default expression
|
||||||
{
|
{
|
||||||
ConsoleValue& value = temp->mDefaultValues[i];
|
const U32 codeletIp = (temp != NULL)
|
||||||
Script::gEvalState.moveConsoleValue(reg, (value));
|
? temp->mDefaultOffsets[i]
|
||||||
|
: code[offsetBase + i];
|
||||||
|
|
||||||
|
if (codeletIp != 0)
|
||||||
|
{
|
||||||
|
// Execute the default codelet.
|
||||||
|
// argv=NULL → uses globalStrings / globalFloats (correct,
|
||||||
|
// since codelets are compiled into those tables).
|
||||||
|
// argc=0 → pushes a frame with 0 locals.
|
||||||
|
// setFrame=-2 → reference to the codelet frame.
|
||||||
|
Con::EvalResult result = exec(
|
||||||
|
codeletIp,
|
||||||
|
NULL, // functionName
|
||||||
|
NULL, // thisNamespace
|
||||||
|
0, // argc
|
||||||
|
NULL, // argv ← signals non-function (codelet) call
|
||||||
|
false, // noCalls
|
||||||
|
NULL, // packageName
|
||||||
|
-2 // setFrame
|
||||||
|
);
|
||||||
|
|
||||||
|
Script::gEvalState.moveConsoleValue(reg, result.value);
|
||||||
|
}
|
||||||
|
// codeletIp == 0: no default; register stays at its zero value.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ip = ip + fnArgc + (2 + 6 + 1 + 1) + fnArgc;
|
// -----------------------------------------------------------------------
|
||||||
|
// Advance ip to the start of the function BODY.
|
||||||
|
//
|
||||||
|
// The header now contains 3*fnArgc words after the fixed 10-word prefix:
|
||||||
|
// fnArgc words for register mappings
|
||||||
|
// fnArgc words for arg flags
|
||||||
|
// fnArgc words for default codelet IPs ← new
|
||||||
|
//
|
||||||
|
// Old: ip + 10 + 2*fnArgc
|
||||||
|
// New: ip + 10 + 3*fnArgc
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
ip = ip + 10 + 3 * fnArgc;
|
||||||
|
|
||||||
curFloatTable = functionFloats;
|
curFloatTable = functionFloats;
|
||||||
curStringTable = functionStrings;
|
curStringTable = functionStrings;
|
||||||
curStringTableLen = functionStringsMaxLen;
|
curStringTableLen = functionStringsMaxLen;
|
||||||
|
}
|
||||||
|
else if (isCodelet)
|
||||||
|
{
|
||||||
|
// ---- Codelet path ----------------------------------------------------
|
||||||
|
//
|
||||||
|
// The codelet was compiled into functionStrings/functionFloats (see
|
||||||
|
// compileStmt).
|
||||||
|
//
|
||||||
|
// functionStrings lives for the lifetime of the CodeBlock, which is
|
||||||
|
// always at least as long as any call to a function it contains.
|
||||||
|
curStringTable = functionStrings;
|
||||||
|
curFloatTable = functionFloats;
|
||||||
|
curStringTableLen = functionStringsMaxLen;
|
||||||
|
|
||||||
|
// Push a minimal empty frame. The codelet contains only an expression;
|
||||||
|
// it has no local variables of its own.
|
||||||
|
Script::gEvalState.pushFrame(NULL, NULL, 0);
|
||||||
|
popFrame = true;
|
||||||
|
|
||||||
|
// setFrame has served its purpose as a mode signal. Reset it so the
|
||||||
|
// telnet debugger guard `if (telDebuggerOn && setFrame < 0)` fires
|
||||||
|
// correctly (codelets should not push a telnet stack frame).
|
||||||
|
setFrame = -1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -727,6 +806,7 @@ Con::EvalResult CodeBlock::exec(U32 ip, const char* functionName, Namespace* thi
|
||||||
switch (instruction)
|
switch (instruction)
|
||||||
{
|
{
|
||||||
case OP_FUNC_DECL:
|
case OP_FUNC_DECL:
|
||||||
|
{
|
||||||
if (!noCalls)
|
if (!noCalls)
|
||||||
{
|
{
|
||||||
fnName = CodeToSTE(code, ip);
|
fnName = CodeToSTE(code, ip);
|
||||||
|
|
@ -739,7 +819,9 @@ Con::EvalResult CodeBlock::exec(U32 ip, const char* functionName, Namespace* thi
|
||||||
ns = Namespace::global();
|
ns = Namespace::global();
|
||||||
else
|
else
|
||||||
ns = Namespace::find(fnNamespace, fnPackage);
|
ns = Namespace::find(fnNamespace, fnPackage);
|
||||||
ns->addFunction(fnName, this, hasBody ? ip : 0);// if no body, set the IP to 0
|
|
||||||
|
ns->addFunction(fnName, this, hasBody ? ip : 0);
|
||||||
|
|
||||||
if (curNSDocBlock)
|
if (curNSDocBlock)
|
||||||
{
|
{
|
||||||
if (fnNamespace == StringTable->lookup(nsDocBlockClass))
|
if (fnNamespace == StringTable->lookup(nsDocBlockClass))
|
||||||
|
|
@ -752,45 +834,48 @@ Con::EvalResult CodeBlock::exec(U32 ip, const char* functionName, Namespace* thi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const U32 fnArgc = code[ip + 8];
|
||||||
U32 fnArgc = code[ip + 2 + 6];
|
|
||||||
|
|
||||||
// Compute pointer to the register mapping like exec() does.
|
|
||||||
U32 readPtr = ip + 2 + 6 + 1; // points to the slot after argc (localNumVarsIP)
|
|
||||||
readPtr += 1; // skip localNumVarsIP
|
|
||||||
readPtr += fnArgc; // skip register mapping
|
|
||||||
|
|
||||||
Namespace::Entry* temp = ns->lookup(fnName);
|
Namespace::Entry* temp = ns->lookup(fnName);
|
||||||
|
|
||||||
temp->mArgFlags.setSize(fnArgc);
|
temp->mArgFlags.setSize(fnArgc);
|
||||||
temp->mDefaultValues.setSize(fnArgc);
|
temp->mDefaultOffsets.setSize(fnArgc);
|
||||||
|
|
||||||
|
// Arg flags: ip + 10 + fnArgc
|
||||||
|
// Codelet IPs: ip + 10 + 2*fnArgc
|
||||||
|
const U32 flagBase = ip + 10 + fnArgc;
|
||||||
|
const U32 offsetBase = ip + 10 + 2 * fnArgc;
|
||||||
|
|
||||||
// Read flags sequentially
|
|
||||||
for (U32 fa = 0; fa < fnArgc; ++fa)
|
for (U32 fa = 0; fa < fnArgc; ++fa)
|
||||||
{
|
{
|
||||||
temp->mArgFlags[fa] = code[readPtr++];
|
temp->mArgFlags[fa] = code[flagBase + fa];
|
||||||
|
temp->mDefaultOffsets[fa] = code[offsetBase + fa];
|
||||||
}
|
}
|
||||||
|
|
||||||
// this might seem weird but because of the order
|
// No stack pops: mDefaultValues is gone.
|
||||||
// the stack accumulates consoleValues we cant be sure
|
|
||||||
// all args have a console value, and we need to pop
|
|
||||||
// the stack, do this in reverse order.
|
|
||||||
for (S32 fa = S32(fnArgc - 1); fa >= 0; fa--)
|
|
||||||
{
|
|
||||||
if (temp->mArgFlags[fa] & 0x1)
|
|
||||||
{
|
|
||||||
temp->mDefaultValues[fa] = stack[_STK--];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Namespace::relinkPackages();
|
Namespace::relinkPackages();
|
||||||
// If we had a docblock, it's definitely not valid anymore, so clear it out.
|
|
||||||
curFNDocBlock = NULL;
|
curFNDocBlock = NULL;
|
||||||
|
|
||||||
//Con::printf("Adding function %s::%s (%d)", fnNamespace, fnName, ip);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Jump past header + body + codelets. endIp is at code[ip + 7].
|
||||||
ip = code[ip + 7];
|
ip = code[ip + 7];
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OP_DEFAULT_END:
|
||||||
|
{
|
||||||
|
returnValue = stack[_STK];
|
||||||
|
_STK--;
|
||||||
|
|
||||||
|
while (iterDepth > 0)
|
||||||
|
{
|
||||||
|
iterStack[--_ITER].mIsStringIter = false;
|
||||||
|
--iterDepth;
|
||||||
|
_STK--;
|
||||||
|
}
|
||||||
|
|
||||||
|
goto execFinished;
|
||||||
|
}
|
||||||
|
|
||||||
case OP_CREATE_OBJECT:
|
case OP_CREATE_OBJECT:
|
||||||
{
|
{
|
||||||
|
|
@ -2331,10 +2416,13 @@ execFinished:
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
delete[] const_cast<char*>(globalStrings);
|
if (!isCodelet)
|
||||||
delete[] globalFloats;
|
{
|
||||||
globalStrings = NULL;
|
delete[] const_cast<char*>(globalStrings);
|
||||||
globalFloats = NULL;
|
delete[] globalFloats;
|
||||||
|
globalStrings = NULL;
|
||||||
|
globalFloats = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Con::getCurrentScriptModuleName())
|
if (Con::getCurrentScriptModuleName())
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ namespace Compiler
|
||||||
enum CompiledInstructions
|
enum CompiledInstructions
|
||||||
{
|
{
|
||||||
OP_FUNC_DECL,
|
OP_FUNC_DECL,
|
||||||
|
OP_DEFAULT_END,
|
||||||
OP_CREATE_OBJECT,
|
OP_CREATE_OBJECT,
|
||||||
OP_ADD_OBJECT,
|
OP_ADD_OBJECT,
|
||||||
OP_END_OBJECT,
|
OP_END_OBJECT,
|
||||||
|
|
|
||||||
|
|
@ -1382,6 +1382,8 @@ void GuiControl::parentResized(const RectI &oldParentRect, const RectI &newParen
|
||||||
|
|
||||||
S32 deltaX = newParentRect.extent.x - oldParentRect.extent.x;
|
S32 deltaX = newParentRect.extent.x - oldParentRect.extent.x;
|
||||||
S32 deltaY = newParentRect.extent.y - oldParentRect.extent.y;
|
S32 deltaY = newParentRect.extent.y - oldParentRect.extent.y;
|
||||||
|
F32 nudgeX = (deltaX > 0) ? -0.001f : 0.001f;
|
||||||
|
F32 nudgeY = (deltaY > 0) ? -0.001f : 0.001f;
|
||||||
|
|
||||||
if (mHorizSizing == horizResizeCenter)
|
if (mHorizSizing == horizResizeCenter)
|
||||||
newPosition.x = (newParentRect.extent.x - getWidth()) >> 1;
|
newPosition.x = (newParentRect.extent.x - getWidth()) >> 1;
|
||||||
|
|
@ -1391,40 +1393,34 @@ void GuiControl::parentResized(const RectI &oldParentRect, const RectI &newParen
|
||||||
newPosition.x += deltaX;
|
newPosition.x += deltaX;
|
||||||
else if (mHorizSizing == horizResizeRelative && oldParentRect.extent.x != 0)
|
else if (mHorizSizing == horizResizeRelative && oldParentRect.extent.x != 0)
|
||||||
{
|
{
|
||||||
S32 newLeft = mRoundToNearest( ( F32( newPosition.x ) / F32( oldParentRect.extent.x ) ) * F32( newParentRect.extent.x ) );
|
S32 newLeft = mRoundToNearest( ( F32( newPosition.x ) / F32( oldParentRect.extent.x ) ) * F32( newParentRect.extent.x ) + nudgeX);
|
||||||
S32 newWidth = mRoundToNearest( ( F32( newExtent.x ) / F32( oldParentRect.extent.x ) ) * F32( newParentRect.extent.x ) );
|
S32 newWidth = mRoundToNearest( ( F32( newExtent.x ) / F32( oldParentRect.extent.x ) ) * F32( newParentRect.extent.x ) + nudgeX);
|
||||||
|
|
||||||
newPosition.x = newLeft;
|
newPosition.x = newLeft;
|
||||||
newExtent.x = newWidth;
|
newExtent.x = newWidth;
|
||||||
}
|
}
|
||||||
else if (mHorizSizing == horizResizeAspectLeft && oldParentRect.extent.x != 0)
|
else if (mHorizSizing == horizResizeAspectLeft && oldParentRect.extent.x != 0)
|
||||||
{
|
{
|
||||||
S32 newLeft = mRoundToNearest((F32(newPosition.x) / F32(oldParentRect.extent.x)) * F32(newParentRect.extent.x));
|
F32 heightRatio = F32(newParentRect.extent.y) / F32(oldParentRect.extent.y);
|
||||||
S32 newWidth = mRoundToNearest((F32(newExtent.x) / F32(oldParentRect.extent.y)) * F32(newParentRect.extent.y));
|
S32 newWidth = mRoundToNearest(F32(newExtent.x) * heightRatio + nudgeX);
|
||||||
|
|
||||||
newPosition.x = newLeft;
|
S32 newRight = newPosition.x + newExtent.x + deltaX;
|
||||||
|
newPosition.x = newRight - newWidth;
|
||||||
newExtent.x = newWidth;
|
newExtent.x = newWidth;
|
||||||
}
|
}
|
||||||
else if (mHorizSizing == horizResizeAspectRight && oldParentRect.extent.x != 0)
|
else if (mHorizSizing == horizResizeAspectRight && oldParentRect.extent.x != 0)
|
||||||
{
|
{
|
||||||
S32 newLeft = mRoundToNearest((F32(newPosition.x) / F32(oldParentRect.extent.x)) * F32(newParentRect.extent.x));
|
F32 heightRatio = F32(newParentRect.extent.y) / F32(oldParentRect.extent.y);
|
||||||
S32 newWidth = mRoundToNearest((F32(newExtent.x) / F32(oldParentRect.extent.y)) * F32(newParentRect.extent.y)); //origional aspect ratio corrected width
|
S32 newWidth = mRoundToNearest(F32(newExtent.x) * heightRatio + nudgeX);
|
||||||
S32 rWidth = mRoundToNearest((F32(newExtent.x) / F32(oldParentRect.extent.x)) * F32(newParentRect.extent.x)); //parent aspect ratio relative width
|
|
||||||
|
|
||||||
S32 offset = rWidth - newWidth; // account for change in relative width
|
|
||||||
newLeft += offset;
|
|
||||||
newPosition.x = newLeft;
|
|
||||||
newExtent.x = newWidth;
|
newExtent.x = newWidth;
|
||||||
}
|
}
|
||||||
else if (mHorizSizing == horizResizeAspectCenter && oldParentRect.extent.x != 0)
|
else if (mHorizSizing == horizResizeAspectCenter && oldParentRect.extent.x != 0)
|
||||||
{
|
{
|
||||||
S32 newLeft = mRoundToNearest((F32(newPosition.x) / F32(oldParentRect.extent.x)) * F32(newParentRect.extent.x));
|
F32 heightRatio = F32(newParentRect.extent.y) / F32(oldParentRect.extent.y);
|
||||||
S32 newWidth = mRoundToNearest((F32(newExtent.x) / F32(oldParentRect.extent.y)) * F32(newParentRect.extent.y)); //origional aspect ratio corrected width
|
S32 newWidth = mRoundToNearest(F32(newExtent.x) * heightRatio + nudgeX);
|
||||||
S32 rWidth = mRoundToNearest((F32(newExtent.x) / F32(oldParentRect.extent.x)) * F32(newParentRect.extent.x)); //parent aspect ratio relative width
|
S32 center = newPosition.x + (newExtent.x >> 1);
|
||||||
|
center += deltaX >> 1;
|
||||||
S32 offset = rWidth - newWidth; // account for change in relative width
|
newPosition.x = center - (newWidth >> 1);
|
||||||
newLeft += offset/2;
|
|
||||||
newPosition.x = newLeft;
|
|
||||||
newExtent.x = newWidth;
|
newExtent.x = newWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1436,40 +1432,32 @@ void GuiControl::parentResized(const RectI &oldParentRect, const RectI &newParen
|
||||||
newPosition.y += deltaY;
|
newPosition.y += deltaY;
|
||||||
else if(mVertSizing == vertResizeRelative && oldParentRect.extent.y != 0)
|
else if(mVertSizing == vertResizeRelative && oldParentRect.extent.y != 0)
|
||||||
{
|
{
|
||||||
S32 newTop = mRoundToNearest( ( F32( newPosition.y ) / F32( oldParentRect.extent.y ) ) * F32( newParentRect.extent.y ) );
|
S32 newHeight = mRoundToNearest( ( F32( newExtent.y ) / F32( oldParentRect.extent.y ) ) * F32( newParentRect.extent.y ) + nudgeY );
|
||||||
S32 newHeight = mRoundToNearest( ( F32( newExtent.y ) / F32( oldParentRect.extent.y ) ) * F32( newParentRect.extent.y ) );
|
S32 newTop = mRoundToNearest( ( F32( newPosition.y ) / F32( oldParentRect.extent.y ) ) * F32( newParentRect.extent.y ) + nudgeY );
|
||||||
|
|
||||||
newPosition.y = newTop;
|
newPosition.y = newTop;
|
||||||
newExtent.y = newHeight;
|
newExtent.y = newHeight;
|
||||||
}
|
}
|
||||||
else if (mVertSizing == vertResizeAspectTop && oldParentRect.extent.y != 0)
|
else if (mVertSizing == vertResizeAspectTop && oldParentRect.extent.y != 0)
|
||||||
{
|
{
|
||||||
S32 newTop = mRoundToNearest((F32(newPosition.y) / F32(oldParentRect.extent.y)) * F32(newParentRect.extent.y));
|
F32 widthRatio = F32(newParentRect.extent.x) / F32(oldParentRect.extent.x);
|
||||||
S32 newHeight = mRoundToNearest((F32(newExtent.y) / F32(oldParentRect.extent.x)) * F32(newParentRect.extent.x));
|
S32 newHeight = mRoundToNearest(F32(newExtent.y) * widthRatio + nudgeY);
|
||||||
|
newPosition.y += deltaY;
|
||||||
newPosition.y = newTop;
|
|
||||||
newExtent.y = newHeight;
|
newExtent.y = newHeight;
|
||||||
}
|
}
|
||||||
else if (mVertSizing == vertResizeAspectBottom && oldParentRect.extent.y != 0)
|
else if (mVertSizing == vertResizeAspectBottom && oldParentRect.extent.y != 0)
|
||||||
{
|
{
|
||||||
S32 newTop = mRoundToNearest((F32(newPosition.y) / F32(oldParentRect.extent.y)) * F32(newParentRect.extent.y));
|
F32 widthRatio = F32(newParentRect.extent.x) / F32(oldParentRect.extent.x);
|
||||||
S32 newHeight = mRoundToNearest((F32(newExtent.y) / F32(oldParentRect.extent.x)) * F32(newParentRect.extent.x)); //origional aspect ratio corrected hieght
|
S32 newHeight = mRoundToNearest(F32(newExtent.y) * widthRatio + nudgeY);
|
||||||
S32 rHeight = mRoundToNearest((F32(newExtent.y) / F32(oldParentRect.extent.y)) * F32(newParentRect.extent.y)); //parent aspect ratio relative hieght
|
|
||||||
|
|
||||||
S32 offset = rHeight - newHeight; // account for change in relative hieght
|
|
||||||
newTop += offset;
|
|
||||||
newPosition.y = newTop;
|
|
||||||
newExtent.y = newHeight;
|
newExtent.y = newHeight;
|
||||||
}
|
}
|
||||||
else if (mVertSizing == vertResizeAspectCenter && oldParentRect.extent.y != 0)
|
else if (mVertSizing == vertResizeAspectCenter && oldParentRect.extent.y != 0)
|
||||||
{
|
{
|
||||||
S32 newTop = mRoundToNearest((F32(newPosition.y) / F32(oldParentRect.extent.y)) * F32(newParentRect.extent.y));
|
F32 widthRatio = F32(newParentRect.extent.x) / F32(oldParentRect.extent.x);
|
||||||
S32 newHeight = mRoundToNearest((F32(newExtent.y) / F32(oldParentRect.extent.x)) * F32(newParentRect.extent.x)); //origional aspect ratio corrected hieght
|
S32 newHeight = mRoundToNearest(F32(newExtent.y) * widthRatio + nudgeY);
|
||||||
S32 rHeight = mRoundToNearest((F32(newExtent.y) / F32(oldParentRect.extent.y)) * F32(newParentRect.extent.y)); //parent aspect ratio relative hieght
|
S32 center = newPosition.y + (newExtent.y >> 1);
|
||||||
|
center += deltaY >> 1;
|
||||||
S32 offset = rHeight - newHeight; // account for change in relative hieght
|
newPosition.y = center - (newHeight >> 1);
|
||||||
newTop += offset / 2;
|
|
||||||
newPosition.y = newTop;
|
|
||||||
newExtent.y = newHeight;
|
newExtent.y = newHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ function LevelAsset::onCreateNew(%this)
|
||||||
AssetName = %assetName;
|
AssetName = %assetName;
|
||||||
versionId = 1;
|
versionId = 1;
|
||||||
LevelFile = %assetName @ %misExtension;
|
LevelFile = %assetName @ %misExtension;
|
||||||
DecalsFile = %assetName @ ".mis.decals";
|
DecalsFile = %assetName @ ".decals";
|
||||||
PostFXPresetFile = %assetName @ ".postfxpreset." @ $TorqueScriptFileExtension;
|
PostFXPresetFile = %assetName @ ".postfxpreset." @ $TorqueScriptFileExtension;
|
||||||
ForestFile = %assetName @ ".forest";
|
ForestFile = %assetName @ ".forest";
|
||||||
NavmeshFile = %assetName @ ".nav";
|
NavmeshFile = %assetName @ ".nav";
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,19 @@ function DatablockEditorPlugin::onExitMission( %this )
|
||||||
|
|
||||||
function DatablockEditorPlugin::openDatablock( %this, %datablock )
|
function DatablockEditorPlugin::openDatablock( %this, %datablock )
|
||||||
{
|
{
|
||||||
|
//We want to do a special-case catch here for any datablock types that have their own unique editor.
|
||||||
|
//The main culprit is the particle editor, but this could be expanded later
|
||||||
|
if(%datablock.isMemberOfClass("ParticleData") ||
|
||||||
|
%datablock.isMemberOfClass("ParticleEmitterData") ||
|
||||||
|
%datablock.isMemberOfClass("ExplosionData") ||
|
||||||
|
%datablock.isMemberOfClass("RibbonData") ||
|
||||||
|
%datablock.isMemberOfClass("afxZodiacData") ||
|
||||||
|
%datablock.isMemberOfClass("afxChoreographerData"))
|
||||||
|
{
|
||||||
|
EditorGui.setEditor( ParticleEditorPlugin );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
EditorGui.setEditor( DatablockEditorPlugin );
|
EditorGui.setEditor( DatablockEditorPlugin );
|
||||||
%this.selectDatablock( %datablock );
|
%this.selectDatablock( %datablock );
|
||||||
DatablockEditorTreeTabBook.selectedPage = 0;
|
DatablockEditorTreeTabBook.selectedPage = 0;
|
||||||
|
|
@ -631,6 +644,21 @@ function DatablockEditorPlugin::deleteDatablock( %this )
|
||||||
//---------------------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------------------
|
||||||
function DatablockEditorPlugin::createNewDatablockOfType(%this, %class, %inheritFrom)
|
function DatablockEditorPlugin::createNewDatablockOfType(%this, %class, %inheritFrom)
|
||||||
{
|
{
|
||||||
|
//We want to do a special-case catch here for any datablock types that have their own unique editor.
|
||||||
|
//The main culprit is the particle editor, but this could be expanded later
|
||||||
|
if(%class.isMemberOfClass("ParticleData") ||
|
||||||
|
%class.isMemberOfClass("ParticleEmitterData") ||
|
||||||
|
%class.isMemberOfClass("ExplosionData") ||
|
||||||
|
%class.isMemberOfClass("RibbonData") ||
|
||||||
|
%class.isMemberOfClass("afxZodiacData") ||
|
||||||
|
%class.isMemberOfClass("afxChoreographerData"))
|
||||||
|
{
|
||||||
|
EditorGui.setEditor( ParticleEditorPlugin );
|
||||||
|
|
||||||
|
ParticleEditorPlugin::createNewDatablockOfType(%class, %inheritFrom);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$DATABLOCK_EDITOR_NEWDB_CLASS = %class;
|
$DATABLOCK_EDITOR_NEWDB_CLASS = %class;
|
||||||
$DATABLOCK_EDITOR_NEWDB_INHERITFROM = %inheritFrom;
|
$DATABLOCK_EDITOR_NEWDB_INHERITFROM = %inheritFrom;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,6 @@ function ParticleEditor::initEditor( %this )
|
||||||
|
|
||||||
if(exec("./PETabTemplate.gui"))
|
if(exec("./PETabTemplate.gui"))
|
||||||
{
|
{
|
||||||
echo("MADE A NEW TAB PAGE: " @ $guiContent);
|
|
||||||
|
|
||||||
$guiContent.text = %groupName;
|
$guiContent.text = %groupName;
|
||||||
$guiContent.typesList = %typesList;
|
$guiContent.typesList = %typesList;
|
||||||
$guiContent.setName(%editorName);
|
$guiContent.setName(%editorName);
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,19 @@ function ParticleEditorPlugin::onActivated( %this )
|
||||||
EditorGuiStatusBar.setInfo( "Particle editor." );
|
EditorGuiStatusBar.setInfo( "Particle editor." );
|
||||||
EditorGuiStatusBar.setSelection( "" );
|
EditorGuiStatusBar.setSelection( "" );
|
||||||
|
|
||||||
|
// Try to start with the object selected in the world editor
|
||||||
|
%count = EWorldEditor.getSelectionSize();
|
||||||
|
for (%i = 0; %i < %count; %i++)
|
||||||
|
{
|
||||||
|
%obj = EWorldEditor.getSelectedObject(%i);
|
||||||
|
%datablock = ParticleEditor.getObjectParticleData(%obj);
|
||||||
|
if (%datablock !$= "" && isObject(%datablock))
|
||||||
|
{
|
||||||
|
ParticleEditor.open(%datablock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Parent::onActivated( %this );
|
Parent::onActivated( %this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,3 +226,12 @@ function ParticleEditor::registerParticleEdType(%this, %groupName, %typesList, %
|
||||||
PE_ParticleDataTypes.add(%groupName, %typesList TAB %editorName);
|
PE_ParticleDataTypes.add(%groupName, %typesList TAB %editorName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
function ParticleEditor::getObjectParticleData( %this, %obj )
|
||||||
|
{
|
||||||
|
%datablock = "";
|
||||||
|
if ( %obj.isMemberOfClass( "ParticleEmitterNode" ) )
|
||||||
|
%datablock = %obj.emitter;
|
||||||
|
|
||||||
|
return %datablock;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,6 @@ function PE_EmitterEditor::selectObject(%this, %obj)
|
||||||
&& %obj.getName() $= %this-->popupMenu.text )
|
&& %obj.getName() $= %this-->popupMenu.text )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//FIXME: disregards particle tab dirty state
|
|
||||||
if( %this.dirty )
|
if( %this.dirty )
|
||||||
{
|
{
|
||||||
if( PE_ParticleEditor.dirty )
|
if( PE_ParticleEditor.dirty )
|
||||||
|
|
@ -483,6 +482,8 @@ function ParticleEditor::addParticleSlot(%this, %field, %emitterObj)
|
||||||
%action.oldValue = %emitterObj.particles;
|
%action.oldValue = %emitterObj.particles;
|
||||||
|
|
||||||
ParticleEditor.submitUndo( %action );
|
ParticleEditor.submitUndo( %action );
|
||||||
|
|
||||||
|
%field.apply(%action.newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ParticleEditor::changeParticleSlot(%this, %field, %emitterObj, %slotIdx)
|
function ParticleEditor::changeParticleSlot(%this, %field, %emitterObj, %slotIdx)
|
||||||
|
|
@ -495,7 +496,6 @@ function ParticleEditor::changeParticleSlot(%this, %field, %emitterObj, %slotIdx
|
||||||
|
|
||||||
foreach( %obj in DatablockGroup )
|
foreach( %obj in DatablockGroup )
|
||||||
{
|
{
|
||||||
echo(%typesList);
|
|
||||||
%typesListCount = getWordCount(%typesList);
|
%typesListCount = getWordCount(%typesList);
|
||||||
for(%i=0; %i < %typesListCount; %i++)
|
for(%i=0; %i < %typesListCount; %i++)
|
||||||
{
|
{
|
||||||
|
|
@ -552,7 +552,7 @@ function ParticleEditor::changeParticleSlot(%this, %field, %emitterObj, %slotIdx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ContextedDropdownListGui.show(%listSet, "Edit Particle Slot[" @ %slotIdx @ "]", "ParticleEditor.editSlot = " @ %slotIdx @ ";ParticleEditor.updateParticleSlot", %field);
|
ContextedDropdownListGui.show(%listSet, "Edit Particle Slot[" @ %slotIdx @ "]", "ParticleEditor.fieldObj = " @ %field @ ";ParticleEditor.editSlot = " @ %slotIdx @ ";ParticleEditor.updateParticleSlot", %field);
|
||||||
%particleData = getWord(%emitterObj.particles, %slotIdx);
|
%particleData = getWord(%emitterObj.particles, %slotIdx);
|
||||||
if(%particleData !$= "")
|
if(%particleData !$= "")
|
||||||
{
|
{
|
||||||
|
|
@ -562,10 +562,10 @@ function ParticleEditor::changeParticleSlot(%this, %field, %emitterObj, %slotIdx
|
||||||
|
|
||||||
function ParticleEditor::updateParticleSlot(%this, %newParticle)
|
function ParticleEditor::updateParticleSlot(%this, %newParticle)
|
||||||
{
|
{
|
||||||
if(ParticleEditor.editSlot $= "")
|
if(ParticleEditor.editSlot $= "" || !isObject(%newParticle))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
%updatedParticlesList = setWord(PE_EmitterEditor.currEmitter.particles, ParticleEditor.editSlot, %newParticle);
|
%updatedParticlesList = setWord(PE_EmitterEditor.currEmitter.particles, ParticleEditor.editSlot, %newParticle.getName());
|
||||||
|
|
||||||
%action = ParticleEditor.createUndo(ActionUpdateActiveEmitter, "Edit Active Emitter Particle Slot");
|
%action = ParticleEditor.createUndo(ActionUpdateActiveEmitter, "Edit Active Emitter Particle Slot");
|
||||||
%action.emitter = PE_EmitterEditor.currEmitter;
|
%action.emitter = PE_EmitterEditor.currEmitter;
|
||||||
|
|
@ -575,16 +575,19 @@ function ParticleEditor::updateParticleSlot(%this, %newParticle)
|
||||||
|
|
||||||
ParticleEditor.submitUndo( %action );
|
ParticleEditor.submitUndo( %action );
|
||||||
|
|
||||||
ParticleEditor.editSlot = "";
|
ParticleEditor.fieldObj.apply(%action.newValue);
|
||||||
|
|
||||||
//%field.apply(%updatedParticlesList, %slotIdx);
|
ParticleEditor.editSlot = "";
|
||||||
//%this-->inspector.refresh();
|
ParticleEditor.fieldObj = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function ParticleEditor::editParticleSlot(%this, %field, %emitterObj, %slotIdx)
|
function ParticleEditor::editParticleSlot(%this, %field, %emitterObj, %slotIdx)
|
||||||
{
|
{
|
||||||
%particleName = getWord(%emitterObj.particles, %slotIdx);
|
%particleName = getWord(%emitterObj.particles, %slotIdx);
|
||||||
|
|
||||||
|
ParticleEditor.editSlot = "";
|
||||||
|
ParticleEditor.fieldObj = "";
|
||||||
|
|
||||||
ParticleEditor.open(%particleName);
|
ParticleEditor.open(%particleName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -600,6 +603,5 @@ function ParticleEditor::clearParticleSlot(%this, %field, %emitterObj, %slotIdx)
|
||||||
|
|
||||||
ParticleEditor.submitUndo( %action );
|
ParticleEditor.submitUndo( %action );
|
||||||
|
|
||||||
//%field.apply(%updatedParticlesList, %slotIdx);
|
%field.apply(%action.newValue);
|
||||||
//%this-->inspector.refresh();
|
|
||||||
}
|
}
|
||||||
|
|
@ -74,7 +74,8 @@ function PE_ParticleEditor::updateVizNode(%this)
|
||||||
function PE_ParticleEditor::selectObject(%this, %obj)
|
function PE_ParticleEditor::selectObject(%this, %obj)
|
||||||
{
|
{
|
||||||
// Bail if the user selected the same particle.
|
// Bail if the user selected the same particle.
|
||||||
if( %obj == %this.currParticle )
|
if( isObject(%obj )
|
||||||
|
&& %obj.getName() $= %this-->popupMenu.text )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Load new particle if we're not in a dirty state
|
// Load new particle if we're not in a dirty state
|
||||||
|
|
|
||||||
|
|
@ -2004,6 +2004,10 @@ function beginLevelImport()
|
||||||
{
|
{
|
||||||
%asset.decalsFile = %fileBase @ ".decal";
|
%asset.decalsFile = %fileBase @ ".decal";
|
||||||
}
|
}
|
||||||
|
else if(isFile(%filePath @ "/" @ %fileBase @ "decal"))
|
||||||
|
{
|
||||||
|
%asset.decalsFile = %fileBase @ "decal";
|
||||||
|
}
|
||||||
else if(isFile(%filePath @ "/" @ %fileBase @ "mis.decal"))
|
else if(isFile(%filePath @ "/" @ %fileBase @ "mis.decal"))
|
||||||
{
|
{
|
||||||
%asset.decalsFile = %fileBase @ "mis.decal";
|
%asset.decalsFile = %fileBase @ "mis.decal";
|
||||||
|
|
|
||||||
|
|
@ -361,20 +361,34 @@ function EditorSaveMission()
|
||||||
%obj.onSaveMission( $Server::MissionFile );
|
%obj.onSaveMission( $Server::MissionFile );
|
||||||
}
|
}
|
||||||
|
|
||||||
//We'll sanity check that we have a valid file association to our level asset first
|
//Force a refresh of the associated paths if they're valid files when we save out the assetDef
|
||||||
%presetFile = $Server::LevelAsset.getPostFXPresetPath();
|
%presetFile = $Server::LevelAsset.getPostFXPresetPath();
|
||||||
|
if(isFile(%presetFile))
|
||||||
if(!isFile(%presetFile))
|
|
||||||
{
|
{
|
||||||
//if it isn't valid, we'll fabricate a new one just to be sure
|
$Server::LevelAsset.postFXPresetFile = fileName(%presetFile);
|
||||||
$Server::LevelAsset.PostFXPresetFile = fileBase($Server::LevelAsset.getLevelPath()) @ $PostFXManager::fileExtension;
|
|
||||||
|
|
||||||
$Server::LevelAsset.saveAsset();
|
|
||||||
$Server::LevelAsset.refresh();
|
|
||||||
|
|
||||||
%presetFile = $Server::LevelAsset.getPostFXPresetPath();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
%decalsFile = $Server::LevelAsset.getDecalsPath();
|
||||||
|
if(isFile(%decalsFile))
|
||||||
|
{
|
||||||
|
$Server::LevelAsset.decalsFile = fileName(%decalsFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
%forestFile = $Server::LevelAsset.getForestPath();
|
||||||
|
if(isFile(%forestFile))
|
||||||
|
{
|
||||||
|
$Server::LevelAsset.forestFile = fileName(%forestFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
%navMeshFile = $Server::LevelAsset.getNavmeshPath();
|
||||||
|
if(isFile(%navMeshFile))
|
||||||
|
{
|
||||||
|
$Server::LevelAsset.navmeshFile = fileName(%navMeshFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
$Server::LevelAsset.saveAsset();
|
||||||
|
$Server::LevelAsset.refresh();
|
||||||
|
|
||||||
//Save out the PostFX config
|
//Save out the PostFX config
|
||||||
PostFXManager::savePresetHandler( %presetFile );
|
PostFXManager::savePresetHandler( %presetFile );
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue