diff --git a/Activities/ActorEditor.cpp b/Activities/ActorEditor.cpp index 4565e92f0..9cd14efed 100644 --- a/Activities/ActorEditor.cpp +++ b/Activities/ActorEditor.cpp @@ -319,7 +319,6 @@ void ActorEditor::DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos, int wh if (m_pEditedActor) { m_pEditedActor->Draw(pTargetBitmap, targetPos, g_DrawColor); - m_pEditedActor->Draw(pTargetBitmap, targetPos, g_DrawDebug); } // Clear out annoying blooms diff --git a/Activities/AreaEditor.cpp b/Activities/AreaEditor.cpp index d8f23884e..466238fa0 100644 --- a/Activities/AreaEditor.cpp +++ b/Activities/AreaEditor.cpp @@ -611,7 +611,7 @@ bool AreaEditor::SaveScene(string saveAsName, bool forceOverwrite) if (g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() == "Scenes.rte") { string sceneFilePath(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/" + saveAsName + ".ini"); - string previewFilePath(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/" + saveAsName + ".preview.bmp"); + string previewFilePath(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/" + saveAsName + ".preview.png"); if (g_PresetMan.AddEntityPreset(g_SceneMan.GetScene(), m_ModuleSpaceID, forceOverwrite, sceneFilePath)) { // Save preview @@ -638,7 +638,7 @@ bool AreaEditor::SaveScene(string saveAsName, bool forceOverwrite) { // Try to save to the data module string sceneFilePath(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes/" + saveAsName + ".ini"); - string previewFilePath(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes/" + saveAsName + ".preview.bmp"); + string previewFilePath(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes/" + saveAsName + ".preview.png"); if (g_PresetMan.AddEntityPreset(g_SceneMan.GetScene(), m_ModuleSpaceID, forceOverwrite, sceneFilePath)) { // Save preview diff --git a/Activities/GibEditor.cpp b/Activities/GibEditor.cpp index f161ca1e2..4ee588654 100644 --- a/Activities/GibEditor.cpp +++ b/Activities/GibEditor.cpp @@ -338,7 +338,7 @@ void GibEditor::Update() // Clear out the terrain after a few tests if (m_TestCounter >= 3) { - g_SceneMan.GetScene()->GetTerrain()->ClearAllMaterial(); + ClearTestArea(); m_TestCounter = 0; } // Clear all crap still flying around @@ -529,7 +529,7 @@ void GibEditor::Update() } // Clear out the testing area - g_SceneMan.GetScene()->GetTerrain()->ClearAllMaterial(); + ClearTestArea(); m_TestCounter = 0; m_pObjectToLoad = 0; @@ -955,4 +955,11 @@ void GibEditor::UpdateOverwriteDialog() m_pOverwriteNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/NewData/" + m_pEditedObject->GetPresetName() + ".ini"); } -} // namespace RTE \ No newline at end of file +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void GibEditor::ClearTestArea() const { + clear_bitmap(g_SceneMan.GetTerrain()->GetFGColorBitmap()); + clear_bitmap(g_SceneMan.GetTerrain()->GetBGColorBitmap()); + clear_bitmap(g_SceneMan.GetTerrain()->GetMaterialBitmap()); + } +} \ No newline at end of file diff --git a/Activities/GibEditor.h b/Activities/GibEditor.h index 029f19fd4..f2623f5df 100644 --- a/Activities/GibEditor.h +++ b/Activities/GibEditor.h @@ -306,6 +306,11 @@ ClassInfoGetters; private: + /// + /// Clears all the layers of the testing area terrain so nothing that somehow settled lingers between edited object changes and testing phases. + /// + void ClearTestArea() const; + ////////////////////////////////////////////////////////////////////////////////////////// // Method: Clear diff --git a/Activities/SceneEditor.cpp b/Activities/SceneEditor.cpp index f60d728f6..bcaf1b603 100644 --- a/Activities/SceneEditor.cpp +++ b/Activities/SceneEditor.cpp @@ -24,6 +24,7 @@ #include "HeldDevice.h" #include "Scene.h" #include "DataModule.h" +#include "SLBackground.h" #include "GUI.h" #include "GUIFont.h" @@ -417,22 +418,22 @@ void SceneEditor::Update() pItem = m_pNewBG1Combo->GetItem(m_pNewBG1Combo->GetSelectedIndex()); if (pItem && !pItem->m_Name.empty()) { - SceneLayer *pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SceneLayer", pItem->m_Name, m_ModuleSpaceID)->Clone()); - RTEAssert(pNewLayer, "No SceneLayer of the name set as BG1 is defined!"); + SLBackground *pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SLBackground", pItem->m_Name, m_ModuleSpaceID)->Clone()); + RTEAssert(pNewLayer, "No SLBackground of the name set as BG1 is defined!"); pNewScene->GetBackLayers().push_back(pNewLayer); } pItem = m_pNewBG2Combo->GetItem(m_pNewBG2Combo->GetSelectedIndex()); if (pItem && !pItem->m_Name.empty()) { - SceneLayer *pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SceneLayer", pItem->m_Name, m_ModuleSpaceID)->Clone()); - RTEAssert(pNewLayer, "No SceneLayer of the name set as BG2 is defined!"); + SLBackground *pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SLBackground", pItem->m_Name, m_ModuleSpaceID)->Clone()); + RTEAssert(pNewLayer, "No SLBackground of the name set as BG2 is defined!"); pNewScene->GetBackLayers().push_back(pNewLayer); } pItem = m_pNewBG3Combo->GetItem(m_pNewBG3Combo->GetSelectedIndex()); if (pItem && !pItem->m_Name.empty()) { - SceneLayer *pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SceneLayer", pItem->m_Name, m_ModuleSpaceID)->Clone()); - RTEAssert(pNewLayer, "No SceneLayer of the name set as BG3 is defined!"); + SLBackground *pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SLBackground", pItem->m_Name, m_ModuleSpaceID)->Clone()); + RTEAssert(pNewLayer, "No SLBackground of the name set as BG3 is defined!"); pNewScene->GetBackLayers().push_back(pNewLayer); } @@ -795,21 +796,21 @@ void SceneEditor::UpdateNewDialog() // Get the list of all read in NEAR background layers list bgList; - g_PresetMan.GetAllOfGroupInModuleSpace(bgList, "Near Backdrops", "SceneLayer", selectedModuleID); + g_PresetMan.GetAllOfGroupInModuleSpace(bgList, "Near Backdrops", "SLBackground", selectedModuleID); // Go through the list and add their names to the combo box for (list::iterator itr = bgList.begin(); itr != bgList.end(); ++itr) m_pNewBG1Combo->AddItem((*itr)->GetPresetName()); // Get the list of all read in MID background layers bgList.clear(); - g_PresetMan.GetAllOfGroupInModuleSpace(bgList, "Mid Backdrops", "SceneLayer", selectedModuleID); + g_PresetMan.GetAllOfGroupInModuleSpace(bgList, "Mid Backdrops", "SLBackground", selectedModuleID); // Go through the list and add their names to the combo box for (list::iterator itr = bgList.begin(); itr != bgList.end(); ++itr) m_pNewBG2Combo->AddItem((*itr)->GetPresetName()); // Get the list of all read in FAR background layers bgList.clear(); - g_PresetMan.GetAllOfGroupInModuleSpace(bgList, "Far Backdrops", "SceneLayer", selectedModuleID); + g_PresetMan.GetAllOfGroupInModuleSpace(bgList, "Far Backdrops", "SLBackground", selectedModuleID); // Go through the list and add their names to the combo box for (list::iterator itr = bgList.begin(); itr != bgList.end(); ++itr) m_pNewBG3Combo->AddItem((*itr)->GetPresetName()); diff --git a/CHANGELOG.md b/CHANGELOG.md index a4ee88f03..c2e8fec7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,24 +8,121 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
Added -- New `DataModule` INI property `SupportedGameVersion` to define what version of the game a mod supports. This must be specified, and must match the current game version, in order for the mod to load succesfully. +- New `DataModule` INI property `SupportedGameVersion` to define what version of the game a mod supports. This must be specified, and must match the current game version, in order for the mod to load successfully. -- New `Actor` INI properties `Organic = 0/1` and `Robotic = 0/1` and supporting Lua functions `Actor:IsOrganic()` and `Actor:IsRobotic()`. These have no direct gameplay effect (and default to false), but will be very useful for inter-mod compatibility, as they allow scripts to know if an `Actor` is organic or robotic, and treat them accordingly. +- New `Actor` INI properties `Organic = 0/1` and `Robotic = 0/1` and supporting Lua functions `Actor:IsOrganic()` and `Actor:IsRobotic()`. + These have no direct gameplay effect (and default to false), but will be very useful for inter-mod compatibility, as they allow scripts to know if an `Actor` is organic or robotic, and treat them accordingly. - New INI and Lua (R/W) `ACDropShip` property `HoverHeightModifier`. This allows for modification of the height at which an `ACDropShip` will hover when unloading cargo, or staying at a location. +- New `Scene` Lua property `BackgroundLayers` (R/O) to access an iterator of the `Scene`'s `SLBackground` layers. + +- `SLBackground` layers can now be animated using the same INI and Lua animation controls as everything else (`FrameCount`, `SpriteAnimMode`, etc). ([Issue #66](https://github.com/cortex-command-community/Cortex-Command-Community-Project-Source/issues/66)) + The collection of the `Scene`'s background layers can be accessed via `scene.BackgroundLayers`. + +- Added `SLBackground` auto-scrolling INI and Lua controls. ([Issue #66](https://github.com/cortex-command-community/Cortex-Command-Community-Project-Source/issues/66)) + The INI definition looks like this: + ``` + CanAutoScrollX = 0/1 // Whether this can auto-scroll on the X axis. Scrolling will take effect only when combined with WrapX = 1, otherwise ignored. + CanAutoScrollY = 0/1 // Whether this can auto-scroll on the Y axis. Scrolling will take effect only when combined with WrapY = 1, otherwise ignored. + + AutoScrollStepInterval = intValue // The duration between auto-scrolling steps on both axis, in milliseconds. + + AutoScrollStep = Vector + X = intValue // The number of pixels to progress the scrolling on the X axis each step. Float values are supported but may end up choppy and inconsistent due to internal rounding and lack of sub-pixel precision. + Y = intValue // The number of pixels to progress the scrolling on the Y axis each step. Float values are supported but may end up choppy and inconsistent due to internal rounding and lack of sub-pixel precision. + ``` + You can read and write the following Lua properties: + ``` + slBackground.CanAutoScrollX = bool -- this may be true even if X axis scrolling is not in effect due to WrapX = 0. + slBackground.CanAutoScrollY = bool -- this may be true even if Y axis scrolling is not in effect due to WrapY = 0. + slBackground.AutoScrollInterval = intValue + slBackground.AutoScrollStep = vector + slBackground.AutoScrollStepX = intValue + slBackground.AutoScrollStepY = intValue + ``` + `slBackground:IsAutoScrolling()` - (R/O) returns whether auto-scrolling is actually in effect on either axis (meaning either `WrapX` and `CanAutoScrollX` or `WrapY` and `CanAutoScrollY` are true). + + The collection of the `Scene`'s background layers can be accessed via `scene.BackgroundLayers`. + +- New `Settings.ini` property `SceneBackgroundAutoScaleMode = 0/1/2`. ([Issue #243](https://github.com/cortex-command-community/Cortex-Command-Community-Project-Source/issues/243)) + Auto-scaling modes are: 0 = Off, 1 = Fit Screen, 2 = Always 2x. + This will auto-scale all `Scene` background layers to cover the whole screen vertically in cases where the layer's sprite is too short and creates a gap at the bottom. + In cases where a layer is short by design, auto-scaling behavior can be disabled on a per-layer basis using the `SLBackground` INI property `IgnoreAutoScaling`, otherwise it may be excessively scaled up to 2x (maximum). + Note that any `ScaleFactor` definitions in layers that aren't set to ignore auto-scaling will be overridden. + + Only relevant when playing on un-scaled vertical resolutions of 640p and above as most background layer sprites are currently around 580px tall, and comes with a minor performance impact. + Note that in fit screen mode each layer is scaled individually so some layers may be scaled more than others, or not scaled at all. + In always 2x mode all layers will be equally scaled to 2x. + +- New `SLBackground` INI property `IgnoreAutoScaling = 0/1` to ignore the global background layer auto-scaling setting and use the `ScaleFactor` defined in the preset, if any. + +- New `SLBackground` INI property `OriginPointOffset` to offset the layer from the top left corner of the screen. + +- Added new `TerrainDebris` scattering functionality. ([Issue #152](https://github.com/cortex-command-community/Cortex-Command-Community-Project-Source/issues/152)) + New INI properties are: + ``` + MinRotation/MaxRotation = angleDegrees // Rotates each debris piece to a random angle within the specified values. Values in degrees, negative values are counter-clockwise. + + FlipChance = floatValue // Chance for a debris piece to be flipped when either `CanHFlip` or `CanYFlip` are set true. 0-1, defaults to 0.5. + + CanHFlip/CanVFlip = 0/1 // Flips each debris piece on the X, Y or both axis. + ``` + +- Added support for `Material` background textures with INI property `BGTextureFile`. +
Changed - Completely replaced `ScriptFile` with `ScriptPath`. +- `Scene` background layer presets in INI are now defined as `SLBackground` rather than `SceneLayer`. + +- `TerrainDebris` INI property `OnlyOnSurface = 0/1` replaced with `DebrisPlacementMode = 0-6`. + Placement modes are: + ``` + 0 (NoPlacementRestrictions) // Debris will be placed anywhere where there is target material (currently not strictly enforced when being offset by min/max depth, so can end up being placed mid-air/cavity unless set to OnlyBuried = 1). + + 1 (OnSurfaceOnly) // Debris will be placed only on the surface (scanning from top to bottom) where no background cavity material (material index 1) was encountered before the target material. + + 2 (OnCavitySurfaceOnly) // Debris will be placed only on the surface (scanning from top to bottom) where background cavity material (material index 1) was encountered before the target material. + + 3 (OnSurfaceAndCavitySurface) // Debris will be placed only on the surface (scanning from top to bottom) regardless whether background cavity material (material index 1) was encountered before the target material. + + 4 (OnOverhangOnly) // Debris will be placed only on overhangs (scanning from bottom to top) where no background cavity material (material index 1) was encountered before the target material. + + 5 (OnCavityOverhangOnly) // Debris will be placed only on overhangs (scanning from bottom to top) where background cavity material (material index 1) was encountered before the target material. + + 6 (OnOverhangAndCavityOverhang) // Debris will be placed only on overhangs (scanning from bottom to top) regardless whether background cavity material (material index 1) was encountered before the target material. + ``` + +- `Material` INI property `TextureFile` renamed to `FGTextureFile` to accommodate new background texture property. + +- `TerrainObject`s no longer have a hard requirement for `FG` and `Mat` layer sprites. Any layer may be omitted as long as at least one is defined. + +- `Scene` layer data will now be saved as compressed PNG to reduce file sizes of MetaGame saves and is threaded to prevent the game from freezing when layer data is being saved. +
Fixed + +- Fixed material view not drawing correctly when viewed in split-screen. ([Issue #54](https://github.com/cortex-command-community/Cortex-Command-Community-Project-Source/issues/54)) + +- Fix `TerrainObject`s not wrapping when placed over the Y seam on Y-wrapped scenes. +
Removed + +- Removed `SLTerrain` and `SLBackground` INI property `Offset`. Internal and shouldn't have been exposed. + +- Removed `SLTerrain` INI alt property `AddTerrainObject`. Use `PlaceTerrainObject` for consistency with similar properties. + +- Removed `TerrainObject` INI property `DisplayAsTerrain`. Wasn't implemented and did nothing. + +- Removed `SceneMan` Lua function `AddTerrainObject`. `SceneMan:AddSceneObject` should be used instead. +
*** diff --git a/Entities/BunkerAssembly.cpp b/Entities/BunkerAssembly.cpp index afe9b589d..501d41764 100644 --- a/Entities/BunkerAssembly.cpp +++ b/Entities/BunkerAssembly.cpp @@ -13,9 +13,6 @@ #include "BunkerAssembly.h" #include "PresetMan.h" -#include "ContentFile.h" -#include "MOSRotating.h" -#include "Actor.h" #include "ADoor.h" #include "TerrainObject.h" #include "BunkerAssemblyScheme.h" @@ -37,9 +34,9 @@ void BunkerAssembly::Clear() m_FGColorFile.Reset(); m_MaterialFile.Reset(); m_BGColorFile.Reset(); - m_pFGColor = 0; - m_pMaterial = 0; - m_pBGColor = 0; + m_FGColorBitmap = 0; + m_MaterialBitmap = 0; + m_BGColorBitmap = 0; m_BitmapOffset.Reset(); m_OffsetDefined = false; m_ChildObjects.clear(); @@ -86,23 +83,24 @@ void BunkerAssembly::AddPlacedObject(SceneObject * pSO) TerrainObject * pTObject = dynamic_cast(pSO); if (pTObject) { - Vector loc = pTObject->GetPos() + pTObject->GetBitmapOffset(); + Vector objectPos = pTObject->GetPos() + pTObject->GetBitmapOffset(); // Regular drawing - draw_sprite(m_pMaterial, pTObject->GetMaterialBitmap(), loc.m_X, loc.m_Y); - if (pTObject->HasBGColor()) - { - draw_sprite(m_pBGColor, pTObject->GetBGColorBitmap(), loc.m_X, loc.m_Y); - draw_sprite(m_pPresentationBitmap, pTObject->GetBGColorBitmap(), loc.m_X, loc.m_Y); + if (pTObject->HasMaterialBitmap()) { draw_sprite(m_MaterialBitmap, pTObject->GetMaterialBitmap(), objectPos.GetFloorIntX(), objectPos.GetFloorIntY()); } + if (pTObject->HasBGColorBitmap()) { + draw_sprite(m_BGColorBitmap, pTObject->GetBGColorBitmap(), objectPos.GetFloorIntX(), objectPos.GetFloorIntY()); + draw_sprite(m_pPresentationBitmap, pTObject->GetBGColorBitmap(), objectPos.GetFloorIntX(), objectPos.GetFloorIntY()); + } + if (pTObject->HasFGColorBitmap()) { + draw_sprite(m_FGColorBitmap, pTObject->GetFGColorBitmap(), objectPos.GetFloorIntX(), objectPos.GetFloorIntY()); + draw_sprite(m_pPresentationBitmap, pTObject->GetFGColorBitmap(), objectPos.GetFloorIntX(), objectPos.GetFloorIntY()); } - draw_sprite(m_pFGColor, pTObject->GetFGColorBitmap(), loc.m_X, loc.m_Y); - draw_sprite(m_pPresentationBitmap, pTObject->GetFGColorBitmap(), loc.m_X, loc.m_Y); // Read and add all child objects pTObject->SetTeam(GetTeam()); - for (list::const_iterator itr = pTObject->GetChildObjects().begin(); itr != pTObject->GetChildObjects().end() ; ++itr) - { - SOPlacer newPlacer = (*itr); + + for (const SceneObject::SOPlacer &childObject : pTObject->GetChildObjects()) { + SceneObject::SOPlacer newPlacer = childObject; newPlacer.SetTeam(pTObject->GetTeam()); // Explicitly set child object's offset, because it will be a part of a bigger 'terrain object' newPlacer.SetOffset(newPlacer.GetOffset() + pTObject->GetPos() + m_BitmapOffset); @@ -124,14 +122,14 @@ int BunkerAssembly::Create(BunkerAssemblyScheme * pScheme) m_pPresentationBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth() , pScheme->GetBitmapHeight()); clear_to_color(m_pPresentationBitmap, g_MaskColor); - m_pFGColor = create_bitmap_ex(8, pScheme->GetBitmapWidth() , pScheme->GetBitmapHeight()); - clear_to_color(m_pFGColor, g_MaskColor); + m_FGColorBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth() , pScheme->GetBitmapHeight()); + clear_to_color(m_FGColorBitmap, g_MaskColor); - m_pMaterial = create_bitmap_ex(8, pScheme->GetBitmapWidth() , pScheme->GetBitmapHeight()); - clear_to_color(m_pMaterial, g_MaskColor); + m_MaterialBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth() , pScheme->GetBitmapHeight()); + clear_to_color(m_MaterialBitmap, g_MaskColor); - m_pBGColor = create_bitmap_ex(8, pScheme->GetBitmapWidth() , pScheme->GetBitmapHeight()); - clear_to_color(m_pBGColor, g_MaskColor); + m_BGColorBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth() , pScheme->GetBitmapHeight()); + clear_to_color(m_BGColorBitmap, g_MaskColor); m_BitmapOffset = pScheme->GetBitmapOffset(); m_ParentAssemblyScheme = pScheme->GetPresetName(); @@ -233,17 +231,17 @@ int BunkerAssembly::ReadProperty(const std::string_view &propName, Reader &reade m_pPresentationBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth() , pScheme->GetBitmapHeight()); clear_to_color(m_pPresentationBitmap, g_MaskColor); - delete m_pFGColor; - m_pFGColor = create_bitmap_ex(8, pScheme->GetBitmapWidth() , pScheme->GetBitmapHeight()); - clear_to_color(m_pFGColor, g_MaskColor); + delete m_FGColorBitmap; + m_FGColorBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth() , pScheme->GetBitmapHeight()); + clear_to_color(m_FGColorBitmap, g_MaskColor); - delete m_pMaterial; - m_pMaterial = create_bitmap_ex(8, pScheme->GetBitmapWidth() , pScheme->GetBitmapHeight()); - clear_to_color(m_pMaterial, g_MaskColor); + delete m_MaterialBitmap; + m_MaterialBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth() , pScheme->GetBitmapHeight()); + clear_to_color(m_MaterialBitmap, g_MaskColor); - delete m_pBGColor; - m_pBGColor = create_bitmap_ex(8, pScheme->GetBitmapWidth() , pScheme->GetBitmapHeight()); - clear_to_color(m_pBGColor, g_MaskColor); + delete m_BGColorBitmap; + m_BGColorBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth() , pScheme->GetBitmapHeight()); + clear_to_color(m_BGColorBitmap, g_MaskColor); m_ParentAssemblyScheme = parentScheme; m_BitmapOffset = pScheme->GetBitmapOffset(); @@ -369,14 +367,14 @@ void BunkerAssembly::Destroy(bool notInherited) //delete m_pPresentationBitmap; //m_pPresentationBitmap = 0; - //delete m_pFGColor; - //m_pFGColor = 0; + //delete m_FGColorBitmap; + //m_FGColorBitmap = 0; - //delete m_pMaterial; - //m_pMaterial = 0; + //delete m_MaterialBitmap; + //m_MaterialBitmap = 0; - //delete m_pBGColor; - //m_pBGColor = 0; + //delete m_BGColorBitmap; + //m_BGColorBitmap = 0; if (!notInherited) SceneObject::Destroy(); @@ -478,7 +476,7 @@ void BunkerAssembly::SetTeam(int team) void BunkerAssembly::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode mode, bool onlyPhysical) const { - if (!m_pFGColor) + if (!m_FGColorBitmap) RTEAbort("TerrainObject's bitmaps are null when drawing!"); // Take care of wrapping situations @@ -489,13 +487,13 @@ void BunkerAssembly::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMo // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap if (targetPos.IsZero() && g_SceneMan.GetSceneWidth() <= pTargetBitmap->w) { - if (aDrawPos[0].m_X < m_pFGColor->w) + if (aDrawPos[0].m_X < m_FGColorBitmap->w) { aDrawPos[passes] = aDrawPos[0]; aDrawPos[passes].m_X += pTargetBitmap->w; passes++; } - else if (aDrawPos[0].m_X > pTargetBitmap->w - m_pFGColor->w) + else if (aDrawPos[0].m_X > pTargetBitmap->w - m_FGColorBitmap->w) { aDrawPos[passes] = aDrawPos[0]; aDrawPos[passes].m_X -= pTargetBitmap->w; @@ -528,15 +526,11 @@ void BunkerAssembly::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMo { if (mode == g_DrawColor) { - masked_blit(m_pPresentationBitmap, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), m_pBGColor->w, m_pBGColor->h); + masked_blit(m_pPresentationBitmap, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), m_BGColorBitmap->w, m_BGColorBitmap->h); } else if (mode == g_DrawMaterial) { - masked_blit(m_pMaterial, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), m_pMaterial->w, m_pMaterial->h); - } - else if (mode == g_DrawLess) - { - masked_blit(m_pPresentationBitmap, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), m_pFGColor->w, m_pFGColor->h); + masked_blit(m_MaterialBitmap, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), m_MaterialBitmap->w, m_MaterialBitmap->h); } else if (mode == g_DrawTrans) { diff --git a/Entities/BunkerAssembly.h b/Entities/BunkerAssembly.h index 23b057e7c..b5a5f1ecb 100644 --- a/Entities/BunkerAssembly.h +++ b/Entities/BunkerAssembly.h @@ -14,18 +14,13 @@ ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files -#include "RTETools.h" #include "TerrainObject.h" -#include "Vector.h" -#include "SceneMan.h" -#include "BunkerAssemblyScheme.h" -#include "Deployment.h" -//#include "MovableMan.h" namespace RTE { -class ContentFile; +class BunkerAssemblyScheme; +class Deployment; ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Entities/BunkerAssemblyScheme.cpp b/Entities/BunkerAssemblyScheme.cpp index 42690e4d4..9faca3f84 100644 --- a/Entities/BunkerAssemblyScheme.cpp +++ b/Entities/BunkerAssemblyScheme.cpp @@ -401,8 +401,6 @@ void BunkerAssemblyScheme::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, masked_blit(m_pPresentationBitmap, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), m_pPresentationBitmap->w, m_pPresentationBitmap->h); else if (mode == g_DrawMaterial) masked_blit(m_pPresentationBitmap, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), m_pPresentationBitmap->w, m_pPresentationBitmap->h); - else if (mode == g_DrawLess) - masked_blit(m_pPresentationBitmap, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), m_pPresentationBitmap->w, m_pPresentationBitmap->h); else if (mode == g_DrawTrans) draw_trans_sprite(pTargetBitmap, m_pPresentationBitmap, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY()); } diff --git a/Entities/Deployment.cpp b/Entities/Deployment.cpp index 4a7619695..1e3b089d7 100644 --- a/Entities/Deployment.cpp +++ b/Entities/Deployment.cpp @@ -731,10 +731,6 @@ void Deployment::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode m // Draw the spawn radius circle too circle(pTargetBitmap, aDrawPos[i].GetFloorIntX() + (pBitmap->w / 2), aDrawPos[i].GetFloorIntY() + (pBitmap->h / 2), m_SpawnRadius, c_GUIColorGray); } - else if (mode == g_DrawLess) - { - masked_blit(pBitmap, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), pBitmap->w, pBitmap->h); - } else if (mode == g_DrawTrans) { draw_trans_sprite(pTargetBitmap, pBitmap, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY()); @@ -807,10 +803,6 @@ void Deployment::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode m { masked_blit(pBitmap, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), pBitmap->w, pBitmap->h); } - else if (mode == g_DrawLess) - { - masked_blit(pBitmap, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), pBitmap->w, pBitmap->h); - } else if (mode == g_DrawTrans) { draw_trans_sprite(pTargetBitmap, pBitmap, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY()); diff --git a/Entities/MOPixel.cpp b/Entities/MOPixel.cpp index 15b16f39c..1c6ad5783 100644 --- a/Entities/MOPixel.cpp +++ b/Entities/MOPixel.cpp @@ -236,12 +236,6 @@ namespace RTE { case g_DrawMaterial: drawColor = m_Atom->GetMaterial()->GetSettleMaterial(); break; - case g_DrawAir: - drawColor = g_MaterialAir; - break; - case g_DrawMask: - drawColor = g_MaskColor; - break; case g_DrawMOID: drawColor = m_MOID; break; diff --git a/Entities/MOSParticle.cpp b/Entities/MOSParticle.cpp index 2a2a51420..88817eb75 100644 --- a/Entities/MOSParticle.cpp +++ b/Entities/MOSParticle.cpp @@ -155,12 +155,6 @@ namespace RTE { case g_DrawMaterial: draw_character_ex(targetBitmap, m_aSprite[m_Frame], spritePos.GetFloorIntX(), spritePos.GetFloorIntY(), m_SettleMaterialDisabled ? GetMaterial()->GetIndex() : GetMaterial()->GetSettleMaterial(), -1); break; - case g_DrawAir: - draw_character_ex(targetBitmap, m_aSprite[m_Frame], spritePos.GetFloorIntX(), spritePos.GetFloorIntY(), g_MaterialAir, -1); - break; - case g_DrawMask: - draw_character_ex(targetBitmap, m_aSprite[m_Frame], spritePos.GetFloorIntX(), spritePos.GetFloorIntY(), g_MaskColor, -1); - break; case g_DrawWhite: draw_character_ex(targetBitmap, m_aSprite[m_Frame], spritePos.GetFloorIntX(), spritePos.GetFloorIntY(), g_WhiteColor, -1); break; diff --git a/Entities/MOSRotating.cpp b/Entities/MOSRotating.cpp index 7f06a1b81..f37ea4315 100644 --- a/Entities/MOSRotating.cpp +++ b/Entities/MOSRotating.cpp @@ -760,13 +760,6 @@ bool MOSRotating::OnBounce(HitData &hd) bool MOSRotating::OnSink(HitData &hd) { -/* - Vector oldPos(m_Pos); - m_Pos = pos; - Draw(g_SceneMan.GetTerrainColorBitmap(), Vector(), g_DrawMask); - Draw(g_SceneMan.GetTerrainMaterialBitmap(), Vector(), g_DrawAir); - m_Pos = oldPos; -*/ return false; } @@ -1316,14 +1309,7 @@ bool MOSRotating::DeepCheck(bool makeMOPs, int skipMOP, int maxMOPs) { m_ForceDeepCheck = false; m_DeepHardness = true; -/* - // Just make the outline of this disappear from the terrain - { -// TODO: These don't work at all because they're drawing shapes of color 0 to an intermediate field of 0! - Draw(g_SceneMan.GetTerrain()->GetFGColorBitmap(), Vector(), g_DrawMask, true); - Draw(g_SceneMan.GetTerrain()->GetMaterialBitmap(), Vector(), g_DrawAir, true); - } -*/ + // TODO: This stuff is just way too slow, EraseSilhouette is a hog // Make particles fly at least somewhat float velMag = MAX(10.0f, m_Vel.GetMagnitude()); @@ -1785,10 +1771,6 @@ void MOSRotating::Draw(BITMAP *pTargetBitmap, // Draw the requested material silhouette on the material bitmap if (mode == g_DrawMaterial) draw_character_ex(pTempBitmap, m_aSprite[m_Frame], 0, 0, m_SettleMaterialDisabled ? GetMaterial()->GetIndex() : GetMaterial()->GetSettleMaterial(), -1); - else if (mode == g_DrawAir) - draw_character_ex(pTempBitmap, m_aSprite[m_Frame], 0, 0, g_MaterialAir, -1); - else if (mode == g_DrawMask) - draw_character_ex(pTempBitmap, m_aSprite[m_Frame], 0, 0, keyColor, -1); else if (mode == g_DrawWhite) draw_character_ex(pTempBitmap, m_aSprite[m_Frame], 0, 0, g_WhiteColor, -1); else if (mode == g_DrawMOID) @@ -1797,8 +1779,6 @@ void MOSRotating::Draw(BITMAP *pTargetBitmap, draw_character_ex(pTempBitmap, m_aSprite[m_Frame], 0, 0, g_NoMOID, -1); else if (mode == g_DrawDoor) draw_character_ex(pTempBitmap, m_aSprite[m_Frame], 0, 0, g_MaterialDoor, -1); - else if (mode == g_DrawRedTrans) - draw_trans_sprite(pTempBitmap, m_aSprite[m_Frame], 0, 0); else { // return; diff --git a/Entities/MOSprite.cpp b/Entities/MOSprite.cpp index a98e63624..c1e5fe510 100644 --- a/Entities/MOSprite.cpp +++ b/Entities/MOSprite.cpp @@ -575,10 +575,6 @@ void MOSprite::Draw(BITMAP *pTargetBitmap, if (mode == g_DrawMaterial) { RTEAbort("Ordered to draw an MOSprite in its material, which is not possible!"); } - else if (mode == g_DrawAir) - draw_character_ex(pTargetBitmap, m_aSprite[m_Frame], aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), g_MaterialAir, -1); - else if (mode == g_DrawMask) - draw_character_ex(pTargetBitmap, m_aSprite[m_Frame], aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), g_MaskColor, -1); else if (mode == g_DrawWhite) draw_character_ex(pTargetBitmap, m_aSprite[m_Frame], aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), g_WhiteColor, -1); else if (mode == g_DrawMOID) diff --git a/Entities/MOSprite.h b/Entities/MOSprite.h index 6ae8ff968..c5539605c 100644 --- a/Entities/MOSprite.h +++ b/Entities/MOSprite.h @@ -42,20 +42,6 @@ class MOSprite : public MovableObject { SerializableOverrideMethods; ClassInfoGetters; - enum SpriteAnimMode - { - NOANIM = 0, - ALWAYSLOOP, - ALWAYSRANDOM, - ALWAYSPINGPONG, - LOOPWHENACTIVE, - LOOPWHENOPENCLOSE, - PINGPONGOPENCLOSE, - OVERLIFETIME, - ONCOLLIDE, - SpriteAnimModeCount - }; - ////////////////////////////////////////////////////////////////////////////////////////// // Constructor: MOSprite diff --git a/Entities/Material.cpp b/Entities/Material.cpp index 4e73d9a5a..6c63b657a 100644 --- a/Entities/Material.cpp +++ b/Entities/Material.cpp @@ -14,18 +14,20 @@ namespace RTE { m_Integrity = 0.0F; m_Restitution = 0.0F; m_Friction = 0.0F; - m_Stickiness = 0.0F; + m_Stickiness = 0.0F; m_VolumeDensity = 0.0F; m_PixelDensity = 0.0F; m_GibImpulseLimitPerLiter = 0.0F; - m_GibWoundLimitPerLiter = 0.0F; + m_GibWoundLimitPerLiter = 0.0F; m_SettleMaterialIndex = 0; m_SpawnMaterialIndex = 0; m_IsScrap = false; m_Color.Reset(); m_UseOwnColor = false; - m_TextureFile.Reset(); - m_TerrainTexture = 0; + m_FGTextureFile.Reset(); + m_BGTextureFile.Reset(); + m_TerrainFGTexture = nullptr; + m_TerrainBGTexture = nullptr; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -49,8 +51,10 @@ namespace RTE { m_IsScrap = reference.m_IsScrap; m_Color = reference.m_Color; m_UseOwnColor = reference.m_UseOwnColor; - m_TextureFile = reference.m_TextureFile; - m_TerrainTexture = reference.m_TerrainTexture; + m_FGTextureFile = reference.m_FGTextureFile; + m_BGTextureFile = reference.m_BGTextureFile; + m_TerrainFGTexture = reference.m_TerrainFGTexture; + m_TerrainBGTexture = reference.m_TerrainBGTexture; return 0; } @@ -60,7 +64,7 @@ namespace RTE { int Material::ReadProperty(const std::string_view &propName, Reader &reader) { if (propName == "Index") { // TODO: Check for index collisions here - reader >> m_Index; + reader >> m_Index; } else if (propName == "Priority") { reader >> m_Priority; } else if (propName == "Piling") { @@ -95,9 +99,12 @@ namespace RTE { reader >> m_Color; } else if (propName == "UseOwnColor") { reader >> m_UseOwnColor; - } else if (propName == "TextureFile") { - reader >> m_TextureFile; - m_TerrainTexture = m_TextureFile.GetAsBitmap(); + } else if (propName == "FGTextureFile") { + reader >> m_FGTextureFile; + m_TerrainFGTexture = m_FGTextureFile.GetAsBitmap(); + } else if (propName == "BGTextureFile") { + reader >> m_BGTextureFile; + m_TerrainBGTexture = m_BGTextureFile.GetAsBitmap(); } else { return Entity::ReadProperty(propName, reader); } @@ -110,37 +117,23 @@ namespace RTE { Entity::Save(writer); // Materials should never be altered, so no point in saving additional properties when it's a copy if (m_IsOriginalPreset) { - writer.NewProperty("Priority"); - writer << m_Priority; - writer.NewProperty("Piling"); - writer << m_Piling; - writer.NewProperty("StructuralIntegrity"); - writer << m_Integrity; - writer.NewProperty("Restitution"); - writer << m_Restitution; - writer.NewProperty("Friction"); - writer << m_Friction; - writer.NewProperty("Stickiness"); - writer << m_Stickiness; - writer.NewProperty("DensityKGPerVolumeL"); - writer << m_VolumeDensity; - writer.NewProperty("GibImpulseLimitPerVolumeL"); - writer << m_GibImpulseLimitPerLiter; - writer.NewProperty("GibWoundLimitPerVolumeL"); - writer << m_GibWoundLimitPerLiter; - writer.NewProperty("SettleMaterial"); - writer << m_SettleMaterialIndex; - writer.NewProperty("SpawnMaterial"); - writer << m_SpawnMaterialIndex; - writer.NewProperty("IsScrap"); - writer << m_IsScrap; - writer.NewProperty("Color"); - writer << m_Color; - writer.NewProperty("UseOwnColor"); - writer << m_UseOwnColor; - writer.NewProperty("TextureFile"); - writer << m_TextureFile; + writer.NewPropertyWithValue("Priority", m_Priority); + writer.NewPropertyWithValue("Piling", m_Piling); + writer.NewPropertyWithValue("StructuralIntegrity", m_Integrity); + writer.NewPropertyWithValue("Restitution", m_Restitution); + writer.NewPropertyWithValue("Friction", m_Friction); + writer.NewPropertyWithValue("Stickiness", m_Stickiness); + writer.NewPropertyWithValue("DensityKGPerVolumeL", m_VolumeDensity); + writer.NewPropertyWithValue("GibImpulseLimitPerVolumeL", m_GibImpulseLimitPerLiter); + writer.NewPropertyWithValue("GibWoundLimitPerVolumeL", m_GibWoundLimitPerLiter); + writer.NewPropertyWithValue("SettleMaterial", m_SettleMaterialIndex); + writer.NewPropertyWithValue("SpawnMaterial", m_SpawnMaterialIndex); + writer.NewPropertyWithValue("IsScrap", m_IsScrap); + writer.NewPropertyWithValue("Color", m_Color); + writer.NewPropertyWithValue("UseOwnColor", m_UseOwnColor); + writer.NewPropertyWithValue("FGTextureFile", m_FGTextureFile); + writer.NewPropertyWithValue("BGTextureFile", m_BGTextureFile); } return 0; } -} \ No newline at end of file +} diff --git a/Entities/Material.h b/Entities/Material.h index b460f2dd9..20e566f27 100644 --- a/Entities/Material.h +++ b/Entities/Material.h @@ -47,10 +47,16 @@ namespace RTE { #pragma region Getters and Setters /// - /// Gets the texture bitmap of this Material, if any is associated with it. + /// Gets the foreground texture bitmap of this Material, if any is associated with it. /// - /// Pointer to the texture bitmap of this Material. - BITMAP * GetTexture() const { return m_TerrainTexture; } + /// Pointer to the foreground texture bitmap of this Material. + BITMAP * GetFGTexture() const { return m_TerrainFGTexture; } + + /// + /// Gets the background texture bitmap of this Material, if any is associated with it. + /// + /// Pointer to the background texture bitmap of this Material. + BITMAP * GetBGTexture() const { return m_TerrainBGTexture; } /// /// Gets the index of this Material in the material palette. @@ -179,8 +185,10 @@ namespace RTE { Color m_Color; //!< The natural color of this material. bool m_UseOwnColor; //!< Whether or not to use the own color when a pixel of this material is knocked loose from the terrain. If 0, then the terrain pixel's color will be applied instead. - ContentFile m_TextureFile; //!< The file pointing to the terrain texture of this Material - BITMAP *m_TerrainTexture; //!< The texture of this material, used when building an SLTerrain. + ContentFile m_FGTextureFile; //!< The file pointing to the terrain foreground texture of this Material. + ContentFile m_BGTextureFile; //!< The file pointing to the terrain background texture of this Material. + BITMAP *m_TerrainFGTexture; //!< The foreground texture of this Material, used when building an SLTerrain. Not owned. + BITMAP *m_TerrainBGTexture; //!< The background texture of this Material, used when building an SLTerrain. Not owned. private: diff --git a/Entities/MovableObject.cpp b/Entities/MovableObject.cpp index 6343cc2ad..72eb584da 100644 --- a/Entities/MovableObject.cpp +++ b/Entities/MovableObject.cpp @@ -19,6 +19,7 @@ #include "LuaMan.h" #include "Atom.h" #include "Actor.h" +#include "SLTerrain.h" namespace RTE { @@ -1087,4 +1088,68 @@ void MovableObject::RegMOID(vector &MOIDIndex, MOID rootMOID, b m_RootMOID = rootMOID == g_NoMOID ? m_MOID : rootMOID; } -} // namespace RTE +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool MovableObject::DrawToTerrain(SLTerrain *terrain) { + if (!terrain) { + return false; + } + if (dynamic_cast(this)) { + auto wrappedMaskedBlit = [](BITMAP *sourceBitmap, BITMAP *destinationBitmap, const Vector &bitmapPos, bool swapSourceWithDestination) { + std::array bitmaps = { sourceBitmap, destinationBitmap }; + std::array srcPos = { + Vector(bitmapPos.GetX(), bitmapPos.GetY()), + Vector(bitmapPos.GetX() + static_cast(g_SceneMan.GetSceneWidth()), bitmapPos.GetY()), + Vector(bitmapPos.GetX() - static_cast(g_SceneMan.GetSceneWidth()), bitmapPos.GetY()), + Vector(bitmapPos.GetX(), bitmapPos.GetY() + static_cast(g_SceneMan.GetSceneHeight())), + Vector(bitmapPos.GetX(), bitmapPos.GetY() - static_cast(g_SceneMan.GetSceneHeight())) + }; + std::array destPos; + destPos.fill(Vector()); + + if (swapSourceWithDestination) { + std::swap(bitmaps.at(0), bitmaps.at(1)); + std::swap(srcPos, destPos); + } + masked_blit(bitmaps.at(0), bitmaps.at(1), srcPos.at(0).GetFloorIntX(), srcPos.at(0).GetFloorIntY(), destPos.at(0).GetFloorIntX(), destPos.at(0).GetFloorIntY(), destinationBitmap->w, destinationBitmap->h); + if (g_SceneMan.SceneWrapsX()) { + if (bitmapPos.GetFloorIntX() < 0) { + masked_blit(bitmaps.at(0), bitmaps.at(1), srcPos.at(1).GetFloorIntX(), srcPos.at(1).GetFloorIntY(), destPos.at(1).GetFloorIntX(), destPos.at(1).GetFloorIntY(), destinationBitmap->w, destinationBitmap->h); + } else if (bitmapPos.GetFloorIntX() + destinationBitmap->w > g_SceneMan.GetSceneWidth()) { + masked_blit(bitmaps.at(0), bitmaps.at(1), srcPos.at(2).GetFloorIntX(), srcPos.at(2).GetFloorIntY(), destPos.at(2).GetFloorIntX(), destPos.at(2).GetFloorIntY(), destinationBitmap->w, destinationBitmap->h); + } + } + if (g_SceneMan.SceneWrapsY()) { + if (bitmapPos.GetFloorIntY() < 0) { + masked_blit(bitmaps.at(0), bitmaps.at(1), srcPos.at(3).GetFloorIntX(), srcPos.at(3).GetFloorIntY(), destPos.at(3).GetFloorIntX(), destPos.at(3).GetFloorIntY(), destinationBitmap->w, destinationBitmap->h); + } else if (bitmapPos.GetFloorIntY() + destinationBitmap->h > g_SceneMan.GetSceneHeight()) { + masked_blit(bitmaps.at(0), bitmaps.at(1), srcPos.at(4).GetFloorIntX(), srcPos.at(4).GetFloorIntY(), destPos.at(4).GetFloorIntX(), destPos.at(4).GetFloorIntY(), destinationBitmap->w, destinationBitmap->h); + } + } + }; + BITMAP *tempBitmap = g_SceneMan.GetIntermediateBitmapForSettlingIntoTerrain(static_cast(GetDiameter())); + Vector tempBitmapPos = m_Pos.GetFloored() - Vector(static_cast(tempBitmap->w / 2), static_cast(tempBitmap->w / 2)); + + clear_bitmap(tempBitmap); + // Draw the object to the temp bitmap, then draw the foreground layer on top of it, then draw it to the foreground layer. + Draw(tempBitmap, tempBitmapPos, DrawMode::g_DrawColor, true); + wrappedMaskedBlit(terrain->GetFGColorBitmap(), tempBitmap, tempBitmapPos, false); + wrappedMaskedBlit(terrain->GetFGColorBitmap(), tempBitmap, tempBitmapPos, true); + + clear_bitmap(tempBitmap); + // Draw the object to the temp bitmap, then draw the material layer on top of it, then draw it to the material layer. + Draw(tempBitmap, tempBitmapPos, DrawMode::g_DrawMaterial, true); + wrappedMaskedBlit(terrain->GetMaterialBitmap(), tempBitmap, tempBitmapPos, false); + wrappedMaskedBlit(terrain->GetMaterialBitmap(), tempBitmap, tempBitmapPos, true); + + terrain->AddUpdatedMaterialArea(Box(tempBitmapPos, static_cast(tempBitmap->w), static_cast(tempBitmap->h))); + g_SceneMan.RegisterTerrainChange(tempBitmapPos.GetFloorIntX(), tempBitmapPos.GetFloorIntY(), tempBitmap->w, tempBitmap->h, ColorKeys::g_MaskColor, false); + } else { + Draw(terrain->GetFGColorBitmap(), Vector(), DrawMode::g_DrawColor, true); + Draw(terrain->GetMaterialBitmap(), Vector(), DrawMode::g_DrawMaterial, true); + g_SceneMan.RegisterTerrainChange(m_Pos.GetFloorIntX(), m_Pos.GetFloorIntY(), 1, 1, DrawMode::g_DrawColor, false); + } + return true; +} + +} // namespace RTE \ No newline at end of file diff --git a/Entities/MovableObject.h b/Entities/MovableObject.h index 6ea8bc122..0240754e8 100644 --- a/Entities/MovableObject.h +++ b/Entities/MovableObject.h @@ -43,6 +43,7 @@ namespace RTE struct HitData; class MOSRotating; +class SLTerrain; ////////////////////////////////////////////////////////////////////////////////////////// // Abstract class: MovableObject @@ -1810,6 +1811,13 @@ enum MOType /// Whether this MO's RootParent can GetHitByMOs and is currently traveling. bool GetTraveling() const { return GetRootParent()->m_IsTraveling; } + /// + /// Draws this MovableObject's graphical and material representations to the specified SLTerrain's respective layers. + /// + /// The SLTerrain to draw this MovableObject to. Ownership is NOT transferred! + /// Whether the object was successfully drawn to the terrain. + bool DrawToTerrain(SLTerrain *terrain); + ////////////////////////////////////////////////////////////////////////////////////////// // Protected member variable and method declarations diff --git a/Entities/SLBackground.cpp b/Entities/SLBackground.cpp new file mode 100644 index 000000000..8d3d2cd66 --- /dev/null +++ b/Entities/SLBackground.cpp @@ -0,0 +1,243 @@ +#include "SLBackground.h" +#include "FrameMan.h" +#include "SceneMan.h" +#include "SettingsMan.h" + +namespace RTE { + + ConcreteClassInfo(SLBackground, SceneLayer, 0); + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SLBackground::Clear() { + m_Bitmaps.clear(); + m_FrameCount = 1; + m_Frame = 0; + m_SpriteAnimMode = SpriteAnimMode::NOANIM; + m_SpriteAnimDuration = 1000; + m_SpriteAnimIsReversingFrames = false; + m_SpriteAnimTimer.Reset(); + m_IsAnimatedManually = false; + m_CanAutoScrollX = false; + m_CanAutoScrollY = false; + m_AutoScrollStep.Reset(); + m_AutoScrollStepInterval = 0; + m_AutoScrollStepTimer.Reset(); + m_AutoScrollOffset.Reset(); + m_FillColorLeft = ColorKeys::g_MaskColor; + m_FillColorRight = ColorKeys::g_MaskColor; + m_FillColorUp = ColorKeys::g_MaskColor; + m_FillColorDown = ColorKeys::g_MaskColor; + + m_IgnoreAutoScale = false; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SLBackground::Create() { + SceneLayer::Create(); + + m_Bitmaps.clear(); + m_BitmapFile.GetAsAnimation(m_Bitmaps, m_FrameCount); + m_MainBitmap = m_Bitmaps.at(0); + + if (m_FrameCount == 1) { + m_SpriteAnimMode = SpriteAnimMode::NOANIM; + } else if (m_FrameCount == 2 && m_SpriteAnimMode != SpriteAnimMode::NOANIM) { + m_SpriteAnimMode = SpriteAnimMode::ALWAYSLOOP; + } + + if (!m_WrapX) { + m_FillColorLeft = _getpixel(m_MainBitmap, 0, m_MainBitmap->h / 2); + m_FillColorRight = _getpixel(m_MainBitmap, m_MainBitmap->w - 1, m_MainBitmap->h / 2); + } + if (!m_WrapY) { + m_FillColorUp = _getpixel(m_MainBitmap, m_MainBitmap->w / 2, 0); + m_FillColorDown = _getpixel(m_MainBitmap, m_MainBitmap->w / 2, m_MainBitmap->h - 1); + } + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SLBackground::Create(const SLBackground &reference) { + SceneLayer::Create(reference); + + // The main bitmap is created and owned by SceneLayer because it can be modified. We need to destroy it to avoid a leak because the bitmaps we'll be using here are owned by ContentFile static maps and are unmodifiable. + destroy_bitmap(m_MainBitmap); + m_MainBitmapOwned = false; + + m_Bitmaps.clear(); + m_Bitmaps = reference.m_Bitmaps; + m_MainBitmap = m_Bitmaps.at(0); + + m_FillColorLeft = reference.m_FillColorLeft; + m_FillColorRight = reference.m_FillColorRight; + m_FillColorUp = reference.m_FillColorUp; + m_FillColorDown = reference.m_FillColorDown; + + m_FrameCount = reference.m_FrameCount; + m_SpriteAnimMode = reference.m_SpriteAnimMode; + m_SpriteAnimDuration = reference.m_SpriteAnimDuration; + + m_CanAutoScrollX = reference.m_CanAutoScrollX; + m_CanAutoScrollY = reference.m_CanAutoScrollY; + m_AutoScrollStep = reference.m_AutoScrollStep; + m_AutoScrollStepInterval = reference.m_AutoScrollStepInterval; + + m_IgnoreAutoScale = reference.m_IgnoreAutoScale; + + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SLBackground::ReadProperty(const std::string_view &propName, Reader &reader) { + if (propName == "FrameCount") { + reader >> m_FrameCount; + } else if (propName == "SpriteAnimMode") { + m_SpriteAnimMode = static_cast(std::stoi(reader.ReadPropValue())); + if (m_SpriteAnimMode < SpriteAnimMode::NOANIM || m_SpriteAnimMode > SpriteAnimMode::ALWAYSPINGPONG) { reader.ReportError("Invalid SLBackground sprite animation mode!"); } + } else if (propName == "SpriteAnimDuration") { + reader >> m_SpriteAnimDuration; + } else if (propName == "IsAnimatedManually") { + reader >> m_IsAnimatedManually; + } else if (propName == "DrawTransparent") { + reader >> m_DrawMasked; + } else if (propName == "ScrollRatio") { + // Actually read the ScrollInfo, not the ratio. The ratios will be initialized later. + reader >> m_ScrollInfo; + } else if (propName == "ScaleFactor") { + reader >> m_ScaleFactor; + SetScaleFactor(m_ScaleFactor); + } else if (propName == "IgnoreAutoScaling") { + reader >> m_IgnoreAutoScale; + } else if (propName == "OriginPointOffset") { + reader >> m_OriginOffset; + } else if (propName == "CanAutoScrollX") { + reader >> m_CanAutoScrollX; + } else if (propName == "CanAutoScrollY") { + reader >> m_CanAutoScrollY; + } else if (propName == "AutoScrollStepInterval") { + reader >> m_AutoScrollStepInterval; + } else if (propName == "AutoScrollStep") { + reader >> m_AutoScrollStep; + } else { + return SceneLayer::ReadProperty(propName, reader); + } + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SLBackground::Save(Writer &writer) const { + SceneLayer::Save(writer); + + writer.NewPropertyWithValue("FrameCount", m_FrameCount); + writer.NewPropertyWithValue("SpriteAnimMode", m_SpriteAnimMode); + writer.NewPropertyWithValue("SpriteAnimDuration", m_SpriteAnimDuration); + writer.NewPropertyWithValue("IsAnimatedManually", m_IsAnimatedManually); + writer.NewPropertyWithValue("DrawTransparent", m_DrawMasked); + writer.NewPropertyWithValue("ScrollRatio", m_ScrollInfo); + writer.NewPropertyWithValue("ScaleFactor", m_ScaleFactor); + writer.NewPropertyWithValue("IgnoreAutoScaling", m_IgnoreAutoScale); + writer.NewPropertyWithValue("OriginPointOffset", m_OriginOffset); + writer.NewPropertyWithValue("CanAutoScrollX", m_CanAutoScrollX); + writer.NewPropertyWithValue("CanAutoScrollY", m_CanAutoScrollY); + writer.NewPropertyWithValue("AutoScrollStepInterval", m_AutoScrollStepInterval); + writer.NewPropertyWithValue("AutoScrollStep", m_AutoScrollStep); + + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SLBackground::InitScaleFactors() { + if (!m_IgnoreAutoScale) { + float fitScreenScaleFactor = std::clamp(static_cast(std::min(g_SceneMan.GetSceneHeight(), g_FrameMan.GetPlayerScreenHeight())) / static_cast(m_MainBitmap->h), 1.0F, 2.0F); + + switch (g_SettingsMan.GetSceneBackgroundAutoScaleMode()) { + case LayerAutoScaleMode::FitScreen: + SetScaleFactor(Vector(fitScreenScaleFactor, fitScreenScaleFactor)); + break; + case LayerAutoScaleMode::AlwaysUpscaled: + SetScaleFactor(Vector(2.0F, 2.0F)); + break; + default: + SetScaleFactor(m_ScaleFactor); + break; + } + m_ScrollInfo *= m_ScaleFactor; + InitScrollRatios(); + } + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SLBackground::Update() { + if (!m_IsAnimatedManually && m_SpriteAnimMode != SpriteAnimMode::NOANIM) { + int prevFrame = m_Frame; + + if (m_SpriteAnimTimer.GetElapsedSimTimeMS() > (m_SpriteAnimDuration / m_FrameCount)) { + switch (m_SpriteAnimMode) { + case SpriteAnimMode::ALWAYSLOOP: + m_Frame = (m_Frame + 1) % m_FrameCount; + break; + case SpriteAnimMode::ALWAYSRANDOM: + while (m_Frame == prevFrame) { + m_Frame = RandomNum(0, m_FrameCount - 1); + } + break; + case SpriteAnimMode::ALWAYSPINGPONG: + if (m_Frame == m_FrameCount - 1) { + m_SpriteAnimIsReversingFrames = true; + } else if (m_Frame == 0) { + m_SpriteAnimIsReversingFrames = false; + } + m_SpriteAnimIsReversingFrames ? m_Frame-- : m_Frame++; + break; + default: + break; + } + m_SpriteAnimTimer.Reset(); + } + } + m_MainBitmap = m_Bitmaps.at(m_Frame); + + if (IsAutoScrolling()) { + if (m_AutoScrollStepTimer.GetElapsedSimTimeMS() > m_AutoScrollStepInterval) { + if (m_WrapX && m_CanAutoScrollX) { m_AutoScrollOffset.SetX(m_AutoScrollOffset.GetX() + m_AutoScrollStep.GetX()); } + if (m_WrapY && m_CanAutoScrollY) { m_AutoScrollOffset.SetY(m_AutoScrollOffset.GetY() + m_AutoScrollStep.GetY()); } + WrapPosition(m_AutoScrollOffset); + m_AutoScrollStepTimer.Reset(); + } + m_Offset.SetXY(std::floor((m_Offset.GetX() * m_ScrollRatio.GetX()) + m_AutoScrollOffset.GetX()), std::floor((m_Offset.GetY() * m_ScrollRatio.GetY()) + m_AutoScrollOffset.GetY())); + } + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SLBackground::Draw(BITMAP *targetBitmap, Box &targetBox, bool offsetNeedsScrollRatioAdjustment) { + SceneLayer::Draw(targetBitmap, targetBox, !IsAutoScrolling()); + + int bitmapWidth = m_ScaledDimensions.GetFloorIntX(); + int bitmapHeight = m_ScaledDimensions.GetFloorIntY(); + int targetBoxCornerX = targetBox.GetCorner().GetFloorIntX(); + int targetBoxCornerY = targetBox.GetCorner().GetFloorIntY(); + int targetBoxWidth = static_cast(targetBox.GetWidth()); + int targetBoxHeight = static_cast(targetBox.GetHeight()); + + set_clip_rect(targetBitmap, targetBoxCornerX, targetBoxCornerY, targetBoxCornerX + targetBoxWidth - 1, targetBoxCornerY + targetBoxHeight - 1); + + // Detect if non-wrapping layer dimensions can't cover the whole target area with its main bitmap. If so, fill in the gap with appropriate solid color sampled from the hanging edge. + if (!m_WrapX && bitmapWidth <= targetBoxWidth) { + if (m_FillColorLeft != ColorKeys::g_MaskColor && m_Offset.GetFloorIntX() != 0) { rectfill(targetBitmap, targetBoxCornerX, targetBoxCornerY, targetBoxCornerX - m_Offset.GetFloorIntX(), targetBoxCornerY + targetBoxHeight, m_FillColorLeft); } + if (m_FillColorRight != ColorKeys::g_MaskColor) { rectfill(targetBitmap, targetBoxCornerX + bitmapWidth - m_Offset.GetFloorIntX(), targetBoxCornerY, targetBoxCornerX + targetBoxWidth, targetBoxCornerY + targetBoxHeight, m_FillColorRight); } + } + if (!m_WrapY && bitmapHeight <= targetBoxHeight) { + if (m_FillColorUp != ColorKeys::g_MaskColor && m_Offset.GetFloorIntY() != 0) { rectfill(targetBitmap, targetBoxCornerX, targetBoxCornerY, targetBoxCornerX + targetBoxWidth, targetBoxCornerY - m_Offset.GetFloorIntY(), m_FillColorUp); } + if (m_FillColorDown != ColorKeys::g_MaskColor) { rectfill(targetBitmap, targetBoxCornerX, targetBoxCornerY + bitmapHeight - m_Offset.GetFloorIntY(), targetBoxCornerX + targetBoxWidth, targetBoxCornerY + targetBoxHeight, m_FillColorDown); } + } + set_clip_rect(targetBitmap, 0, 0, targetBitmap->w - 1, targetBitmap->h - 1); + } +} \ No newline at end of file diff --git a/Entities/SLBackground.h b/Entities/SLBackground.h new file mode 100644 index 000000000..ffc122589 --- /dev/null +++ b/Entities/SLBackground.h @@ -0,0 +1,249 @@ +#ifndef _RTESLBACKGROUND_ +#define _RTESLBACKGROUND_ + +#include "SceneLayer.h" +#include "Timer.h" + +namespace RTE { + + /// + /// A scrolling background layer of the Scene, placed behind the terrain. + /// + class SLBackground : public SceneLayer { + friend class NetworkServer; + + public: + + EntityAllocation(SLBackground); + SerializableOverrideMethods; + ClassInfoGetters; + +#pragma region Creation + /// + /// Constructor method used to instantiate a SLBackground object in system memory. Create() should be called before using the object. + /// + SLBackground() { Clear(); } + + /// + /// Makes the SLBackground object ready for use. + /// + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int Create() override; + + /// + /// Creates a SLBackground to be identical to another, by deep copy. + /// + /// A reference to the SLBackground to deep copy. + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int Create(const SLBackground &reference); +#pragma endregion + +#pragma region Destruction + /// + /// Destructor method used to clean up a SLBackground object before deletion from system memory. + /// + ~SLBackground() override { Destroy(true); } + + /// + /// Destroys and resets (through Clear()) the SLBackground object. + /// + /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + void Destroy(bool notInherited = false) override { if (!notInherited) { SceneLayer::Destroy(); } Clear(); } +#pragma endregion + +#pragma region Getters and Setters + /// + /// Gets whether this SLBackground's animation is handled manually/externally. + /// + /// Whether this SLBackground's animation is handled manually/externally. If true, animation will not be handled during Update(). + bool IsAnimatedManually() const { return m_IsAnimatedManually; } + + /// + /// Sets whether this SLBackground is animated manually/externally. + /// + /// Whether this SLBackground is animated manually/externally and should skip animation handling during Update(). + void SetAnimatedManually(bool isAnimatedManually) { m_IsAnimatedManually = isAnimatedManually; } + + /// + /// Gets the frame number of this SLBackground that is currently set to be drawn. + /// + /// The frame number that is currently set to be drawn. + int GetFrame() const { return m_Frame; } + + /// + /// Sets frame number that this SLBackground will draw. + /// + /// The frame number that is supposed to be drawn. + void SetFrame(int newFrame) { m_Frame = std::clamp(newFrame, 0, m_FrameCount - 1); } + + /// + /// Gets the animation mode of this SLBackground. + /// + /// The currently set animation mode. See SpriteAnimMode enumeration. + int GetSpriteAnimMode() const { return m_SpriteAnimMode; } + + /// + /// Sets the animation mode of this SLBackground. + /// + /// The new animation mode. See SpirteAnimMode enumeration. + void SetSpriteAnimMode(SpriteAnimMode newAnimMode = SpriteAnimMode::NOANIM) { m_SpriteAnimMode = std::clamp(newAnimMode, SpriteAnimMode::NOANIM, SpriteAnimMode::ALWAYSPINGPONG); } + + /// + /// Gets the time it takes to complete a full animation cycle of this SLBackground. + /// + /// The animation cycle duration, in milliseconds. + int GetSpriteAnimDuration() const { return m_SpriteAnimDuration; } + + /// + /// Sets the time it takes to complete a full animation cycle of this SLBackground. + /// + /// The new animation cycle duration, in milliseconds. + void SetSpriteAnimDuration(int newDuration) { m_SpriteAnimDuration = newDuration; } + + /// + /// Gets whether this SLBackground has auto-scrolling enabled and meets the requirements to actually auto-scroll. + /// + /// Whether this has auto-scrolling enabled and meets the requirements to actually auto-scroll. + bool IsAutoScrolling() const { return (m_WrapX && m_CanAutoScrollX) || (m_WrapY && m_CanAutoScrollY); } + + /// + /// Gets whether auto-scrolling is enabled on the X axis. + /// + /// Whether auto-scrolling is enabled on the X axis. This may be true even if auto-scrolling isn't actually happening due to not meeting requirements. + bool GetAutoScrollX() const { return m_CanAutoScrollX; } + + /// + /// Sets whether auto-scrolling is enabled on the X axis. + /// + /// Whether auto-scrolling is enabled on the X axis or not. If requirements aren't met, this will not auto-scroll even if set to true. + void SetAutoScrollX(bool autoScroll) { m_CanAutoScrollX = autoScroll; } + + /// + /// Gets whether auto-scrolling is enabled on the Y axis. + /// + /// Whether auto-scrolling is enabled on the Y axis. This may be true even if auto-scrolling isn't actually happening due to not meeting requirements. + bool GetAutoScrollY() const { return m_CanAutoScrollY; } + + /// + /// Sets whether auto-scrolling is enabled on the Y axis. + /// + /// Whether auto-scrolling is enabled on the Y axis or not. If requirements aren't met, this will not auto-scroll even if set to true. + void SetAutoScrollY(bool autoScroll) { m_CanAutoScrollY = autoScroll; } + + /// + /// Gets the duration between auto-scroll steps. + /// + /// The duration between auto-scroll steps, in milliseconds. + int GetAutoScrollStepInterval() const { return m_AutoScrollStepInterval; } + + /// + /// Sets the duration between auto-scroll steps. + /// + /// The new duration between auto-scroll steps, in milliseconds. + void SetAutoScrollStepInterval(int newStepInterval) { m_AutoScrollStepInterval = newStepInterval; } + + /// + /// Gets the auto-scroll step (pixels to advance per interval) values. + /// + /// A Vector with the auto-scroll step values. + Vector GetAutoScrollStep() const { return m_AutoScrollStep; } + + /// + /// Sets the auto-scroll step (pixels to advance per interval) values. + /// + /// A Vector with the new auto-scroll step values. + void SetAutoScrollStep(const Vector &newStep) { m_AutoScrollStep = newStep; } + + /// + /// Gets the auto-scroll step (pixels to advance per interval) value on the X axis. + /// + /// The auto-scroll step value on the X axis. + float GetAutoScrollStepX() const { return m_AutoScrollStep.GetX(); } + + /// + /// Sets the auto-scroll step (pixels to advance per interval) value on the X axis. + /// + /// The new auto-scroll step value on the X axis. + void SetAutoScrollStepX(float newStepX) { m_AutoScrollStep.SetX(newStepX); } + + /// + /// Gets the auto-scroll step (pixels to advance per interval) value on the Y axis. + /// + /// The auto-scroll step value on the Y axis. + float GetAutoScrollStepY() const { return m_AutoScrollStep.GetY(); } + + /// + /// Sets the auto-scroll step (pixels to advance per interval) value on the Y axis. + /// + /// The new auto-scroll step value on the Y axis. + void SetAutoScrollStepY(float newStepY) { m_AutoScrollStep.SetY(newStepY); } +#pragma endregion + +#pragma region Concrete Methods + /// + /// Initializes the scale factors for all auto-scaling modes for this SLBackground, then sets the appropriate factor according to the auto-scaling setting. + /// Has to be done during Scene loading to correctly adjust the factors in cases the Scene does not vertically cover the player's whole screen. + /// + void InitScaleFactors(); +#pragma endregion + +#pragma region Virtual Override Methods + /// + /// Updates the state of this SLBackground. + /// + void Update() override; + + /// + /// Draws this SLBackground's current scrolled position to a bitmap. + /// + /// The bitmap to draw to. + /// The box on the target bitmap to limit drawing to, with the corner of box being where the scroll position lines up. + /// Whether the offset of this SceneLayer or the passed in offset override need to be adjusted to scroll ratio. + void Draw(BITMAP *targetBitmap, Box &targetBox, bool offsetNeedsScrollRatioAdjustment = false) override; +#pragma endregion + + private: + + /// + /// Enumeration for the different modes of SLBackground auto-scaling. + /// + enum LayerAutoScaleMode { AutoScaleOff, FitScreen, AlwaysUpscaled, LayerAutoScaleModeCount }; + + static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. + + std::vector m_Bitmaps; //!< Vector containing all the BITMAPs of this SLBackground. Not owned. + int m_FrameCount; //!< The total number of frames in this SLBackground's animation. + int m_Frame; //!< The frame that is currently being shown/drawn. + + SpriteAnimMode m_SpriteAnimMode; //!< The mode in which this SLBackground is animating. See SpriteAnimMode enumeration. + int m_SpriteAnimDuration; //!< The duration it takes to complete a full animation cycle, in milliseconds. + bool m_SpriteAnimIsReversingFrames; //!< Keeps track of animation direction (mainly for ALWAYSPINGPONG). True is decreasing frame, false is increasing frame. + Timer m_SpriteAnimTimer; //!< Timer to keep track of animation. + + bool m_IsAnimatedManually; //!< Whether this SLBackground's animation is handled manually/externally and should not be handled during Update(). + + bool m_CanAutoScrollX; //!< Whether auto-scrolling is enabled on the X axis, but not whether auto-scrolling actually happens on this axis. + bool m_CanAutoScrollY; //!< Whether auto-scrolling is enabled on the Y axis, but not whether auto-scrolling actually happens on this axis. + Vector m_AutoScrollStep; //!< Vector with the number of pixels on each axis to advance per interval when auto-scrolling. Can be fractions of a pixel, but will not be visible until AutoScrollOffset adds up to a full pixel step. + int m_AutoScrollStepInterval; //!< The duration between auto-scroll steps, in milliseconds. + Timer m_AutoScrollStepTimer; //!< Timer to keep track of auto-scrolling steps. + Vector m_AutoScrollOffset; //!< The offset to adjust the main offset with when auto-scrolling to actually get the auto-scrolling effect, adjusted for wrapping. + + int m_FillColorLeft; //!< Palette index to use for filling the gap between the left edge of the bitmap and the left edge of the screen/scene box in cases where the bitmap doesn't cover the whole target area. + int m_FillColorRight; //!< Palette index to use for filling the gap between the right edge of the bitmap and the right edge of the screen/scene box in cases where the bitmap doesn't cover the whole target area. + int m_FillColorUp; //!< Palette index to use for filling the gap between the upper edge of the bitmap and the upper edge of the screen/scene box in cases where the bitmap doesn't cover the whole target area. + int m_FillColorDown; //!< Palette index to use for filling the gap between the lower edge of the bitmap and the lower edge of the screen/scene box in cases where the bitmap doesn't cover the whole target area. + + bool m_IgnoreAutoScale; //!< Whether auto-scaling settings are ignored and the read-in scale factor is used instead. + + /// + /// Clears all the member variables of this SLBackground, effectively resetting the members of this abstraction level only. + /// + void Clear(); + + // Disallow the use of some implicit methods. + SLBackground(const SLBackground &reference) = delete; + SLBackground & operator=(const SLBackground &rhs) = delete; + }; +} +#endif \ No newline at end of file diff --git a/Entities/SLTerrain.cpp b/Entities/SLTerrain.cpp index aaa482771..dae232fbd 100644 --- a/Entities/SLTerrain.cpp +++ b/Entities/SLTerrain.cpp @@ -1,1436 +1,468 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: SLTerrain.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the SLTerrain class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "SLTerrain.h" +#include "TerrainFrosting.h" #include "TerrainDebris.h" #include "TerrainObject.h" -#include "PresetMan.h" -#include "DataModule.h" #include "SceneObject.h" -#include "MOPixel.h" #include "MOSprite.h" +#include "MOPixel.h" #include "Atom.h" +#include "DataModule.h" +#include "PresetMan.h" namespace RTE { -ConcreteClassInfo(SLTerrain, SceneLayer, 0); - -const string SLTerrain::TerrainFrosting::c_ClassName = "TerrainFrosting"; -BITMAP * SLTerrain::m_spTempBitmap16 = 0; -BITMAP * SLTerrain::m_spTempBitmap32 = 0; -BITMAP * SLTerrain::m_spTempBitmap64 = 0; -BITMAP * SLTerrain::m_spTempBitmap128 = 0; -BITMAP * SLTerrain::m_spTempBitmap256 = 0; -BITMAP * SLTerrain::m_spTempBitmap512 = 0; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this TerrainFrosting, effectively -// resetting the members of this abstraction level only. - -void SLTerrain::TerrainFrosting::Clear() -{ - m_TargetMaterial.Reset(); - m_FrostingMaterial.Reset(); - m_MinThickness = 5; - m_MaxThickness = 5; - m_InAirOnly = true; -} - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the TerrainFrosting object ready for use. - -int SLTerrain::TerrainFrosting::Create() -{ - if (Serializable::Create() < 0) - return -1; - - return 0; -} -*/ - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a TerrainFrosting to be identical to another, by deep copy. - -int SLTerrain::TerrainFrosting::Create(const TerrainFrosting &reference) -{ - m_TargetMaterial = reference.m_TargetMaterial; - m_FrostingMaterial = reference.m_FrostingMaterial; - m_MinThickness = reference.m_MinThickness; - m_MaxThickness = reference.m_MaxThickness; - m_InAirOnly = reference.m_InAirOnly; - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int SLTerrain::TerrainFrosting::ReadProperty(const std::string_view &propName, Reader &reader) -{ - if (propName == "TargetMaterial") - reader >> m_TargetMaterial; - else if (propName == "FrostingMaterial") - reader >> m_FrostingMaterial; - else if (propName == "MinThickness") - reader >> m_MinThickness; - else if (propName == "MaxThickness") - reader >> m_MaxThickness; - else if (propName == "InAirOnly") - reader >> m_InAirOnly; - else - return Serializable::ReadProperty(propName, reader); - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this TerrainFrosting with a Writer for -// later recreation with Create(Reader &reader); - -int SLTerrain::TerrainFrosting::Save(Writer &writer) const -{ - Serializable::Save(writer); - - writer.NewProperty("TargetMaterial"); - writer << m_TargetMaterial; - writer.NewProperty("FrostingMaterial"); - writer << m_FrostingMaterial; - writer.NewProperty("MinThickness"); - writer << m_MinThickness; - writer.NewProperty("MaxThickness"); - writer << m_MaxThickness; - writer.NewProperty("InAirOnly"); - writer << m_InAirOnly; - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this SLTerrain, effectively -// resetting the members of this abstraction level only. - -void SLTerrain::Clear() -{ - m_pFGColor = 0; - m_pBGColor = 0; - m_pStructural = 0; - m_BGTextureFile.Reset(); - m_TerrainFrostings.clear(); - m_TerrainDebris.clear(); - m_TerrainObjects.clear(); - m_UpdatedMateralAreas.clear(); - m_DrawMaterial = false; - m_NeedToClearFrostings = false; - m_NeedToClearDebris = false; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the SLTerrain object ready for use. - -int SLTerrain::Create() -{ - if (SceneLayer::Create() < 0) - return -1; - - if (!m_pFGColor) - m_pFGColor = new SceneLayer(); - if (!m_pBGColor) - m_pBGColor = new SceneLayer(); - - // Can't create these earlier in the static declaration because allegro_init needs to be called before create_bitmap - if (!m_spTempBitmap16) - m_spTempBitmap16 = create_bitmap_ex(8, 16, 16); - if (!m_spTempBitmap32) - m_spTempBitmap32 = create_bitmap_ex(8, 32, 32); - if (!m_spTempBitmap64) - m_spTempBitmap64 = create_bitmap_ex(8, 64, 64); - if (!m_spTempBitmap128) - m_spTempBitmap128 = create_bitmap_ex(8, 128, 128); - if (!m_spTempBitmap256) - m_spTempBitmap256 = create_bitmap_ex(8, 256, 256); - if (!m_spTempBitmap512) - m_spTempBitmap512 = create_bitmap_ex(8, 512, 512); - - return 0; -} - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the SLTerrain object ready for use. - -int SLTerrain::Create(char *filename, - bool drawTrans, - Vector offset, - bool wrapX, - bool wrapY, - Vector scrollInfo) -{ - //SceneLayer::Create(); - - return 0; -} -*/ - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a SLTerrain to be identical to another, by deep copy. - -int SLTerrain::Create(const SLTerrain &reference) -{ - SceneLayer::Create(reference); - - // Leave these because they are loaded late by LoadData - m_pFGColor = dynamic_cast(reference.m_pFGColor->Clone()); - m_pBGColor = dynamic_cast(reference.m_pBGColor->Clone()); -// m_pStructural; - m_BGTextureFile = reference.m_BGTextureFile; - - //////////////////////////// - // Frostings - m_TerrainFrostings.clear(); - for (list::const_iterator tfItr = reference.m_TerrainFrostings.begin(); tfItr != reference.m_TerrainFrostings.end(); ++tfItr) - m_TerrainFrostings.push_back(TerrainFrosting((*tfItr))); - - ////////////////////// - // Debris - // First have to clear out what we've got - for (list::iterator tdItr = m_TerrainDebris.begin(); tdItr != m_TerrainDebris.end(); ++tdItr) - delete (*tdItr); - m_TerrainDebris.clear(); - // Then add the copies - for (list::const_iterator tdItr = reference.m_TerrainDebris.begin(); tdItr != reference.m_TerrainDebris.end(); ++tdItr) - m_TerrainDebris.push_back(dynamic_cast((*tdItr)->Clone())); - - ///////////////// - // Objects - // First clear out what we've got - for (list::iterator toItr = m_TerrainObjects.begin(); toItr != m_TerrainObjects.end(); ++toItr) - delete (*toItr); - m_TerrainObjects.clear(); - // Then add the copies - for (list::const_iterator toItr = reference.m_TerrainObjects.begin(); toItr != reference.m_TerrainObjects.end(); ++toItr) - m_TerrainObjects.push_back(dynamic_cast((*toItr)->Clone())); - - m_DrawMaterial = reference.m_DrawMaterial; - - m_NeedToClearFrostings = true; - m_NeedToClearDebris = true; - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: LoadData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Actually loads previously specified/created data into memory. Has -// to be done before using this SceneLayer. - -int SLTerrain::LoadData() -{ - // Load the materials bitmap into the main bitmap - if (SceneLayer::LoadData()) - return -1; - - RTEAssert(m_pFGColor, "Terrain's foreground layer not instantiated before trying to load its data!"); - RTEAssert(m_pBGColor, "Terrain's background layer not instantiated before trying to load its data!"); - - // Check if our color layers' BITMAP data is also to be loaded from disk, and not be generated from the material bitmap! - if (m_pFGColor->IsFileData() && m_pBGColor->IsFileData()) - { - if (m_pFGColor->LoadData() < 0) - { - RTEAbort("Could not load the Foreground Color SceneLayer data from file, when a path was specified for it!"); - return -1; - } - if (m_pBGColor->LoadData() < 0) - { - RTEAbort("Could not load the Background Color SceneLayer data from file, when a path was specified for it!"); - return -1; - } - // Ok, we have now loaded the layers in from files, don't need to generate them from the material layer - InitScrollRatios(); - return 0; - } - - // Create blank foreground layer - m_pFGColor->Destroy(); - BITMAP *pFGBitmap = create_bitmap_ex(8, m_pMainBitmap->w, m_pMainBitmap->h); - if (!pFGBitmap || m_pFGColor->Create(pFGBitmap, true, m_Offset, m_WrapX, m_WrapY, m_ScrollRatio)) - { - RTEAbort("Failed to create terrain's foreground layer's bitmap!"); - return -1; - } - - // Create blank background layer - m_pBGColor->Destroy(); - BITMAP *pBGBitmap = create_bitmap_ex(8, m_pMainBitmap->w, m_pMainBitmap->h); - if (!pBGBitmap || m_pBGColor->Create(pBGBitmap, true, m_Offset, m_WrapX, m_WrapY, m_ScrollRatio)) - { - RTEAbort("Failed to create terrain's background layer's bitmap!"); - return -1; - } - - // Structural integrity calc buffer bitmap - destroy_bitmap(m_pStructural); - m_pStructural = create_bitmap_ex(8, m_pMainBitmap->w, m_pMainBitmap->h); - RTEAssert(m_pStructural, "Failed to allocate BITMAP in Terrain::Create"); - clear_bitmap(m_pStructural); - - /////////////////////////////////////////////// - // Load and texturize the FG color bitmap, based on the materials defined in the recently loaded (main) material layer! - - int xPos, yPos, matIndex, pixelColor; - - // Temporary references for all the materials' textures and colors, since we'll access them a lot - BITMAP *apTexBitmaps[256]; - int aColors[256]; - // Null em out so we can tell which ones we've already got once so far - for (matIndex = 0; matIndex < 256; ++matIndex) - { - apTexBitmaps[matIndex] = 0; - aColors[matIndex] = 0; - } - - // Get the background texture - BITMAP *m_pBGTexture = m_BGTextureFile.GetAsBitmap(); - // Get the material palette for quicker access - const std::array &apMaterials = g_SceneMan.GetMaterialPalette(); - // Get the Material palette ID mappings local to the DataModule this SLTerrain is loaded from - const std::array &materialMappings = g_PresetMan.GetDataModule(m_BitmapFile.GetDataModuleID())->GetAllMaterialMappings(); - Material *pMaterial = 0; - - // Lock all involved bitmaps - acquire_bitmap(m_pMainBitmap); - acquire_bitmap(pFGBitmap); - acquire_bitmap(pBGBitmap); - acquire_bitmap(m_pBGTexture); - - // Go through each pixel on the main bitmap, which contains all the material pixels loaded from the bitmap - // Place texture pixels on the FG layer corresponding to the materials on the main material bitmap - for (xPos = 0; xPos < m_pMainBitmap->w; ++xPos) - { - for (yPos = 0; yPos < m_pMainBitmap->h; ++yPos) - { - // Read which material the current pixel represents - matIndex = _getpixel(m_pMainBitmap, xPos, yPos); - // Map any materials defined in this data module but initially collided with other material ID's and thus were displaced to other ID's - if (materialMappings.at(matIndex) != 0) - { - // Assign the mapping and put it onto the material bitmap too - matIndex = materialMappings.at(matIndex); - _putpixel(m_pMainBitmap, xPos, yPos, matIndex); - } - - // Validate the material, or default to default material - if (matIndex >= 0 && matIndex < c_PaletteEntriesNumber && apMaterials.at(matIndex)) - pMaterial = apMaterials.at(matIndex); - else - pMaterial = apMaterials.at(g_MaterialDefault); - - // If haven't read a pixel of this material before, then get its texture so we can quickly access it - if (!apTexBitmaps[matIndex]) - { - // Get, and acquire the texture bitmap if material has any - if (apTexBitmaps[matIndex] = pMaterial->GetTexture()) - acquire_bitmap(apTexBitmaps[matIndex]); - } - - // If actually no texture for the material, then use the material's solid color instead - if (!apTexBitmaps[matIndex]) - { - // If the color hasn't been retrieved yet, then do so - if (!aColors[matIndex]) - aColors[matIndex] = pMaterial->GetColor().GetIndex(); - // Use the color - pixelColor = aColors[matIndex]; - } - // Use the texture's color - else - { -// acquire_bitmap(apTexBitmaps[matIndex]); - pixelColor = _getpixel(apTexBitmaps[matIndex], xPos % apTexBitmaps[matIndex]->w, yPos % apTexBitmaps[matIndex]->h); - } - - // Draw the correct color pixel on the foreground - _putpixel(pFGBitmap, xPos, yPos, pixelColor); - - // Draw background texture on the background where this is stuff on the foreground - if (m_pBGTexture && pixelColor != g_MaskColor) - { - pixelColor = _getpixel(m_pBGTexture, xPos % m_pBGTexture->w, yPos % m_pBGTexture->h); - _putpixel(pBGBitmap, xPos, yPos, pixelColor); - } - // Put a keycolor pixel in the bg otherwise - else - _putpixel(pBGBitmap, xPos, yPos, g_MaskColor); - } - } - - /////////////////////////////////////// - // Material frostings application! - - bool targetFound = false, applyingFrosting = false; - int targetId, frostingId, thickness, thicknessGoal; - BITMAP *pFrostingTex = 0; - for (list::iterator tfItr = m_TerrainFrostings.begin(); tfItr != m_TerrainFrostings.end(); ++tfItr) - { - targetId = (*tfItr).GetTargetMaterial().GetIndex(); - frostingId = (*tfItr).GetFrostingMaterial().GetIndex(); - // Try to get the color texture of the frosting material. If fail, we'll use the color isntead - pFrostingTex = (*tfItr).GetFrostingMaterial().GetTexture(); - if (pFrostingTex) - acquire_bitmap(pFrostingTex); - - // Loop through all columns - for (xPos = 0; xPos < m_pMainBitmap->w; ++xPos) - { - // Get the thickness for this column - thicknessGoal = (*tfItr).GetThicknessSample(); - - // Work upward from the bottom of each column - for (yPos = m_pMainBitmap->h - 1; yPos >= 0; --yPos) - { - // Read which material the current pixel represents - matIndex = _getpixel(m_pMainBitmap, xPos, yPos); - - // We've encountered the target material! Prepare to apply frosting as soon as it ends! - if (!targetFound && matIndex == targetId) - { - targetFound = true; - thickness = 0; - } - // Target material has ended! See if we shuold start putting on the frosting - else if (targetFound && matIndex != targetId && thickness <= thicknessGoal) - { - applyingFrosting = true; - targetFound = false; - } - - // If time to put down frosting pixels, then do so IF there is air, OR we're set to ignore what we're overwriting - if (applyingFrosting && (matIndex == g_MaterialAir || !(*tfItr).InAirOnly()) && thickness <= thicknessGoal) - { - // Get the color either from the frosting material's texture or the solid color - if (pFrostingTex) - pixelColor = _getpixel(pFrostingTex, xPos % pFrostingTex->w, yPos % pFrostingTex->h); - else - pixelColor = (*tfItr).GetFrostingMaterial().GetColor().GetIndex(); - - // Put the frosting pixel color on the FG color layer - _putpixel(pFGBitmap, xPos, yPos, pixelColor); - // Put the material ID pixel on the material layer - _putpixel(m_pMainBitmap, xPos, yPos, frostingId); - - // Keep track of the applied thickness - thickness++; - } - else - applyingFrosting = false; - } - } - - if (pFrostingTex) - release_bitmap(pFrostingTex); - } - - // Release all involved bitmaps - release_bitmap(m_pMainBitmap); - release_bitmap(pFGBitmap); - release_bitmap(pBGBitmap); - release_bitmap(m_pBGTexture); - - for (matIndex = 0; matIndex < 256; ++matIndex) - { - if (apTexBitmaps[matIndex]) - release_bitmap(apTexBitmaps[matIndex]); - } - - /////////////////////////////////////////////// - // TerrainDebris application - - for (list::iterator tdItr = m_TerrainDebris.begin(); tdItr != m_TerrainDebris.end(); ++tdItr) - { - (*tdItr)->ApplyDebris(this); - } - - /////////////////////////////////////////////// - // Now take care of the TerrainObjects - - for (list::iterator toItr = m_TerrainObjects.begin(); toItr != m_TerrainObjects.end(); ++toItr) - { - ApplyTerrainObject(*toItr); - } - CleanAir(); - - InitScrollRatios(); - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SaveData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves currently data in memory to disk. - -int SLTerrain::SaveData(string pathBase) -{ - if (pathBase.empty()) - return -1; - - // Save the bitmap of the material bitmap - if (SceneLayer::SaveData(pathBase + " Mat.bmp") < 0) - { - RTEAbort("Failed to write the material bitmap data saving an SLTerrain!"); - return -1; - } - // Then the foreground color layer - if (m_pFGColor->SaveData(pathBase + " FG.bmp") < 0) - { - RTEAbort("Failed to write the FG color bitmap data saving an SLTerrain!"); - return -1; - } - // Then the background color layer - if (m_pBGColor->SaveData(pathBase + " BG.bmp") < 0) - { - RTEAbort("Failed to write the BG color bitmap data saving an SLTerrain!"); - return -1; - } - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ClearData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears out any previously loaded bitmap data from memory. - -int SLTerrain::ClearData() -{ - // Clear the material layer - if (SceneLayer::ClearData() < 0) - { - RTEAbort("Failed to clear material bitmap data of an SLTerrain!"); - return -1; - } - // Clear the foreground color layer - if (m_pFGColor && m_pFGColor->ClearData() < 0) - { - RTEAbort("Failed to clear the foreground color bitmap data of an SLTerrain!"); - return -1; - } - // Clear the background color layer - if (m_pBGColor && m_pBGColor->ClearData() < 0) - { - RTEAbort("Failed to clear the background color bitmap data of an SLTerrain!"); - return -1; - } - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int SLTerrain::ReadProperty(const std::string_view &propName, Reader &reader) -{ - if (propName == "BackgroundTexture") - reader >> m_BGTextureFile; - else if (propName == "FGColorLayer") - { - delete m_pFGColor; - m_pFGColor = new SceneLayer(); - reader >> m_pFGColor; - } - else if (propName == "BGColorLayer") - { - delete m_pBGColor; - m_pBGColor = new SceneLayer(); - reader >> m_pBGColor; - } - else if (propName == "AddTerrainFrosting") - { - // Clear frostings if we derived them from some other SLTerrain object - // and then read another set from explicit terrain definition - if (m_NeedToClearFrostings) - { - m_NeedToClearFrostings = false; - m_TerrainFrostings.clear(); + ConcreteClassInfo(SLTerrain, SceneLayer, 0); + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SLTerrain::Clear() { + m_LayerToDraw = LayerType::ForegroundLayer; + m_FGColorLayer = nullptr; + m_BGColorLayer = nullptr; + m_DefaultBGTextureFile.Reset(); + m_TerrainFrostings.clear(); + m_TerrainDebris.clear(); + m_TerrainObjects.clear(); + m_UpdatedMaterialAreas.clear(); + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SLTerrain::Create() { + SceneLayer::Create(); + + m_FGColorLayer = std::make_unique(); + m_BGColorLayer = std::make_unique(); + + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SLTerrain::Create(const SLTerrain &reference) { + SceneLayer::Create(reference); + + // Copy the layers but not the layer BITMAPs because they will be loaded later by LoadData. + m_FGColorLayer.reset(dynamic_cast(reference.m_FGColorLayer->Clone())); + m_BGColorLayer.reset(dynamic_cast(reference.m_BGColorLayer->Clone())); + + m_DefaultBGTextureFile = reference.m_DefaultBGTextureFile; + + m_TerrainFrostings.clear(); + for (TerrainFrosting *terrainFrosting : reference.m_TerrainFrostings) { + m_TerrainFrostings.emplace_back(terrainFrosting); + } + m_TerrainDebris.clear(); + for (TerrainDebris *terrainDebris : reference.m_TerrainDebris) { + m_TerrainDebris.emplace_back(terrainDebris); + } + m_TerrainObjects.clear(); + for (TerrainObject *terrainObject : reference.m_TerrainObjects) { + m_TerrainObjects.emplace_back(terrainObject); } - TerrainFrosting frosting; - reader >> frosting; - m_TerrainFrostings.push_back(frosting); - } - else if (propName == "AddTerrainDebris") - { - // Clear debris if we derived them from some other SLTerrain object - // and then read another set from explicit terrain definition - if (m_NeedToClearDebris) - { - m_NeedToClearDebris = false; - m_TerrainDebris.clear(); + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SLTerrain::ReadProperty(const std::string_view &propName, Reader &reader) { + if (propName == "BackgroundTexture") { + reader >> m_DefaultBGTextureFile; + } else if (propName == "FGColorLayer") { + m_FGColorLayer = std::make_unique(); + reader >> m_FGColorLayer.get(); + } else if (propName == "BGColorLayer") { + m_BGColorLayer = std::make_unique(); + reader >> m_BGColorLayer.get(); + } else if (propName == "AddTerrainFrosting") { + std::unique_ptr terrainFrosting = std::make_unique(); + reader >> terrainFrosting.get(); + m_TerrainFrostings.emplace_back(terrainFrosting.release()); + } else if (propName == "AddTerrainDebris") { + std::unique_ptr terrainDebris = std::make_unique(); + reader >> terrainDebris.get(); + m_TerrainDebris.emplace_back(terrainDebris.release()); + } else if (propName == "PlaceTerrainObject") { + std::unique_ptr terrainObject = std::make_unique(); + reader >> terrainObject.get(); + m_TerrainObjects.emplace_back(terrainObject.release()); + } else { + return SceneLayer::ReadProperty(propName, reader); + } + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SLTerrain::Save(Writer &writer) const { + SceneLayer::Save(writer); + + // Only write the background texture info if the background itself is not saved out as a file already, since saved, pre-rendered bitmaps don't need texturing. + if (m_BGColorLayer->IsLoadedFromDisk()) { + writer.NewPropertyWithValue("BGColorLayer", m_BGColorLayer.get()); + } else { + writer.NewPropertyWithValue("BackgroundTexture", m_DefaultBGTextureFile); } - TerrainDebris *pTerrainDebris = new TerrainDebris; - reader >> pTerrainDebris; - m_TerrainDebris.push_back(pTerrainDebris); - } - else if (propName == "AddTerrainObject" || propName == "PlaceTerrainObject") - { - TerrainObject *pTerrainObject = new TerrainObject; - reader >> pTerrainObject; - m_TerrainObjects.push_back(pTerrainObject); - } - else - return SceneLayer::ReadProperty(propName, reader); - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this SceneLayer with a Writer for -// later recreation with Create(Reader &reader); - -int SLTerrain::Save(Writer &writer) const -{ - SceneLayer::Save(writer); - - // Only write the background texture info if the background itself is not saved out as a file already - if (m_pBGColor->IsFileData()) - { - writer.NewProperty("BGColorLayer"); - writer << m_pBGColor; - } - else - { - writer.NewProperty("BackgroundTexture"); - writer << m_BGTextureFile; - } - - // Only if we haven't saved out the FG as a done and altered bitmap file should we save the procedural params here - if (m_pFGColor->IsFileData()) - { - writer.NewProperty("FGColorLayer"); - writer << m_pFGColor; - } - // Layer data is not saved into a bitmap file yet, so just write out the procedural params to build the terrain - else - { - for (list::const_iterator tfItr = m_TerrainFrostings.begin(); tfItr != m_TerrainFrostings.end(); ++tfItr) - { - writer.NewProperty("AddTerrainFrosting"); - writer << *tfItr; - } - for (list::const_iterator tdItr = m_TerrainDebris.begin(); tdItr != m_TerrainDebris.end(); ++tdItr) - { - writer.NewProperty("AddTerrainDebris"); - writer << *tdItr; - } - for (list::const_iterator toItr = m_TerrainObjects.begin(); toItr != m_TerrainObjects.end(); ++toItr) - { - // Write out only what is needed to place a copy of this in the Terrain - writer.NewProperty("AddTerrainObject"); - writer.ObjectStart((*toItr)->GetClassName()); - writer.NewProperty("CopyOf"); - writer << (*toItr)->GetModuleAndPresetName(); - writer.NewProperty("Position"); - writer << (*toItr)->GetPos(); - writer.ObjectEnd(); - } - } - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SLTerrain object. - -void SLTerrain::Destroy(bool notInherited) -{ - delete m_pFGColor; - delete m_pBGColor; - destroy_bitmap(m_pStructural); - - for (list::iterator tdItr = m_TerrainDebris.begin(); tdItr != m_TerrainDebris.end(); ++tdItr) - { - delete (*tdItr); - *tdItr = 0; - } - for (list::iterator toItr = m_TerrainObjects.begin(); toItr != m_TerrainObjects.end(); ++toItr) - { - delete (*toItr); - *toItr = 0; - } - - if (!notInherited) - SceneLayer::Destroy(); - Clear(); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetFGColorPixel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a specific pixel from the foreground color bitmap of this. -// LockBitmaps() must be called before using this method. - -unsigned char SLTerrain::GetFGColorPixel(const int pixelX, const int pixelY) const -{ - int posX = pixelX; - int posY = pixelY; - - WrapPosition(posX, posY); - - // If it's still below or to the sides out of bounds after - // wrapping what is supposed to be wrapped, shit is out of bounds. - if (posX < 0 || posX >= m_pMainBitmap->w || posY >= m_pMainBitmap->h) - return g_MaskColor; - - // If above terrain bitmap, return key color. - if (posY < 0) - return g_MaskColor; - -// RTEAssert(m_pMainBitmap->m_LockCount > 0, "Trying to access unlocked terrain bitmap"); - return _getpixel(m_pFGColor->GetBitmap(), posX, posY); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBGColorPixel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a specific pixel from the background color bitmap of this. -// LockBitmaps() must be called before using this method. - -unsigned char SLTerrain::GetBGColorPixel(const int pixelX, const int pixelY) const -{ - int posX = pixelX; - int posY = pixelY; - - WrapPosition(posX, posY); - - // If it's still below or to the sides out of bounds after - // wrapping what is supposed to be wrapped, shit is out of bounds. - if (posX < 0 || posX >= m_pMainBitmap->w || posY >= m_pMainBitmap->h) - return g_MaskColor; - - // If above terrain bitmap, return key color. - if (posY < 0) - return g_MaskColor; - -// RTEAssert(m_pMainBitmap->m_LockCount > 0, "Trying to access unlocked terrain bitmap"); - return _getpixel(m_pBGColor->GetBitmap(), posX, posY); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMaterialPixel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a specific pixel from the material bitmap of this SceneLayer. -// LockMaterialBitmap() must be called before using this method. - -unsigned char SLTerrain::GetMaterialPixel(const int pixelX, const int pixelY) const -{ - int posX = pixelX; - int posY = pixelY; - - WrapPosition(posX, posY); - - // If it's still below or to the sides out of bounds after - // wrapping what is supposed to be wrapped, shit is out of bounds. - if (posX < 0 || posX >= m_pMainBitmap->w || posY >= m_pMainBitmap->h) -// return g_MaterialOutOfBounds; - return g_MaterialAir; - - // If above terrain bitmap, return air material. - if (posY < 0) - return g_MaterialAir; - -// RTEAssert(m_pMainBitmap->m_LockCount > 0, "Trying to access unlocked terrain bitmap"); - return _getpixel(m_pMainBitmap, posX, posY); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsAirPixel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether a terrain pixel is of air or cavity material. - -bool SLTerrain::IsAirPixel(const int pixelX, const int pixelY) const -{ - int posX = pixelX; - int posY = pixelY; - - WrapPosition(posX, posY); - - // If it's still below or to the sides out of bounds after - // wrapping what is supposed to be wrapped, shit is out of bounds. - if (posX < 0 || posX >= m_pMainBitmap->w || posY >= m_pMainBitmap->h) -// return g_MaterialOutOfBounds; - return true; - - // If above terrain bitmap, return air material. - if (posY < 0) - return true; - - int checkPixel = _getpixel(m_pMainBitmap, posX, posY); -// RTEAssert(m_pMainBitmap->m_LockCount > 0, "Trying to access unlocked terrain bitmap"); - return checkPixel == g_MaterialAir || checkPixel == g_MaterialCavity; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetFGColorPixel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets a specific pixel on the FG Color bitmap of this SLTerrain to a -// specific color. LockBitmaps() must be called before using this -// method. - -void SLTerrain::SetFGColorPixel(const int pixelX, const int pixelY, const int color) -{ - int posX = pixelX; - int posY = pixelY; - - WrapPosition(posX, posY); - - // If it's still below or to the sides out of bounds after - // wrapping what is supposed to be wrapped, shit is out of bounds. - if (posX < 0 || - posX >= m_pMainBitmap->w || - posY < 0 || - posY >= m_pMainBitmap->h) - return; - -// RTEAssert(m_pFGColor->GetBitmap()->m_LockCount > 0, "Trying to access unlocked terrain bitmap"); - _putpixel(m_pFGColor->GetBitmap(), posX, posY, color); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetBGColorPixel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets a specific pixel on the BG Color bitmap of this SLTerrain to a -// specific color. LockBitmaps() must be called before using this -// method. - -void SLTerrain::SetBGColorPixel(const int pixelX, const int pixelY, const int color) -{ - int posX = pixelX; - int posY = pixelY; - - WrapPosition(posX, posY); - - // If it's still below or to the sides out of bounds after - // wrapping what is supposed to be wrapped, shit is out of bounds. - if (posX < 0 || - posX >= m_pMainBitmap->w || - posY < 0 || - posY >= m_pMainBitmap->h) - return; - -// RTEAssert(m_pBGColor->GetBitmap()->m_LockCount > 0, "Trying to access unlocked terrain bitmap"); - _putpixel(m_pBGColor->GetBitmap(), posX, posY, color); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetMaterialPixel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets a specific pixel on the material bitmap of this SLTerrain to a -// specific material. LockMaterialBitmap() must be called before using this -// method. - -void SLTerrain::SetMaterialPixel(const int pixelX, const int pixelY, const unsigned char material) -{ - int posX = pixelX; - int posY = pixelY; - - WrapPosition(posX, posY); - - // If it's still below or to the sides out of bounds after - // wrapping what is supposed to be wrapped, shit is out of bounds. - if (posX < 0 || - posX >= m_pMainBitmap->w || - posY < 0 || - posY >= m_pMainBitmap->h) - return; -// RTEAssert(m_pMainBitmap->m_LockCount > 0, "Trying to access unlocked terrain bitmap"); - _putpixel(m_pMainBitmap, posX, posY, material); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EraseSilhouette -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Takes a BITMAP and scans through the pixels on this terrain for pixels -// which overlap with it. Erases them from the terrain and can optionally -// generate MOPixel:s based on the erased or 'dislodged' terrain pixels. - -deque SLTerrain::EraseSilhouette(BITMAP *pSprite, - Vector pos, - Vector pivot, - Matrix rotation, - float scale, - bool makeMOPs, - int skipMOP, - int maxMOPs) -{ -// TODO: OPTIMIZE THIS, IT'S A TIME HOG. MAYBE JSUT STAMP THE OUTLINE AND SAMPLE SOME RANDOM PARTICLES? - - RTEAssert(pSprite, "Null BITMAP passed to SLTerrain::EraseSilhouette"); - - deque MOPDeque; - - // This will point to the chosen intermediate test bitmap - BITMAP *pTempBitmap = 0; - - // Find the maximum possible sized bitmap that the passed-in sprite will need - - int halfWidth = pSprite->w / 2; - int halfHeight = pSprite->h / 2; - double SinA = sin(rotation.GetRadAngle()); - double CosA = cos(rotation.GetRadAngle()); - int maxWidth = (pSprite->w + abs(pivot.m_X - halfWidth)) * scale; - int maxHeight = (pSprite->h + abs(pivot.m_Y - halfHeight)) * scale; - int maxDiameter = sqrt((float)(maxWidth * maxWidth + maxHeight * maxHeight)) * 2; - int skipCount = skipMOP; - - // Choose an appropriate size - if (maxDiameter >= 256) - pTempBitmap = m_spTempBitmap512; - if (maxDiameter >= 128) - pTempBitmap = m_spTempBitmap256; - else if (maxDiameter >= 64) - pTempBitmap = m_spTempBitmap128; - else if (maxDiameter >= 32) - pTempBitmap = m_spTempBitmap64; - else if (maxDiameter >= 16) - pTempBitmap = m_spTempBitmap32; - else - pTempBitmap = m_spTempBitmap16; - - // Clear and draw the source sprite onto the test bitmap - clear_bitmap(pTempBitmap); - pivot_scaled_sprite(pTempBitmap, pSprite, pTempBitmap->w / 2, pTempBitmap->h / 2, pivot.m_X, pivot.m_Y, ftofix(rotation.GetAllegroAngle()), ftofix(scale)); - - // Do the test of intersection between color pixels of the test bitmap and non-air pixels of the terrain - // Generate and collect MOPixels that represent the terrain overlap and clear the same pixels out of the terrain - int testX, testY, terrX, terrY; - MOPixel *pPixel = 0; - Material const * sceneMat = g_SceneMan.GetMaterialFromID(g_MaterialAir); - Material const * spawnMat = g_SceneMan.GetMaterialFromID(g_MaterialAir); - halfWidth = pTempBitmap->w / 2; - halfHeight = pTempBitmap->h / 2; - unsigned char testPixel = 0, matPixel = 0, colorPixel = 0; - - for (testY = 0; testY < pTempBitmap->h; ++testY) - { - for (testX = 0; testX < pTempBitmap->w; ++testX) - { - terrX = pos.m_X - halfWidth + testX; - terrY = pos.m_Y - halfHeight + testY; - - // Make sure we're checking within bounds - if (terrX < 0) { - if (m_WrapX) - while (terrX < 0) - terrX += m_pMainBitmap->w; - else - continue; - } - if (terrY < 0) { - if (m_WrapY) - while (terrY < 0) - terrY += m_pMainBitmap->h; - else - continue; - } - if (terrX >= m_pMainBitmap->w) { - if (m_WrapX) - terrX %= m_pMainBitmap->w; - else - continue; - } - if (terrY >= m_pMainBitmap->h) { - if (m_WrapY) - terrY %= m_pMainBitmap->h; - else - continue; - } - - testPixel = getpixel(pTempBitmap, testX, testY); - matPixel = getpixel(m_pMainBitmap, terrX, terrY); - colorPixel = getpixel(m_pFGColor->GetBitmap(), terrX, terrY); - - if (testPixel != g_MaskColor) - { - // Only add PixelMO if we're not due to skip any - if (makeMOPs && matPixel != g_MaterialAir && colorPixel != g_MaskColor && ++skipCount > skipMOP && MOPDeque.size() < maxMOPs) - { - skipCount = 0; - sceneMat = g_SceneMan.GetMaterialFromID(matPixel); - spawnMat = sceneMat->GetSpawnMaterial() ? g_SceneMan.GetMaterialFromID(sceneMat->GetSpawnMaterial()) : sceneMat; - // Create the MOPixel based off the Terrain data. - pPixel = new MOPixel(colorPixel, - spawnMat->GetPixelDensity(), - Vector(terrX, terrY), - Vector(), - new Atom(Vector(), spawnMat->GetIndex(), 0, colorPixel, 2), - 0); - - pPixel->SetToHitMOs(false); - MOPDeque.push_back(pPixel); - pPixel = 0; - } - - // Clear the terrain pixels - if (matPixel != g_MaterialAir) - putpixel(m_pMainBitmap, terrX, terrY, g_MaterialAir); - if (colorPixel != g_MaskColor) - { - putpixel(m_pFGColor->GetBitmap(), terrX, terrY, g_MaskColor); - g_SceneMan.RegisterTerrainChange(terrX, terrY, 1, 1, g_MaskColor, false); + // Only write the procedural parameters if the foreground itself is not saved out as a file already, since saved, pre-rendered bitmaps don't need procedural generation. + if (m_FGColorLayer->IsLoadedFromDisk()) { + writer.NewPropertyWithValue("FGColorLayer", m_FGColorLayer.get()); + } else { + for (const TerrainFrosting *terrainFrosting : m_TerrainFrostings) { + writer.NewPropertyWithValue("AddTerrainFrosting", terrainFrosting); + } + for (const TerrainDebris *terrainDebris : m_TerrainDebris) { + writer.NewPropertyWithValue("AddTerrainDebris", terrainDebris); + } + for (const TerrainObject *terrainObject : m_TerrainObjects) { + // Write out only what is needed to place a copy of this in the Terrain + writer.NewProperty("PlaceTerrainObject"); + writer.ObjectStart(terrainObject->GetClassName()); + writer.NewPropertyWithValue("CopyOf", terrainObject->GetModuleAndPresetName()); + writer.NewPropertyWithValue("Position", terrainObject->GetPos()); + writer.ObjectEnd(); + } + } + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // TODO: Break this down and refactor. + void SLTerrain::TexturizeTerrain() { + BITMAP *defaultBGLayerTexture = m_DefaultBGTextureFile.GetAsBitmap(); + + const std::array &materialPalette = g_SceneMan.GetMaterialPalette(); + const std::array &materialMappings = g_PresetMan.GetDataModule(m_BitmapFile.GetDataModuleID())->GetAllMaterialMappings(); + + std::array materialFGTextures; + materialFGTextures.fill(nullptr); + std::array materialBGTextures; + materialBGTextures.fill(nullptr); + std::array materialColors; + materialColors.fill(0); + + // Reference. Do not remove. + //acquire_bitmap(m_MainBitmap); + //acquire_bitmap(m_FGColorLayer->GetBitmap()); + //acquire_bitmap(m_BGColorLayer->GetBitmap()); + //acquire_bitmap(defaultBGLayerTexture); + + // Go through each pixel on the main bitmap, which contains all the material pixels loaded from the bitmap. + // Place texture pixels on the FG layer corresponding to the materials on the main material bitmap. + for (int xPos = 0; xPos < m_MainBitmap->w; ++xPos) { + for (int yPos = 0; yPos < m_MainBitmap->h; ++yPos) { + int matIndex = _getpixel(m_MainBitmap, xPos, yPos); + + // Map any materials defined in this data module but initially collided with other material ID's and thus were displaced to other ID's. + if (materialMappings.at(matIndex) != 0) { + // Assign the mapping and put it onto the material bitmap too. + matIndex = materialMappings.at(matIndex); + _putpixel(m_MainBitmap, xPos, yPos, matIndex); } - } - } - } - - // Add a box to the updated areas list to show there's been change to the materials layer -// TODO: improve fit/tightness of box here - m_UpdatedMateralAreas.push_back(Box(pos - pivot, maxWidth, maxHeight)); - - return MOPDeque; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ApplyObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws a passed in Object's graphical and material representations to -// this Terrain's respective layers. - -bool SLTerrain::ApplyObject(Entity *pEntity) -{ - if (!pEntity) - return false; - - // Find out what kind it is - if (MovableObject *pMO = dynamic_cast(pEntity)) - { - ApplyMovableObject(pMO); - return true; - } - else if (TerrainObject *pTO = dynamic_cast(pEntity)) - { - ApplyTerrainObject(pTO); - return true; - } - - return false; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ApplyMovableObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws a passed in MovableObject's graphical and material -// representations to this Terrain's respective layers. - -void SLTerrain::ApplyMovableObject(MovableObject *pMObject) -{ - if (!pMObject) - return; - - // Determine whether a sprite or just a pixel-based MO - MOSprite *pMOSprite = dynamic_cast(pMObject); - - // Sprite, so try to integrate it into the terrain, with terrain on top - if (pMOSprite) - { - deque MOPDeque; - - // This will point to the chosen intermediate test bitmap - BITMAP *pTempBitmap = 0; - // Temporary bitmap holder, doesn't own - BITMAP *pSprite = pMOSprite->GetSpriteFrame(); - - float diameter = pMOSprite->GetDiameter(); - // Choose an appropriate size - if (diameter >= 256) - pTempBitmap = m_spTempBitmap512; - if (diameter >= 128) - pTempBitmap = m_spTempBitmap256; - else if (diameter >= 64) - pTempBitmap = m_spTempBitmap128; - else if (diameter >= 32) - pTempBitmap = m_spTempBitmap64; - else if (diameter >= 16) - pTempBitmap = m_spTempBitmap32; - else - pTempBitmap = m_spTempBitmap16; - - // The position of the upper left corner of the temporary bitmap in the scene - Vector bitmapScroll = pMOSprite->GetPos().GetFloored() - Vector(pTempBitmap->w / 2, pTempBitmap->w / 2); - - Box notUsed; - - // COLOR - // Clear and draw the source sprite onto the temp bitmap - clear_to_color(pTempBitmap, g_MaskColor); - // Draw the actor and then the scene foreground to temp bitmap - pMOSprite->Draw(pTempBitmap, bitmapScroll, g_DrawColor, true); - m_pFGColor->Draw(pTempBitmap, notUsed, bitmapScroll); - // Finally draw temporary bitmap to the Scene - masked_blit(pTempBitmap, GetFGColorBitmap(), 0, 0, bitmapScroll.m_X, bitmapScroll.m_Y, pTempBitmap->w, pTempBitmap->h); - - // Register terrain change - g_SceneMan.RegisterTerrainChange(bitmapScroll.m_X, bitmapScroll.m_Y, pTempBitmap->w, pTempBitmap->h, g_MaskColor, false); - - -// TODO: centralize seam drawing! - // Draw over seams - if (g_SceneMan.SceneWrapsX()) - { - if (bitmapScroll.m_X < 0) - masked_blit(pTempBitmap, GetFGColorBitmap(), 0, 0, bitmapScroll.m_X + g_SceneMan.GetSceneWidth(), bitmapScroll.m_Y, pTempBitmap->w, pTempBitmap->h); - else if (bitmapScroll.m_X + pTempBitmap->w > g_SceneMan.GetSceneWidth()) - masked_blit(pTempBitmap, GetFGColorBitmap(), 0, 0, bitmapScroll.m_X - g_SceneMan.GetSceneWidth(), bitmapScroll.m_Y, pTempBitmap->w, pTempBitmap->h); - } - if (g_SceneMan.SceneWrapsY()) - { - if (bitmapScroll.m_Y < 0) - masked_blit(pTempBitmap, GetFGColorBitmap(), 0, 0, bitmapScroll.m_X, bitmapScroll.m_Y + g_SceneMan.GetSceneHeight(), pTempBitmap->w, pTempBitmap->h); - else if (bitmapScroll.m_Y + pTempBitmap->h > g_SceneMan.GetSceneHeight()) - masked_blit(pTempBitmap, GetFGColorBitmap(), 0, 0, bitmapScroll.m_X, bitmapScroll.m_Y - g_SceneMan.GetSceneHeight(), pTempBitmap->w, pTempBitmap->h); - } - - // Material - clear_to_color(pTempBitmap, g_MaterialAir); - // Draw the actor and then the scene material layer to temp bitmap - pMOSprite->Draw(pTempBitmap, bitmapScroll, g_DrawMaterial, true); - SceneLayer::Draw(pTempBitmap, notUsed, bitmapScroll); - // Finally draw temporary bitmap to the Scene - masked_blit(pTempBitmap, GetMaterialBitmap(), 0, 0, bitmapScroll.m_X, bitmapScroll.m_Y, pTempBitmap->w, pTempBitmap->h); - // Add a box to the updated areas list to show there's been change to the materials layer - m_UpdatedMateralAreas.push_back(Box(bitmapScroll, pTempBitmap->w, pTempBitmap->h)); -// TODO: centralize seam drawing! - // Draw over seams - if (g_SceneMan.SceneWrapsX()) - { - if (bitmapScroll.m_X < 0) - masked_blit(pTempBitmap, GetMaterialBitmap(), 0, 0, bitmapScroll.m_X + g_SceneMan.GetSceneWidth(), bitmapScroll.m_Y, pTempBitmap->w, pTempBitmap->h); - else if (bitmapScroll.m_X + pTempBitmap->w > g_SceneMan.GetSceneWidth()) - masked_blit(pTempBitmap, GetMaterialBitmap(), 0, 0, bitmapScroll.m_X - g_SceneMan.GetSceneWidth(), bitmapScroll.m_Y, pTempBitmap->w, pTempBitmap->h); - } - if (g_SceneMan.SceneWrapsY()) - { - if (bitmapScroll.m_Y < 0) - masked_blit(pTempBitmap, GetMaterialBitmap(), 0, 0, bitmapScroll.m_X, bitmapScroll.m_Y + g_SceneMan.GetSceneHeight(), pTempBitmap->w, pTempBitmap->h); - else if (bitmapScroll.m_Y + pTempBitmap->h > g_SceneMan.GetSceneHeight()) - masked_blit(pTempBitmap, GetMaterialBitmap(), 0, 0, bitmapScroll.m_X, bitmapScroll.m_Y - g_SceneMan.GetSceneHeight(), pTempBitmap->w, pTempBitmap->h); - } - } - // Not a big sprite, so just draw the representations - else - { - pMObject->Draw(GetFGColorBitmap(), Vector(), g_DrawColor, true); - // Register terrain change - g_SceneMan.RegisterTerrainChange(pMObject->GetPos().m_X, pMObject->GetPos().m_Y, 1, 1, g_DrawColor, false); - - pMObject->Draw(GetMaterialBitmap(), Vector(), g_DrawMaterial, true); - } -} - - - -void SLTerrain::RegisterTerrainChange(TerrainObject *pTObject) -{ - if (!pTObject) - return; - - Vector loc = pTObject->GetPos() + pTObject->GetBitmapOffset(); - - if (pTObject->HasBGColor()) - { - g_SceneMan.RegisterTerrainChange(loc.m_X, loc.m_Y, pTObject->GetBitmapWidth(), pTObject->GetBitmapHeight(), g_MaskColor, true); + + // Validate the material, or fallback to default material. + const Material *material = (matIndex >= 0 && matIndex < c_PaletteEntriesNumber && materialPalette.at(matIndex)) ? materialPalette.at(matIndex) : materialPalette.at(MaterialColorKeys::g_MaterialOutOfBounds); + + // If haven't read a pixel of this material before, then get its texture so we can quickly access it. + if (!materialFGTextures.at(matIndex) && material->GetFGTexture()) { + materialFGTextures.at(matIndex) = material->GetFGTexture(); + //acquire_bitmap(materialFGTextures.at(matIndex)); + } + if (!materialBGTextures.at(matIndex) && material->GetBGTexture()) { + materialBGTextures.at(matIndex) = material->GetBGTexture(); + //acquire_bitmap(materialBGTextures.at(matIndex)); + } + + int fgPixelColor = 0; + // If actually no texture for the material, then use the material's solid color instead. + if (!materialFGTextures.at(matIndex)) { + if (materialColors.at(matIndex) == 0) { materialColors.at(matIndex) = material->GetColor().GetIndex(); } + fgPixelColor = materialColors.at(matIndex); + } else { + fgPixelColor = _getpixel(materialFGTextures.at(matIndex), xPos % materialFGTextures.at(matIndex)->w, yPos % materialFGTextures.at(matIndex)->h); + } + _putpixel(m_FGColorLayer->GetBitmap(), xPos, yPos, fgPixelColor); + + int bgPixelColor = 0; + if (matIndex == 0) { + bgPixelColor = ColorKeys::g_MaskColor; + } else { + if (!materialBGTextures.at(matIndex)) { + bgPixelColor = _getpixel(defaultBGLayerTexture, xPos % defaultBGLayerTexture->w, yPos % defaultBGLayerTexture->h); + } else { + bgPixelColor = _getpixel(materialBGTextures.at(matIndex), xPos % materialBGTextures.at(matIndex)->w, yPos % materialBGTextures.at(matIndex)->h); + } + } + _putpixel(m_BGColorLayer->GetBitmap(), xPos, yPos, bgPixelColor); + } + } + // Reference. Do not remove. + //release_bitmap(m_MainBitmap); + //release_bitmap(m_FGColorLayer->GetBitmap()); + //release_bitmap(m_BGColorLayer->GetBitmap()); + //release_bitmap(defaultBGLayerTexture); + /* + for (BITMAP *fgTextureBitmap : materialFGTextures) { + release_bitmap(fgTextureBitmap); + } + for (BITMAP *bgTextureBitmap : materialBGTextures) { + release_bitmap(bgTextureBitmap); + } + */ + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SLTerrain::LoadData() { + SceneLayer::LoadData(); + + RTEAssert(m_FGColorLayer.get(), "Terrain's foreground layer not instantiated before trying to load its data!"); + RTEAssert(m_BGColorLayer.get(), "Terrain's background layer not instantiated before trying to load its data!"); + + if (m_FGColorLayer->IsLoadedFromDisk() && m_BGColorLayer->IsLoadedFromDisk()) { + m_FGColorLayer->LoadData(); + m_BGColorLayer->LoadData(); + } else { + m_FGColorLayer->Destroy(); + m_FGColorLayer->Create(create_bitmap_ex(8, m_MainBitmap->w, m_MainBitmap->h), true, m_Offset, m_WrapX, m_WrapY, m_ScrollInfo); + + m_BGColorLayer->Destroy(); + m_BGColorLayer->Create(create_bitmap_ex(8, m_MainBitmap->w, m_MainBitmap->h), true, m_Offset, m_WrapX, m_WrapY, m_ScrollInfo); + + TexturizeTerrain(); + + for (const TerrainFrosting *terrainFrosting : m_TerrainFrostings) { + terrainFrosting->FrostTerrain(this); + } + for (TerrainDebris *terrainDebris : m_TerrainDebris) { + terrainDebris->ScatterOnTerrain(this); + } + for (TerrainObject *terrainObject : m_TerrainObjects) { + terrainObject->PlaceOnTerrain(this); + } + CleanAir(); + } + return 0; } - // Register terrain change - g_SceneMan.RegisterTerrainChange(loc.m_X, loc.m_Y, pTObject->GetBitmapWidth(), pTObject->GetBitmapHeight(), g_MaskColor, false); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ApplyTerrainObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws a passed in TerrainObject's graphical and material -// representations to this Terrain's respective layers. - -void SLTerrain::ApplyTerrainObject(TerrainObject *pTObject) -{ - if (!pTObject) - return; - - Vector loc = pTObject->GetPos() + pTObject->GetBitmapOffset(); - - // Do duplicate drawing if the terrain object straddles a wrapping border - if (loc.m_X < 0) - { - draw_sprite(m_pMainBitmap, pTObject->GetMaterialBitmap(), loc.m_X + m_pMainBitmap->w, loc.m_Y); - draw_sprite(m_pFGColor->GetBitmap(), pTObject->GetFGColorBitmap(), loc.m_X + m_pFGColor->GetBitmap()->w, loc.m_Y); - if (pTObject->HasBGColor()) - draw_sprite(m_pBGColor->GetBitmap(), pTObject->GetBGColorBitmap(), loc.m_X + m_pBGColor->GetBitmap()->w, loc.m_Y); - } - else if (loc.m_X >= m_pMainBitmap->w - pTObject->GetFGColorBitmap()->w) - { - draw_sprite(m_pMainBitmap, pTObject->GetMaterialBitmap(), loc.m_X - m_pMainBitmap->w, loc.m_Y); - draw_sprite(m_pFGColor->GetBitmap(), pTObject->GetFGColorBitmap(), loc.m_X - m_pFGColor->GetBitmap()->w, loc.m_Y); - if (pTObject->HasBGColor()) - draw_sprite(m_pBGColor->GetBitmap(), pTObject->GetBGColorBitmap(), loc.m_X - m_pBGColor->GetBitmap()->w, loc.m_Y); - } - - // Regular drawing - draw_sprite(m_pMainBitmap, pTObject->GetMaterialBitmap(), loc.m_X, loc.m_Y); - draw_sprite(m_pFGColor->GetBitmap(), pTObject->GetFGColorBitmap(), loc.m_X, loc.m_Y); - if (pTObject->HasBGColor()) - { - draw_sprite(m_pBGColor->GetBitmap(), pTObject->GetBGColorBitmap(), loc.m_X, loc.m_Y); - g_SceneMan.RegisterTerrainChange(loc.m_X, loc.m_Y, pTObject->GetBitmapWidth(), pTObject->GetBitmapHeight(), g_MaskColor, true); +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SLTerrain::SaveData(const std::string &pathBase) { + if (pathBase.empty()) { + return -1; + } + SceneLayer::SaveData(pathBase + " Mat.png"); + m_FGColorLayer->SaveData(pathBase + " FG.png"); + m_BGColorLayer->SaveData(pathBase + " BG.png"); + return 0; } - // Register terrain change - g_SceneMan.RegisterTerrainChange(loc.m_X, loc.m_Y, pTObject->GetBitmapWidth(), pTObject->GetBitmapHeight(), g_MaskColor, false); - - // Add a box to the updated areas list to show there's been change to the materials layer - m_UpdatedMateralAreas.push_back(Box(loc, pTObject->GetMaterialBitmap()->w, pTObject->GetMaterialBitmap()->h)); - - // Apply all the child objects of the TO, and first reapply the team so all its children are guaranteed to be on the same team! - pTObject->SetTeam(pTObject->GetTeam()); - for (list::const_iterator itr = pTObject->GetChildObjects().begin(); itr != pTObject->GetChildObjects().end() ; ++itr) - { -// TODO: check if we're placing a brain, and have it replace the resident brain of the scene! - // Copy and apply, transferring ownership of the new copy into the application - g_SceneMan.AddSceneObject((*itr).GetPlacedCopy(pTObject)); - } - -// CleanAir(); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsBoxBuried -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks whether a bounding box is completely buried in the terrain. - -bool SLTerrain::IsBoxBuried(const Box &checkBox) const -{ - bool buried = true; - - // Center -// Not important for buriedness, only corners are, really -// buried = buried && GetMaterialPixel(checkBox.GetCenter().m_X, checkBox.GetCenter().m_Y) != g_MaterialAir; - - // Corners - buried = buried && !IsAirPixel(checkBox.GetCorner().m_X, checkBox.GetCorner().m_Y); - buried = buried && !IsAirPixel(checkBox.GetCorner().m_X + checkBox.GetWidth(), checkBox.GetCorner().m_Y); - buried = buried && !IsAirPixel(checkBox.GetCorner().m_X, checkBox.GetCorner().m_Y + checkBox.GetHeight()); - buried = buried && !IsAirPixel(checkBox.GetCorner().m_X + checkBox.GetWidth(), checkBox.GetCorner().m_Y + checkBox.GetHeight()); - - return buried; -} - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CleanAirBox -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes any color pixel in the color layer of this SLTerrain wherever -// there is an air material pixel in the material layer. Whether the scene is X-wrapped or Y-wrapped. - -void SLTerrain::CleanAirBox(Box box, bool wrapsX, bool wrapsY) -{ - acquire_bitmap(m_pMainBitmap); - acquire_bitmap(m_pFGColor->GetBitmap()); - - int width = m_pMainBitmap->w; - int height = m_pMainBitmap->h; - unsigned char matPixel; - - for (int y = box.m_Corner.m_Y; y < box.m_Corner.m_Y + box.m_Height; ++y) { - for (int x = box.m_Corner.m_X; x < box.m_Corner.m_X + box.m_Width; ++x) { - float wrapX = x; - float wrapY = y; - - //Fix coords in case of seam - if (wrapsX) - { - if (wrapX < 0) - wrapX = wrapX + m_pMainBitmap->w; - - if (wrapX >= m_pMainBitmap->w) - wrapX = wrapX - m_pMainBitmap->w; +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SLTerrain::ClearData() { + RTEAssert(SceneLayer::ClearData() == 0, "Failed to clear material bitmap data of an SLTerrain!"); + RTEAssert(m_FGColorLayer && m_FGColorLayer->ClearData() == 0, "Failed to clear the foreground color bitmap data of an SLTerrain!"); + RTEAssert(m_BGColorLayer && m_BGColorLayer->ClearData() == 0, "Failed to clear the background color bitmap data of an SLTerrain!"); + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SLTerrain::IsAirPixel(const int pixelX, const int pixelY) const { + int checkPixel = GetPixel(pixelX, pixelY); + return checkPixel == MaterialColorKeys::g_MaterialAir || checkPixel == MaterialColorKeys::g_MaterialCavity; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SLTerrain::IsBoxBuried(const Box &checkBox) const { + bool buried = true; + buried = buried && !IsAirPixel(checkBox.GetCorner().GetFloorIntX(), checkBox.GetCorner().GetFloorIntY()); + buried = buried && !IsAirPixel(static_cast(checkBox.GetCorner().GetX() + checkBox.GetWidth()), checkBox.GetCorner().GetFloorIntY()); + buried = buried && !IsAirPixel(checkBox.GetCorner().GetFloorIntX(), static_cast(checkBox.GetCorner().GetY() + checkBox.GetHeight())); + buried = buried && !IsAirPixel(static_cast(checkBox.GetCorner().GetX() + checkBox.GetWidth()), static_cast(checkBox.GetCorner().GetY() + checkBox.GetHeight())); + return buried; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SLTerrain::CleanAir() { + // Reference. Do not remove. + //acquire_bitmap(m_MainBitmap); + //acquire_bitmap(m_FGColorLayer->GetBitmap()); + + int width = m_MainBitmap->w; + int height = m_MainBitmap->h; + + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + int matPixel = _getpixel(m_MainBitmap, x, y); + if (matPixel == MaterialColorKeys::g_MaterialCavity) { + _putpixel(m_MainBitmap, x, y, MaterialColorKeys::g_MaterialAir); + matPixel = MaterialColorKeys::g_MaterialAir; + } + if (matPixel == MaterialColorKeys::g_MaterialAir) { _putpixel(m_FGColorLayer->GetBitmap(), x, y, ColorKeys::g_MaskColor); } } + } + // Reference. Do not remove. + //release_bitmap(m_MainBitmap); + //release_bitmap(m_FGColorLayer->GetBitmap()); + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SLTerrain::CleanAirBox(const Box &box, bool wrapsX, bool wrapsY) { + // Reference. Do not remove. + //acquire_bitmap(m_MainBitmap); + //acquire_bitmap(m_FGColorLayer->GetBitmap()); + + int width = m_MainBitmap->w; + int height = m_MainBitmap->h; - if (wrapsY) - { - if (wrapY < 0) - wrapY = wrapY + m_pMainBitmap->h; + for (int y = box.m_Corner.GetFloorIntY(); y < static_cast(box.m_Corner.GetY() + box.m_Height); ++y) { + for (int x = box.m_Corner.GetFloorIntX(); x < static_cast(box.m_Corner.GetX() + box.m_Width); ++x) { + int wrappedX = x; + int wrappedY = y; - if (wrapY >= m_pMainBitmap->h) - wrapY = wrapY - m_pMainBitmap->h; + if (wrapsX) { + if (wrappedX < 0) { wrappedX += width; } + if (wrappedX >= width) { wrappedX -= width; } + } + if (wrapsY) { + if (wrappedY < 0) { wrappedY += height; } + if (wrappedY >= height) { wrappedY -= height; } + } + if (wrappedX >= 0 && wrappedX < width && wrappedY >= 0 && wrappedY < height) { + int matPixel = _getpixel(m_MainBitmap, wrappedX, wrappedY); + if (matPixel == MaterialColorKeys::g_MaterialCavity) { + _putpixel(m_MainBitmap, wrappedX, wrappedY, MaterialColorKeys::g_MaterialAir); + matPixel = MaterialColorKeys::g_MaterialAir; + } + if (matPixel == MaterialColorKeys::g_MaterialAir) { _putpixel(m_FGColorLayer->GetBitmap(), wrappedX, wrappedY, ColorKeys::g_MaskColor); } + } } + } + // Reference. Do not remove. + //release_bitmap(m_MainBitmap); + //release_bitmap(m_FGColorLayer->GetBitmap()); + } - if (wrapX >= 0 && wrapY >=0 && wrapX < width && wrapY < height) - { - matPixel = _getpixel(m_pMainBitmap, wrapX, wrapY); - if (matPixel == g_MaterialCavity) { - _putpixel(m_pMainBitmap, wrapX, wrapY, g_MaterialAir); - matPixel = g_MaterialAir; +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // TODO: OPTIMIZE THIS, IT'S A TIME HOG. MAYBE JSUT STAMP THE OUTLINE AND SAMPLE SOME RANDOM PARTICLES? + std::deque SLTerrain::EraseSilhouette(BITMAP *sprite, const Vector &pos, const Vector &pivot, const Matrix &rotation, float scale, bool makeMOPs, int skipMOP, int maxMOPs) { + RTEAssert(sprite, "Null BITMAP passed to SLTerrain::EraseSilhouette"); + + int maxWidth = static_cast(static_cast(sprite->w + std::abs(pivot.GetFloorIntX() - (sprite->w / 2))) * scale); + int maxHeight = static_cast(static_cast(sprite->h + std::abs(pivot.GetFloorIntY() - (sprite->h / 2))) * scale); + int maxDiameter = static_cast(std::sqrt(static_cast(maxWidth * maxWidth + maxHeight * maxHeight)) * 2.0F); + int skipCount = skipMOP; + + BITMAP *tempBitmap = g_SceneMan.GetIntermediateBitmapForSettlingIntoTerrain(maxDiameter); + clear_bitmap(tempBitmap); + pivot_scaled_sprite(tempBitmap, sprite, tempBitmap->w / 2, tempBitmap->h / 2, pivot.GetFloorIntX(), pivot.GetFloorIntY(), ftofix(rotation.GetAllegroAngle()), ftofix(scale)); + + std::deque dislodgedMOPixels; + + // Test intersection between color pixels of the test bitmap and non-air pixels of the terrain, then generate and collect MOPixels that represent the terrain overlap and clear the same pixels out of the terrain. + for (int testY = 0; testY < tempBitmap->h; ++testY) { + for (int testX = 0; testX < tempBitmap->w; ++testX) { + int terrX = pos.GetFloorIntX() - (tempBitmap->w / 2) + testX; + int terrY = pos.GetFloorIntY() - (tempBitmap->h / 2) + testY; + + if (terrX < 0) { + if (m_WrapX) { + while (terrX < 0) { + terrX += m_MainBitmap->w; + } + } else { + continue; + } + } + if (terrY < 0) { + if (m_WrapY) { + while (terrY < 0) { + terrY += m_MainBitmap->h; + } + } else { + continue; + } + } + if (terrX >= m_MainBitmap->w) { + if (m_WrapX) { + terrX %= m_MainBitmap->w; + } else { + continue; + } + } + if (terrY >= m_MainBitmap->h) { + if (m_WrapY) { + terrY %= m_MainBitmap->h; + } else { + continue; + } + } + int matPixel = getpixel(m_MainBitmap, terrX, terrY); + int colorPixel = getpixel(m_FGColorLayer->GetBitmap(), terrX, terrY); + + if (getpixel(tempBitmap, testX, testY) != ColorKeys::g_MaskColor) { + // Only add PixelMO if we're not due to skip any. + if (makeMOPs && matPixel != MaterialColorKeys::g_MaterialAir && colorPixel != ColorKeys::g_MaskColor && ++skipCount > skipMOP && dislodgedMOPixels.size() < maxMOPs) { + skipCount = 0; + const Material *sceneMat = g_SceneMan.GetMaterialFromID(matPixel); + const Material *spawnMat = sceneMat->GetSpawnMaterial() ? g_SceneMan.GetMaterialFromID(sceneMat->GetSpawnMaterial()) : sceneMat; + + std::unique_ptr terrainPixelAtom = std::make_unique(Vector(), spawnMat->GetIndex(), nullptr, colorPixel, 2); + std::unique_ptr terrainPixel = std::make_unique(colorPixel, spawnMat->GetPixelDensity(), Vector(static_cast(terrX), static_cast(terrY)), Vector(), terrainPixelAtom.release(), 0); +#ifndef RELEASE_BUILD + terrainPixel->SetPresetName("Dislodged Terrain Pixel from Material " + std::to_string(sceneMat->GetIndex())); +#endif + terrainPixel->SetToHitMOs(false); + dislodgedMOPixels.emplace_back(terrainPixel.release()); + } + + // Clear the terrain pixels. + if (matPixel != MaterialColorKeys::g_MaterialAir) { putpixel(m_MainBitmap, terrX, terrY, MaterialColorKeys::g_MaterialAir); } + if (colorPixel != ColorKeys::g_MaskColor) { + putpixel(m_FGColorLayer->GetBitmap(), terrX, terrY, ColorKeys::g_MaskColor); + g_SceneMan.RegisterTerrainChange(terrX, terrY, 1, 1, ColorKeys::g_MaskColor, false); + } } - if (matPixel == g_MaterialAir) - _putpixel(m_pFGColor->GetBitmap(), wrapX, wrapY, g_MaskColor); } + } + // TODO: improve fit/tightness of box here. + m_UpdatedMaterialAreas.emplace_back(Box(pos - pivot, static_cast(maxWidth), static_cast(maxHeight))); + + return dislodgedMOPixels; + } - } - } - - release_bitmap(m_pMainBitmap); - release_bitmap(m_pFGColor->GetBitmap()); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CleanAir -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes any color pixel in the color layer of this SLTerrain wherever -// there is an air material pixel in the material layer. - -void SLTerrain::CleanAir() -{ - acquire_bitmap(m_pMainBitmap); - acquire_bitmap(m_pFGColor->GetBitmap()); - - int width = m_pMainBitmap->w; - int height = m_pMainBitmap->h; - unsigned char matPixel; - - for (int y = 0; y < height; ++y) { - for (int x = 0; x < width; ++x) { - matPixel = _getpixel(m_pMainBitmap, x, y); - if (matPixel == g_MaterialCavity) { - _putpixel(m_pMainBitmap, x, y, g_MaterialAir); - matPixel = g_MaterialAir; - } - if (matPixel == g_MaterialAir) - _putpixel(m_pFGColor->GetBitmap(), x, y, g_MaskColor); - } - } - - release_bitmap(m_pMainBitmap); - release_bitmap(m_pFGColor->GetBitmap()); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearAllMaterial -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes any FG and material pixels completely form teh terrain. -// For the editor mostly. - -void SLTerrain::ClearAllMaterial() -{ - clear_to_color(m_pMainBitmap, g_MaskColor); - clear_to_color(m_pFGColor->GetBitmap(), g_MaterialAir); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this SLTerrain. Supposed to be done every frame. - -void SLTerrain::Update() -{ - SceneLayer::Update(); - - m_pFGColor->SetOffset(m_Offset); - m_pBGColor->SetOffset(m_Offset); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawBackground -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this SLTerrain's current scrolled position to a bitmap. - -void SLTerrain::DrawBackground(BITMAP *pTargetBitmap, Box &targetBox, const Vector &scrollOverride) -{ - m_pBGColor->Draw(pTargetBitmap, targetBox, scrollOverride); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this SLTerrain's current scrolled position to a bitmap. - -void SLTerrain::Draw(BITMAP *pTargetBitmap, Box &targetBox, const Vector &scrollOverride) const -{ - if (m_DrawMaterial) - { - SceneLayer::Draw(pTargetBitmap, targetBox, scrollOverride); - } - else - { - m_pFGColor->Draw(pTargetBitmap, targetBox, scrollOverride); - } -} - -} // namespace RTE \ No newline at end of file +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SLTerrain::Update() { + SceneLayer::Update(); + + m_FGColorLayer->SetOffset(m_Offset); + m_BGColorLayer->SetOffset(m_Offset); + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SLTerrain::Draw(BITMAP *targetBitmap, Box &targetBox, bool offsetNeedsScrollRatioAdjustment) { + switch (m_LayerToDraw) { + case LayerType::MaterialLayer: + SceneLayer::Draw(targetBitmap, targetBox); + break; + case LayerType::ForegroundLayer: + m_FGColorLayer->Draw(targetBitmap, targetBox); + break; + case LayerType::BackgroundLayer: + m_BGColorLayer->Draw(targetBitmap, targetBox); + break; + default: + RTEAbort("Invalid LayerType was set to draw in SLTerrain::Draw!"); + break; + } + } +} \ No newline at end of file diff --git a/Entities/SLTerrain.h b/Entities/SLTerrain.h index 4af09eb92..5b613d8eb 100644 --- a/Entities/SLTerrain.h +++ b/Entities/SLTerrain.h @@ -1,751 +1,272 @@ #ifndef _RTESLTERRAIN_ #define _RTESLTERRAIN_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: SLTerrain.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the SLTerrain class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "SceneLayer.h" #include "Matrix.h" -#include "Box.h" -#include "Material.h" - -namespace RTE -{ - -class MOPixel; -class TerrainDebris; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: SLTerrain -////////////////////////////////////////////////////////////////////////////////////////// -// Description: The terrain of the RTE scene. -// Parent(s): SceneLayer -// Class history: 01/08/2002 SLTerrain created. - -class SLTerrain : public SceneLayer { - - ////////////////////////////////////////////////////////////////////////////////////////// - // Nested class: TerrainFrosting - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Specifies a 'frosting' layer of material on top of another material - // in the terrain. Gets applied upon loading the scene. - // Parent(s): Serializable. - // Class history: 04/04/2007 TerrainFrosting created. - class TerrainFrosting: - public Serializable - { +namespace RTE { + class MOPixel; + class TerrainFrosting; + class TerrainObject; + class TerrainDebris; - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations + /// + /// Collection of scrolling layers that compose the terrain of the Scene. + /// + class SLTerrain : public SceneLayer { - public: + public: - SerializableClassNameGetter; + EntityAllocation(SLTerrain); SerializableOverrideMethods; - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: TerrainFrosting - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a TerrainFrosting object in system - // memory. Create() should be called before using the object. - // Arguments: None. - - TerrainFrosting() { Clear(); } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: TerrainFrosting - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a TerrainFrosting object in system - // memory, deep copying another. - // Arguments: None. - - TerrainFrosting(const TerrainFrosting &reference) { Clear(); Create(reference); } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a TerrainFrosting to be identical to another, by deep copy. - // Arguments: A reference to the TerrainFrosting to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - - int Create(const TerrainFrosting &reference); - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire Serializable, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - - void Reset() override { Clear(); } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetTargetMaterial - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the target Material that this TerrainFrosting will appear on top of. - // Arguments: None. - // Return value: A copy of the target Material - - Material &GetTargetMaterial() { return m_TargetMaterial; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetFrostingMaterial - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the Material of this TerrainFrosting. - // Arguments: None. - // Return value: A copy of the Material - - Material &GetFrostingMaterial() { return m_FrostingMaterial; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetThicknessSample - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a random thickness sample between the min and max possible - // thicknesses of this TerrainFrosting - // Arguments: None. - // Return value: The thickness sample. - - int GetThicknessSample() { return m_MinThickness + RandomNum(0, m_MaxThickness - m_MinThickness); } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: InAirOnly - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the frosting only appears where there is air in the - // terrain. - // Arguments: None. - // Return value: Whether only appears in air particles. - - bool InAirOnly() { return m_InAirOnly; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - - protected: - - // The material this frosting will sit on top in the terrain - Material m_TargetMaterial; - // Material of this frosting that will be piled on top of the target - Material m_FrostingMaterial; - // The minimum height in pixels above the target material - int m_MinThickness; - // The max thickness - int m_MaxThickness; - // Whether the frosting only appears where there is air - bool m_InAirOnly; - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - - private: - - static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this TerrainFrosting, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - - void Clear(); - - }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions -EntityAllocation(SLTerrain) -SerializableOverrideMethods -ClassInfoGetters - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: SLTerrain -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a SLTerrain object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - SLTerrain() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~SLTerrain -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a SLTerrain object before deletion -// from system memory. -// Arguments: None. - - ~SLTerrain() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the SLTerrain object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the SLTerrain object ready for use. -// Arguments: The path of the bitmap file to load as this SLTerrain's graphical -// representation. -// Whether to draw transperently using a color key specified by the pixel -// in the upper left corner of the loaded bitmap. -// The initial scroll offset. -// Two bools that define whether the layer should wrap around or stop when -// scrolling beyond its bitmap's boundries. -// A vector whose components defines two different things, depending on -// wrapX/Y arguments. If a wrap argument is set to false, the -// corresponding component here will be interpreted as the width (X) or -// height (Y) (in pixels) of the total bitmap area that this layer is -// allowed to scroll across before stopping at an edge. If wrapping is -// set to true, the value in scrollInfo is simply the ratio of offset at -// which any scroll operations will be done in. A special command is if -// wrap is false and the corresponding component is -1.0, that signals -// that the own width or height should be used as scrollInfo input. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - -// TODO: streamline interface") - int Create(char *filename, - bool drawTrans, - Vector offset, - bool wrapX, - bool wrapY, - Vector scrollInfo); -*/ - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a SLTerrain to be identical to another, by deep copy. -// Arguments: A reference to the SLTerrain to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const SLTerrain &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: LoadData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Actually loads previously specified/created data into memory. Has -// to be done before using this SceneLayer. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int LoadData() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SaveData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves data currently in memory to disk. -// Arguments: The filepath base to the where to save the Bitmap data. This means -// everything up to the extension. "FG" and "Mat" etc will be added. -// Return value: An error return value signaling success or any particular failure. -// Anything below 0 is an error signal. - - int SaveData(std::string pathBase) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ClearData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears out any previously loaded bitmap data from memory. -// Arguments: None. -// Return value: An error return value signaling success or any particular failure. -// Anything below 0 is an error signal. - - int ClearData() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsFileData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Whether this' bitmap data is loaded from a file or was generated. -// Arguments: None. -// Return value: Whether the data in this' bitmap was loaded from a datafile, or generated. - - bool IsFileData() const override { return m_pFGColor && m_pFGColor->IsFileData() && m_pBGColor && m_pBGColor->IsFileData(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire SLTerrain, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); SceneLayer::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SLTerrain object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: LockBitmaps -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Lock the internal bitmaps so it can be accessed by GetColorPixel() etc. -// Doing it in a separate method like this is more efficient because -// many bitmap accesses can be performed between a lock and unlock. -// UnlockBitmaps() should always be called after accesses are completed. -// Arguments: None. -// Return value: None. - - void LockBitmaps() override { SceneLayer::LockBitmaps(); acquire_bitmap(m_pMainBitmap); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UnlockBitmaps -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Unlocks the internal bitmaps and prevents access to display memory. -// Doing it in a separate method like this is more efficient because -// many bitmap accesses can be performed between a lock and an unlock. -// UnlockBitmaps() should only be called after LockBitmaps(). -// Arguments: None. -// Return value: None. - - void UnlockBitmaps() override { SceneLayer::UnlockBitmaps(); release_bitmap(m_pMainBitmap); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetFGColorBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the foreground color bitmap of this SLTerrain. -// Arguments: None. -// Return value: A pointer to the foreground color bitmap. - - BITMAP * GetFGColorBitmap() { return m_pFGColor->GetBitmap(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBGColorBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the background color bitmap of this SLTerrain. -// Arguments: None. -// Return value: A pointer to the foreground color bitmap. - - BITMAP * GetBGColorBitmap() { return m_pBGColor->GetBitmap(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMaterialBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the material bitmap of this SLTerrain. -// Arguments: None. -// Return value: A pointer to the material bitmap. - - BITMAP * GetMaterialBitmap() { return m_pMainBitmap; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetFGColorPixel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a specific pixel from the foreground color bitmap of this. -// LockBitmaps() must be called before using this method. -// Arguments: The X and Y coordinates of which pixel to get. -// Return value: An unsigned char specifying the requested pixel's BG Color. - - unsigned char GetFGColorPixel(const int pixelX, const int pixelY) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBGColorPixel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a specific pixel from the background color bitmap of this. -// LockBitmaps() must be called before using this method. -// Arguments: The X and Y coordinates of which pixel to get. -// Return value: An unsigned char specifying the requested pixel's BG Color. - - unsigned char GetBGColorPixel(const int pixelX, const int pixelY) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMaterialPixel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a specific pixel from the material bitmap of this SceneLayer. -// LockBitmaps() must be called before using this method. -// Arguments: The X and Y coordinates of which pixel to get. -// Return value: An unsigned char specifying the requested pixel's material index. - - unsigned char GetMaterialPixel(const int pixelX, const int pixelY) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsAirPixel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether a terrain pixel is of air or cavity material. -// Arguments: The X and Y coordinates of which pixel to check for airness. -// Return value: A bool with the answer. - - bool IsAirPixel(const int pixelX, const int pixelY) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetFGColorPixel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets a specific pixel on the FG Color bitmap of this SLTerrain to a -// specific color. LockBitmaps() must be called before using this -// method. -// Arguments: The X and Y coordinates of which pixel to set. -// The color index to set the pixel to. -// Return value: None. - - void SetFGColorPixel(const int pixelX, const int pixelY, const int color); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetBGColorPixel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets a specific pixel on the BG Color bitmap of this SLTerrain to a -// specific color. LockBitmaps() must be called before using this -// method. -// Arguments: The X and Y coordinates of which pixel to set. -// The color index to set the pixel to. -// Return value: None. - - void SetBGColorPixel(const int pixelX, const int pixelY, const int color); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetMaterialPixel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets a specific pixel on the material bitmap of this SLTerrain to a -// specific material. LockMaterialBitmap() must be called before using this -// method. -// Arguments: The X and Y coordinates of which pixel to set. -// The material index to set the pixel to. -// Return value: None. - - void SetMaterialPixel(const int pixelX, const int pixelY, const unsigned char material); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetStructuralBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the structural bitmap of this Terrain. -// Arguments: None. -// Return value: A pointer to the material bitmap. - - BITMAP * GetStructuralBitmap() { return m_pStructural; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetToDrawMaterial -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets whether drawing the material layer instead of the normal color -// layer when drawing this SLTerrain. -// Arguments: The setting, whether to draw the material later instead of the color -// layer or not. -// Return value: None. - - bool GetToDrawMaterial() { return m_DrawMaterial; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EraseSilhouette -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Takes a BITMAP and scans through the pixels on this terrain for pixels -// which overlap with it. Erases them from the terrain and can optionally -// generate MOPixel:s based on the erased or 'dislodged' terrain pixels. -// Arguments: A pointer to the source BITMAP whose rotozoomed silhouette will be used -// as a cookie-cutter on the terrain. -// The position coordinates of the sprite. -// The sprite's current rotation in radians. -// The sprite's current scale coefficient -// Whether to generate any MOPixel:s from the erased terrain pixels. -// How many pixels to skip making MOPixels from, between each that gets -// made. 0 means every pixel turns into an MOPixel. -// The max number of MOPixels to make, if they are to be made. -// Return value: A deque filled with the MOPixel:s of the terrain that are now dislodged. -// Note that ownership of all the MOPixel:s in the deque ARE transferred! -// This will be empty if makeMOPs is false. - - std::deque EraseSilhouette(BITMAP *pSprite, - Vector pos, - Vector pivot, - Matrix rotation, - float scale, - bool makeMOPs = true, - int skipMOP = 2, - int maxMOPs = 150); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetToDrawMaterial -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether to draw the material layer instead of the normal color -// layer when drawing this SLTerrain. -// Arguments: The setting, whether to draw the material later instead of the color -// layer or not. -// Return value: None. - - void SetToDrawMaterial(bool drawMaterial) { m_DrawMaterial = drawMaterial; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ApplyObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws a passed in Object's graphical and material representations to -// this Terrain's respective layers. -// Arguments: The Object to apply to this Terrain. Ownership is NOT xferred! -// Return value: Whether successful or not. - - bool ApplyObject(Entity *pEntity); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ApplyMovableObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws a passed in MovableObject's graphical and material -// representations to this Terrain's respective layers. -// Arguments: The MovableObject to apply to this Terrain. Ownership is NOT xferred! -// Return value: None. - - void ApplyMovableObject(MovableObject *pMObject); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ApplyTerrainObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws a passed in TerrainObject's graphical and material -// representations to this Terrain's respective layers. -// Arguments: The TerrainObject to apply to this Terrain. Ownership is NOT xferred! -// Return value: None. - - void ApplyTerrainObject(TerrainObject *pTObject); - - - void RegisterTerrainChange(TerrainObject *pTObject); - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddUpdatedMaterialArea -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds a notification that an area of the material terrain has been -// updated. -// Arguments: The Box defining the newly updated material area that can be unwrapped -// and may be out of bounds of the scene. -// Return value: None. - - void AddUpdatedMaterialArea(const Box &newArea) { m_UpdatedMateralAreas.push_back(newArea); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetUpdatedMaterialAreas -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a list of unwrapped boxes which show the areas where the material -// layer has had objects applied to it since last call to ClearUpdatedAreas(). -// Arguments: None. -// Return value: Reference to the list that has been filled with Box:es which are -// unwrapped and may be out of bounds of the scene! - - std::list & GetUpdatedMaterialAreas() { return m_UpdatedMateralAreas; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsBoxBuried -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks whether a bounding box is completely buried in the terrain. -// Arguments: The box to check. -// Return value: Whether the box is completely buried., ie no corner sticks out in the -// air. - - bool IsBoxBuried(const Box &checkBox) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearUpdatedAreas -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears the list of updated areas in the material layer. -// Arguments: None. -// Return value: None. - - void ClearUpdatedAreas() { m_UpdatedMateralAreas.clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CleanAirBox -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes any color pixel in the color layer of this SLTerrain wherever -// there is an air material pixel in the material layer inside the specified box. -// Arguments: Box to clean. Whether the scene is X-wrapped or Y-wrapped. -// Return value: None. - - void CleanAirBox(Box box, bool wrapsX, bool wrapsY); - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CleanAir -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes any color pixel in the color layer of this SLTerrain wherever -// there is an air material pixel in the material layer. -// Arguments: None. -// Return value: None. - - void CleanAir(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearAllMaterial -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes any FG and material pixels completely form teh terrain. -// For the editor mostly. -// Arguments: None. -// Return value: None. - - void ClearAllMaterial(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this SLTerrain. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - void Update(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawDrawBackground -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this SLTerrain's background layer's current scrolled position -// to a bitmap. -// Arguments: The bitmap to draw to. -// The box on the target bitmap to limit drawing to, with the corner of -// box being where the scroll position lines up. -// If a non-{-1,-1} vector is passed, the internal scroll offset of this -// is overridder with it. It becomes the new source coordinates. -// Return value: None. - - void DrawBackground(BITMAP *pTargetBitmap, Box& targetBox, const Vector &scrollOverride = Vector(-1, -1)); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this SLTerrain's foreground's current scrolled position to a bitmap. -// Arguments: The bitmap to draw to. -// The box on the target bitmap to limit drawing to, with the corner of -// box being where the scroll position lines up. -// If a non-{-1,-1} vector is passed, the internal scroll offset of this -// is overridder with it. It becomes the new source coordinates. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, Box& targetBox, const Vector &scrollOverride = Vector(-1, -1)) const override; - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - // Member variables - static Entity::ClassInfo m_sClass; - - SceneLayer *m_pFGColor; - SceneLayer *m_pBGColor; - BITMAP *m_pStructural; - ContentFile m_BGTextureFile; - - std::list m_TerrainFrostings; - std::list m_TerrainDebris; - std::list m_TerrainObjects; - - // List of areas of the material layer which have been affected by the updating of new objects copied to it - // These boxes are NOT wrapped, and can be out of bounds! - std::list m_UpdatedMateralAreas; - - // Draw the material layer instead of the color layer. - bool m_DrawMaterial; - - // Intermediate test layers, deffernt sizes for efficiency - static BITMAP *m_spTempBitmap16; - static BITMAP *m_spTempBitmap32; - static BITMAP *m_spTempBitmap64; - static BITMAP *m_spTempBitmap128; - static BITMAP *m_spTempBitmap256; - static BITMAP *m_spTempBitmap512; - - // Indicates, that before processing frostings-related properties for this terrain - // derived list with frostings must be cleared to avoid duplication when loading scenes - bool m_NeedToClearFrostings; - // Indicates, that before processing debris-related properties for this terrain - // derived list with debris must be cleared to avoid duplication when loading scenes - bool m_NeedToClearDebris; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this SLTerrain, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - SLTerrain(const SLTerrain &reference) = delete; - SLTerrain & operator=(const SLTerrain &rhs) = delete; - -}; - -} // namespace RTE - -#endif // File \ No newline at end of file + ClassInfoGetters; + + /// + /// Enumeration for the different type of layers in the SLTerrain. + /// + enum class LayerType { ForegroundLayer, BackgroundLayer, MaterialLayer }; + +#pragma region Creation + /// + /// Constructor method used to instantiate a SLTerrain object in system memory. Create() should be called before using the object. + /// + SLTerrain() { Clear(); } + + /// + /// Makes the SLTerrain object ready for use. + /// + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int Create() override; + + /// + /// Creates a SLTerrain to be identical to another, by deep copy. + /// + /// A reference to the SLTerrain to deep copy. + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int Create(const SLTerrain &reference); +#pragma endregion + +#pragma region Destruction + /// + /// Destructor method used to clean up a SLTerrain object before deletion from system memory. + /// + ~SLTerrain() override { Destroy(true); } + + /// + /// Destroys and resets (through Clear()) the SLTerrain object. + /// + /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + void Destroy(bool notInherited = false) override { if (!notInherited) { SceneLayer::Destroy(); } Clear(); } +#pragma endregion + +#pragma region Data Handling + /// + /// Whether this SLTerrain's bitmap data is loaded from a file or was generated at runtime. + /// + /// Whether this SLTerrain's bitmap data was loaded from a file or was generated at runtime. + bool IsLoadedFromDisk() const override { return (m_FGColorLayer && m_FGColorLayer->IsLoadedFromDisk()) && (m_BGColorLayer && m_BGColorLayer->IsLoadedFromDisk()); } + + /// + /// Loads previously specified/created bitmap data into memory. Has to be done before using this SLTerrain if the bitmap was not generated at runtime. + /// + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int LoadData() override; + + /// + /// Saves bitmap data currently in memory to disk. + /// + /// The filepath base to the where to save the Bitmap data. This means everything up to the extension. "FG" and "Mat" etc will be added. + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int SaveData(const std::string &pathBase) override; + + /// + /// Clears out any previously loaded bitmap data from memory. + /// + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int ClearData() override; +#pragma endregion + +#pragma region Getters and Setters + /// + /// Sets the layer of this SLTerrain that should be drawn to the screen when Draw() is called. + /// + /// The layer that should be drawn. See LayerType enumeration. + void SetLayerToDraw(LayerType layerToDraw) { m_LayerToDraw = layerToDraw; } + + /// + /// Gets the foreground color bitmap of this SLTerrain. + /// + /// A pointer to the foreground color bitmap. + BITMAP * GetFGColorBitmap() { return m_FGColorLayer->GetBitmap(); } + + /// + /// Gets the background color bitmap of this SLTerrain. + /// + /// A pointer to the background color bitmap. + BITMAP * GetBGColorBitmap() { return m_BGColorLayer->GetBitmap(); } + + /// + /// Gets the material bitmap of this SLTerrain. + /// + /// A pointer to the material bitmap. + BITMAP * GetMaterialBitmap() { return m_MainBitmap; } + + /// + /// Gets a specific pixel from the foreground color bitmap of this. LockBitmaps() must be called before using this method. + /// + /// The X coordinate of the pixel to get. + /// The Y coordinate of the pixel to get. + /// An int specifying the requested pixel's foreground color index. + int GetFGColorPixel(int pixelX, int pixelY) const { return m_FGColorLayer->GetPixel(pixelX, pixelY); } + + /// + /// Sets a specific pixel on the foreground color bitmap of this SLTerrain to a specific color. LockBitmaps() must be called before using this method. + /// + /// The X coordinate of the pixel to set. + /// The Y coordinate of the pixel to set. + /// The color index to set the pixel to. + void SetFGColorPixel(int pixelX, int pixelY, const int materialID) const { m_FGColorLayer->SetPixel(pixelX, pixelY, materialID); } + + /// + /// Gets a specific pixel from the background color bitmap of this. LockBitmaps() must be called before using this method. + /// + /// The X coordinate of the pixel to get. + /// The Y coordinate of the pixel to get. + /// An int specifying the requested pixel's background color index. + int GetBGColorPixel(int pixelX, int pixelY) const { return m_BGColorLayer->GetPixel(pixelX, pixelY); } + + /// + /// Sets a specific pixel on the background color bitmap of this SLTerrain to a specific color. LockBitmaps() must be called before using this method. + /// + /// The X coordinate of the pixel to set. + /// The Y coordinate of the pixel to set. + /// The color index to set the pixel to. + void SetBGColorPixel(int pixelX, int pixelY, int materialID) const { m_BGColorLayer->SetPixel(pixelX, pixelY, materialID); } + + /// + /// Gets a specific pixel from the material bitmap of this SceneLayer. LockBitmaps() must be called before using this method. + /// + /// The X coordinate of the pixel to get. + /// The Y coordinate of the pixel to get. + /// An int specifying the requested pixel's material index. + int GetMaterialPixel(int pixelX, int pixelY) const { return GetPixel(pixelX, pixelY); } + + /// + /// Sets a specific pixel on the material bitmap of this SLTerrain to a specific material. LockMaterialBitmap() must be called before using this method. + /// + /// The X coordinate of the pixel to set. + /// The Y coordinate of the pixel to set. + /// The material index to set the pixel to. + void SetMaterialPixel(int pixelX, int pixelY, int materialID) const { SetPixel(pixelX, pixelY, materialID); } + + /// + /// Indicates whether a terrain pixel is of Air or Cavity material. + /// + /// The X coordinate of the pixel to check. + /// The Y coordinate of the pixel to check. + /// Whether the terrain pixel is of Air or Cavity material. + bool IsAirPixel(int pixelX, int pixelY) const; + + /// + /// Checks whether a bounding box is completely buried in the terrain. + /// + /// The box to check. + /// Whether the box is completely buried, i.e. no corner sticks out in the Air or Cavity. + bool IsBoxBuried(const Box &checkBox) const; +#pragma endregion + +#pragma region Concrete Methods + /// + /// Gets a deque of unwrapped boxes which show the areas where the material layer has had objects applied to it since last call to ClearUpdatedMaterialAreas(). + /// + /// Reference to the deque that has been filled with Boxes which are unwrapped and may be out of bounds of the scene! + const std::deque & GetUpdatedMaterialAreas() const { return m_UpdatedMaterialAreas; } + + /// + /// Adds a notification that an area of the material terrain has been updated. + /// + /// The Box defining the newly updated material area that can be unwrapped and may be out of bounds of the scene. + void AddUpdatedMaterialArea(const Box &newArea) { m_UpdatedMaterialAreas.emplace_back(newArea); } + + /// + /// Clears the list of updated areas in the material layer (main bitmap). + /// + void ClearUpdatedMaterialAreas() { m_UpdatedMaterialAreas.clear(); } + + /// + /// Removes any color pixel in the color layer of this SLTerrain wherever there is an air material pixel in the material layer. + /// + void CleanAir(); + + /// + /// Removes any color pixel in the color layer of this SLTerrain wherever there is an air material pixel in the material layer inside the specified box. + /// + /// Box to clean. + /// Whether the scene is X-wrapped. + /// Whether the scene is Y-wrapped. + void CleanAirBox(const Box &box, bool wrapsX, bool wrapsY); + + /// + /// Takes a BITMAP and scans through the pixels on this terrain for pixels which overlap with it. Erases them from the terrain and can optionally generate MOPixels based on the erased or 'dislodged' terrain pixels. + /// + /// A pointer to the source BITMAP whose silhouette will be used as a cookie-cutter on the terrain. + /// The position coordinates of the sprite. + /// The pivot coordinate of the sprite. + /// The sprite's current rotation in radians. + /// The sprite's current scale coefficient. + /// Whether to generate any MOPixels from the erased terrain pixels. + /// How many pixels to skip making MOPixels from, between each that gets made. 0 means every pixel turns into an MOPixel. + /// The max number of MOPixels to make, if they are to be made. + /// A deque filled with the MOPixels of the terrain that are now dislodged. This will be empty if makeMOPs is false. Note that ownership of all the MOPixels in the deque IS transferred! + std::deque EraseSilhouette(BITMAP *sprite, const Vector &pos, const Vector &pivot, const Matrix &rotation, float scale, bool makeMOPs = true, int skipMOP = 2, int maxMOPs = 150); +#pragma endregion + +#pragma region Virtual Override Methods + /// + /// Updates the state of this SLTerrain. + /// + void Update() override; + + /// + /// Draws this SLTerrain's current scrolled position to a bitmap. + /// + /// The bitmap to draw to. + /// The box on the target bitmap to limit drawing to, with the corner of box being where the scroll position lines up. + /// Whether the offset of this SceneLayer or the passed in offset override need to be adjusted to scroll ratio. + void Draw(BITMAP *targetBitmap, Box &targetBox, bool offsetNeedsScrollRatioAdjustment = false) override; +#pragma endregion + + private: + + static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. + + std::unique_ptr m_FGColorLayer; //!< The foreground color layer of this SLTerrain. + std::unique_ptr m_BGColorLayer; //!< The background color layer of this SLTerrain. + + LayerType m_LayerToDraw; //!< The layer of this SLTerrain that should be drawn to the screen when Draw() is called. See LayerType enumeration. + + ContentFile m_DefaultBGTextureFile; //!< The background texture file that will be used to texturize Materials that have no defined background texture. + + std::vector m_TerrainFrostings; //!< The TerrainFrostings that need to be placed on this SLTerrain. + std::vector m_TerrainDebris; //!< The TerrainDebris that need to be placed on this SLTerrain. + std::vector m_TerrainObjects; //!< The TerrainObjects that need to be placed on this SLTerrain. + + std::deque m_UpdatedMaterialAreas; //!< List of areas of the material layer (main bitmap) which have been affected by new objects copied to it. These boxes are NOT wrapped, and can be out of bounds! + + /// + /// Applies Material textures to the foreground and background color layers, based on the loaded material layer (main bitmap). + /// + void TexturizeTerrain(); + + /// + /// Clears all the member variables of this SLTerrain, effectively resetting the members of this abstraction level only. + /// + void Clear(); + + // Disallow the use of some implicit methods. + SLTerrain(const SLTerrain &reference) = delete; + SLTerrain & operator=(const SLTerrain &rhs) = delete; + }; +} +#endif \ No newline at end of file diff --git a/Entities/Scene.cpp b/Entities/Scene.cpp index 9de596df4..b30673f54 100644 --- a/Entities/Scene.cpp +++ b/Entities/Scene.cpp @@ -27,6 +27,7 @@ #include "Actor.h" #include "BunkerAssemblyScheme.h" #include "BunkerAssembly.h" +#include "SLBackground.h" #include "ADoor.h" #include "AHuman.h" @@ -533,8 +534,8 @@ int Scene::Create(const Scene &reference) m_PlacedObjects[set].push_back(dynamic_cast((*oItr)->Clone())); } - for (list::const_iterator lItr = reference.m_BackLayerList.begin(); lItr != reference.m_BackLayerList.end(); ++lItr) - m_BackLayerList.push_back(dynamic_cast((*lItr)->Clone())); + for (list::const_iterator lItr = reference.m_BackLayerList.begin(); lItr != reference.m_BackLayerList.end(); ++lItr) + m_BackLayerList.push_back(dynamic_cast((*lItr)->Clone())); for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) { @@ -590,7 +591,10 @@ int Scene::LoadData(bool placeObjects, bool initPathfinding, bool placeUnits) RTEAbort("Loading Terrain " + m_pTerrain->GetPresetName() + "\'s data failed!"); return -1; } - + for (SLBackground *backgroundLayer : m_BackLayerList) { + backgroundLayer->InitScaleFactors(); + } + /////////////////////////////////// // Load Unseen layers before applying objects to the scene, // so we can reveal around stuff that is getting placed for the appropriate team @@ -834,19 +838,21 @@ int Scene::LoadData(bool placeObjects, bool initPathfinding, bool placeUnits) // Finally place TerrainObject if (pTO) { + const BITMAP *terrainObjectBitmap = pTO->GetFGColorBitmap() ? pTO->GetFGColorBitmap() : pTO->GetBGColorBitmap(); + // First clear out the box of unseen layer for whichever team placed this - if (pTO->GetFGColorBitmap() && pTO->GetPlacedByPlayer() != Players::NoPlayer && g_ActivityMan.GetActivity()) + if (terrainObjectBitmap && pTO->GetPlacedByPlayer() != Players::NoPlayer && g_ActivityMan.GetActivity()) { // Learn which team placed this thing so we can reveal for them only int ownerTeam = pTO->GetTeam(); if (ownerTeam != Activity::NoTeam && m_apUnseenLayer[ownerTeam] && m_apUnseenLayer[ownerTeam]->GetBitmap()) { // Translate to the scaled unseen layer's coordinates - Vector scale = m_apUnseenLayer[ownerTeam]->GetScaleInverse(); - int scaledX = std::floor((pTO->GetPos().m_X - (float)(pTO->GetFGColorBitmap()->w / 2)) * scale.m_X); - int scaledY = std::floor((pTO->GetPos().m_Y - (float)(pTO->GetFGColorBitmap()->h / 2)) * scale.m_Y); - int scaledW = std::ceil(pTO->GetFGColorBitmap()->w * scale.m_X); - int scaledH = std::ceil(pTO->GetFGColorBitmap()->h * scale.m_Y); + Vector scale = m_apUnseenLayer[ownerTeam]->GetScaleFactor(); + int scaledX = std::floor((pTO->GetPos().m_X - (float)(terrainObjectBitmap->w / 2)) / scale.m_X); + int scaledY = std::floor((pTO->GetPos().m_Y - (float)(terrainObjectBitmap->h / 2)) / scale.m_Y); + int scaledW = std::ceil(terrainObjectBitmap->w / scale.m_X); + int scaledH = std::ceil(terrainObjectBitmap->h / scale.m_Y); // Fill the box with key color for the owner ownerTeam, revealing the area that this thing is on rectfill(m_apUnseenLayer[ownerTeam]->GetBitmap(), scaledX, scaledY, scaledX + scaledW, scaledY + scaledH, g_MaskColor); // Expand the box a little so the whole placed object is going to be hidden @@ -863,7 +869,7 @@ int Scene::LoadData(bool placeObjects, bool initPathfinding, bool placeUnits) } } // Now actually stamp the terrain object onto the terrain's scene layers - m_pTerrain->ApplyTerrainObject(pTO); + pTO->PlaceOnTerrain(m_pTerrain); //delete pTO; //pTO = 0; } @@ -915,17 +921,6 @@ int Scene::LoadData(bool placeObjects, bool initPathfinding, bool placeUnits) m_pPathFinder = new PathFinder(this, 20, 2000); // Update all the pathfinding data m_pPathFinder->RecalculateAllCosts(); - - // Load Background layers' data - for (list::iterator slItr = m_BackLayerList.begin(); slItr != m_BackLayerList.end(); ++slItr) - { - RTEAssert((*slItr), "Background layer not instantiated before trying to load its data!"); - if ((*slItr)->LoadData() < 0) - { - g_ConsoleMan.PrintString("ERROR: Loading background layer " + (*slItr)->GetPresetName() + "\'s data failed!"); - return -1; - } - } } return 0; @@ -1043,7 +1038,7 @@ int Scene::SaveData(string pathBase) { std::snprintf(str, sizeof(str), "T%d", team); // Save unseen layer data to disk - if (m_apUnseenLayer[team]->SaveData(pathBase + " US" + str + ".bmp") < 0) + if (m_apUnseenLayer[team]->SaveData(pathBase + " US" + str + ".png") < 0) { g_ConsoleMan.PrintString("ERROR: Saving unseen layer " + m_apUnseenLayer[team]->GetPresetName() + "\'s data failed!"); return -1; @@ -1143,17 +1138,6 @@ int Scene::ClearData() return -1; } - // Clear Background layers' data - for (list::iterator slItr = m_BackLayerList.begin(); slItr != m_BackLayerList.end(); ++slItr) - { - RTEAssert((*slItr), "Background layer not instantiated before trying to clear its data!"); - if ((*slItr)->ClearData() < 0) - { - g_ConsoleMan.PrintString("ERROR: Clearing background layer " + (*slItr)->GetPresetName() + "\'s data failed!"); - return -1; - } - } - // Clear unseen layers' data for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) { @@ -1256,7 +1240,7 @@ int Scene::ReadProperty(const std::string_view &propName, Reader &reader) } else if (propName == "AddBackgroundLayer") { - SceneLayer *pLayer = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); + SLBackground *pLayer = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); RTEAssert(pLayer, "Something went wrong with reading SceneLayer"); if (pLayer) m_BackLayerList.push_back(pLayer); @@ -1545,7 +1529,7 @@ int Scene::Save(Writer &writer) const } } - for (list::const_iterator slItr = m_BackLayerList.begin(); slItr != m_BackLayerList.end(); ++slItr) + for (list::const_iterator slItr = m_BackLayerList.begin(); slItr != m_BackLayerList.end(); ++slItr) { writer.NewProperty("AddBackgroundLayer"); (*slItr)->SavePresetCopy(writer); @@ -1648,7 +1632,7 @@ void Scene::Destroy(bool notInherited) } } - for (list::iterator slItr = m_BackLayerList.begin(); slItr != m_BackLayerList.end(); ++slItr) + for (list::iterator slItr = m_BackLayerList.begin(); slItr != m_BackLayerList.end(); ++slItr) { delete (*slItr); *slItr = 0; @@ -1792,7 +1776,7 @@ bool Scene::CleanOrphanPixel(int posX, int posY, NeighborDirection checkingFrom, return false; // Do any necessary wrapping - m_apUnseenLayer[team]->WrapPosition(posX, posY, false); + m_apUnseenLayer[team]->WrapPosition(posX, posY); // First check the actual position of the checked pixel, it may already been seen. if (getpixel(m_apUnseenLayer[team]->GetBitmap(), posX, posY) == g_MaskColor) @@ -1805,56 +1789,56 @@ bool Scene::CleanOrphanPixel(int posX, int posY, NeighborDirection checkingFrom, { testPosX = posX + 1; testPosY = posY; - m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY, false); + m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 1 : 0; } if (checkingFrom != W) { testPosX = posX - 1; testPosY = posY; - m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY, false); + m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 1 : 0; } if (checkingFrom != S) { testPosX = posX; testPosY = posY + 1; - m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY, false); + m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 1 : 0; } if (checkingFrom != N) { testPosX = posX; testPosY = posY - 1; - m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY, false); + m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 1 : 0; } if (checkingFrom != SE) { testPosX = posX + 1; testPosY = posY + 1; - m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY, false); + m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 0.5f : 0; } if (checkingFrom != SW) { testPosX = posX - 1; testPosY = posY + 1; - m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY, false); + m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 0.5f : 0; } if (checkingFrom != NW) { testPosX = posX - 1; testPosY = posY - 1; - m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY, false); + m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 0.5f : 0; } if (checkingFrom != NE) { testPosX = posX + 1; testPosY = posY - 1; - m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY, false); + m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 0.5f : 0; } @@ -2910,7 +2894,7 @@ void Scene::ResetPathFinding() void Scene::UpdatePathFinding() { m_pPathFinder->RecalculateAreaCosts(m_pTerrain->GetUpdatedMaterialAreas()); - m_pTerrain->ClearUpdatedAreas(); + m_pTerrain->ClearUpdatedMaterialAreas(); m_PartialPathUpdateTimer.Reset(); m_PathfindingUpdated = true; } diff --git a/Entities/Scene.h b/Entities/Scene.h index 6fe86df81..05a9ddf86 100644 --- a/Entities/Scene.h +++ b/Entities/Scene.h @@ -15,10 +15,8 @@ // Inclusions of header files #include "Entity.h" -#include "SceneMan.h" -#include "ActivityMan.h" #include "Box.h" -#include "BunkerAssembly.h" +#include "Activity.h" namespace RTE { @@ -26,6 +24,12 @@ namespace RTE class ContentFile; class MovableObject; class PathFinder; +class SLBackground; +class SLTerrain; +class SceneLayer; +class BunkerAssembly; +class SceneObject; +class Deployment; ////////////////////////////////////////////////////////////////////////////////////////// @@ -597,7 +601,7 @@ EntityAllocation(Scene) // Return value: A reference to the std::list containing all the background layers. // Ownership is NOT transferred! - std::list & GetBackLayers() { return m_BackLayerList; } + std::list & GetBackLayers() { return m_BackLayerList; } @@ -1346,7 +1350,7 @@ const SceneObject * PickPlacedActorInRange(int whichSet, Vector &scenePoint, int // SceneObject:s to be placed in the scene, divided up by different sets - OWNED HERE std::list m_PlacedObjects[PLACEDSETSCOUNT]; // List of background layers, first is the closest to the terrain, last is closest to the back - std::list m_BackLayerList; + std::list m_BackLayerList; // Dimensions of the pixels of the unseen layers, when they are dynamically generated. If 0, the layer was not generated Vector m_UnseenPixelSize[Activity::MaxTeamCount]; // Layers representing the unknown areas for each team diff --git a/Entities/SceneLayer.cpp b/Entities/SceneLayer.cpp index 1605b0eed..7e6e9f4ff 100644 --- a/Entities/SceneLayer.cpp +++ b/Entities/SceneLayer.cpp @@ -1,1036 +1,434 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: SceneLayer.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the SceneLayer class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com +#include "SceneLayer.h" +#include "FrameMan.h" +#include "SceneMan.h" +#include "SettingsMan.h" -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files +namespace RTE { -#include "SceneLayer.h" -#include "ContentFile.h" + ConcreteClassInfo(SceneLayer, Entity, 0); + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SceneLayer::Clear() { + m_BitmapFile.Reset(); + m_MainBitmap = nullptr; + m_MainBitmapOwned = false; + m_DrawMasked = true; + m_WrapX = true; + m_WrapY = true; + m_OriginOffset.Reset(); + m_Offset.Reset(); + m_ScrollInfo.SetXY(1.0F, 1.0F); + m_ScrollRatio.SetXY(1.0F, 1.0F); + m_ScaleFactor.SetXY(1.0F, 1.0F); + m_ScaledDimensions.SetXY(1.0F, 1.0F); + } -namespace RTE { +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SceneLayer::Create(const ContentFile &bitmapFile, bool drawMasked, const Vector &offset, bool wrapX, bool wrapY, const Vector &scrollInfo) { + m_BitmapFile = bitmapFile; + m_MainBitmap = m_BitmapFile.GetAsBitmap(); + Create(m_MainBitmap, drawMasked, offset, wrapX, wrapY, scrollInfo); + m_MainBitmapOwned = false; + + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SceneLayer::Create(BITMAP *bitmap, bool drawMasked, const Vector &offset, bool wrapX, bool wrapY, const Vector &scrollInfo) { + m_MainBitmap = bitmap; + RTEAssert(m_MainBitmap, "Null bitmap passed in when creating SceneLayer"); + + m_MainBitmapOwned = true; + + m_DrawMasked = drawMasked; + m_Offset = offset; + m_WrapX = wrapX; + m_WrapY = wrapY; + m_ScrollInfo = scrollInfo; + + InitScrollRatios(); + + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SceneLayer::Create(const SceneLayer &reference) { + Entity::Create(reference); + + m_BitmapFile = reference.m_BitmapFile; + m_DrawMasked = reference.m_DrawMasked; + m_WrapX = reference.m_WrapX; + m_WrapY = reference.m_WrapY; + m_OriginOffset = reference.m_OriginOffset; + m_ScrollInfo = reference.m_ScrollInfo; + m_ScrollRatio = reference.m_ScrollRatio; + m_ScaleFactor = reference.m_ScaleFactor; + m_ScaledDimensions = reference.m_ScaledDimensions; + + if (reference.m_MainBitmap) { + // Make a copy of the bitmap because it can be modified in some use cases. + BITMAP *bitmapToCopy = reference.m_MainBitmap; + RTEAssert(bitmapToCopy, "Couldn't load the bitmap file specified for SceneLayer!"); + + m_MainBitmap = create_bitmap_ex(8, bitmapToCopy->w, bitmapToCopy->h); + RTEAssert(m_MainBitmap, "Failed to allocate BITMAP in SceneLayer::Create"); + blit(bitmapToCopy, m_MainBitmap, 0, 0, 0, 0, bitmapToCopy->w, bitmapToCopy->h); + + InitScrollRatios(); + + m_MainBitmapOwned = true; + } else { + // If no bitmap to copy, then it has to be loaded with LoadData. + m_MainBitmapOwned = false; + } + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SceneLayer::ReadProperty(const std::string_view &propName, Reader &reader) { + if (propName == "WrapX") { + reader >> m_WrapX; + } else if (propName == "WrapY") { + reader >> m_WrapY; + } else if (propName == "BitmapFile") { + reader >> m_BitmapFile; + } else { + return Entity::ReadProperty(propName, reader); + } + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SceneLayer::Save(Writer &writer) const { + Entity::Save(writer); + + writer.NewPropertyWithValue("WrapX", m_WrapX); + writer.NewPropertyWithValue("WrapY", m_WrapY); + writer.NewPropertyWithValue("BitmapFile", m_BitmapFile); + + return 0; + } -ConcreteClassInfo(SceneLayer, Entity, 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this SceneLayer, effectively -// resetting the members of this abstraction level only. - -void SceneLayer::Clear() -{ - m_BitmapFile.Reset(); - m_pMainBitmap = 0; - m_MainBitmapOwned = false; - m_DrawTrans = true; - m_Offset.Reset(); - m_ScrollInfo.SetXY(1.0, 1.0); - m_ScrollRatio.SetXY(1.0, 1.0); - m_ScaleFactor.SetXY(1.0, 1.0); - m_ScaleInverse.SetXY(1.0, 1.0); - m_ScaledDimensions.SetXY(1.0, 1.0); - m_WrapX = true; - m_WrapY = true; - m_FillLeftColor = g_MaskColor; - m_FillRightColor = g_MaskColor; - m_FillUpColor = g_MaskColor; - m_FillDownColor = g_MaskColor; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the SceneLayer object ready for use. - -int SceneLayer::Create() -{ - // Read all the properties - if (Entity::Create() < 0) - return -1; - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the SceneLayer object ready for use. - -int SceneLayer::Create(ContentFile bitmapFile, - bool drawTrans, - Vector offset, - bool wrapX, - bool wrapY, - Vector scrollInfo) -{ - m_BitmapFile = bitmapFile; - - m_pMainBitmap = m_BitmapFile.GetAsBitmap(); - RTEAssert(m_pMainBitmap, "Failed to load BITMAP in SceneLayer::Create"); - - Create(m_pMainBitmap, drawTrans, offset, wrapX, wrapY, scrollInfo); - - m_MainBitmapOwned = false; - - // Establisht he scaled dimensions of this - m_ScaledDimensions.SetXY(m_pMainBitmap->w * m_ScaleFactor.m_X, m_pMainBitmap->h * m_ScaleFactor.m_Y); - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the SceneLayer object ready for use. - -int SceneLayer::Create(BITMAP *pBitmap, - bool drawTrans, - Vector offset, - bool wrapX, - bool wrapY, - Vector scrollInfo) -{ - m_pMainBitmap = pBitmap; - RTEAssert(m_pMainBitmap, "Null bitmap passed in when creating SceneLayer"); - m_MainBitmapOwned = true; - - m_DrawTrans = drawTrans; - m_Offset = offset; - m_WrapX = wrapX; - m_WrapY = wrapY; - m_ScrollInfo = scrollInfo; - - if (m_WrapX) - m_ScrollRatio.m_X = m_ScrollInfo.m_X; - else - if (m_ScrollInfo.m_X == -1.0 || m_ScrollInfo.m_X == 1.0) - m_ScrollRatio.m_X = 1.0; - else if (m_ScrollInfo.m_X == g_FrameMan.GetPlayerScreenWidth()) - m_ScrollRatio.m_X = m_pMainBitmap->w - g_FrameMan.GetPlayerScreenWidth(); - else if (m_pMainBitmap->w == g_FrameMan.GetPlayerScreenWidth()) - m_ScrollRatio.m_X = 1.0f / (float)(m_ScrollInfo.m_X - g_FrameMan.GetPlayerScreenWidth()); - else - m_ScrollRatio.m_X = (float)(m_pMainBitmap->w - g_FrameMan.GetPlayerScreenWidth()) / - (float)(m_ScrollInfo.m_X - g_FrameMan.GetPlayerScreenWidth()); - - if (m_WrapY) - m_ScrollRatio.m_Y = m_ScrollInfo.m_Y; - else - if (m_ScrollInfo.m_Y == -1.0 || m_ScrollInfo.m_Y == 1.0) - m_ScrollRatio.m_Y = 1.0; - else if (m_ScrollInfo.m_Y == g_FrameMan.GetPlayerScreenHeight()) - m_ScrollRatio.m_Y = m_pMainBitmap->h - g_FrameMan.GetPlayerScreenHeight(); - else if (m_pMainBitmap->h == g_FrameMan.GetPlayerScreenHeight()) - m_ScrollRatio.m_Y = 1.0f / (float)(m_ScrollInfo.m_Y - g_FrameMan.GetPlayerScreenHeight()); - else - m_ScrollRatio.m_Y = (float)(m_pMainBitmap->h - g_FrameMan.GetPlayerScreenHeight()) / - (float)(m_ScrollInfo.m_Y - g_FrameMan.GetPlayerScreenHeight()); - - // Establisht he scaled dimensions of this - m_ScaledDimensions.SetXY(m_pMainBitmap->w * m_ScaleFactor.m_X, m_pMainBitmap->h * m_ScaleFactor.m_Y); - - // Sampled color at the edges of the layer that can be used to fill gap if the layer isn't large enough to cover a target bitmap - m_FillLeftColor = m_WrapX ? g_MaskColor : _getpixel(m_pMainBitmap, 0, m_pMainBitmap->h / 2); - m_FillRightColor = m_WrapX ? g_MaskColor : _getpixel(m_pMainBitmap, m_pMainBitmap->w - 1, m_pMainBitmap->h / 2); - m_FillUpColor = m_WrapY ? g_MaskColor : _getpixel(m_pMainBitmap, m_pMainBitmap->w / 2, 0); - m_FillDownColor = m_WrapY ? g_MaskColor : _getpixel(m_pMainBitmap, m_pMainBitmap->w / 2, m_pMainBitmap->h - 1); - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a SceneLayer to be identical to another, by deep copy. -// Arguments: A reference to the SceneLayer to deep copy. - -int SceneLayer::Create(const SceneLayer &reference) -{ - Entity::Create(reference); - - m_BitmapFile = reference.m_BitmapFile; - - // Deep copy the bitmap - if (reference.m_pMainBitmap) - { - // Copy the bitmap from the ContentFile, because we're going to be changing it! - BITMAP *pCopyFrom = reference.m_pMainBitmap; - RTEAssert(pCopyFrom, "Couldn't load the bitmap file specified for SceneLayer!"); - - // Destination - m_pMainBitmap = create_bitmap_ex(8, pCopyFrom->w, pCopyFrom->h); - RTEAssert(m_pMainBitmap, "Failed to allocate BITMAP in SceneLayer::Create"); - - // Copy! - blit(pCopyFrom, m_pMainBitmap, 0, 0, 0, 0, pCopyFrom->w, pCopyFrom->h); - - InitScrollRatios(); - - m_MainBitmapOwned = true; - } - // If no bitmap to copy, has to load the data (LoadData) to create this in the copied to SL - else - m_MainBitmapOwned = false; - - m_DrawTrans = reference.m_DrawTrans; - m_Offset = reference.m_Offset; - m_WrapX = reference.m_WrapX; - m_WrapY = reference.m_WrapY; - m_ScrollInfo = reference.m_ScrollInfo; - // The ratios need to be calculated from the ScrollInfo with InitScrollRatios later in LoadData - m_ScrollRatio = reference.m_ScrollRatio; - m_ScaleFactor = reference.m_ScaleFactor; - m_ScaleInverse = reference.m_ScaleInverse; - m_ScaledDimensions = reference.m_ScaledDimensions; - m_FillLeftColor = reference.m_FillLeftColor; - m_FillRightColor = reference.m_FillRightColor; - m_FillUpColor = reference.m_FillUpColor; - m_FillDownColor = reference.m_FillDownColor; - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: LoadData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Actually loads previously specified/created data into memory. Has -// to be done before using this SceneLayer. - -int SceneLayer::LoadData() -{ -/* No need to do this copying, we are re-loading fresh from disk each time - // Copy the bitmap from the ContentFile, because we're going to be changing it! - BITMAP *pCopyFrom = m_BitmapFile.GetAsBitmap(); - RTEAssert(pCopyFrom, "Couldn't load the bitmap file specified for SceneLayer!"); - - // Destination - m_pMainBitmap = create_bitmap_ex(8, pCopyFrom->w, pCopyFrom->h); - RTEAssert(m_pMainBitmap, "Failed to allocate BITMAP in SceneLayer::Create"); - - // Copy! - blit(pCopyFrom, m_pMainBitmap, 0, 0, 0, 0, pCopyFrom->w, pCopyFrom->h); -*/ - // Re-load directly from disk each time; don't do any caching of these bitmaps - m_pMainBitmap = m_BitmapFile.GetAsBitmap(COLORCONV_NONE, false); - - m_MainBitmapOwned = true; - - InitScrollRatios(); - - // Sampled color at the edges of the layer that can be used to fill gap if the layer isn't large enough to cover a target bitmap - m_FillLeftColor = m_WrapX ? g_MaskColor : _getpixel(m_pMainBitmap, 0, m_pMainBitmap->h / 2); - m_FillRightColor = m_WrapX ? g_MaskColor : _getpixel(m_pMainBitmap, m_pMainBitmap->w - 1, m_pMainBitmap->h / 2); - m_FillUpColor = m_WrapY ? g_MaskColor : _getpixel(m_pMainBitmap, m_pMainBitmap->w / 2, 0); - m_FillDownColor = m_WrapY ? g_MaskColor : _getpixel(m_pMainBitmap, m_pMainBitmap->w / 2, m_pMainBitmap->h - 1); - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SaveData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves current data in memory to disk. - -int SceneLayer::SaveData(string bitmapPath) -{ - if (bitmapPath.empty()) - return -1; - - // Save out the bitmap - if (m_pMainBitmap) - { - PALETTE palette; - get_palette(palette); - if (save_bmp(bitmapPath.c_str(), m_pMainBitmap, palette) != 0) - return -1; - - // Set the new path to point to the new file location - only if there was a successful save of the bitmap - m_BitmapFile.SetDataPath(bitmapPath); - } - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ClearData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears out any previously loaded bitmap data from memory. - -int SceneLayer::ClearData() -{ - if (m_pMainBitmap && m_MainBitmapOwned) - destroy_bitmap(m_pMainBitmap); - m_pMainBitmap = 0; - - m_MainBitmapOwned = false; - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int SceneLayer::ReadProperty(const std::string_view &propName, Reader &reader) -{ - if (propName == "BitmapFile") - reader >> m_BitmapFile; - else if (propName == "DrawTransparent") - reader >> m_DrawTrans; - else if (propName == "Offset") - reader >> m_Offset; - else if (propName == "WrapX") - reader >> m_WrapX; - else if (propName == "WrapY") - reader >> m_WrapY; - // Actually read the scrollinfo, not the ratio. The ratios will be initalized later - else if (propName == "ScrollRatio") - reader >> m_ScrollInfo; - else if (propName == "ScaleFactor") - { - reader >> m_ScaleFactor; - // Gotto init more - SetScaleFactor(m_ScaleFactor); - } - else - return Entity::ReadProperty(propName, reader); - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this SceneLayer with a Writer for -// later recreation with Create(Reader &reader); - -int SceneLayer::Save(Writer &writer) const -{ - Entity::Save(writer); - - writer.NewProperty("BitmapFile"); - writer << m_BitmapFile; - writer.NewProperty("DrawTransparent"); - writer << m_DrawTrans; - writer.NewProperty("Offset"); - writer << m_Offset; - writer.NewProperty("WrapX"); - writer << m_WrapX; - writer.NewProperty("WrapY"); - writer << m_WrapY; - writer.NewProperty("ScrollRatio"); - writer << m_ScrollInfo; - writer.NewProperty("ScaleFactor"); - writer << m_ScaleFactor; - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneLayer object. - -void SceneLayer::Destroy(bool notInherited) -{ - if (m_MainBitmapOwned) - destroy_bitmap(m_pMainBitmap); - - if (!notInherited) - Entity::Destroy(); - Clear(); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetScaleFactor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the scale that this should be drawn at when using DrawScaled. - -void SceneLayer::SetScaleFactor(const Vector newScale) -{ - m_ScaleFactor = newScale; - m_ScaleInverse.SetXY(1.0f / newScale.m_X, 1.0f / newScale.m_Y); - if (m_pMainBitmap) - m_ScaledDimensions.SetXY(m_pMainBitmap->w * newScale.m_X, m_pMainBitmap->h * newScale.m_Y); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPixel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a specific pixel from the main bitmap of this SceneLayer. -// LockColorBitmap() must be called before using this method. - -unsigned char SceneLayer::GetPixel(const int pixelX, const int pixelY) -{ - // Make sure it's within the boundaries of the bitmap. - if (pixelX < 0 || pixelX >= m_pMainBitmap->w || pixelY < 0 || pixelY >= m_pMainBitmap->h) - return 0; -// RTEAssert(m_pTerrain->GetBitmap()->m_LockCount > 0, "Trying to access unlocked terrain bitmap"); -// RTEAssert(is_inside_bitmap(m_pMainBitmap, pixelX, pixelY, 0), "Trying to access pixel outside of SceneLayer's bitmap's boundaries!"); - return _getpixel(m_pMainBitmap, pixelX, pixelY); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPixel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets a specific pixel on the main bitmap of this SceneLayer to a -// specific value. LockColorBitmap() must be called before using this -// method. - -void SceneLayer::SetPixel(const int pixelX, const int pixelY, const unsigned char value) -{ - RTEAssert(m_MainBitmapOwned, "Trying to set a pixel of a SceneLayer's bitmap which isn't owned!"); - - // Make sure it's within the boundaries of the bitmap. - if (pixelX < 0 || - pixelX >= m_pMainBitmap->w || - pixelY < 0 || - pixelY >= m_pMainBitmap->h) - return; -// RTEAssert(m_pTerrain->GetBitmap()->m_LockCount > 0, "Trying to access unlocked terrain bitmap"); -// RTEAssert(is_inside_bitmap(m_pMainBitmap, pixelX, pixelY, 0), "Trying to access pixel outside of SceneLayer's bitmap's boundaries!"); -// _putpixel(m_pMainBitmap, pixelX, pixelY, value); - putpixel(m_pMainBitmap, pixelX, pixelY, value); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ForceBounds -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Wraps or bounds a position coordinate if it is off bounds of the -// SceneLayer, depending on the wrap settings of this SceneLayer. - -bool SceneLayer::ForceBounds(int &posX, int &posY, bool scaled) const -{ - bool wrapped = false; - int width = scaled ? m_ScaledDimensions.GetFloorIntX() : m_pMainBitmap->w; - int height = scaled ? m_ScaledDimensions.GetFloorIntY() : m_pMainBitmap->h; - - if (posX < 0) { - if (m_WrapX) - { - while (posX < 0) - posX += width; - wrapped = true; - } - else - posX = 0; - } - - if (posY < 0) { - if (m_WrapY) - { - while (posY < 0) - posY += height; - wrapped = true; - } - else - posY = 0; - } - - if (posX >= width) { - if (m_WrapX) - { - posX %= width; - wrapped = true; - } - else - posX = width - 1; - } - - if (posY >= height) { - if (m_WrapY) - { - posY %= height; - wrapped = true; - } - else - posY = height - 1; - } - - return wrapped; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ForceBounds -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Wraps or bounds a position coordinate if it is off bounds of the -// SceneLayer, depending on the wrap settings of this SceneLayer. - -bool SceneLayer::ForceBounds(Vector &pos, bool scaled) const -{ - int posX = std::floor(pos.m_X); - int posY = std::floor(pos.m_Y); - - bool wrapped = ForceBounds(posX, posY, scaled); - - pos.m_X = posX + (pos.m_X - std::floor(pos.m_X)); - pos.m_Y = posY + (pos.m_Y - std::floor(pos.m_Y)); - - return wrapped; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WrapPosition -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Only wraps a position coordinate if it is off bounds of the SceneLayer -// and wrapping in the corresponding axes are turned on. - -bool SceneLayer::WrapPosition(int &posX, int &posY, bool scaled) const -{ - bool wrapped = false; - int width = scaled ? m_ScaledDimensions.GetFloorIntX() : m_pMainBitmap->w; - int height = scaled ? m_ScaledDimensions.GetFloorIntY() : m_pMainBitmap->h; - - if (m_WrapX) { - if (posX < 0) { - while (posX < 0) - posX += width; - wrapped = true; - } - else if (posX >= width) { - posX %= width; - wrapped = true; - } - } - - if (m_WrapY) { - if (posY < 0) { - while (posY < 0) - posY += height; - wrapped = true; - } - else if (posY >= height) { - posY %= height; - wrapped = true; - } - } - - return wrapped; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WrapPosition -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Only wraps a position coordinate if it is off bounds of the SceneLayer -// and wrapping in the corresponding axes are turned on. - -bool SceneLayer::WrapPosition(Vector &pos, bool scaled) const -{ - int posX = std::floor(pos.m_X); - int posY = std::floor(pos.m_Y); - - bool wrapped = WrapPosition(posX, posY, scaled); - - pos.m_X = posX + (pos.m_X - std::floor(pos.m_X)); - pos.m_Y = posY + (pos.m_Y - std::floor(pos.m_Y)); - - return wrapped; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this SceneLayer. Supposed to be done every frame. - -void SceneLayer::Update() -{ - ; -} - - -// TODO: Declare this in the header and remove the dupe declaration in NetworkClient.cpp -// Data structure for constructing the draw boxes we'll need to use for drawing -struct SLDrawBox -{ - int sourceX; - int sourceY; - int sourceW; - int sourceH; - int destX; - int destY; -}; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this SceneLayer's current scrolled position to a bitmap. - -void SceneLayer::Draw(BITMAP *pTargetBitmap, Box& targetBox, const Vector &scrollOverride) const -{ - RTEAssert(m_pMainBitmap, "Data of this SceneLayer has not been loaded before trying to draw!"); - - int sourceX = 0; - int sourceY = 0; - int sourceW = 0; - int sourceH = 0; - int destX = 0; - int destY = 0; - list drawList; - - int offsetX; - int offsetY; - bool scrollOverridden = !(scrollOverride.m_X == -1 && scrollOverride.m_Y == -1); - - // Overridden scroll position - if (scrollOverridden) - { - offsetX = scrollOverride.GetFloorIntX(); - offsetY = scrollOverride.GetFloorIntY(); - } - // Regular scroll - else - { - offsetX = std::floor(m_Offset.m_X * m_ScrollRatio.m_X); - offsetY = std::floor(m_Offset.m_Y * m_ScrollRatio.m_Y); - // Only force bounds when doing regular scroll offset because the override is used to do terrain object application tricks and sometimes needs the offsets to be < 0 -// ForceBounds(offsetX, offsetY); - WrapPosition(offsetX, offsetY); - } - - // Make target box valid size if it's empty - if (targetBox.IsEmpty()) - { - targetBox.SetCorner(Vector(0, 0)); - targetBox.SetWidth(pTargetBitmap->w); - targetBox.SetHeight(pTargetBitmap->h); - } - - // Set the clipping rectangle of the target bitmap to match the specified target box - set_clip_rect(pTargetBitmap, targetBox.GetCorner().m_X, targetBox.GetCorner().m_Y, targetBox.GetCorner().m_X + targetBox.GetWidth() - 1, targetBox.GetCorner().m_Y + targetBox.GetHeight() - 1); - - // Choose the correct blitting function based on transparency setting - void (*pfBlit)(BITMAP *source, BITMAP *dest, int source_x, int source_y, int dest_x, int dest_y, int width, int height) = m_DrawTrans ? &masked_blit : &blit; - - // See if this SceneLayer is wider AND higher than the target bitmap; then use simple wrapping logic - oterhwise need to tile - if (m_pMainBitmap->w >= pTargetBitmap->w && m_pMainBitmap->h >= pTargetBitmap->h) - { - sourceX = offsetX; - sourceY = offsetY; - sourceW = m_pMainBitmap->w - offsetX; - sourceH = m_pMainBitmap->h - offsetY; - destX = targetBox.GetCorner().m_X; - destY = targetBox.GetCorner().m_Y; - pfBlit(m_pMainBitmap, pTargetBitmap, sourceX, sourceY, destX, destY, sourceW, sourceH); - - sourceX = 0; - sourceY = offsetY; - sourceW = offsetX; - sourceH = m_pMainBitmap->h - offsetY; - destX = targetBox.GetCorner().m_X + m_pMainBitmap->w - offsetX; - destY = targetBox.GetCorner().m_Y; - pfBlit(m_pMainBitmap, pTargetBitmap, sourceX, sourceY, destX, destY, sourceW, sourceH); - - sourceX = offsetX; - sourceY = 0; - sourceW = m_pMainBitmap->w - offsetX; - sourceH = offsetY; - destX = targetBox.GetCorner().m_X; - destY = targetBox.GetCorner().m_Y + m_pMainBitmap->h - offsetY; - pfBlit(m_pMainBitmap, pTargetBitmap, sourceX, sourceY, destX, destY, sourceW, sourceH); - - sourceX = 0; - sourceY = 0; - sourceW = offsetX; - sourceH = offsetY; - destX = targetBox.GetCorner().m_X + m_pMainBitmap->w - offsetX; - destY = targetBox.GetCorner().m_Y + m_pMainBitmap->h - offsetY; - pfBlit(m_pMainBitmap, pTargetBitmap, sourceX, sourceY, destX, destY, sourceW, sourceH); - } - // Target bitmap is larger in some dimension, so need to draw this tiled as many times as necessary to cover the whole target - else - { - int tiledOffsetX = 0; - int tiledOffsetY = 0; - // Use the dimensions of the target box, if it has any area at all - int targetWidth = MIN(pTargetBitmap->w, targetBox.GetWidth()); - int targetHeight = MIN(pTargetBitmap->h, targetBox.GetHeight()); - int toCoverX = offsetX + targetBox.GetCorner().m_X + targetWidth; - int toCoverY = offsetY + targetBox.GetCorner().m_Y + targetHeight; - - // Check for special case adjustment when the screen is larger than the scene - bool screenLargerThanSceneX = false; - bool screenLargerThanSceneY = false; - if (!scrollOverridden && g_SceneMan.GetSceneWidth() > 0) - { - screenLargerThanSceneX = pTargetBitmap->w > g_SceneMan.GetSceneWidth(); - screenLargerThanSceneY = pTargetBitmap->h > g_SceneMan.GetSceneHeight(); - } - - // Y tiling - do - { - // X tiling - do - { - sourceX = 0; - sourceY = 0; - sourceW = m_pMainBitmap->w; - sourceH = m_pMainBitmap->h; - // If the unwrapped and untiled direction can't cover the target area, place it in the middle of the target bitmap, and leave the excess perimeter on each side untouched - destX = (!m_WrapX && screenLargerThanSceneX) ? ((pTargetBitmap->w / 2) - (m_pMainBitmap->w / 2)) : (targetBox.GetCorner().m_X + tiledOffsetX - offsetX); - destY = (!m_WrapY && screenLargerThanSceneY) ? ((pTargetBitmap->h / 2) - (m_pMainBitmap->h / 2)) : (targetBox.GetCorner().m_Y + tiledOffsetY - offsetY); - pfBlit(m_pMainBitmap, pTargetBitmap, sourceX, sourceY, destX, destY, sourceW, sourceH); - - tiledOffsetX += m_pMainBitmap->w; - } - // Only tile if we're supposed to wrap widthwise - while (m_WrapX && toCoverX > tiledOffsetX); - - tiledOffsetY += m_pMainBitmap->h; - } - // Only tile if we're supposed to wrap heightwise - while (m_WrapY && toCoverY > tiledOffsetY); - -// TODO: Do this above instead, testing down here only - // Detect if nonwrapping layer dimensions can't cover the whole target area with its main bitmap. If so, fill in the gap with appropriate solid color sampled from the hanging edge - if (!m_WrapX && !screenLargerThanSceneX && m_ScrollRatio.m_X < 0) - { - if (m_FillLeftColor != g_MaskColor && offsetX != 0) - rectfill(pTargetBitmap, targetBox.GetCorner().m_X, targetBox.GetCorner().m_Y, targetBox.GetCorner().m_X - offsetX, targetBox.GetCorner().m_Y + targetBox.GetHeight(), m_FillLeftColor); - if (m_FillRightColor != g_MaskColor) - rectfill(pTargetBitmap, (targetBox.GetCorner().m_X - offsetX) + m_pMainBitmap->w, targetBox.GetCorner().m_Y, targetBox.GetCorner().m_X + targetBox.GetWidth(), targetBox.GetCorner().m_Y + targetBox.GetHeight(), m_FillRightColor); - } - - if (!m_WrapY && !screenLargerThanSceneY && m_ScrollRatio.m_Y < 0) - { - if (m_FillUpColor != g_MaskColor && offsetY != 0) - rectfill(pTargetBitmap, targetBox.GetCorner().m_X, targetBox.GetCorner().m_Y, targetBox.GetCorner().m_X + targetBox.GetWidth(), targetBox.GetCorner().m_Y - offsetY, m_FillUpColor); - if (m_FillDownColor != g_MaskColor) - rectfill(pTargetBitmap, targetBox.GetCorner().m_X, (targetBox.GetCorner().m_Y - offsetY) + m_pMainBitmap->h, targetBox.GetCorner().m_X + targetBox.GetWidth(), targetBox.GetCorner().m_Y + targetBox.GetHeight(), m_FillDownColor); - } - } - - // Reset the clip rect back to the entire target bitmap - set_clip_rect(pTargetBitmap, 0, 0, pTargetBitmap->w - 1, pTargetBitmap->h - 1); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawScaled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this SceneLayer's current scrolled position to a bitmap, but also -// scaled according to what has been set with SetScaleFactor. - -void SceneLayer::DrawScaled(BITMAP *pTargetBitmap, Box &targetBox, const Vector &scrollOverride) const -{ - // If no scaling, use the regular scaling routine - if (m_ScaleFactor.m_X == 1.0 && m_ScaleFactor.m_Y == 1.0) - return Draw(pTargetBitmap, targetBox, scrollOverride); - - RTEAssert(m_pMainBitmap, "Data of this SceneLayer has not been loaded before trying to draw!"); - - -/* - - // Calculate how many times smaller the unseen map is compared to the entire terrain's dimensions - Vector resDenom((float)pTerrain->GetBitmap()->w / (float)pUnseenLayer->GetBitmap()->w, (float)pTerrain->GetBitmap()->h / (float)pUnseenLayer->GetBitmap()->h); - resDenom.Floor(); - // Now convert it back to the multiplier so that the flooring that happened above is reflected in the multiplier too, to avoid jittering - Vector resMult(1.0f / resDenom.m_X, 1.0f / resDenom.m_Y); - // This is the sub-source-pixel offset on the target to make scrolling of the pixelated bitmap smooth - Vector subOffset((int)targetPos.m_X % (int)resDenom.m_X, (int)targetPos.m_Y % (int)resDenom.m_Y); - masked_stretch_blit(pUnseenLayer->GetBitmap(), pTargetBitmap, (int)(targetPos.m_X * resMult.m_X + 0.0001f), (int)(targetPos.m_Y * resMult.m_Y + 0.0001f), pTargetBitmap->w * resMult.m_X + 1.0001f, pTargetBitmap->h * resMult.m_Y + 1.0001f, -subOffset.m_X, -subOffset.m_Y, pTargetBitmap->w + resDenom.m_X, pTargetBitmap->h + resDenom.m_Y); - - - -// TODO: Remove -// char balle[245]; -// std::snprintf(balle, sizeof(balle), "y: %f becomes %f through %f and %f, needs to match: %f, which is: %i", targetPos.m_Y, subOffset.m_Y, resMult.m_Y, resDenom.m_Y, targetPos.m_Y * resMult.m_Y + 0.0001f, (int)(targetPos.m_Y * resMult.m_Y + 0.0001f)); -// g_FrameMan.SetScreenText(string(balle)); - - -*/ - - - - int sourceX = 0; - int sourceY = 0; - int sourceW = 0; - int sourceH = 0; - int destX = 0; - int destY = 0; - int destW = 0; - int destH = 0; - list drawList; - - int offsetX; - int offsetY; - bool scrollOverridden = !(scrollOverride.m_X == -1 && scrollOverride.m_Y == -1); - - // Overridden scroll position - if (scrollOverridden) - { - offsetX = scrollOverride.GetFloorIntX(); - offsetY = scrollOverride.GetFloorIntY(); - } - // Regular scroll - else - { - offsetX = std::floor(m_Offset.m_X * m_ScrollRatio.m_X); - offsetY = std::floor(m_Offset.m_Y * m_ScrollRatio.m_Y); - // Only force bounds when doing regular scroll offset because the override is used to do terrain object application tricks and sometimes needs the offsets to be < 0 -// ForceBounds(offsetX, offsetY); - WrapPosition(offsetX, offsetY); - } - - // Make target box valid size if it's empty - if (targetBox.IsEmpty()) - { - targetBox.SetCorner(Vector(0, 0)); - targetBox.SetWidth(pTargetBitmap->w); - targetBox.SetHeight(pTargetBitmap->h); - } - - // Set the clipping rectangle of the target bitmap to match the specified target box - set_clip_rect(pTargetBitmap, targetBox.GetCorner().m_X, targetBox.GetCorner().m_Y, targetBox.GetCorner().m_X + targetBox.GetWidth() - 1, targetBox.GetCorner().m_Y + targetBox.GetHeight() - 1); - - // Choose the correct blitting function based on transparency setting - void (*pfBlit)(BITMAP *source, BITMAP *dest, int source_x, int source_y, int source_w, int source_h, int dest_x, int dest_y, int dest_w, int dest_h) = m_DrawTrans ? &masked_stretch_blit : &stretch_blit; -// void (*pfBlit)(BITMAP *source, BITMAP *dest, int source_x, int source_y, int dest_x, int dest_y, int width, int height) = m_DrawTrans ? &masked_blit : &blit; - - // Get a scaled offset for the source layer - Vector sourceOffset(offsetX * m_ScaleInverse.m_X, offsetY * m_ScaleInverse.m_Y); - - // See if this SceneLayer is wider AND higher than the target bitmap when scaled; then use simple wrapping logic - oterhwise need to tile - if (m_ScaledDimensions.m_X >= pTargetBitmap->w && m_ScaledDimensions.m_Y >= pTargetBitmap->h) - { - // Upper left - sourceX = 0; - sourceY = 0; - sourceW = m_pMainBitmap->w; - sourceH = m_pMainBitmap->h; - destX = targetBox.GetCorner().m_X - offsetX; - destY = targetBox.GetCorner().m_Y - offsetY; - destW = sourceW * m_ScaleFactor.m_X + 1; - destH = sourceH * m_ScaleFactor.m_Y + 1; - pfBlit(m_pMainBitmap, pTargetBitmap, sourceX, sourceY, sourceW, sourceH, destX, destY, destW, destH); - - // Upper right - sourceX = 0; - sourceY = 0; - sourceW = sourceOffset.m_X; - sourceH = m_pMainBitmap->h; - destX = targetBox.GetCorner().m_X + m_ScaledDimensions.m_X - offsetX; - destY = targetBox.GetCorner().m_Y - offsetY; - destW = sourceW * m_ScaleFactor.m_X + 1; - destH = sourceH * m_ScaleFactor.m_Y + 1; - pfBlit(m_pMainBitmap, pTargetBitmap, sourceX, sourceY, sourceW, sourceH, destX, destY, destW, destH); - - // Lower left - sourceX = 0; - sourceY = 0; - sourceW = m_pMainBitmap->w; - sourceH = sourceOffset.m_Y; - destX = targetBox.GetCorner().m_X - offsetX; - destY = targetBox.GetCorner().m_Y + m_ScaledDimensions.m_Y - offsetY; - destW = sourceW * m_ScaleFactor.m_X + 1; - destH = sourceH * m_ScaleFactor.m_Y + 1; - pfBlit(m_pMainBitmap, pTargetBitmap, sourceX, sourceY, sourceW, sourceH, destX, destY, destW, destH); - - // Lower right - sourceX = 0; - sourceY = 0; - sourceW = sourceOffset.m_X; - sourceH = sourceOffset.m_Y; - destX = targetBox.GetCorner().m_X + m_ScaledDimensions.m_X - offsetX; - destY = targetBox.GetCorner().m_Y + m_ScaledDimensions.m_Y - offsetY; - destW = sourceW * m_ScaleFactor.m_X + 1; - destH = sourceH * m_ScaleFactor.m_Y + 1; - pfBlit(m_pMainBitmap, pTargetBitmap, sourceX, sourceY, sourceW, sourceH, destX, destY, destW, destH); - } - // Target bitmap is larger in some dimension, so need to draw this tiled as many times as necessary to cover the whole target - else - { - int tiledOffsetX = 0; - int tiledOffsetY = 0; - // Use the dimensions of the target box, if it has any area at all - int targetWidth = MIN(pTargetBitmap->w, targetBox.GetWidth()); - int targetHeight = MIN(pTargetBitmap->h, targetBox.GetHeight()); - int toCoverX = offsetX + targetBox.GetCorner().m_X + targetWidth; - int toCoverY = offsetY + targetBox.GetCorner().m_Y + targetHeight; - - // Check for special case adjustment when the screen is larger than the scene - bool screenLargerThanSceneX = false; - bool screenLargerThanSceneY = false; - if (!scrollOverridden && g_SceneMan.GetSceneWidth() > 0) - { - screenLargerThanSceneX = pTargetBitmap->w > g_SceneMan.GetSceneWidth(); - screenLargerThanSceneY = pTargetBitmap->h > g_SceneMan.GetSceneHeight(); - } - - // Y tiling - do - { - // X tiling - do - { - sourceX = 0; - sourceY = 0; - sourceW = m_pMainBitmap->w; - sourceH = m_pMainBitmap->h; - // If the unwrapped and untiled direction can't cover the target area, place it in the middle of the target bitmap, and leave the excess perimeter on each side untouched - destX = (!m_WrapX && screenLargerThanSceneX) ? ((pTargetBitmap->w / 2) - (m_ScaledDimensions.m_X / 2)) : (targetBox.GetCorner().m_X + tiledOffsetX - offsetX); - destY = (!m_WrapY && screenLargerThanSceneY) ? ((pTargetBitmap->h / 2) - (m_ScaledDimensions.m_Y / 2)) : (targetBox.GetCorner().m_Y + tiledOffsetY - offsetY); - destW = m_ScaledDimensions.m_X; - destH = m_ScaledDimensions.m_Y; - pfBlit(m_pMainBitmap, pTargetBitmap, sourceX, sourceY, sourceW, sourceH, destX, destY, destW, destH); - - tiledOffsetX += m_ScaledDimensions.m_X; - } - // Only tile if we're supposed to wrap widthwise - while (m_WrapX && toCoverX > tiledOffsetX); - - tiledOffsetY += m_ScaledDimensions.m_Y; - } - // Only tile if we're supposed to wrap heightwise - while (m_WrapY && toCoverY > tiledOffsetY); - -// TODO: Do this above instead, testing down here only -/* - // Detect if nonwrapping layer dimensions can't cover the whole target area with its main bitmap. If so, fill in the gap with appropriate solid color sampled from the hanging edge - if (!m_WrapX && !screenLargerThanSceneX && m_ScrollRatio.m_X < 0) - { - if (m_FillLeftColor != g_MaskColor && offsetX != 0) - rectfill(pTargetBitmap, targetBox.GetCorner().m_X, targetBox.GetCorner().m_Y, targetBox.GetCorner().m_X - offsetX, targetBox.GetCorner().m_Y + targetBox.GetHeight(), m_FillLeftColor); - if (m_FillRightColor != g_MaskColor) - rectfill(pTargetBitmap, (targetBox.GetCorner().m_X - offsetX) + m_pMainBitmap->w, targetBox.GetCorner().m_Y, targetBox.GetCorner().m_X + targetBox.GetWidth(), targetBox.GetCorner().m_Y + targetBox.GetHeight(), m_FillRightColor); - } - - if (!m_WrapY && !screenLargerThanSceneY && m_ScrollRatio.m_Y < 0) - { - if (m_FillUpColor != g_MaskColor && offsetY != 0) - rectfill(pTargetBitmap, targetBox.GetCorner().m_X, targetBox.GetCorner().m_Y, targetBox.GetCorner().m_X + targetBox.GetWidth(), targetBox.GetCorner().m_Y - offsetY, m_FillUpColor); - if (m_FillDownColor != g_MaskColor) - rectfill(pTargetBitmap, targetBox.GetCorner().m_X, (targetBox.GetCorner().m_Y - offsetY) + m_pMainBitmap->h, targetBox.GetCorner().m_X + targetBox.GetWidth(), targetBox.GetCorner().m_Y + targetBox.GetHeight(), m_FillDownColor); - } -*/ - } - - // Reset the clip rect back to the entire target bitmap - set_clip_rect(pTargetBitmap, 0, 0, pTargetBitmap->w - 1, pTargetBitmap->h - 1); -} - -/* not neccessary -////////////////////////////////////////////////////////////////////////////////////////// -// Method: LoadContour -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Loads a terrain contour file and applies it to this SceneLayer. - -virtual int SceneLayer::LoadContour(char material, bool dirtBelowContour, ContentFile &contourFile) -{ - char *contour -} -*/ - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: InitScrollRatios -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Initialize the scroll ratios from the encoded scroll info. Must be -// done after the bitmap has been created in the derived concrete classes. - -void SceneLayer::InitScrollRatios() -{ - if (m_WrapX) - m_ScrollRatio.m_X = m_ScrollInfo.m_X; - else - { - if (m_ScrollInfo.m_X == -1.0 || m_ScrollInfo.m_X == 1.0) - m_ScrollRatio.m_X = 1.0; - else if (m_ScrollInfo.m_X == g_FrameMan.GetPlayerScreenWidth()) - m_ScrollRatio.m_X = m_pMainBitmap->w - g_FrameMan.GetPlayerScreenWidth(); - else if (m_pMainBitmap->w == g_FrameMan.GetPlayerScreenWidth()) - m_ScrollRatio.m_X = 1.0f / (float)(m_ScrollInfo.m_X - g_FrameMan.GetPlayerScreenWidth()); - else - m_ScrollRatio.m_X = (float)(m_pMainBitmap->w - g_FrameMan.GetPlayerScreenWidth()) / - (float)(m_ScrollInfo.m_X - g_FrameMan.GetPlayerScreenWidth()); - } - - if (m_WrapY) - m_ScrollRatio.m_Y = m_ScrollInfo.m_Y; - else - { - if (m_ScrollInfo.m_Y == -1.0 || m_ScrollInfo.m_Y == 1.0) - m_ScrollRatio.m_Y = 1.0; - else if (m_ScrollInfo.m_Y == g_FrameMan.GetPlayerScreenHeight()) - m_ScrollRatio.m_Y = m_pMainBitmap->h - g_FrameMan.GetPlayerScreenHeight(); - else if (m_pMainBitmap->h == g_FrameMan.GetPlayerScreenHeight()) - m_ScrollRatio.m_Y = 1.0f / (float)(m_ScrollInfo.m_Y - g_FrameMan.GetPlayerScreenHeight()); - else - m_ScrollRatio.m_Y = (float)(m_pMainBitmap->h - g_FrameMan.GetPlayerScreenHeight()) / - (float)(m_ScrollInfo.m_Y - g_FrameMan.GetPlayerScreenHeight()); - } - - // Establish the scaled dimensions of this - m_ScaledDimensions.SetXY(m_pMainBitmap->w * m_ScaleFactor.m_X, m_pMainBitmap->h * m_ScaleFactor.m_Y); -} - - -void SceneLayer::UpdateScrollRatiosForNetworkPlayer(int player) -{ - if (m_WrapX) - m_ScrollRatio.m_X = m_ScrollInfo.m_X; - else - { - if (m_ScrollInfo.m_X == -1.0 || m_ScrollInfo.m_X == 1.0) - m_ScrollRatio.m_X = 1.0; - else if (m_ScrollInfo.m_X == g_FrameMan.GetPlayerFrameBufferWidth(player)) - m_ScrollRatio.m_X = m_pMainBitmap->w - g_FrameMan.GetPlayerFrameBufferWidth(player); - else if (m_pMainBitmap->w == g_FrameMan.GetPlayerFrameBufferWidth(player)) - m_ScrollRatio.m_X = 1.0f / (float)(m_ScrollInfo.m_X - g_FrameMan.GetPlayerFrameBufferWidth(player)); - else - m_ScrollRatio.m_X = (float)(m_pMainBitmap->w - g_FrameMan.GetPlayerFrameBufferWidth(player)) / - (float)(m_ScrollInfo.m_X - g_FrameMan.GetPlayerFrameBufferWidth(player)); +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SceneLayer::Destroy(bool notInherited) { + if (m_MainBitmapOwned) { destroy_bitmap(m_MainBitmap); } + if (!notInherited) { Entity::Destroy(); } + Clear(); + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SceneLayer::InitScrollRatios(bool initForNetworkPlayer, int player) { + float mainBitmapWidth = static_cast(m_MainBitmap->w); + float mainBitmapHeight = static_cast(m_MainBitmap->h); + float playerScreenWidth = static_cast(initForNetworkPlayer ? g_FrameMan.GetPlayerFrameBufferWidth(player) : g_FrameMan.GetPlayerScreenWidth()); + float playerScreenHeight = static_cast(initForNetworkPlayer ? g_FrameMan.GetPlayerFrameBufferHeight(player) : g_FrameMan.GetPlayerScreenHeight()); + + if (m_WrapX) { + m_ScrollRatio.SetX(m_ScrollInfo.GetX()); + } else { + if (m_ScrollInfo.GetX() == -1.0F || m_ScrollInfo.GetX() == 1.0F) { + m_ScrollRatio.SetX(1.0F); + } else if (m_ScrollInfo.GetX() == playerScreenWidth) { + m_ScrollRatio.SetX(mainBitmapWidth - playerScreenWidth); + } else if (mainBitmapWidth == playerScreenWidth) { + m_ScrollRatio.SetX(1.0F / (m_ScrollInfo.GetX() - playerScreenWidth)); + } else { + m_ScrollRatio.SetX((mainBitmapWidth - playerScreenWidth) / (m_ScrollInfo.GetX() - playerScreenWidth)); + } + } + if (m_WrapY) { + m_ScrollRatio.SetY(m_ScrollInfo.GetY()); + } else { + if (m_ScrollInfo.GetY() == -1.0F || m_ScrollInfo.GetY() == 1.0) { + m_ScrollRatio.SetY(1.0F); + } else if (m_ScrollInfo.GetY() == playerScreenHeight) { + m_ScrollRatio.SetY(mainBitmapHeight - playerScreenHeight); + } else if (mainBitmapHeight == playerScreenHeight) { + m_ScrollRatio.SetY(1.0F / (m_ScrollInfo.GetY() - playerScreenHeight)); + } else { + m_ScrollRatio.SetY((mainBitmapHeight - playerScreenHeight) / (m_ScrollInfo.GetY() - playerScreenHeight)); + } + } + m_ScaledDimensions.SetXY(mainBitmapWidth * m_ScaleFactor.GetX(), mainBitmapHeight * m_ScaleFactor.GetY()); + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SceneLayer::LoadData() { + // Load from disk and take ownership. Don't cache because the bitmap will be modified. + m_MainBitmap = m_BitmapFile.GetAsBitmap(COLORCONV_NONE, false); + m_MainBitmapOwned = true; + InitScrollRatios(); + return 0; } - if (m_WrapY) - m_ScrollRatio.m_Y = m_ScrollInfo.m_Y; - else - { - if (m_ScrollInfo.m_Y == -1.0 || m_ScrollInfo.m_Y == 1.0) - m_ScrollRatio.m_Y = 1.0; - else if (m_ScrollInfo.m_Y == g_FrameMan.GetPlayerFrameBufferHeight(player)) - m_ScrollRatio.m_Y = m_pMainBitmap->h - g_FrameMan.GetPlayerFrameBufferHeight(player); - else if (m_pMainBitmap->h == g_FrameMan.GetPlayerFrameBufferHeight(player)) - m_ScrollRatio.m_Y = 1.0f / (float)(m_ScrollInfo.m_Y - g_FrameMan.GetPlayerFrameBufferHeight(player)); - else - m_ScrollRatio.m_Y = (float)(m_pMainBitmap->h - g_FrameMan.GetPlayerFrameBufferHeight(player)) / - (float)(m_ScrollInfo.m_Y - g_FrameMan.GetPlayerFrameBufferHeight(player)); +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SceneLayer::SaveData(const std::string &bitmapPath) { + if (bitmapPath.empty()) { + return -1; + } + if (m_MainBitmap) { + // Make a copy of the bitmap to pass to the thread because the bitmap may be offloaded mid thread and everything will be on fire. + BITMAP *outputBitmap = create_bitmap_ex(8, m_MainBitmap->w, m_MainBitmap->h); + blit(m_MainBitmap, outputBitmap, 0, 0, 0, 0, m_MainBitmap->w, m_MainBitmap->h); + + auto saveLayerBitmap = [bitmapPath](BITMAP *bitmapToSave) { + PALETTE palette; + get_palette(palette); + if (save_png(bitmapPath.c_str(), bitmapToSave, palette) != 0) { + // TODO: This will not kill the main thread. Figure this out! + RTEAbort(std::string("Failed to save SceneLayer bitmap to path and name: " + bitmapPath)); + } + destroy_bitmap(bitmapToSave); + }; + std::thread saveThread(saveLayerBitmap, outputBitmap); + m_BitmapFile.SetDataPath(bitmapPath); + // TODO: Move this into some global thread container or a ThreadMan™ instead of detaching. + saveThread.detach(); + } + return 0; } - // Establish the scaled dimensions of this - m_ScaledDimensions.SetXY(m_pMainBitmap->w * m_ScaleFactor.m_X, m_pMainBitmap->h * m_ScaleFactor.m_Y); -} +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SceneLayer::ClearData() { + if (m_MainBitmap && m_MainBitmapOwned) { destroy_bitmap(m_MainBitmap); } + m_MainBitmap = nullptr; + m_MainBitmapOwned = false; + return 0; + } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -} // namespace RTE + void SceneLayer::SetScaleFactor(const Vector &newScale) { + m_ScaleFactor = newScale; + if (m_MainBitmap) { m_ScaledDimensions.SetXY(static_cast(m_MainBitmap->w) * newScale.GetX(), static_cast(m_MainBitmap->h) * newScale.GetY()); } + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SceneLayer::GetPixel(int pixelX, int pixelY) const { + WrapPosition(pixelX, pixelY); + return (pixelX < 0 || pixelX >= m_MainBitmap->w || pixelY < 0 || pixelY >= m_MainBitmap->h) ? MaterialColorKeys::g_MaterialAir : _getpixel(m_MainBitmap, pixelX, pixelY); + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SceneLayer::SetPixel(int pixelX, int pixelY, int materialID) const { + RTEAssert(m_MainBitmapOwned, "Trying to set a pixel of a SceneLayer's bitmap which isn't owned!"); + + WrapPosition(pixelX, pixelY); + + if (pixelX < 0 || pixelX >= m_MainBitmap->w || pixelY < 0 || pixelY >= m_MainBitmap->h) { + return; + } + _putpixel(m_MainBitmap, pixelX, pixelY, materialID); + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SceneLayer::IsWithinBounds(const int pixelX, const int pixelY, const int margin) const { + // TODO: This doesn't take Y wrapping into account! + return (m_WrapX || (pixelX >= -margin) && pixelX < (m_MainBitmap->w + margin)) && (pixelY >= -1000) && (pixelY < (m_MainBitmap->h + margin)); + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SceneLayer::WrapPosition(int &posX, int &posY) const { + bool wrapped = false; + int width = m_ScaledDimensions.GetFloorIntX(); + int height = m_ScaledDimensions.GetFloorIntY(); + + if (m_WrapX) { + if (posX < 0) { + while (posX < 0) { + posX += width; + } + wrapped = true; + } else if (posX >= width) { + posX %= width; + wrapped = true; + } + } + if (m_WrapY) { + if (posY < 0) { + while (posY < 0) { + posY += height; + } + wrapped = true; + } else if (posY >= height) { + posY %= height; + wrapped = true; + } + } + return wrapped; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SceneLayer::ForceBounds(int &posX, int &posY) const { + bool wrapped = false; + int width = m_ScaledDimensions.GetFloorIntX(); + int height = m_ScaledDimensions.GetFloorIntY(); + + if (posX < 0) { + if (m_WrapX) { + while (posX < 0) { + posX += width; + } + wrapped = true; + } else { + posX = 0; + } + } + if (posY < 0) { + if (m_WrapY) { + while (posY < 0) { + posY += height; + } + wrapped = true; + } else { + posY = 0; + } + } + if (posX >= width) { + if (m_WrapX) { + posX %= width; + wrapped = true; + } else { + posX = width - 1; + } + } + if (posY >= height) { + if (m_WrapY) { + posY %= height; + wrapped = true; + } else { + posY = height - 1; + } + } + return wrapped; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SceneLayer::ForceBoundsOrWrapPosition(Vector &pos, bool forceBounds) const { + int posX = pos.GetFloorIntX(); + int posY = pos.GetFloorIntY(); + bool wrapped = forceBounds ? ForceBounds(posX, posY) : WrapPosition(posX, posY); + pos.SetXY(static_cast(posX) + (pos.GetX() - std::floor(pos.GetX())), static_cast(posY) + (pos.GetY() - std::floor(pos.GetY()))); + + return wrapped; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SceneLayer::Draw(BITMAP *targetBitmap, Box &targetBox, bool offsetNeedsScrollRatioAdjustment) { + RTEAssert(m_MainBitmap, "Data of this SceneLayer has not been loaded before trying to draw!"); + + if (offsetNeedsScrollRatioAdjustment) { m_Offset.SetXY(std::floor(m_Offset.GetX() * m_ScrollRatio.GetX()), std::floor(m_Offset.GetY() * m_ScrollRatio.GetY())); } + if (targetBox.IsEmpty()) { targetBox = Box(Vector(), static_cast(targetBitmap->w), static_cast(targetBitmap->h)); } + if (!m_WrapX && static_cast(targetBitmap->w) > targetBox.GetWidth()) { m_Offset.SetX(0); } + if (!m_WrapY && static_cast(targetBitmap->h) > targetBox.GetHeight()) { m_Offset.SetY(0); } + + m_Offset -= m_OriginOffset; + WrapPosition(m_Offset); + + set_clip_rect(targetBitmap, targetBox.GetCorner().GetFloorIntX(), targetBox.GetCorner().GetFloorIntY(), static_cast(targetBox.GetCorner().GetX() + targetBox.GetWidth()) - 1, static_cast(targetBox.GetCorner().GetY() + targetBox.GetHeight()) - 1); + bool drawScaled = m_ScaleFactor.GetX() > 1.0F || m_ScaleFactor.GetY() > 1.0F; + + if (m_MainBitmap->w > targetBitmap->w && m_MainBitmap->h > targetBitmap->h) { + DrawWrapped(targetBitmap, targetBox, drawScaled); + } else { + DrawTiled(targetBitmap, targetBox, drawScaled); + } + set_clip_rect(targetBitmap, 0, 0, targetBitmap->w - 1, targetBitmap->h - 1); + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SceneLayer::DrawWrapped(BITMAP *targetBitmap, const Box &targetBox, bool drawScaled) const { + if (!drawScaled) { + std::array sourcePosX = { m_Offset.GetFloorIntX(), 0 }; + std::array sourcePosY = { m_Offset.GetFloorIntY(), 0 }; + std::array sourceWidth = { m_MainBitmap->w - m_Offset.GetFloorIntX(), m_Offset.GetFloorIntX() }; + std::array sourceHeight = { m_MainBitmap->h - m_Offset.GetFloorIntY(), m_Offset.GetFloorIntY() }; + std::array destPosX = { targetBox.GetCorner().GetFloorIntX(), targetBox.GetCorner().GetFloorIntX() + m_MainBitmap->w - m_Offset.GetFloorIntX() }; + std::array destPosY = { targetBox.GetCorner().GetFloorIntY(), targetBox.GetCorner().GetFloorIntY() + m_MainBitmap->h - m_Offset.GetFloorIntY() }; + + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 2; ++j) { + if (m_DrawMasked) { + masked_blit(m_MainBitmap, targetBitmap, sourcePosX.at(j), sourcePosY.at(i), destPosX.at(j), destPosY.at(i), sourceWidth.at(j), sourceHeight.at(i)); + } else { + blit(m_MainBitmap, targetBitmap, sourcePosX.at(j), sourcePosY.at(i), destPosX.at(j), destPosY.at(i), sourceWidth.at(j), sourceHeight.at(i)); + } + } + } + } else { + std::array sourceWidth = { m_MainBitmap->w, m_Offset.GetFloorIntX() / m_ScaleFactor.GetFloorIntX() }; + std::array sourceHeight = { m_MainBitmap->h, m_Offset.GetFloorIntY() / m_ScaleFactor.GetFloorIntY() }; + std::array destPosX = { targetBox.GetCorner().GetFloorIntX() - m_Offset.GetFloorIntX(), targetBox.GetCorner().GetFloorIntX() + m_ScaledDimensions.GetFloorIntX() - m_Offset.GetFloorIntX() }; + std::array destPosY = { targetBox.GetCorner().GetFloorIntY() - m_Offset.GetFloorIntY(), targetBox.GetCorner().GetFloorIntY() + m_ScaledDimensions.GetFloorIntY() - m_Offset.GetFloorIntY() }; + + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 2; ++j) { + if (m_DrawMasked) { + masked_stretch_blit(m_MainBitmap, targetBitmap, 0, 0, sourceWidth.at(j), sourceHeight.at(i), destPosX.at(j), destPosY.at(i), sourceWidth.at(j) * m_ScaleFactor.GetFloorIntX() + 1, sourceHeight.at(i) * m_ScaleFactor.GetFloorIntY() + 1); + } else { + stretch_blit(m_MainBitmap, targetBitmap, 0, 0, sourceWidth.at(j), sourceHeight.at(i), destPosX.at(j), destPosY.at(i), sourceWidth.at(j) * m_ScaleFactor.GetFloorIntX() + 1, sourceHeight.at(i) * m_ScaleFactor.GetFloorIntY() + 1); + } + } + } + } + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SceneLayer::DrawTiled(BITMAP *targetBitmap, const Box &targetBox, bool drawScaled) const { + int bitmapWidth = m_ScaledDimensions.GetFloorIntX(); + int bitmapHeight = m_ScaledDimensions.GetFloorIntY(); + int areaToCoverX = m_Offset.GetFloorIntX() + targetBox.GetCorner().GetFloorIntX() + std::min(targetBitmap->w, static_cast(targetBox.GetWidth())); + int areaToCoverY = m_Offset.GetFloorIntY() + targetBox.GetCorner().GetFloorIntY() + std::min(targetBitmap->h, static_cast(targetBox.GetHeight())); + + for (int tiledOffsetX = 0; tiledOffsetX < areaToCoverX;) { + int destX = targetBox.GetCorner().GetFloorIntX() + tiledOffsetX - m_Offset.GetFloorIntX(); + + for (int tiledOffsetY = 0; tiledOffsetY < areaToCoverY;) { + int destY = targetBox.GetCorner().GetFloorIntY() + tiledOffsetY - m_Offset.GetFloorIntY(); + + if (!drawScaled) { + if (m_DrawMasked) { + masked_blit(m_MainBitmap, targetBitmap, 0, 0, destX, destY, bitmapWidth, bitmapHeight); + } else { + blit(m_MainBitmap, targetBitmap, 0, 0, destX, destY, bitmapWidth, bitmapHeight); + } + } else { + if (m_DrawMasked) { + masked_stretch_blit(m_MainBitmap, targetBitmap, 0, 0, m_MainBitmap->w, m_MainBitmap->h, destX, destY, bitmapWidth, bitmapHeight); + } else { + stretch_blit(m_MainBitmap, targetBitmap, 0, 0, m_MainBitmap->w, m_MainBitmap->h, destX, destY, bitmapWidth, bitmapHeight); + } + } + if (!m_WrapY) { + break; + } + tiledOffsetY += bitmapHeight; + } + if (!m_WrapX) { + break; + } + tiledOffsetX += bitmapWidth; + } + } +} \ No newline at end of file diff --git a/Entities/SceneLayer.h b/Entities/SceneLayer.h index a34c73ba6..10b426750 100644 --- a/Entities/SceneLayer.h +++ b/Entities/SceneLayer.h @@ -1,544 +1,323 @@ #ifndef _RTESCENELAYER_ #define _RTESCENELAYER_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: SceneLayer.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the SceneLayer class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "Entity.h" +#include "ContentFile.h" #include "Box.h" -#include "FrameMan.h" -#include "SceneMan.h" - -namespace RTE -{ - -class ContentFile; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: SceneLayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A scrolling layer of the scene. -// Parent(s): Entity. -// Class history: 12/31/2001 SceneLayer created. - -class SceneLayer : public Entity { - friend class NetworkServer; - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions -EntityAllocation(SceneLayer); -SerializableOverrideMethods; -ClassInfoGetters; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: SceneLayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a SceneLayer object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - SceneLayer() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~SceneLayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a SceneLayer object before deletion -// from system memory. -// Arguments: None. - - ~SceneLayer() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the SceneLayer object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the SceneLayer object ready for use. -// Arguments: The ContentFile representing the bitmap file to load as this -// SceneLayer's graphical representation. -// Whether to draw transperently using a color key specified by the pixel -// in the upper left corner of the loaded bitmap. -// The initial scroll offset. -// Two bools that define whether the layer should wrap around or stop when -// scrolling beyond its bitmap's boundries. -// A vector whose components define two different things, depending on -// wrapX/Y arguments. If a wrap argument is set to false, the -// corresponding component here will be interpreted as the width (X) or -// height (Y) (in pixels) of the total bitmap area that this layer is -// allowed to scroll across before stopping at an edge. If wrapping is -// set to true, the value in scrollInfo is simply the ratio of offset at -// which any scroll operations will be done in. A special command is if -// wrap is false and the corresponding component is -1.0, that signals -// that the own width or height should be used as scrollInfo input. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - -// TODO: streamline interface") - int Create(ContentFile bitmapFile, bool drawTrans, Vector offset, bool wrapX, bool wrapY, Vector scrollInfo); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the SceneLayer object ready for use. -// Arguments: The prepared BITMAP to use as for this SceneLayer. Ownership IS -// transferred! -// Whether to draw transperently using a color key specified by the pixel -// in the upper left corner of the loaded bitmap. -// The initial scroll offset. -// Two bools that define whether the layer should wrap around or stop when -// scrolling beyond its bitmap's boundries. -// A vector whose components define two different things, depending on -// wrapX/Y arguments. If a wrap argument is set to false, the -// corresponding component here will be interpreted as the width (X) or -// height (Y) (in pixels) of the total bitmap area that this layer is -// allowed to scroll across before stopping at an edge. If wrapping is -// set to true, the value in scrollInfo is simply the ratio of offset at -// which any scroll operations will be done in. A special command is if -// wrap is false and the corresponding component is -1.0, that signals -// that the own width or height should be used as scrollInfo input. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - -// TODO: streamline interface") - int Create(BITMAP *pBitmap, bool drawTrans, Vector offset, bool wrapX, bool wrapY, Vector scrollInfo); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a SceneLayer to be identical to another, by deep copy. -// Arguments: A reference to the SceneLayer to deep copy. -// Return value: An error return value signaling success or any particular failure. -// Anything below 0 is an error signal. - - int Create(const SceneLayer &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: LoadData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Actually loads previously specified/created data into memory. Has -// to be done before using this SceneLayer. -// Arguments: None. -// Return value: An error return value signaling success or any particular failure. -// Anything below 0 is an error signal. - - virtual int LoadData(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SaveData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves data currently in memory to disk. -// Arguments: The filepath to the where to save the Bitmap data. -// Return value: An error return value signaling success or any particular failure. -// Anything below 0 is an error signal. - - virtual int SaveData(std::string bitmapPath); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ClearData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears out any previously loaded bitmap data from memory. -// Arguments: None. -// Return value: An error return value signaling success or any particular failure. -// Anything below 0 is an error signal. - - virtual int ClearData(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsFileData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Whether this' bitmap data is loaded from a file or was generated. -// Arguments: None. -// Return value: Whether the data in this' bitmap was loaded from a datafile, or generated. - - virtual bool IsFileData() const { return !m_BitmapFile.GetDataPath().empty(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire SLTerrain, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); Entity::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneLayer object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the BITMAP object that this SceneLayer uses. -// Arguments: None. -// Return value: A pointer to the BITMAP object. Ownership is NOT transferred! - - BITMAP * GetBitmap() const { return m_pMainBitmap; } - - size_t GetBitmapHash() const { return m_BitmapFile.GetHash(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the scroll offset. -// Arguments: None. -// Return value: A copy of the offset. - - Vector GetOffset() const { return m_Offset; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetScrollRatio -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the scroll ratio that modifies the offset. -// Arguments: None. -// Return value: A copy of the ratio. - - Vector GetScrollRatio() const { return m_ScrollRatio; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetScaleFactor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the scale factor that this is drawn in. -// Arguments: None. -// Return value: The scale factor of this to the target it is drawn to. (2x if this is -// half the res, etc.) - - Vector GetScaleFactor() const { return m_ScaleFactor; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetScaleInverse -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the inverse scale factor that this is drawn in. -// Arguments: None. -// Return value: The scale factor of this to the target it is drawn to. (1/2x if this is -// half the res, etc.) - - Vector GetScaleInverse() const { return m_ScaleInverse; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WrapsX -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the layer is set to wrap around the Y axis when -// scrolled out of bounds. -// Arguments: None. -// Return value: Whether this SceneLayer wraps or not in the Y axis. - - bool WrapsX() const { return m_WrapX; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WrapsY -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the layer is set to wrap around the Y axis when -// scrolled out of bounds. -// Arguments: None. -// Return value: Whether this SceneLayer wraps or not in the Y axis. - - bool WrapsY() const { return m_WrapY; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the offset of this SceneLayer. Observe that this offset will be -// modified by the scroll ratio before applied. -// Arguments: The new offset vector. -// Return value: None. - - void SetOffset(const Vector newOffset) { m_Offset = newOffset; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetScrollRatio -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the scroll ratio of this SceneLayer. This modifies the offset -// before any actual scrolling occurs. -// Arguments: The new scroll ratio vector. -// Return value: None. - - void SetScrollRatio(const Vector newRatio) { m_ScrollRatio = newRatio; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetScaleFactor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the scale that this should be drawn at when using DrawScaled. -// Arguments: The new scale factor vector. -// Return value: None. - - void SetScaleFactor(const Vector newScale); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: LockBitmaps -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Lock the internal bitmaps so it can be accessed by GetColorPixel() etc. -// Doing it in a separate method like this is more efficient because -// many bitmap accesses can be performed between a lock and unlock. -// UnlockBitmaps() should always be called after accesses are completed. -// Arguments: None. -// Return value: None. - - virtual void LockBitmaps() { acquire_bitmap(m_pMainBitmap); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UnlockBitmaps -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Unlocks the internal bitmaps and prevents access to display memory. -// Doing it in a separate method like this is more efficient because -// many bitmap accesses can be performed between a lock and an unlock. -// UnlockBitmaps() should only be called after LockBitmaps(). -// Arguments: None. -// Return value: None. - - virtual void UnlockBitmaps() { release_bitmap(m_pMainBitmap); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPixel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a specific pixel from the main bitmap of this SceneLayer. -// LockColorBitmap() must be called before using this method. -// Arguments: The X and Y coordinates of which pixel to get. -// Return value: An unsigned char specifying the requested pixel's value. - - unsigned char GetPixel(const int pixelX, const int pixelY); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPixel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets a specific pixel on the main bitmap of this SceneLayer to a -// specific value. LockColorBitmap() must be called before using this -// method. -// Arguments: The X and Y coordinates of which pixel to set. -// The value to set the pixel to. -// Return value: None. - - void SetPixel(const int pixelX, const int pixelY, const unsigned char value); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsWithinBounds -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns whether the integer coordinates passed in are within the -// bounds of this SceneLayer. -// Arguments: Int coordinates. -// Return value: Whether within bounds or not. - - bool IsWithinBounds(const int pixelX, const int pixelY, const int margin = 0) - { -// TODO: This doesn't take Y wrapping into acocunt!$@#$ - return (m_WrapX || (pixelX >= -margin) && pixelX < (m_pMainBitmap->w + margin)) && pixelY >= -1000 && pixelY < (m_pMainBitmap->h + margin); - } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ForceBounds -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Wraps or bounds a position coordinate if it is off bounds of the -// SceneLayer, depending on the wrap settings of this SceneLayer. -// Arguments: The X and Y coordinates of the position to wrap, if needed. -// Whether the coordinates above are of this' scale factor, or in its -// native pixels. -// Return value: Whether wrapping was performed or not. (Does not report on bounding) - - bool ForceBounds(int &posX, int &posY, bool scaled = true) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ForceBounds -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Wraps or bounds a position coordinate if it is off bounds of the -// SceneLayer, depending on the wrap settings of this SceneLayer. -// Arguments: The vector coordinates of the position to wrap, if needed. -// Whether the coordinates above are of this' scale factor, or in its -// native pixels. -// Return value: Whether wrapping was performed or not. (Does not report on bounding) - - bool ForceBounds(Vector &pos, bool scaled = true) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WrapPosition -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Only wraps a position coordinate if it is off bounds of the SceneLayer -// and wrapping in the corresponding axes are turned on. -// Arguments: The X and Y coordinates of the position to wrap, if needed. -// Whether the coordinates above are of this' scale factor, or in its -// native pixels. -// Return value: Whether wrapping was performed or not. - - bool WrapPosition(int &posX, int &posY, bool scaled = true) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WrapPosition -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Only wraps a position coordinate if it is off bounds of the SceneLayer -// and wrapping in the corresponding axes are turned on. -// Arguments: The vector coordinates of the position to wrap, if needed. -// Whether the coordinates above are of this' scale factor, or in its -// native pixels. -// Return value: Whether wrapping was performed or not. - - bool WrapPosition(Vector &pos, bool scaled = true) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this SceneLayer. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - void Update(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this SceneLayer's current scrolled position to a bitmap. -// Arguments: The bitmap to draw to. -// The box on the target bitmap to limit drawing to, with the corner of -// box being where the scroll position lines up. -// If a non-{-1,-1} vector is passed, the internal scroll offset of this -// is overridder with it. It becomes the new source coordinates. -// Return value: None. - - virtual void Draw(BITMAP *pTargetBitmap, Box &targetBox, const Vector &scrollOverride = Vector(-1, -1)) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawScaled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this SceneLayer's current scrolled position to a bitmap, but also -// scaled according to what has been set with SetScaleFactor. -// Arguments: The bitmap to draw to. -// The box on the target bitmap to limit drawing to, with the corner of -// box being where the scroll position lines up. -// If a non-{-1,-1} vector is passed, the internal scroll offset of this -// is overridder with it. It becomes the new source coordinates. -// Return value: None. - - virtual void DrawScaled(BITMAP *pTargetBitmap, Box &targetBox, const Vector &scrollOverride = Vector(-1, -1)) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: InitScrollRatios -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Initialize the scroll ratios from the encoded scroll info. Must be -// done after the bitmap has been created in the derived concrete classes. -// Arguments: None. -// Return value: None. - - void InitScrollRatios(); - - void UpdateScrollRatiosForNetworkPlayer(int player); - - - // Member variables - static Entity::ClassInfo m_sClass; - - ContentFile m_BitmapFile; - - BITMAP *m_pMainBitmap; - // Whether main bitmap is owned by this - bool m_MainBitmapOwned; - bool m_DrawTrans; - Vector m_Offset; - // The original scrollinfo with special encoded info that is then made into the actual scroll ratios - Vector m_ScrollInfo; - Vector m_ScrollRatio; - Vector m_ScaleFactor; - Vector m_ScaleInverse; - Vector m_ScaledDimensions; - - bool m_WrapX; - bool m_WrapY; - int m_FillLeftColor; - int m_FillRightColor; - int m_FillUpColor; - int m_FillDownColor; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this SceneLayer, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - SceneLayer(const SceneLayer &reference) = delete; - void operator=(const SceneLayer &rhs) = delete; - -}; - -} // namespace RTE -#endif // File \ No newline at end of file +namespace RTE { + + /// + /// A scrolling layer of the Scene. + /// + class SceneLayer : public Entity { + friend class NetworkServer; + + public: + + EntityAllocation(SceneLayer); + SerializableOverrideMethods; + ClassInfoGetters; + +#pragma region Creation + /// + /// Constructor method used to instantiate a SceneLayer object in system memory. Create() should be called before using the object. + /// + SceneLayer() { Clear(); } + + /// + /// Makes the SceneLayer object ready for use. + /// + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int Create() override { return Entity::Create(); } + + /// + /// Makes the SceneLayer object ready for use. + /// + /// The ContentFile to load as this SceneLayer's graphical representation. + /// Whether to draw masked (transparent) or not. + /// The initial scroll offset. + /// Whether the layer should wrap around or stop when scrolling beyond its bitmap's boundaries on the X axis. + /// Whether the layer should wrap around or stop when scrolling beyond its bitmap's boundaries on the Y axis. + /// + /// A vector whose components define two different things, depending on wrap arguments. + /// If a wrap argument is set to false, the corresponding component here will be interpreted as the width (X) or height (Y) (in pixels) of the total bitmap area that this layer is allowed to scroll across before stopping at an edge. + /// If wrapping is set to true, the value in scrollInfo is simply the ratio of offset at which any scroll operations will be done in. + /// A special command is if wrap is false and the corresponding component is -1.0, that signals that the own width or height should be used as scrollInfo input. + /// + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int Create(const ContentFile &bitmapFile, bool drawMasked, const Vector &offset, bool wrapX, bool wrapY, const Vector &scrollInfo); + + /// + /// Makes the SceneLayer object ready for use. + /// + /// The BITMAP to use for this SceneLayer. Ownership IS transferred! + /// Whether to draw masked (transparent) or not. + /// The initial scroll offset. + /// Whether the layer should wrap around or stop when scrolling beyond its bitmap's boundaries on the X axis. + /// Whether the layer should wrap around or stop when scrolling beyond its bitmap's boundaries on the Y axis. + /// + /// A vector whose components define two different things, depending on wrap arguments. + /// If a wrap argument is set to false, the corresponding component here will be interpreted as the width (X) or height (Y) (in pixels) of the total bitmap area that this layer is allowed to scroll across before stopping at an edge. + /// If wrapping is set to true, the value in scrollInfo is simply the ratio of offset at which any scroll operations will be done in. + /// A special command is if wrap is false and the corresponding component is -1.0, that signals that the own width or height should be used as scrollInfo input. + /// + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int Create(BITMAP *bitmap, bool drawMasked, const Vector &offset, bool wrapX, bool wrapY, const Vector &scrollInfo); + + /// + /// Creates a SceneLayer to be identical to another, by deep copy. + /// + /// A reference to the SceneLayer to deep copy. + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int Create(const SceneLayer &reference); +#pragma endregion + +#pragma region Destruction + /// + /// Destructor method used to clean up a SceneLayer object before deletion from system memory. + /// + ~SceneLayer() override { Destroy(true); } + + /// + /// Destroys and resets (through Clear()) the SceneLayer object. + /// + /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + void Destroy(bool notInherited = false) override; +#pragma endregion + +#pragma region Data Handling + /// + /// Whether this SceneLayer's bitmap data is loaded from a file or was generated at runtime. + /// + /// Whether this SceneLayer's bitmap data was loaded from a file or not. + virtual bool IsLoadedFromDisk() const { return !m_BitmapFile.GetDataPath().empty(); } + + /// + /// Loads previously specified/created data into memory. Has to be done before using this SceneLayer if the bitmap was not generated at runtime. + /// + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + virtual int LoadData(); + + /// + /// Saves data currently in memory to disk. + /// + /// The filepath to the where to save the bitmap data. + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + virtual int SaveData(const std::string &bitmapPath); + + /// + /// Clears out any previously loaded bitmap data from memory. + /// + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + virtual int ClearData(); +#pragma endregion + +#pragma region Getters and Setters + /// + /// Gets the BITMAP that this SceneLayer uses. + /// + /// A pointer to the BITMAP of this SceneLayer. Ownership is NOT transferred! + BITMAP * GetBitmap() const { return m_MainBitmap; } + + /// + /// Gets the scroll offset of this SceneLayer. + /// + /// A Vector with the scroll offset. + Vector GetOffset() const { return m_Offset; } + + /// + /// Sets the scroll offset of this SceneLayer. Observe that this offset will be modified by the scroll ratio before applied. + /// + /// The new offset Vector. + void SetOffset(const Vector &newOffset) { m_Offset = newOffset; } + + /// + /// Gets the scroll ratio that modifies the offset. + /// + /// A copy of the ratio. + Vector GetScrollRatio() const { return m_ScrollRatio; } + + /// + /// Sets the scroll ratio of this SceneLayer. This modifies the offset before any actual scrolling occurs. + /// + /// The new scroll ratio vector. + void SetScrollRatio(const Vector &newRatio) { m_ScrollRatio = newRatio; } + + /// + /// Gets the scale factor that this is drawn in. + /// + /// The scale factor of this to the target it is drawn to. (2x if this is half the res, etc.) + Vector GetScaleFactor() const { return m_ScaleFactor; } + + /// + /// Sets the scale that this should be drawn at when using DrawScaled. + /// + /// The new scale factor vector. + void SetScaleFactor(const Vector &newScale); + + /// + /// Indicates whether the layer is set to wrap around the X axis when scrolled out of bounds. + /// + /// Whether this SceneLayer wraps on the X axis or not. + bool WrapsX() const { return m_WrapX; } + + /// + /// Indicates whether the layer is set to wrap around the Y axis when scrolled out of bounds. + /// + /// Whether this SceneLayer wraps on the Y axis or not. + bool WrapsY() const { return m_WrapY; } + + /// + /// Gets a specific pixel from the bitmap of this SceneLayer. LockBitmaps() must be called before using this method. + /// + /// The X coordinate of the pixel to get. + /// The Y coordinate of the pixel to get. + /// An int specifying the requested pixel's color index. + int GetPixel(int pixelX, int pixelY) const; + + /// + /// Sets a specific pixel on the bitmap of this SceneLayer to a specific value. LockBitmaps() must be called before using this method. + /// + /// The X coordinate of the pixel to set. + /// The Y coordinate of the pixel to set. + /// The color index to set the pixel to. + void SetPixel(int pixelX, int pixelY, int materialID) const; + + /// + /// Returns whether the integer coordinates passed in are within the bounds of this SceneLayer. + /// + /// The X coordinates of the pixel. + /// The Y coordinates of the pixel. + /// + /// Whether within bounds or not. + bool IsWithinBounds(int pixelX, int pixelY, int margin = 0) const; +#pragma endregion + +#pragma region Concrete Methods + /// + /// Lock the internal bitmap so it can be accessed by GetPixel() etc. UnlockBitmaps() should always be called after accesses are completed. + /// Doing it in a separate method like this is more efficient because many bitmap accesses can be performed between a lock and unlock. + /// + void LockBitmaps() { /*acquire_bitmap(m_MainBitmap);*/ } + + /// + /// Unlocks the internal bitmaps and prevents access to display memory. UnlockBitmaps() should only be called after LockBitmaps(). + /// Doing it in a separate method like this is more efficient because many bitmap accesses can be performed between a lock and an unlock. + /// + void UnlockBitmaps() { /*release_bitmap(m_MainBitmap);*/ } + + /// + /// Wraps the given position coordinate if it is out of bounds of this SceneLayer and wrapping is enabled on the appropriate axes. + /// Does not force the position coordinate within bounds if wrapping is not enabled. + /// + /// The X coordinates of the position to wrap. + /// The Y coordinates of the position to wrap. + /// Whether wrapping was performed or not. + bool WrapPosition(int &posX, int &posY) const; + + /// + /// Wraps the given position Vector if it is out of bounds of this SceneLayer and wrapping is enabled on the appropriate axes. + /// Does not force the position Vector within bounds if wrapping is not enabled. + /// + /// The vector coordinates of the position to wrap. + /// Whether wrapping was performed or not. + bool WrapPosition(Vector &pos) const { return ForceBoundsOrWrapPosition(pos, false); } + + /// + /// Wraps or bounds a position coordinate if it is out of bounds of the SceneLayer, depending on the wrap settings of this SceneLayer. + /// + /// The X coordinates of the position to wrap. + /// The Y coordinates of the position to wrap. + /// Whether wrapping was performed or not. Does not report on bounding. + bool ForceBounds(int &posX, int &posY) const; + + /// + /// Wraps or bounds a position coordinate if it is out of bounds of the SceneLayer, depending on the wrap settings of this SceneLayer. + /// + /// The Vector coordinates of the position to wrap. + /// Whether wrapping was performed or not. Does not report on bounding. + bool ForceBounds(Vector &pos) const { return ForceBoundsOrWrapPosition(pos, true); } +#pragma endregion + +#pragma region Virtual Methods + /// + /// Updates the state of this SceneLayer. + /// + virtual void Update() {} + + /// + /// Draws this SceneLayer's current scrolled position to a bitmap. + /// + /// The bitmap to draw to. + /// The box on the target bitmap to limit drawing to, with the corner of box being where the scroll position lines up. + /// Whether the offset of this SceneLayer or the passed in offset override need to be adjusted to scroll ratio. + virtual void Draw(BITMAP *targetBitmap, Box &targetBox, bool offsetNeedsScrollRatioAdjustment = false); +#pragma endregion + + protected: + + static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. + + ContentFile m_BitmapFile; //!< ContentFile containing the path to this SceneLayer's sprite file. + BITMAP *m_MainBitmap; //!< The BITMAP of this SceneLayer. + bool m_MainBitmapOwned; //!< Whether the main bitmap is owned by this. + bool m_DrawMasked; //!< Whether pixels marked as transparent (index 0, magenta) are skipped when drawing or not (masked drawing). + + bool m_WrapX; //!< Whether wrapping is enabled on the X axis. + bool m_WrapY; //!< Whether wrapping is enable on the Y axis. + + Vector m_OriginOffset; //!< Offset of this SceneLayer off the top left edge of the screen. + Vector m_Offset; //!< The current scrolled offset of this SceneLayer, before being adjusted with the origin offset. + + Vector m_ScrollInfo; //!< The initial scrolling ratio of this SceneLayer as set in INI. Used to calculate the actual scrolling ratios. + Vector m_ScrollRatio; //!< The scrolling ratios of this SceneLayer, adjusted to the Scene, player screen dimensions and scaling factor as necessary. + Vector m_ScaleFactor; //!< The scaling factor of this SceneLayer. Used for scaled drawing and adjusting scrolling ratios. + Vector m_ScaledDimensions; //!< The dimensions of this SceneLayer adjusted to the scaling factor. + + /// + /// Initialize the scroll ratios from the scroll info. Must be done after the bitmap has been created. + /// + /// + /// + void InitScrollRatios(bool initForNetworkPlayer = false, int player = Players::NoPlayer); + + private: + + /// + /// Wraps or bounds a position coordinate if it is out of bounds of the SceneLayer, depending on the wrap settings of this SceneLayer. + /// + /// The Vector coordinates of the position to wrap. + /// Whether to attempt bounding or wrapping, or just wrapping. + /// Whether wrapping was performed or not. Does not report on bounding. + bool ForceBoundsOrWrapPosition(Vector &pos, bool forceBounds) const; + +#pragma region Draw Breakdown + /// + /// Performs wrapped drawing of this SceneLayer's bitmap to the screen in cases where it is both wider and taller than the target bitmap. + /// + /// The bitmap to draw to. + /// The box on the target bitmap to limit drawing to, with the corner of box being where the scroll position lines up. + /// Whether to use scaled drawing routines or not. + void DrawWrapped(BITMAP *targetBitmap, const Box &targetBox, bool drawScaled) const; + + /// + /// Performs tiled drawing of this SceneLayer's bitmap to the screen in cases where the target bitmap is larger in some dimension. + /// + /// The bitmap to draw to. + /// The box on the target bitmap to limit drawing to, with the corner of box being where the scroll position lines up. + /// Whether to use scaled drawing routines or not. + void DrawTiled(BITMAP *targetBitmap, const Box &targetBox, bool drawScaled) const; +#pragma endregion + + /// + /// Clears all the member variables of this SceneLayer, effectively resetting the members of this abstraction level only. + /// + void Clear(); + + // Disallow the use of some implicit methods. + SceneLayer(const SceneLayer &reference) = delete; + void operator=(const SceneLayer &rhs) = delete; + }; +} +#endif \ No newline at end of file diff --git a/Entities/TerrainDebris.cpp b/Entities/TerrainDebris.cpp index bc7c598f3..2b6ef7969 100644 --- a/Entities/TerrainDebris.cpp +++ b/Entities/TerrainDebris.cpp @@ -13,22 +13,24 @@ namespace RTE { m_BitmapCount = 0; m_Material.Reset(); m_TargetMaterial.Reset(); - m_OnlyOnSurface = false; + m_DebrisPlacementMode = DebrisPlacementMode::NoPlacementRestrictions; m_OnlyBuried = false; m_MinDepth = 0; m_MaxDepth = 10; + m_MinRotation = 0; + m_MaxRotation = 0; + m_CanHFlip = false; + m_CanVFlip = false; + m_FlipChance = 0.5F; m_Density = 0.01F; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int TerrainDebris::Create() { - if (Entity::Create() < 0) { - return -1; - } + Entity::Create(); m_DebrisFile.GetAsAnimation(m_Bitmaps, m_BitmapCount); - RTEAssert(!m_Bitmaps.empty() && m_Bitmaps.at(0), "Failed to load debris bitmaps!"); - + RTEAssert(!m_Bitmaps.empty() && m_Bitmaps.at(0), "Failed to load debris bitmaps during TerrainDebris::Create!"); return 0; } @@ -38,14 +40,20 @@ namespace RTE { Entity::Create(reference); m_DebrisFile = reference.m_DebrisFile; + m_Bitmaps.clear(); m_Bitmaps = reference.m_Bitmaps; m_BitmapCount = reference.m_BitmapCount; m_Material = reference.m_Material; m_TargetMaterial = reference.m_TargetMaterial; - m_OnlyOnSurface = reference.m_OnlyOnSurface; + m_DebrisPlacementMode = reference.m_DebrisPlacementMode; m_OnlyBuried = reference.m_OnlyBuried; m_MinDepth = reference.m_MinDepth; m_MaxDepth = reference.m_MaxDepth; + m_MinRotation = reference.m_MinRotation; + m_MaxRotation = reference.m_MaxRotation; + m_CanHFlip = reference.m_CanHFlip; + m_CanVFlip = reference.m_CanVFlip; + m_FlipChance = reference.m_FlipChance; m_Density = reference.m_Density; return 0; @@ -63,14 +71,25 @@ namespace RTE { reader >> m_Material; } else if (propName == "TargetMaterial") { reader >> m_TargetMaterial; - } else if (propName == "OnlyOnSurface") { - reader >> m_OnlyOnSurface; + } else if (propName == "DebrisPlacementMode") { + m_DebrisPlacementMode = static_cast(std::stoi(reader.ReadPropValue())); + if (m_DebrisPlacementMode < DebrisPlacementMode::NoPlacementRestrictions || m_DebrisPlacementMode > DebrisPlacementMode::OnOverhangAndCavityOverhang) { reader.ReportError("Invalid TerrainDebris placement mode!"); } } else if (propName == "OnlyBuried") { reader >> m_OnlyBuried; } else if (propName == "MinDepth") { reader >> m_MinDepth; } else if (propName == "MaxDepth") { reader >> m_MaxDepth; + } else if (propName == "MinRotation") { + reader >> m_MinRotation; + } else if (propName == "MaxRotation") { + reader >> m_MaxRotation; + } else if (propName == "CanHFlip") { + reader >> m_CanHFlip; + } else if (propName == "CanVFlip") { + reader >> m_CanVFlip; + } else if (propName == "FlipChance") { + reader >> m_FlipChance; } else if (propName == "DensityPerMeter") { reader >> m_Density; } else { @@ -84,104 +103,148 @@ namespace RTE { int TerrainDebris::Save(Writer &writer) const { Entity::Save(writer); - writer.NewProperty("DebrisFile"); - writer << m_DebrisFile; - writer.NewProperty("DebrisPieceCount"); - writer << m_BitmapCount; - writer.NewProperty("DebrisMaterial"); - writer << m_Material; - writer.NewProperty("TargetMaterial"); - writer << m_TargetMaterial; - writer.NewProperty("OnlyOnSurface"); - writer << m_OnlyOnSurface; - writer.NewProperty("OnlyBuried"); - writer << m_OnlyBuried; - writer.NewProperty("MinDepth"); - writer << m_MinDepth; - writer.NewProperty("MaxDepth"); - writer << m_MaxDepth; - writer.NewProperty("DensityPerMeter"); - writer << m_Density; + writer.NewPropertyWithValue("DebrisFile", m_DebrisFile); + writer.NewPropertyWithValue("DebrisPieceCount", m_BitmapCount); + writer.NewPropertyWithValue("DebrisMaterial", m_Material); + writer.NewPropertyWithValue("TargetMaterial", m_TargetMaterial); + writer.NewPropertyWithValue("DebrisPlacementMode", m_DebrisPlacementMode); + writer.NewPropertyWithValue("OnlyBuried", m_OnlyBuried); + writer.NewPropertyWithValue("MinDepth", m_MinDepth); + writer.NewPropertyWithValue("MaxDepth", m_MaxDepth); + writer.NewPropertyWithValue("MinRotation", m_MinRotation); + writer.NewPropertyWithValue("MaxRotation", m_MaxRotation); + writer.NewPropertyWithValue("CanHFlip", m_CanHFlip); + writer.NewPropertyWithValue("CanVFlip", m_CanVFlip); + writer.NewPropertyWithValue("FlipChance", m_FlipChance); + writer.NewPropertyWithValue("DensityPerMeter", m_Density); return 0; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TerrainDebris::ApplyDebris(SLTerrain *terrain) { - RTEAssert(!m_Bitmaps.empty() && m_BitmapCount > 0, "No bitmaps loaded for terrain debris!"); - - BITMAP *terrBitmap = terrain->GetFGColorBitmap(); + bool TerrainDebris::GetPiecePlacementPosition(SLTerrain *terrain, Box &possiblePiecePosition) const { BITMAP *matBitmap = terrain->GetMaterialBitmap(); - - // How many pieces of debris we're spreading out. - unsigned int terrainWidth = terrBitmap->w; - unsigned int pieceCount = static_cast((static_cast(terrainWidth) * c_MPP) * m_Density); - - // First is index in the bitmap array, Second is blit location - std::list> piecesToPlace; - Vector location; - Box pieceBox; - - unsigned char checkPixel; - - acquire_bitmap(terrBitmap); - acquire_bitmap(matBitmap); - - for (unsigned int piece = 0; piece < pieceCount; ++piece) { - bool place = false; - unsigned int currentBitmap = RandomNum(0, m_BitmapCount - 1); - RTEAssert(currentBitmap >= 0 && currentBitmap < m_BitmapCount, "Bitmap index is out of bounds!"); - - pieceBox.SetWidth(static_cast(m_Bitmaps.at(currentBitmap)->w)); - pieceBox.SetHeight(static_cast(m_Bitmaps.at(currentBitmap)->h)); - - int x = RandomNum(0, terrainWidth); - int y = 0; - int depth = RandomNum(m_MinDepth, m_MaxDepth); - - while (y < terrBitmap->h) { - // Find the air-terrain boundary - for (; y < terrBitmap->h; ++y) { - checkPixel = _getpixel(matBitmap, x, y); - // Check for terrain hit - if (checkPixel != g_MaterialAir) { - if (checkPixel == m_TargetMaterial.GetIndex()) { - place = true; - break; - // If we didn't hit target material, but are specified to, then don't place - } else if (m_OnlyOnSurface) { - place = false; - break; + int posX = RandomNum(0, matBitmap->w); + int depth = RandomNum(m_MinDepth, m_MaxDepth); + int buriedDepthOffset = m_OnlyBuried ? static_cast(possiblePiecePosition.GetHeight() * 0.6F) : 0; + int prevMaterialCheckPixel = -1; + + bool scanForOverhang = m_DebrisPlacementMode == DebrisPlacementMode::OnOverhangOnly || m_DebrisPlacementMode == DebrisPlacementMode::OnCavityOverhangOnly || m_DebrisPlacementMode == DebrisPlacementMode::OnOverhangAndCavityOverhang; + + // For overhangs scan from the bottom so it's easier to detect. + for (int surfacePosY = 0, overhangPosY = matBitmap->h - 1; surfacePosY < matBitmap->h && overhangPosY > 0; surfacePosY++, overhangPosY--) { + int posY = scanForOverhang ? overhangPosY : surfacePosY; + int materialCheckPixel = _getpixel(matBitmap, posX, posY); + if (materialCheckPixel != MaterialColorKeys::g_MaterialAir) { + // TODO: Adding depth here will properly place debris only on target material, rather than any random pixel within depth range from the original target material pixel. + int depthAdjustedPosY = posY;// + (depth * (scanForOverhang ? -1 : 1)); + if (scanForOverhang ? (depthAdjustedPosY > 0) : (depthAdjustedPosY < matBitmap->h)) { + // TODO: Enable the depth check once multi TargetMaterial debris is supported. + //materialCheckPixel = _getpixel(matBitmap, posX, depthAdjustedPosY); + if (MaterialPixelIsValidTarget(materialCheckPixel, prevMaterialCheckPixel)) { + surfacePosY += (depth + buriedDepthOffset); + overhangPosY -= (depth + buriedDepthOffset); + possiblePiecePosition.SetCenter(Vector(static_cast(posX), static_cast(scanForOverhang ? overhangPosY : surfacePosY))); + if (!m_OnlyBuried || terrain->IsBoxBuried(possiblePiecePosition)) { + return true; } } } - if (!place) { + } + prevMaterialCheckPixel = materialCheckPixel; + } + return false; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool TerrainDebris::MaterialPixelIsValidTarget(int materialCheckPixel, int prevMaterialCheckPixel) const { + bool checkResult = true; + + if (materialCheckPixel != m_TargetMaterial.GetIndex()) { + checkResult = false; + } else { + // TODO: Consider using disgustang bitwise/shifting junk instead of enum. + switch (m_DebrisPlacementMode) { + case DebrisPlacementMode::OnSurfaceOnly: + case DebrisPlacementMode::OnOverhangOnly: + if (prevMaterialCheckPixel != MaterialColorKeys::g_MaterialAir) { checkResult = false; } break; - } - // The target locations are on the center of the objects; if supposed to be buried, move down so it is - y += depth + static_cast(m_OnlyBuried ? pieceBox.GetHeight() * 0.6F : 0); - pieceBox.SetCenter(Vector(static_cast(x), static_cast(y))); - - // Make sure we're not trying to place something into a cave or other air pocket - if (!g_SceneMan.GetTerrain()->IsAirPixel(x, y) && (!m_OnlyBuried || g_SceneMan.GetTerrain()->IsBoxBuried(pieceBox))) { - // Do delayed drawing so that we don't end up placing things on top of each other - piecesToPlace.emplace_back(currentBitmap, pieceBox.GetCorner()); + case DebrisPlacementMode::OnCavitySurfaceOnly: + case DebrisPlacementMode::OnCavityOverhangOnly: + if (prevMaterialCheckPixel != MaterialColorKeys::g_MaterialCavity) { checkResult = false; } + break; + case DebrisPlacementMode::OnSurfaceAndCavitySurface: + case DebrisPlacementMode::OnOverhangAndCavityOverhang: + if (prevMaterialCheckPixel != MaterialColorKeys::g_MaterialAir && prevMaterialCheckPixel != MaterialColorKeys::g_MaterialCavity) { checkResult = false; } + break; + default: + // No placement restrictions, pixel just needs to be of target material. break; - } } } + return checkResult; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - for (const auto &[pieceFrameNum, piecePos] : piecesToPlace) { - // Draw the color sprite onto the terrain color layer. - draw_sprite(terrBitmap, m_Bitmaps.at(pieceFrameNum), piecePos.GetFloorIntX(), piecePos.GetFloorIntY()); - // Draw the material representation onto the terrain's material layer - draw_character_ex(matBitmap, m_Bitmaps.at(pieceFrameNum), piecePos.GetFloorIntX(), piecePos.GetFloorIntY(), m_Material.GetIndex(), -1); + void TerrainDebris::DrawToTerrain(SLTerrain *terrain, BITMAP *bitmapToDraw, const Vector &position) const { + // Create a square temp bitmap that is larger than the original to avoid clipping if rotating. + int dimensions = 10 + std::max(bitmapToDraw->w, bitmapToDraw->h); + BITMAP *tempDrawBitmap = create_bitmap_ex(8, dimensions, dimensions); + clear_bitmap(tempDrawBitmap); + + // Offset the original bitmap on the temp bitmap so it's centered, otherwise will be positioned incorrectly and can clip if rotated or flipped. + int offsetX = (dimensions - bitmapToDraw->w) / 2; + int offsetY = (dimensions - bitmapToDraw->h) / 2; + blit(bitmapToDraw, tempDrawBitmap, 0, 0, offsetX, offsetY, bitmapToDraw->w, bitmapToDraw->h); + + BITMAP *tempFlipAndRotBitmap = nullptr; + if (m_CanHFlip || m_CanVFlip || m_MinRotation != 0 || m_MaxRotation != 0) { + tempFlipAndRotBitmap = create_bitmap_ex(8, dimensions, dimensions); + + if (m_CanHFlip && m_FlipChance >= RandomNum()) { + clear_bitmap(tempFlipAndRotBitmap); + draw_sprite_h_flip(tempFlipAndRotBitmap, tempDrawBitmap, 0, 0); + blit(tempFlipAndRotBitmap, tempDrawBitmap, 0, 0, 0, 0, dimensions, dimensions); + } + if (m_CanVFlip && m_FlipChance >= RandomNum()) { + clear_bitmap(tempFlipAndRotBitmap); + draw_sprite_v_flip(tempFlipAndRotBitmap, tempDrawBitmap, 0, 0); + blit(tempFlipAndRotBitmap, tempDrawBitmap, 0, 0, 0, 0, dimensions, dimensions); + } + if (m_MinRotation != 0 || m_MaxRotation != 0) { + clear_bitmap(tempFlipAndRotBitmap); + rotate_sprite(tempFlipAndRotBitmap, tempDrawBitmap, 0, 0, ftofix(GetAllegroAngle(static_cast(RandomNum(m_MinRotation, m_MaxRotation))))); + blit(tempFlipAndRotBitmap, tempDrawBitmap, 0, 0, 0, 0, dimensions, dimensions); + } } + draw_sprite(terrain->GetFGColorBitmap(), tempDrawBitmap, position.GetFloorIntX() - offsetX, position.GetFloorIntY() - offsetY); + draw_character_ex(terrain->GetMaterialBitmap(), tempDrawBitmap, position.GetFloorIntX() - offsetX, position.GetFloorIntY() - offsetY, m_Material.GetIndex(), -1); - release_bitmap(terrBitmap); - release_bitmap(matBitmap); - terrBitmap = nullptr; - matBitmap = nullptr; + if (tempFlipAndRotBitmap) { destroy_bitmap(tempFlipAndRotBitmap); } + destroy_bitmap(tempDrawBitmap); + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void TerrainDebris::ScatterOnTerrain(SLTerrain *terrain) { + RTEAssert(!m_Bitmaps.empty() && m_BitmapCount > 0, "No bitmaps loaded for terrain debris during TerrainDebris::ScatterOnTerrain!"); + + // Reference. Do not remove. + //acquire_bitmap(terrain->GetFGColorBitmap()); + //acquire_bitmap(terrain->GetMaterialBitmap()); + + int possiblePieceToPlaceCount = static_cast((static_cast(terrain->GetMaterialBitmap()->w) * c_MPP) * m_Density); + for (int piece = 0; piece < possiblePieceToPlaceCount; ++piece) { + int pieceBitmapIndex = RandomNum(0, m_BitmapCount - 1); + RTEAssert(pieceBitmapIndex >= 0 && pieceBitmapIndex < m_BitmapCount, "Bitmap index was out of bounds during TerrainDebris::ScatterOnTerrain!"); + Box possiblePiecePosition(Vector(), static_cast(m_Bitmaps.at(pieceBitmapIndex)->w), static_cast(m_Bitmaps.at(pieceBitmapIndex)->h)); + if (GetPiecePlacementPosition(terrain, possiblePiecePosition)) { DrawToTerrain(terrain, m_Bitmaps.at(pieceBitmapIndex), possiblePiecePosition.GetCorner()); } + } + // Reference. Do not remove. + //release_bitmap(terrain->GetMaterialBitmap()); + //release_bitmap(terrain->GetFGColorBitmap()); } } \ No newline at end of file diff --git a/Entities/TerrainDebris.h b/Entities/TerrainDebris.h index 33c130949..ddcf01a29 100644 --- a/Entities/TerrainDebris.h +++ b/Entities/TerrainDebris.h @@ -6,10 +6,12 @@ namespace RTE { + class Vector; + class Box; class SLTerrain; /// - /// Specifies a certain type of debris scattered randomly throughout a Terrain, before TerrainObjects are placed. + /// Debris objects scattered randomly throughout the terrain. /// class TerrainDebris : public Entity { @@ -25,12 +27,6 @@ namespace RTE { /// TerrainDebris() { Clear(); } - /// - /// Copy constructor method used to instantiate a TerrainDebris object identical to an already existing one. - /// - /// A TerrainDebris object which is passed in by reference. - TerrainDebris(const TerrainDebris &reference) { Clear(); Create(reference); } - /// /// Makes the TerrainDebris object ready for use. /// @@ -55,62 +51,90 @@ namespace RTE { /// Destroys and resets (through Clear()) the TerrainDebris object. /// /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. - /// Don't delete bitmaps since they are owned in the CoententFile static maps. void Destroy(bool notInherited = false) override { if (!notInherited) { Entity::Destroy(); } Clear(); } +#pragma endregion +#pragma region Concrete Methods /// - /// Resets the entire TerrainDebris, including its inherited members, to their default settings or values. + /// Places random pieces of this TerrainDebris's at random positions on the specified SLTerrain. /// - void Reset() override { Clear(); Entity::Reset(); } + /// The SLTerrain to scatter this TerrainDebris on. Ownership is NOT transferred! + void ScatterOnTerrain(SLTerrain *terrain); #pragma endregion -#pragma region Getters - /// - /// Gets all the bitmaps of this collection of debris. - /// - /// A vector of pointers to BITMAPs. Ownership is NOT transferred. - std::vector GetBitmaps() const { return m_Bitmaps; } + private: /// - /// Gets the current number of BITMAPs that describe all the different pieces of debris. + /// Enumeration for the different debris placement modes. /// - /// The number of BITMAPs this TerrainDebris has. - int GetBitmapCount() const { return m_BitmapCount; } -#pragma endregion + enum DebrisPlacementMode { + NoPlacementRestrictions, + OnSurfaceOnly, + OnCavitySurfaceOnly, + OnSurfaceAndCavitySurface, + OnOverhangOnly, + OnCavityOverhangOnly, + OnOverhangAndCavityOverhang + }; -#pragma region Concrete Methods - /// - /// Applies the debris to an SLTerrain as its read parameters specify. - /// - /// Pointer to the terrain to place the debris on. Ownership is NOT transferred! - void ApplyDebris(SLTerrain *terrain); -#pragma endregion + static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. - protected: + ContentFile m_DebrisFile; //!< ContentFile containing the path to the debris sprites. + std::vector m_Bitmaps; //!< All the different bitmaps of this debris. Not owned. + int m_BitmapCount; //!< How many individual pieces this debris has. - static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. + Material m_Material; //!< The Material of the debris. + Material m_TargetMaterial; //!< The Material which pieces of this debris should be placed on in the terrain. - ContentFile m_DebrisFile; //!< Shows where the bitmaps are. - std::vector m_Bitmaps; //!< All the different bitmaps for each chunk of debris. Not owned. - int m_BitmapCount; //!< How many bitmaps we have loaded. + DebrisPlacementMode m_DebrisPlacementMode; //!< This will determine how target Material checking and debris applying should behave. If set to NoPlacementRestrictions, checking will continue to penetrate down into non-air Materials to try to find the target Material. + bool m_OnlyBuried; //!< Whether to place a piece of this only if a spot where it fits completely buried in the terrain is found. - Material m_Material; //!< The material of all this debris. - Material m_TargetMaterial; //!< The target material in which this debris should only exist in. + int m_MinDepth; //!< Minimum depth debris pieces can be placed into the terrain contour, in pixels. This can be negative for debris placed above ground. + int m_MaxDepth; //!< Maximum depth debris pieces can be placed into the terrain contour, in pixels. This can be negative for debris placed above ground. - bool m_OnlyOnSurface; //!< Whether to only place if the target material is exposed on the surface of the terrain. If false, checking will continue to penetrate down into non-air materials to try to find the target material. - bool m_OnlyBuried; //!< Whether to only place a piece of this if we find a spot for it to fit completely buried in the terrain. + int m_MinRotation; //!< Minimum rotation debris pieces can be rotated, in degrees. Positive values are clockwise. + int m_MaxRotation; //!< Maximum rotation debris pieces can be rotated, in degrees. Positive values are clockwise. - int m_MinDepth; //!< Minimum depth into the terrain contour. This can be negative for debris placed above ground. - int m_MaxDepth; //!< Max depth into the terrain contour. This can be negative for debris placed above ground. + bool m_CanHFlip; //!< Whether debris pieces can be flipped horizontally. + bool m_CanVFlip; //!< Whether debris pieces can be flipped vertically. + float m_FlipChance; //!< The chance a debris piece will be flipped on either axis. - float m_Density; //!< Approximate Density count per meter. + float m_Density; //!< Approximate density of debris pieces per meter. - private: +#pragma region Debris Application Breakdown + /// + /// Checks if conditions apply for a debris piece to be placed to the terrain. The actual position is returned via the passed in Box's center position. + /// + /// Pointer to the SLTerrain to check debris placement on. Ownership is NOT transferred! + /// A Box that holds the debris piece's dimensions. The center position of the Box will be modified during checking. + /// True if a valid placement position was found, which means the passed in Box's center or corner positions are good to be used as the piece's drawing position. + bool GetPiecePlacementPosition(SLTerrain *terrain, Box &possiblePiecePosition) const; + + /// + /// Checks whether the passed in pixel color value is of target Material, and if extra conditions apply for it to be valid for placement, depending on DebrisPlacementMode. + /// + /// The pixel color value to check. + /// The previously checked pixel color value to check extra conditions with. Does not apply when DebrisPlacementMode is NoPlacementRestrictions. + /// Whether the passed in pixel color value is valid for debris placement. + bool MaterialPixelIsValidTarget(int materialCheckPixel, int prevMaterialCheckPixel) const; + + /// + /// Draws the debris piece bitmap on the terrain at the specified position. Performs flipping and rotating if necessary. + /// + /// Pointer to the SLTerrain to draw the debris piece on. Ownership is NOT transferred! + /// The BITMAP to draw. Ownership is NOT transferred! + /// The position to draw the debris piece on the terrain. + void DrawToTerrain(SLTerrain *terrain, BITMAP *bitmapToDraw, const Vector &position) const; +#pragma endregion /// /// Clears all the member variables of this TerrainDebris, effectively resetting the members of this abstraction level only. /// void Clear(); + + // Disallow the use of some implicit methods. + TerrainDebris(const TerrainDebris &reference) = delete; + void operator=(const TerrainDebris &rhs) = delete; }; } #endif \ No newline at end of file diff --git a/Entities/TerrainFrosting.cpp b/Entities/TerrainFrosting.cpp new file mode 100644 index 000000000..4d0104644 --- /dev/null +++ b/Entities/TerrainFrosting.cpp @@ -0,0 +1,94 @@ +#include "TerrainFrosting.h" +#include "SLTerrain.h" + +namespace RTE { + + const std::string TerrainFrosting::c_ClassName = "TerrainFrosting"; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void TerrainFrosting::Clear() { + m_FrostingMaterial.Reset(); + m_TargetMaterial.Reset(); + m_MinThickness = 5; + m_MaxThickness = 5; + m_InAirOnly = true; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int TerrainFrosting::ReadProperty(const std::string_view &propName, Reader &reader) { + if (propName == "FrostingMaterial") { + reader >> m_FrostingMaterial; + } else if (propName == "TargetMaterial") { + reader >> m_TargetMaterial; + } else if (propName == "MinThickness") { + reader >> m_MinThickness; + } else if (propName == "MaxThickness") { + reader >> m_MaxThickness; + } else if (propName == "InAirOnly") { + reader >> m_InAirOnly; + } else { + return Serializable::ReadProperty(propName, reader); + } + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int TerrainFrosting::Save(Writer &writer) const { + Serializable::Save(writer); + + writer.NewPropertyWithValue("FrostingMaterial", m_FrostingMaterial); + writer.NewPropertyWithValue("TargetMaterial", m_TargetMaterial); + writer.NewPropertyWithValue("MinThickness", m_MinThickness); + writer.NewPropertyWithValue("MaxThickness", m_MaxThickness); + writer.NewPropertyWithValue("InAirOnly", m_InAirOnly); + + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void TerrainFrosting::FrostTerrain(SLTerrain *terrain) const { + BITMAP *frostingTexture = m_FrostingMaterial.GetFGTexture(); + BITMAP *fgColorBitmap = terrain->GetFGColorBitmap(); + BITMAP *matBitmap = terrain->GetBitmap(); + + bool targetMatFound = false; + bool applyingFrosting = false; + int appliedThickness = 0; + + // Reference. Do not remove. + //acquire_bitmap(matBitmap); + //acquire_bitmap(fgColorBitmap); + //if (frostingTexture) { acquire_bitmap(frostingTexture); } + + for (int xPos = 0; xPos < matBitmap->w; ++xPos) { + int thicknessGoal = RandomNum(m_MinThickness, m_MaxThickness); + + for (int yPos = matBitmap->h - 1; yPos >= 0; --yPos) { + int materialCheckPixel = _getpixel(matBitmap, xPos, yPos); + + if (!targetMatFound && materialCheckPixel == m_TargetMaterial.GetIndex()) { + targetMatFound = true; + appliedThickness = 0; + } else if (targetMatFound && materialCheckPixel != m_TargetMaterial.GetIndex() && appliedThickness <= thicknessGoal) { + targetMatFound = false; + applyingFrosting = true; + } + if (applyingFrosting && (materialCheckPixel == MaterialColorKeys::g_MaterialAir || !m_InAirOnly) && appliedThickness <= thicknessGoal) { + _putpixel(fgColorBitmap, xPos, yPos, frostingTexture ? _getpixel(frostingTexture, xPos % frostingTexture->w, yPos % frostingTexture->h) : m_FrostingMaterial.GetColor().GetIndex()); + _putpixel(matBitmap, xPos, yPos, m_FrostingMaterial.GetIndex()); + appliedThickness++; + } else { + applyingFrosting = false; + } + } + } + // Reference. Do not remove. + //if (frostingTexture) { release_bitmap(frostingTexture); } + //release_bitmap(fgColorBitmap); + //release_bitmap(matBitmap); + } +} \ No newline at end of file diff --git a/Entities/TerrainFrosting.h b/Entities/TerrainFrosting.h new file mode 100644 index 000000000..c2cb37a26 --- /dev/null +++ b/Entities/TerrainFrosting.h @@ -0,0 +1,55 @@ +#ifndef _RTETERRAINFROSTING_ +#define _RTETERRAINFROSTING_ + +#include "Material.h" + +namespace RTE { + + class SLTerrain; + + /// + /// A layer of material on top of another material on the terrain. + /// + class TerrainFrosting : public Serializable { + + public: + + SerializableClassNameGetter + SerializableOverrideMethods + +#pragma region Creation + /// + /// Constructor method used to instantiate a TerrainFrosting object in system memory and make it ready for use. + /// + TerrainFrosting() { Clear(); } +#pragma endregion + +#pragma region Concrete Methods + /// + /// Draws the frosting layer to the specified SLTerrain according to the read-in parameters. + /// + /// The SLTerrain to frost. Ownership is NOT transferred! + void FrostTerrain(SLTerrain *terrain) const; +#pragma endregion + + private: + + static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. + + Material m_FrostingMaterial; //!< The Material this frosting is made of. + Material m_TargetMaterial; //!< The Material this frosting will be placed on top of in the terrain. + int m_MinThickness; //!< The minimum height above the target Material, in pixels. + int m_MaxThickness; //!< The maximum height above the target Material, in pixels. + bool m_InAirOnly; //!< Whether the frosting only appears where there is air (i.e. does not appear where the terrain background layer is showing). + + /// + /// Clears all the member variables of this TerrainFrosting, effectively resetting the members of this abstraction level only. + /// + void Clear(); + + // Disallow the use of some implicit methods. + TerrainFrosting(const TerrainFrosting &reference) = delete; + void operator=(const TerrainFrosting &rhs) = delete; + }; +} +#endif \ No newline at end of file diff --git a/Entities/TerrainObject.cpp b/Entities/TerrainObject.cpp index 9e78c82f3..976cb10b9 100644 --- a/Entities/TerrainObject.cpp +++ b/Entities/TerrainObject.cpp @@ -1,447 +1,257 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: TerrainObject.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the TerrainObject class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com +#include "TerrainObject.h" +#include "SLTerrain.h" +#include "SceneMan.h" +namespace RTE { -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files + ConcreteClassInfo(TerrainObject, SceneObject, 0); -#include "TerrainObject.h" -#include "PresetMan.h" -#include "ContentFile.h" +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -namespace RTE { + void TerrainObject::Clear() { + m_FGColorFile.Reset(); + m_FGColorBitmap = nullptr; + m_BGColorFile.Reset(); + m_BGColorBitmap = nullptr; + m_MaterialFile.Reset(); + m_MaterialBitmap = nullptr; + m_BitmapOffset.Reset(); + m_OffsetDefined = false; + m_ChildObjects.clear(); + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int TerrainObject::Create() { + SceneObject::Create(); + + if (!m_OffsetDefined) { + int bitmapWidth = GetBitmapWidth(); + int bitmapHeight = GetBitmapHeight(); + + if (bitmapWidth > 24) { m_BitmapOffset.SetX(-(static_cast(bitmapWidth / 2))); } + if (bitmapHeight > 24) { m_BitmapOffset.SetY(-(static_cast(bitmapHeight / 2))); } + } + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int TerrainObject::Create(const TerrainObject &reference) { + SceneObject::Create(reference); + + m_FGColorFile = reference.m_FGColorFile; + m_FGColorBitmap = reference.m_FGColorBitmap; + m_BGColorFile = reference.m_BGColorFile; + m_BGColorBitmap = reference.m_BGColorBitmap; + m_MaterialFile = reference.m_MaterialFile; + m_MaterialBitmap = reference.m_MaterialBitmap; + m_BitmapOffset = reference.m_BitmapOffset; + m_OffsetDefined = reference.m_OffsetDefined; + + for (const SceneObject::SOPlacer &childObject : reference.m_ChildObjects) { + m_ChildObjects.emplace_back(childObject); + } + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int TerrainObject::ReadProperty(const std::string_view &propName, Reader &reader) { + if (propName == "FGColorFile") { + reader >> m_FGColorFile; + m_FGColorBitmap = m_FGColorFile.GetAsBitmap(); + } else if (propName == "BGColorFile") { + reader >> m_BGColorFile; + m_BGColorBitmap = m_BGColorFile.GetAsBitmap(); + } else if (propName == "MaterialFile") { + reader >> m_MaterialFile; + m_MaterialBitmap = m_MaterialFile.GetAsBitmap(); + } else if (propName == "BitmapOffset") { + reader >> m_BitmapOffset; + m_OffsetDefined = true; + } else if (propName == "AddChildObject") { + SceneObject::SOPlacer newChildObject; + reader >> newChildObject; + newChildObject.SetTeam(m_Team); + m_ChildObjects.emplace_back(newChildObject); + } else { + return SceneObject::ReadProperty(propName, reader); + } + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int TerrainObject::Save(Writer &writer) const { + SceneObject::Save(writer); + + if (!m_FGColorFile.GetDataPath().empty()) { writer.NewPropertyWithValue("FGColorFile", m_FGColorFile); } + if (!m_BGColorFile.GetDataPath().empty()) { writer.NewPropertyWithValue("BGColorFile", m_BGColorFile); } + if (!m_MaterialFile.GetDataPath().empty()) { writer.NewPropertyWithValue("MaterialFile", m_MaterialFile); } + + if (m_OffsetDefined) { writer.NewPropertyWithValue("BitmapOffset", m_BitmapOffset); } + + for (const SceneObject::SOPlacer &childObject : m_ChildObjects) { + writer.NewPropertyWithValue("AddChildObject", childObject); + } + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + BITMAP * TerrainObject::GetGraphicalIcon() const { + if (m_FGColorBitmap) { + // Check several spots on the FG bitmap, to be sure it has parts that aren't transparent. If not, show the background layer instead. + int piece = m_FGColorBitmap->w / 10; + if (_getpixel(m_FGColorBitmap, m_FGColorBitmap->w / 2, m_FGColorBitmap->h / 2) != ColorKeys::g_MaskColor || _getpixel(m_FGColorBitmap, piece, piece) != ColorKeys::g_MaskColor || _getpixel(m_FGColorBitmap, m_FGColorBitmap->w - piece, piece) != ColorKeys::g_MaskColor || _getpixel(m_FGColorBitmap, piece, m_FGColorBitmap->h - piece) != ColorKeys::g_MaskColor) { + return m_FGColorBitmap; + } + } + return m_BGColorBitmap; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void TerrainObject::SetTeam(int team) { + SceneObject::SetTeam(team); + for (SceneObject::SOPlacer &childObject : m_ChildObjects) { + childObject.SetTeam(team); + } + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool TerrainObject::IsOnScenePoint(Vector &scenePoint) const { + BITMAP *bitmapToCheck = nullptr; + if (m_FGColorBitmap) { + bitmapToCheck = m_FGColorBitmap; + } else { + bitmapToCheck = m_BGColorBitmap ? m_BGColorBitmap : m_MaterialBitmap; + } + // TODO: TAKE CARE OF WRAPPING + Vector bitmapPos = m_Pos + m_BitmapOffset; + if (WithinBox(scenePoint, bitmapPos, static_cast(GetBitmapWidth()), static_cast(GetBitmapHeight()))) { + Vector bitmapPoint = scenePoint - bitmapPos; + if (getpixel(bitmapToCheck, bitmapPoint.GetFloorIntX(), bitmapPoint.GetFloorIntY()) != MaterialColorKeys::g_MaterialAir) { + return true; + } + } + return false; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool TerrainObject::PlaceOnTerrain(SLTerrain *terrain) { + if (!terrain) { + return false; + } + DrawToTerrain(terrain); + + // Reapply the team so all children are guaranteed to be on the same team. + SetTeam(GetTeam()); + + for (const SceneObject::SOPlacer &childObject : GetChildObjects()) { + // TODO: check if we're placing a brain, and have it replace the resident brain of the scene! + g_SceneMan.AddSceneObject(childObject.GetPlacedCopy(this)); + } + return true; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void TerrainObject::Draw(BITMAP *targetBitmap, const Vector &targetPos, DrawMode drawMode, bool onlyPhysical) const { + std::array drawPos = { m_Pos + m_BitmapOffset - targetPos }; + int wrapPasses = 1; + + // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap. + if (targetPos.IsZero() && g_SceneMan.GetSceneWidth() <= targetBitmap->w) { + if (drawPos.at(0).GetFloorIntX() < m_FGColorBitmap->w) { + drawPos.at(wrapPasses) = drawPos.at(0); + drawPos.at(wrapPasses).m_X += static_cast(targetBitmap->w); + wrapPasses++; + } else if (drawPos.at(0).GetFloorIntX() > targetBitmap->w - m_FGColorBitmap->w) { + drawPos.at(wrapPasses) = drawPos.at(0); + drawPos.at(wrapPasses).m_X -= static_cast(targetBitmap->w); + wrapPasses++; + } + } else { + // Only screenwide target bitmap, so double draw within the screen if the screen is straddling a scene seam. + if (g_SceneMan.SceneWrapsX()) { + float sceneWidth = static_cast(g_SceneMan.GetSceneWidth()); + if (targetPos.m_X < 0) { + drawPos.at(wrapPasses) = drawPos.at(0); + drawPos.at(wrapPasses).m_X -= sceneWidth; + wrapPasses++; + } + if (targetPos.m_X + static_cast(targetBitmap->w) > sceneWidth) { + drawPos.at(wrapPasses) = drawPos.at(0); + drawPos.at(wrapPasses).m_X += sceneWidth; + wrapPasses++; + } + } + } + for (int i = 0; i < wrapPasses; ++i) { + switch (drawMode) { + case DrawMode::g_DrawColor: + if (HasBGColorBitmap()) { masked_blit(m_BGColorBitmap, targetBitmap, 0, 0, drawPos.at(i).GetFloorIntX(), drawPos.at(i).GetFloorIntY(), m_BGColorBitmap->w, m_BGColorBitmap->h); } + if (HasFGColorBitmap()) { masked_blit(m_FGColorBitmap, targetBitmap, 0, 0, drawPos.at(i).GetFloorIntX(), drawPos.at(i).GetFloorIntY(), m_FGColorBitmap->w, m_FGColorBitmap->h); } + break; + case DrawMode::g_DrawMaterial: + if (HasMaterialBitmap()) { masked_blit(m_MaterialBitmap, targetBitmap, 0, 0, drawPos.at(i).GetFloorIntX(), drawPos.at(i).GetFloorIntY(), m_MaterialBitmap->w, m_MaterialBitmap->h); } + break; + case DrawMode::g_DrawTrans: + if (HasFGColorBitmap()) { draw_trans_sprite(targetBitmap, m_FGColorBitmap, drawPos.at(i).GetFloorIntX(), drawPos.at(i).GetFloorIntY()); } + if (HasBGColorBitmap()) { draw_trans_sprite(targetBitmap, m_BGColorBitmap, drawPos.at(i).GetFloorIntX(), drawPos.at(i).GetFloorIntY()); } + break; + default: + break; + } + } + } -ConcreteClassInfo(TerrainObject, SceneObject, 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this TerrainObject, effectively -// resetting the members of this abstraction level only. - -void TerrainObject::Clear() -{ - m_FGColorFile.Reset(); - m_MaterialFile.Reset(); - m_BGColorFile.Reset(); - m_pFGColor = 0; - m_pMaterial = 0; - m_pBGColor = 0; - m_BitmapOffset.Reset(); - m_OffsetDefined = false; - m_ChildObjects.clear(); - m_DisplayAsTerrain = false; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the TerrainObject object ready for use. - -int TerrainObject::Create() -{ - if (SceneObject::Create() < 0) - return -1; - - // If no bitmapoffset is set, make it point to dead center by default - if (!m_OffsetDefined && m_pFGColor) - { -/* - if (m_pFGColor->w > 24) - m_BitmapOffset.m_X = -std::ceil(((float)m_pFGColor->w / (float)2) + 0.5f); - if (m_pFGColor->h > 24) - m_BitmapOffset.m_Y = -std::ceil(((float)m_pFGColor->h / (float)2) + 0.5f); -*/ - if (m_pFGColor->w > 24) - m_BitmapOffset.m_X = -(m_pFGColor->w / 2); - if (m_pFGColor->h > 24) - m_BitmapOffset.m_Y = -(m_pFGColor->h / 2); - } - - return 0; -} - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the TerrainObject object ready for use. - -int TerrainObject::Create(ContentFile *pBitmapFile, - bool drawTrans, - Vector offset, - bool wrapX, - bool wrapY, - Vector scrollInfo) -{ - m_pBitmapFile = pBitmapFile; - - m_pBitmap = new BITMAP(); - RTEAssert(m_pBitmap, "Failed to allocate BITMAP in TerrainObject::Create"); - - if (FAILED(m_pBitmap->Create(g_FrameMan.GetScreen(), m_pBitmapFile->GetDataSize(), m_pBitmapFile->GetContent()))) { - CDXError(g_FrameMan.GetScreen(), "Could not create TerrainObject bitmap"); - return -1; - } - - if (m_DrawTrans = drawTrans) - m_pBitmap->SetColorKey(g_MaskColor); - - m_Offset = offset; - - m_WrapX = wrapX; - m_WrapY = wrapY; - - if (m_WrapX) - m_ScrollRatio.m_X = scrollInfo.m_X; - else - if (scrollInfo.m_X == -1.0) - m_ScrollRatio.m_X = 1.0; - else - m_ScrollRatio.m_X = (float)(m_pBitmap->GetWidth() - g_FrameMan.GetResX()) / - (float)(scrollInfo.m_X - g_FrameMan.GetResX()); - - if (m_WrapY) - m_ScrollRatio.m_Y = scrollInfo.m_Y; - else - if (scrollInfo.m_Y == -1.0) - m_ScrollRatio.m_Y = 1.0; - else - m_ScrollRatio.m_Y = (float)(m_pBitmap->GetHeight() - g_FrameMan.GetResY()) / - (float)(scrollInfo.m_Y - g_FrameMan.GetResY()); - - return 0; -} -*/ - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a MOPixel to be identical to another, by deep copy. - -int TerrainObject::Create(const TerrainObject &reference) -{ - SceneObject::Create(reference); - - m_FGColorFile = reference.m_FGColorFile; - m_MaterialFile = reference.m_MaterialFile; - m_BGColorFile = reference.m_BGColorFile; - - m_pFGColor = reference.m_pFGColor; - m_pMaterial = reference.m_pMaterial; - m_pBGColor = reference.m_pBGColor; - - m_BitmapOffset = reference.m_BitmapOffset; - m_OffsetDefined = reference.m_OffsetDefined; - - for (list::const_iterator itr = reference.m_ChildObjects.begin(); itr != reference.m_ChildObjects.end(); ++itr) - m_ChildObjects.push_back(*itr); - - m_DisplayAsTerrain = reference.m_DisplayAsTerrain; - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int TerrainObject::ReadProperty(const std::string_view &propName, Reader &reader) -{ - if (propName == "FGColorFile") - { - reader >> m_FGColorFile; - m_pFGColor = m_FGColorFile.GetAsBitmap(); - } - else if (propName == "MaterialFile") - { - reader >> m_MaterialFile; - m_pMaterial = m_MaterialFile.GetAsBitmap(); - } - else if (propName == "BGColorFile") - { - reader >> m_BGColorFile; - m_pBGColor = m_BGColorFile.GetAsBitmap(); - } - else if (propName == "BitmapOffset") - { - reader >> m_BitmapOffset; - m_OffsetDefined = true; - } - // This is to handle legacy placement of terrain objects, without the bitmap offset - else if (propName == "Location") - { - reader >> m_Pos; - m_Pos -= m_BitmapOffset; - } - else if (propName == "AddChildObject") - { - SOPlacer newChild; - reader >> newChild; - newChild.SetTeam(m_Team); - m_ChildObjects.push_back(newChild); - } - else if (propName == "DisplayAsTerrain") - { - reader >> m_DisplayAsTerrain; +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void TerrainObject::DrawToTerrain(SLTerrain *terrain) { + BITMAP *terrainMatBitmap = terrain->GetMaterialBitmap(); + BITMAP *terrainBGBitmap = terrain->GetBGColorBitmap(); + BITMAP *terrainFGBitmap = terrain->GetFGColorBitmap(); + + Vector posOnScene = m_Pos + m_BitmapOffset; + + if (terrain->WrapsX()) { + if (posOnScene.GetFloorIntX() < 0) { + if (HasMaterialBitmap()) { draw_sprite(terrainMatBitmap, m_MaterialBitmap, posOnScene.GetFloorIntX() + terrainMatBitmap->w, posOnScene.GetFloorIntY()); } + if (HasBGColorBitmap()) { draw_sprite(terrainBGBitmap, m_BGColorBitmap, posOnScene.GetFloorIntX() + terrainBGBitmap->w, posOnScene.GetFloorIntY()); } + if (HasFGColorBitmap()) { draw_sprite(terrainFGBitmap, m_FGColorBitmap, posOnScene.GetFloorIntX() + terrainFGBitmap->w, posOnScene.GetFloorIntY()); } + } else if (posOnScene.GetFloorIntX() >= terrainMatBitmap->w - GetBitmapWidth()) { + if (HasMaterialBitmap()) { draw_sprite(terrainMatBitmap, m_MaterialBitmap, posOnScene.GetFloorIntX() - terrainMatBitmap->w, posOnScene.GetFloorIntY()); } + if (HasBGColorBitmap()) { draw_sprite(terrainBGBitmap, m_BGColorBitmap, posOnScene.GetFloorIntX() - terrainBGBitmap->w, posOnScene.GetFloorIntY()); } + if (HasFGColorBitmap()) { draw_sprite(terrainFGBitmap, m_FGColorBitmap, posOnScene.GetFloorIntX() - terrainFGBitmap->w, posOnScene.GetFloorIntY()); } + } + } + if (terrain->WrapsY()) { + if (posOnScene.GetFloorIntY() < 0) { + if (HasMaterialBitmap()) { draw_sprite(terrainMatBitmap, m_MaterialBitmap, posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY() + terrainMatBitmap->h); } + if (HasBGColorBitmap()) { draw_sprite(terrainBGBitmap, m_BGColorBitmap, posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY() + terrainBGBitmap->h); } + if (HasFGColorBitmap()) { draw_sprite(terrainFGBitmap, m_FGColorBitmap, posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY() + terrainFGBitmap->h); } + } else if (posOnScene.GetFloorIntY() >= terrainMatBitmap->h - GetBitmapHeight()) { + if (HasMaterialBitmap()) { draw_sprite(terrainMatBitmap, m_MaterialBitmap, posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY() - terrainMatBitmap->h); } + if (HasBGColorBitmap()) { draw_sprite(terrainBGBitmap, m_BGColorBitmap, posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY() - terrainBGBitmap->h); } + if (HasFGColorBitmap()) { draw_sprite(terrainFGBitmap, m_FGColorBitmap, posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY() - terrainFGBitmap->h); } + } + } + if (HasMaterialBitmap()) { + draw_sprite(terrainMatBitmap, m_MaterialBitmap, posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY()); + terrain->AddUpdatedMaterialArea(Box(posOnScene, static_cast(m_MaterialBitmap->w), static_cast(m_MaterialBitmap->h))); + } + if (HasBGColorBitmap()) { + draw_sprite(terrainBGBitmap, m_BGColorBitmap, posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY()); + g_SceneMan.RegisterTerrainChange(posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY(), m_BGColorBitmap->w, m_BGColorBitmap->h, ColorKeys::g_MaskColor, true); + } + if (HasFGColorBitmap()) { + draw_sprite(terrainFGBitmap, m_FGColorBitmap, posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY()); + g_SceneMan.RegisterTerrainChange(posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY(), m_FGColorBitmap->w, m_FGColorBitmap->h, ColorKeys::g_MaskColor, false); + } } - else - return SceneObject::ReadProperty(propName, reader); - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this TerrainObject with a Writer for -// later recreation with Create(Reader &reader); - -int TerrainObject::Save(Writer &writer) const -{ - SceneObject::Save(writer); - - writer.NewProperty("FGColorFile"); - writer << m_FGColorFile; - writer.NewProperty("MaterialFile"); - writer << m_MaterialFile; - writer.NewProperty("BGColorFile"); - writer << m_BGColorFile; - if (m_OffsetDefined) - { - writer.NewProperty("BitmapOffset"); - writer << m_BitmapOffset; - } - for (list::const_iterator itr = m_ChildObjects.begin(); itr != m_ChildObjects.end(); ++itr) - { - writer.NewProperty("AddChildObject"); - writer << (*itr); - } - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the TerrainObject object. - -void TerrainObject::Destroy(bool notInherited) -{ -/* None of these owned by this - delete m_pFGColor; - delete m_pMaterial; - if (m_pBGColor) - delete m_pBGColor; -*/ - - if (!notInherited) - SceneObject::Destroy(); - Clear(); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGraphicalIcon -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a bitmap showing a good identifyable icon of this, for use in -// GUI lists etc. - -BITMAP * TerrainObject::GetGraphicalIcon() const -{ - if (!m_pFGColor) - return m_pBGColor; - - // Checking if the FG has anything to show, if not, show the bg layer isntead - int piece = m_pFGColor->w / 10; - if (getpixel(m_pFGColor, m_pFGColor->w / 2, m_pFGColor->h / 2) != g_MaskColor || - getpixel(m_pFGColor, piece, piece) != g_MaskColor || - getpixel(m_pFGColor, m_pFGColor->w - piece, piece) != g_MaskColor || - getpixel(m_pFGColor, piece, m_pFGColor->h - piece) != g_MaskColor) - return m_pFGColor; - else - return m_pBGColor; - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsOnScenePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this' current graphical representation overlaps -// a point in absolute scene coordinates. - -bool TerrainObject::IsOnScenePoint(Vector &scenePoint) const -{ - if (!m_pFGColor) - return false; -// TODO: TAKE CARE OF WRAPPING -/* - // Take care of wrapping situations - bitmapPos = m_Pos + m_BitmapOffset; - Vector aScenePoint[4]; - aScenePoint[0] = scenePoint; - int passes = 1; - - // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap - if (targetPos.IsZero()) - { - if (g_SceneMan.SceneWrapsX()) - { - if (bitmapPos.m_X < m_pFGColor->w) - { - aScenePoint[passes] = aScenePoint[0]; - aScenePoint[passes].m_X += g_SceneMan.GetSceneWidth(); - passes++; - } - else if (aScenePoint[0].m_X > pTargetBitmap->w - m_pFGColor->w) - { - aScenePoint[passes] = aScenePoint[0]; - aScenePoint[passes].m_X -= g_SceneMan.GetSceneWidth(); - passes++; - } - } - if (g_SceneMan.SceneWrapsY()) - { - - } - } - - // Check all the passes needed - for (int i = 0; i < passes; ++i) - { - - if (IsWithinBox(aScenePoint[i], m_Pos + m_BitmapOffset, m_pFGColor->w, m_pFGColor->h)) - { - if (getpixel(m_pFGColor, aScenePoint[i].m_X, aScenePoint[i].m_Y) != g_MaskColor || - (m_pBGColor && getpixel(m_pBGColor, aScenePoint[i].m_X, aScenePoint[i].m_Y) != g_MaskColor) || - (m_pMaterial && getpixel(m_pMaterial, aScenePoint[i].m_X, aScenePoint[i].m_Y) != g_MaterialAir)) - return true; - } - } -*/ - Vector bitmapPos = m_Pos + m_BitmapOffset; - if (WithinBox(scenePoint, bitmapPos, m_pFGColor->w, m_pFGColor->h)) - { - // Scene point on the bitmap - Vector bitmapPoint = scenePoint - bitmapPos; - if (getpixel(m_pFGColor, bitmapPoint.m_X, bitmapPoint.m_Y) != g_MaskColor || - (m_pBGColor && getpixel(m_pBGColor, bitmapPoint.m_X, bitmapPoint.m_Y) != g_MaskColor) || - (m_pMaterial && getpixel(m_pMaterial, bitmapPoint.m_X, bitmapPoint.m_Y) != g_MaterialAir)) - return true; - } - - return false; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which team this Actor belongs to. - -void TerrainObject::SetTeam(int team) -{ - SceneObject::SetTeam(team); - - // Make sure all the objects to be placed will be of the same team - for (list::iterator itr = m_ChildObjects.begin(); itr != m_ChildObjects.end(); ++itr) - (*itr).SetTeam(team); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this TerrainObject's current graphical representation to a -// BITMAP of choice. - -void TerrainObject::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode mode, bool onlyPhysical) const -{ - if (!m_pFGColor) - RTEAbort("TerrainObject's bitmaps are null when drawing!"); - - // Take care of wrapping situations - Vector aDrawPos[4]; - aDrawPos[0] = m_Pos + m_BitmapOffset - targetPos; - int passes = 1; - - // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap - if (targetPos.IsZero() && g_SceneMan.GetSceneWidth() <= pTargetBitmap->w) - { - if (aDrawPos[0].m_X < m_pFGColor->w) - { - aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X += pTargetBitmap->w; - passes++; - } - else if (aDrawPos[0].m_X > pTargetBitmap->w - m_pFGColor->w) - { - aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X -= pTargetBitmap->w; - passes++; - } - } - // Only screenwide target bitmap, so double draw within the screen if the screen is straddling a scene seam - else - { - if (g_SceneMan.SceneWrapsX()) - { - int sceneWidth = g_SceneMan.GetSceneWidth(); - if (targetPos.m_X < 0) - { - aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X -= sceneWidth; - passes++; - } - if (targetPos.m_X + pTargetBitmap->w > sceneWidth) - { - aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X += sceneWidth; - passes++; - } - } - } - - // Draw all the passes needed - for (int i = 0; i < passes; ++i) - { - if (mode == g_DrawColor) - { - masked_blit(m_pBGColor, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), m_pBGColor->w, m_pBGColor->h); - masked_blit(m_pFGColor, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), m_pFGColor->w, m_pFGColor->h); - } - else if (mode == g_DrawMaterial) - { - masked_blit(m_pMaterial, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), m_pMaterial->w, m_pMaterial->h); - } - else if (mode == g_DrawLess) - { - masked_blit(m_pFGColor, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), m_pFGColor->w, m_pFGColor->h); - } - else if (mode == g_DrawTrans) - { - draw_trans_sprite(pTargetBitmap, m_pFGColor, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY()); - draw_trans_sprite(pTargetBitmap, m_pBGColor, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY()); - } - } -} - - - -} // namespace RTE +} \ No newline at end of file diff --git a/Entities/TerrainObject.h b/Entities/TerrainObject.h index e878cec01..c043afcd3 100644 --- a/Entities/TerrainObject.h +++ b/Entities/TerrainObject.h @@ -1,348 +1,192 @@ #ifndef _RTETERRAINOBJECT_ #define _RTETERRAINOBJECT_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: TerrainObject.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the TerrainObject class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - -#include "RTETools.h" #include "SceneObject.h" -#include "Vector.h" -#include "SceneMan.h" -//#include "MovableMan.h" - -namespace RTE -{ - -class ContentFile; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: TerrainObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A feature of the terrain, which includes foreground color layer, -// material layer and optional background layer. -// Parent(s): SceneObject. -// Class history: 08/23/2002 TerrainObject created. - -class TerrainObject : public SceneObject { - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions -EntityAllocation(TerrainObject); -SerializableOverrideMethods; -ClassInfoGetters; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: TerrainObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a TerrainObject object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - TerrainObject() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~TerrainObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a TerrainObject object before deletion -// from system memory. -// Arguments: None. - - ~TerrainObject() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Pure V. method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the TerrainObject object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the TerrainObject object ready for use. -// Arguments: The ContentFile representing the bitmap file to load as this -// TerrainObject's graphical representation. Ownership is transferred. -// Whether to draw transperently using a color key specified by the pixel -// in the upper left corner of the loaded bitmap. -// The initial scroll offset. -// Two bools that define whether the layer should wrap around or stop when -// scrolling beyond its bitmap's boundries. -// A vector whose components defines two different things, depending on -// wrapX/Y arguments. If a wrap argument is set to false, the -// corresponding component here will be interpreted as the width (X) or -// height (Y) (in pixels) of the total bitmap area that this layer is -// allowed to scroll across before stopping at an edge. If wrapping is -// set to true, the value in scrollInfo is simply the ratio of offset at -// which any scroll operations will be done in. A special command is if -// wrap is false and the corresponding component is -1.0, that signals -// that the own width or height should be used as scrollInfo input. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - -// TODO: streamline interface") - int Create(ContentFile *pBitmapFile, - bool drawTrans, - Vector offset, - bool wrapX, - bool wrapY, - Vector scrollInfo); -*/ - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a TerrainObject to be identical to another, by deep copy. -// Arguments: A reference to the TerrainObject to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const TerrainObject &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire TerrainObject, including its inherited members, to -// their default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); SceneObject::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Pure V. method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the TerrainObject object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetBitmapOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the offset from teh position to the upper left corner of this' -// bitmaps. -// Arguments: None. -// Return value: A Vector describing the bitmap offset, in pixels. - - const Vector & GetBitmapOffset() const { return m_BitmapOffset; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBitmapWidth -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the width this' material bitmap. -// Arguments: None. -// Return value: Width of 'material' bitmap. - - const int GetBitmapWidth() const { return m_pMaterial->w; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBitmapHeight -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the height this' material bitmap. -// Arguments: None. -// Return value: Height of 'material' bitmap. - - const int GetBitmapHeight() const { return m_pMaterial->h; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetFGColorBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the BITMAP object that this TerrainObject uses for its fore- -// ground color representation. -// Arguments: None. -// Return value: A pointer to the foreground color BITMAP object. Ownership is not -// transferred. - - BITMAP * GetFGColorBitmap() const { return m_pFGColor; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBGColorBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the BITMAP object that this TerrainObject uses for its back- -// ground color representation, if any. -// Arguments: None. -// Return value: A pointer to the background color BITMAP object. Ownership is not -// transferred. This may be 0 if there is no BG info! - - BITMAP * GetBGColorBitmap() const { return m_pBGColor; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMaterialBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the BITMAP object that this TerrainObject uses for its -// material representation. -// Arguments: None. -// Return value: A pointer to the material BITMAP object. Ownership is not -// transferred. - - BITMAP * GetMaterialBitmap() const { return m_pMaterial; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HasBGColor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns whetehr this TerrainObject has any background color data. -// Arguments: None. -// Return value: Whether this TerrainOBject has any background color data. - - bool HasBGColor() const { return m_pBGColor != 0; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGraphicalIcon -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a bitmap showing a good identifyable icon of this, for use in -// GUI lists etc. -// Arguments: None. -// Return value: A good identifyable graphical representation of this in a BITMAP, if -// available. If not, 0 is returned. Ownership is NOT TRANSFERRED! - - BITMAP * GetGraphicalIcon() const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetChildObjects -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the list of child objects that should be placed when this is -// placed. Ownership of the list is NOT transferred! -// Arguments: None. -// Return value: A reference to the list of child objects. - - const std::list & GetChildObjects() const { return m_ChildObjects; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which team this belongs to. -// Arguments: The assigned team number. -// Return value: None. - - void SetTeam(int team) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsOnScenePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this' current graphical representation overlaps -// a point in absolute scene coordinates. -// Arguments: The point in absolute scene coordinates. -// Return value: Whether this' graphical rep overlaps the scene point. - - bool IsOnScenePoint(Vector &scenePoint) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this TerrainObject's current graphical representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// In which mode to draw in. See the DrawMode enumeration for the modes. -// Whether to not draw any extra 'ghost' items of this MovableObject, -// like indicator arrows or hovering HUD text and so on. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetDisplayAsTerrain -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns whether this bunker module should be drawn as terrain object on minimap -// -// Arguments: None. -// Return value: Returns true if this module should be drawn as part of terrain on minimap. - - bool const GetDisplayAsTerrain() { return m_DisplayAsTerrain; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - // Member variables - static Entity::ClassInfo m_sClass; - - ContentFile m_FGColorFile; - ContentFile m_MaterialFile; - ContentFile m_BGColorFile; - - // Not owned by this - BITMAP *m_pFGColor; - BITMAP *m_pMaterial; - BITMAP *m_pBGColor; - - // Offset from the position of this to the top left corner of the bitmap. - // The inverson of this should point to a corner or pattern in the bitmaps which will snap well with a 24px grid - Vector m_BitmapOffset; - // Whether the offset has been defined and shuoldn't be automatically set - bool m_OffsetDefined; - - // The objects that are placed along with this in the scene - std::list m_ChildObjects; - - // If true, then this bunker module will be drawn as part of terrain on preview - bool m_DisplayAsTerrain; - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this TerrainObject, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - TerrainObject(const TerrainObject &reference) = delete; - void operator=(const TerrainObject &rhs) = delete; - -}; - -} // namespace RTE - -#endif // File \ No newline at end of file +#include "ContentFile.h" + +namespace RTE { + + class SLTerrain; + + /// + /// A feature of the terrain, which includes foreground color layer, material layer and optional background layer. + /// + class TerrainObject : public SceneObject { + + public: + + EntityAllocation(TerrainObject); + SerializableOverrideMethods; + ClassInfoGetters; + +#pragma region Creation + /// + /// Constructor method used to instantiate a TerrainObject object in system memory. Create() should be called before using the object. + /// + TerrainObject() { Clear(); } + + /// + /// Makes the TerrainObject object ready for use. + /// + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int Create() override; + + /// + /// Creates a TerrainObject to be identical to another, by deep copy. + /// + /// A reference to the TerrainObject to deep copy. + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int Create(const TerrainObject &reference); +#pragma endregion + +#pragma region Destruction + /// + /// Destructor method used to clean up a TerrainObject object before deletion from system memory. + /// + ~TerrainObject() override { Destroy(true); } + + /// + /// Destroys and resets (through Clear()) the TerrainObject object. + /// + /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + void Destroy(bool notInherited = false) override { if (!notInherited) { SceneObject::Destroy(); } Clear(); } +#pragma endregion + +#pragma region Getters and Setters + /// + /// Returns whether this TerrainObject has any foreground color data. + /// + /// Whether this TerrainOBject has any foreground color data. + bool HasFGColorBitmap() const { return m_FGColorBitmap != nullptr; } + + /// + /// Gets the BITMAP that this TerrainObject uses for its foreground color representation. + /// + /// A pointer to the foreground color BITMAP object. Ownership is NOT transferred! + BITMAP * GetFGColorBitmap() const { return m_FGColorBitmap; } + + /// + /// Returns whether this TerrainObject has any background color data. + /// + /// Whether this TerrainOBject has any background color data. + bool HasBGColorBitmap() const { return m_BGColorBitmap != nullptr; } + + /// + /// Gets the BITMAP that this TerrainObject uses for its background color representation, if any. + /// + /// A pointer to the background color BITMAP object. This may be nullptr if there is no BG bitmap. Ownership is NOT transferred! + BITMAP * GetBGColorBitmap() const { return m_BGColorBitmap; } + + /// + /// Returns whether this TerrainObject has any material data. + /// + /// Whether this TerrainOBject has any material data. + bool HasMaterialBitmap() const { return m_MaterialBitmap != nullptr; } + + /// + /// Gets the BITMAP that this TerrainObject uses for its material representation, if any. + /// + /// A pointer to the material BITMAP object. Ownership is NOT transferred! + BITMAP * GetMaterialBitmap() const { return m_MaterialBitmap; } + + /// + /// Gets the offset from the position to the upper left corner of this TerrainObject's BITMAPs. + /// + /// A Vector describing the bitmap offset, in pixels. + const Vector & GetBitmapOffset() const { return m_BitmapOffset; } + + /// + /// Gets the width of the widest BITMAP of this TerrainObject's layers. + /// + /// The width of this TerrainObject. + int GetBitmapWidth() const { return std::max({ m_MaterialBitmap ? m_MaterialBitmap->w : 0, m_BGColorBitmap ? m_BGColorBitmap->w : 0, m_FGColorBitmap ? m_FGColorBitmap->w : 0 }); } + + /// + /// Gets the height of the highest BITMAP of this TerrainObject's layers. + /// + /// The height of this TerrainObject. + int GetBitmapHeight() const { return std::max({ m_MaterialBitmap ? m_MaterialBitmap->h : 0, m_BGColorBitmap ? m_BGColorBitmap->h : 0, m_FGColorBitmap ? m_FGColorBitmap->h : 0 }); } + + /// + /// Gets the list of child objects that should be placed when this TerrainObject is placed. + /// + /// A reference to the list of child objects. Ownership of the list is NOT transferred! + const std::vector & GetChildObjects() const { return m_ChildObjects; } + + /// + /// Gets a BITMAP showing a good identifiable icon of this, for use in GUI lists. + /// + /// A good identifiable graphical representation of this in a BITMAP, if available. If not, nullptr is returned. Ownership is NOT transferred! + BITMAP * GetGraphicalIcon() const override; + + /// + /// Sets which team this TerrainObject belongs to. + /// + /// The assigned team number. + void SetTeam(int team) override; +#pragma endregion + +#pragma region Concrete Methods + /// + /// Places this TerrainObject and all its children on the specified SLTerrain. + /// + /// The SLTerrain to place this TerrainObject on. Ownership is NOT transferred! + /// Whether the object was successfully placed on the terrain. + bool PlaceOnTerrain(SLTerrain *terrain); +#pragma endregion + +#pragma region Virtual Override Methods + /// + /// Indicates whether this TerrainObject's current graphical representation overlaps a point in absolute scene coordinates. + /// + /// The point in absolute scene coordinates. + /// Whether this' graphical rep overlaps the scene point. + bool IsOnScenePoint(Vector &scenePoint) const override; + + /// + /// Draws this TerrainObject's current graphical representation to a BITMAP of choice. + /// + /// A pointer to a BITMAP to draw on. + /// The absolute position of the target bitmap's upper left corner in the Scene. + /// In which mode to draw in. See the DrawMode enumeration for the modes. + /// Whether to not draw any extra 'ghost' items of this TerrainObject, like indicator arrows or hovering HUD text and so on. + void Draw(BITMAP *targetBitmap, const Vector &targetPos = Vector(), DrawMode drawMode = DrawMode::g_DrawColor, bool onlyPhysical = false) const override; +#pragma endregion + + protected: + + static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. + + ContentFile m_FGColorFile; //!< ContentFile containing the path to this TerrainObject's foreground color layer representation. + BITMAP *m_FGColorBitmap; //!< Foreground color BITMAP of this TerrainObject. + + ContentFile m_BGColorFile; //!< ContentFile containing the path to this TerrainObject's background color layer representation. + BITMAP *m_BGColorBitmap; //!< Background color BITMAP of this TerrainObject. + + ContentFile m_MaterialFile; //!< ContentFile containing the path to this TerrainObject's Material layer representation. + BITMAP *m_MaterialBitmap; //!< Material BITMAP of this TerrainObject. + + Vector m_BitmapOffset; //!< Offset from the position of this to the top left corner of the bitmap. The inversion of this should point to a corner or pattern in the bitmaps which will snap well with a 24px grid. + bool m_OffsetDefined; //!< Whether the offset has been defined and shouldn't be automatically set. + + std::vector m_ChildObjects; //!< The objects that are placed along with this TerrainObject on the Scene. + + private: + + /// + /// Draws this TerrainObject's graphical and material representations to the specified SLTerrain's respective layers. + /// + /// The SLTerrain to draw this TerrainObject to. Ownership is NOT transferred! + void DrawToTerrain(SLTerrain *terrain); + + /// + /// Clears all the member variables of this TerrainObject, effectively resetting the members of this abstraction level only. + /// + void Clear(); + + // Disallow the use of some implicit methods. + TerrainObject(const TerrainObject &reference) = delete; + void operator=(const TerrainObject &rhs) = delete; + }; +} +#endif \ No newline at end of file diff --git a/Entities/meson.build b/Entities/meson.build index dfb70b284..6ac4c56c7 100644 --- a/Entities/meson.build +++ b/Entities/meson.build @@ -38,11 +38,13 @@ sources += files( 'Scene.cpp', 'SceneLayer.cpp', 'SceneObject.cpp', +'SLBackground.cpp', 'SLTerrain.cpp', 'SoundContainer.cpp', 'SoundSet.cpp', 'TDExplosive.cpp', 'TerrainDebris.cpp', +'TerrainFrosting.cpp', 'TerrainObject.cpp', 'ThrownDevice.cpp', 'Turret.cpp', diff --git a/Lua/LuaAdaptersEntities.h b/Lua/LuaAdaptersEntities.h index 742b2ae97..65fa901bb 100644 --- a/Lua/LuaAdaptersEntities.h +++ b/Lua/LuaAdaptersEntities.h @@ -29,6 +29,7 @@ #include "MOSRotating.h" #include "Scene.h" #include "SLTerrain.h" +#include "SLBackground.h" #include "TerrainObject.h" #include "SoundContainer.h" #include "TDExplosive.h" @@ -36,6 +37,7 @@ #include "Turret.h" #include "PEmitter.h" #include "GlobalScript.h" +#include "Deployment.h" #include "GameActivity.h" #include "GAScripted.h" diff --git a/Lua/LuaBindingRegisterDefinitions.h b/Lua/LuaBindingRegisterDefinitions.h index 79b6e174e..c620c5530 100644 --- a/Lua/LuaBindingRegisterDefinitions.h +++ b/Lua/LuaBindingRegisterDefinitions.h @@ -6,6 +6,7 @@ #include "GameActivity.h" #include "MetaPlayer.h" #include "SLTerrain.h" +#include "TerrainObject.h" #include "GUIBanner.h" #include "BuyMenuGUI.h" @@ -154,7 +155,9 @@ namespace RTE { LuaBindingRegisterFunctionDeclarationForType(Round); LuaBindingRegisterFunctionDeclarationForType(Scene); LuaBindingRegisterFunctionDeclarationForType(SceneArea); + LuaBindingRegisterFunctionDeclarationForType(SceneLayer); LuaBindingRegisterFunctionDeclarationForType(SceneObject); + LuaBindingRegisterFunctionDeclarationForType(SLBackground); LuaBindingRegisterFunctionDeclarationForType(SoundContainer); LuaBindingRegisterFunctionDeclarationForType(SoundSet); LuaBindingRegisterFunctionDeclarationForType(TDExplosive); diff --git a/Lua/LuaBindingsEntities.cpp b/Lua/LuaBindingsEntities.cpp index 87fd92673..08fb539d2 100644 --- a/Lua/LuaBindingsEntities.cpp +++ b/Lua/LuaBindingsEntities.cpp @@ -805,15 +805,15 @@ namespace RTE { .def("GetExitWoundPresetName", &MOSprite::GetExitWoundPresetName) .enum_("SpriteAnimMode")[ - luabind::value("NOANIM", MOSprite::SpriteAnimMode::NOANIM), - luabind::value("ALWAYSLOOP", MOSprite::SpriteAnimMode::ALWAYSLOOP), - luabind::value("ALWAYSRANDOM", MOSprite::SpriteAnimMode::ALWAYSRANDOM), - luabind::value("ALWAYSPINGPONG", MOSprite::SpriteAnimMode::ALWAYSPINGPONG), - luabind::value("LOOPWHENACTIVE", MOSprite::SpriteAnimMode::LOOPWHENACTIVE), - luabind::value("LOOPWHENOPENCLOSE", MOSprite::SpriteAnimMode::LOOPWHENOPENCLOSE), - luabind::value("PINGPONGOPENCLOSE", MOSprite::SpriteAnimMode::PINGPONGOPENCLOSE), - luabind::value("OVERLIFETIME", MOSprite::SpriteAnimMode::OVERLIFETIME), - luabind::value("ONCOLLIDE", MOSprite::SpriteAnimMode::ONCOLLIDE) + luabind::value("NOANIM", SpriteAnimMode::NOANIM), + luabind::value("ALWAYSLOOP", SpriteAnimMode::ALWAYSLOOP), + luabind::value("ALWAYSRANDOM", SpriteAnimMode::ALWAYSRANDOM), + luabind::value("ALWAYSPINGPONG", SpriteAnimMode::ALWAYSPINGPONG), + luabind::value("LOOPWHENACTIVE", SpriteAnimMode::LOOPWHENACTIVE), + luabind::value("LOOPWHENOPENCLOSE", SpriteAnimMode::LOOPWHENOPENCLOSE), + luabind::value("PINGPONGOPENCLOSE", SpriteAnimMode::PINGPONGOPENCLOSE), + luabind::value("OVERLIFETIME", SpriteAnimMode::OVERLIFETIME), + luabind::value("ONCOLLIDE", SpriteAnimMode::ONCOLLIDE) ]; } @@ -1041,6 +1041,8 @@ namespace RTE { .def_readwrite("ScenePath", &Scene::m_ScenePath, luabind::return_stl_iterator) .def_readwrite("Deployments", &Scene::m_Deployments, luabind::return_stl_iterator) + .def_readonly("BackgroundLayers", &Scene::m_BackLayerList, luabind::return_stl_iterator) + .def("GetBuildBudget", &Scene::GetBuildBudget) .def("SetBuildBudget", &Scene::SetBuildBudget) .def("IsScanScheduled", &Scene::IsScanScheduled) @@ -1096,6 +1098,12 @@ namespace RTE { .def("GetRandomPoint", &Scene::Area::GetRandomPoint); } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, SceneLayer) { + return luabind::class_("SceneLayer"); + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, SceneObject) { @@ -1118,6 +1126,25 @@ namespace RTE { .def("GetTotalValue", &GetTotalValue); } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, SLBackground) { + return luabind::class_("SLBackground") + + .property("Frame", &SLBackground::GetFrame, &SLBackground::SetFrame) + .property("SpriteAnimMode", &SLBackground::GetSpriteAnimMode, &SLBackground::SetSpriteAnimMode) + .property("SpriteAnimDuration", &SLBackground::GetSpriteAnimDuration, &SLBackground::SetSpriteAnimDuration) + .property("IsAnimatedManually", &SLBackground::IsAnimatedManually, &SLBackground::SetAnimatedManually) + .property("AutoScrollX", &SLBackground::GetAutoScrollX, &SLBackground::SetAutoScrollX) + .property("AutoScrollY", &SLBackground::GetAutoScrollY, &SLBackground::SetAutoScrollY) + .property("AutoScrollInterval", &SLBackground::GetAutoScrollStepInterval, &SLBackground::SetAutoScrollStepInterval) + .property("AutoScrollStep", &SLBackground::GetAutoScrollStep, &SLBackground::SetAutoScrollStep) + .property("AutoScrollStepX", &SLBackground::GetAutoScrollStepX, &SLBackground::SetAutoScrollStepX) + .property("AutoScrollStepY", &SLBackground::GetAutoScrollStepY, &SLBackground::SetAutoScrollStepY) + + .def("IsAutoScrolling", &SLBackground::IsAutoScrolling); + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, SoundContainer) { diff --git a/Lua/LuaBindingsManagers.cpp b/Lua/LuaBindingsManagers.cpp index 9e758561d..bbdaf0b13 100644 --- a/Lua/LuaBindingsManagers.cpp +++ b/Lua/LuaBindingsManagers.cpp @@ -305,7 +305,6 @@ namespace RTE { .def("ObscuredPoint", (bool (SceneMan::*)(Vector &, int))&SceneMan::ObscuredPoint)//, out_value(_2)) .def("ObscuredPoint", (bool (SceneMan::*)(int, int, int))&SceneMan::ObscuredPoint) .def("AddSceneObject", &SceneMan::AddSceneObject, luabind::adopt(_2)) - .def("AddTerrainObject", &SceneMan::AddTerrainObject, luabind::adopt(_2)) .def("CheckAndRemoveOrphans", (int (SceneMan::*)(int, int, int, int, bool))&SceneMan::RemoveOrphans); } @@ -388,4 +387,4 @@ namespace RTE { .def("MouseButtonReleased", &MouseButtonReleased) .def("MouseButtonHeld", &MouseButtonHeld); } -} \ No newline at end of file +} diff --git a/Main.cpp b/Main.cpp index eb2c7b6e6..ed33ea10b 100644 --- a/Main.cpp +++ b/Main.cpp @@ -63,6 +63,7 @@ namespace RTE { g_UInputMan.Initialize(); g_ConsoleMan.Initialize(); + g_SceneMan.Initialize(); g_MovableMan.Initialize(); g_MetaMan.Initialize(); g_MenuMan.Initialize(); @@ -195,9 +196,6 @@ namespace RTE { if (g_ActivityMan.ActivitySetToRestart() && !g_ActivityMan.RestartActivity()) { g_MenuMan.GetTitleScreen()->SetTitleTransitionState(TitleScreen::TitleTransition::ScrollingFadeIn); } while (!System::IsSetToQuit()) { - // Need to clear this out; sometimes background layers don't cover the whole back. - g_FrameMan.ClearBackBuffer8(); - g_TimerMan.Update(); bool serverUpdated = false; diff --git a/Managers/FrameMan.cpp b/Managers/FrameMan.cpp index 28a7ae966..748d67e65 100644 --- a/Managers/FrameMan.cpp +++ b/Managers/FrameMan.cpp @@ -8,6 +8,7 @@ #include "UInputMan.h" #include "SLTerrain.h" +#include "SLBackground.h" #include "Scene.h" #include "GUI.h" @@ -985,6 +986,10 @@ namespace RTE { drawScreen = m_NetworkBackBufferIntermediate8[m_NetworkFrameCurrent][playerScreen]; drawScreenGUI = m_NetworkBackBufferIntermediateGUI8[m_NetworkFrameCurrent][playerScreen]; } + // Need to clear the backbuffers because Scene background layers can be too small to fill the whole backbuffer or drawn masked resulting in artifacts from the previous frame. + clear_to_color(drawScreenGUI, ColorKeys::g_MaskColor); + clear_to_color(drawScreen, m_BlackColor); + AllegroBitmap playerGUIBitmap(drawScreenGUI); // Update the scene view to line up with a specific screen and then draw it onto the intermediate screen @@ -1017,8 +1022,6 @@ namespace RTE { if (!IsInMultiplayerMode()) { g_SceneMan.Draw(drawScreen, drawScreenGUI, targetPos); } else { - clear_to_color(drawScreen, g_MaskColor); - clear_to_color(drawScreenGUI, g_MaskColor); g_SceneMan.Draw(drawScreen, drawScreenGUI, targetPos, true, true); } diff --git a/Managers/LuaMan.cpp b/Managers/LuaMan.cpp index 1e067f384..c3ec8bcd1 100644 --- a/Managers/LuaMan.cpp +++ b/Managers/LuaMan.cpp @@ -119,6 +119,8 @@ namespace RTE { RegisterLuaBindingsOfType(SystemLuaBindings, Timer), RegisterLuaBindingsOfConcreteType(EntityLuaBindings, Scene), RegisterLuaBindingsOfType(EntityLuaBindings, SceneArea), + RegisterLuaBindingsOfType(EntityLuaBindings, SceneLayer), + RegisterLuaBindingsOfType(EntityLuaBindings, SLBackground), RegisterLuaBindingsOfAbstractType(EntityLuaBindings, Deployment), RegisterLuaBindingsOfType(SystemLuaBindings, DataModule), RegisterLuaBindingsOfType(ActivityLuaBindings, Activity), diff --git a/Managers/MetaMan.cpp b/Managers/MetaMan.cpp index 893b0bdc0..a9aaac100 100644 --- a/Managers/MetaMan.cpp +++ b/Managers/MetaMan.cpp @@ -400,7 +400,7 @@ int MetaMan::SaveSceneData(string pathBase) for (vector::const_iterator sItr = m_Scenes.begin(); sItr != m_Scenes.end(); ++sItr) { // Only save the data of revealed scenes that have already had their layers built and saved into files - if ((*sItr)->IsRevealed() && (*sItr)->GetTerrain() && (*sItr)->GetTerrain()->IsFileData()) + if ((*sItr)->IsRevealed() && (*sItr)->GetTerrain() && (*sItr)->GetTerrain()->IsLoadedFromDisk()) { // Save the scene data to a good unique prefix for the Scene's layers' bitmap files as they are saved if ((*sItr)->SaveData(pathBase + " - " + (*sItr)->GetPresetName()) < 0) @@ -422,7 +422,7 @@ int MetaMan::LoadSceneData() for (vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) { // Only load the data of revealed scenes that have already had their layers built and saved into files - if ((*sItr)->IsRevealed() && (*sItr)->GetTerrain() && (*sItr)->GetTerrain()->IsFileData()) + if ((*sItr)->IsRevealed() && (*sItr)->GetTerrain() && (*sItr)->GetTerrain()->IsLoadedFromDisk()) { // Only load the scene layer data, don't place objects or do any init for actually playing the scene if ((*sItr)->LoadData(false, false) < 0) diff --git a/Managers/MovableMan.cpp b/Managers/MovableMan.cpp index 40a1abcbd..fa1290607 100644 --- a/Managers/MovableMan.cpp +++ b/Managers/MovableMan.cpp @@ -1964,7 +1964,7 @@ void MovableMan::Update() } (*parIt)->SetPos(parPos.GetFloored()); } - if ((*parIt)->GetDrawPriority() >= terrMat->GetPriority()) { g_SceneMan.GetTerrain()->ApplyMovableObject(*parIt); } + if ((*parIt)->GetDrawPriority() >= terrMat->GetPriority()) { (*parIt)->DrawToTerrain(g_SceneMan.GetTerrain()); } delete *(parIt++); } m_Particles.erase(midIt, m_Particles.end()); diff --git a/Managers/NetworkClient.cpp b/Managers/NetworkClient.cpp index dad48b56e..2bd0ecbbc 100644 --- a/Managers/NetworkClient.cpp +++ b/Managers/NetworkClient.cpp @@ -13,16 +13,6 @@ namespace RTE { - // Data structure for constructing the draw boxes we'll need to use for drawing SceneLayers. - struct SLDrawBox { - int sourceX; - int sourceY; - int sourceW; - int sourceH; - int destX; - int destY; - }; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkClient::Clear() { @@ -721,7 +711,6 @@ namespace RTE { int sourceH = 0; int destX = 0; int destY = 0; - std::list drawList; int offsetX; int offsetY; diff --git a/Managers/NetworkServer.cpp b/Managers/NetworkServer.cpp index 78ecd6b19..8541da30d 100644 --- a/Managers/NetworkServer.cpp +++ b/Managers/NetworkServer.cpp @@ -6,6 +6,7 @@ #include "Scene.h" #include "SLTerrain.h" +#include "SLBackground.h" #include "GameActivity.h" #include "SettingsMan.h" @@ -678,15 +679,15 @@ namespace RTE { Scene *scene = g_SceneMan.GetScene(); - std::list sceneLayers = scene->GetBackLayers(); + std::list sceneLayers = scene->GetBackLayers(); short index = 0; - for (SceneLayer * &layer : sceneLayers) { + for (SLBackground * &layer : sceneLayers) { // Recalculate layers internal values for this player - layer->UpdateScrollRatiosForNetworkPlayer(player); - msgSceneSetup.BackgroundLayers[index].BitmapHash = layer->GetBitmapHash(); + layer->InitScrollRatios(true, player); + msgSceneSetup.BackgroundLayers[index].BitmapHash = layer->m_BitmapFile.GetHash(); - msgSceneSetup.BackgroundLayers[index].DrawTrans = layer->m_DrawTrans; + msgSceneSetup.BackgroundLayers[index].DrawTrans = layer->m_DrawMasked; msgSceneSetup.BackgroundLayers[index].OffsetX = layer->m_Offset.m_X; msgSceneSetup.BackgroundLayers[index].OffsetY = layer->m_Offset.m_Y; @@ -696,18 +697,16 @@ namespace RTE { msgSceneSetup.BackgroundLayers[index].ScrollRatioY = layer->m_ScrollRatio.m_Y; msgSceneSetup.BackgroundLayers[index].ScaleFactorX = layer->m_ScaleFactor.m_X; msgSceneSetup.BackgroundLayers[index].ScaleFactorY = layer->m_ScaleFactor.m_Y; - msgSceneSetup.BackgroundLayers[index].ScaleInverseX = layer->m_ScaleInverse.m_X; - msgSceneSetup.BackgroundLayers[index].ScaleInverseY = layer->m_ScaleInverse.m_Y; msgSceneSetup.BackgroundLayers[index].ScaledDimensionsX = layer->m_ScaledDimensions.m_X; msgSceneSetup.BackgroundLayers[index].ScaledDimensionsY = layer->m_ScaledDimensions.m_Y; msgSceneSetup.BackgroundLayers[index].WrapX = layer->m_WrapX; msgSceneSetup.BackgroundLayers[index].WrapY = layer->m_WrapY; - msgSceneSetup.BackgroundLayers[index].FillLeftColor = layer->m_FillLeftColor; - msgSceneSetup.BackgroundLayers[index].FillRightColor = layer->m_FillRightColor; - msgSceneSetup.BackgroundLayers[index].FillUpColor = layer->m_FillUpColor; - msgSceneSetup.BackgroundLayers[index].FillDownColor = layer->m_FillDownColor; + msgSceneSetup.BackgroundLayers[index].FillLeftColor = layer->m_FillColorLeft; + msgSceneSetup.BackgroundLayers[index].FillRightColor = layer->m_FillColorRight; + msgSceneSetup.BackgroundLayers[index].FillUpColor = layer->m_FillColorUp; + msgSceneSetup.BackgroundLayers[index].FillDownColor = layer->m_FillColorDown; index++; // Set everything back to what it was just in case diff --git a/Managers/SceneMan.cpp b/Managers/SceneMan.cpp index effa09d27..c960f2da4 100644 --- a/Managers/SceneMan.cpp +++ b/Managers/SceneMan.cpp @@ -22,6 +22,7 @@ #include "SettingsMan.h" #include "Scene.h" #include "SLTerrain.h" +#include "SLBackground.h" #include "TerrainObject.h" #include "MovableObject.h" #include "ContentFile.h" @@ -40,7 +41,7 @@ namespace RTE #define COMPACTINGHEIGHT 25 const std::string SceneMan::c_ClassName = "SceneMan"; - +std::vector> SceneMan::m_IntermediateSettlingBitmaps; ////////////////////////////////////////////////////////////////////////////////////////// // Method: IntersectionCut @@ -117,6 +118,25 @@ void SceneMan::Clear() m_pOrphanSearchBitmap = create_bitmap_ex(8, MAXORPHANRADIUS , MAXORPHANRADIUS); } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void SceneMan::Initialize() const { + // Can't create these earlier in the static declaration because allegro_init needs to be called before create_bitmap + m_IntermediateSettlingBitmaps = { + { 16, create_bitmap_ex(8, 16, 16) }, + { 32, create_bitmap_ex(8, 32, 32) }, + { 48, create_bitmap_ex(8, 48, 48) }, + { 64, create_bitmap_ex(8, 64, 64) }, + { 96, create_bitmap_ex(8, 96, 96) }, + { 128, create_bitmap_ex(8, 128, 128) }, + { 192, create_bitmap_ex(8, 192, 192) }, + { 256, create_bitmap_ex(8, 256, 256) }, + { 384, create_bitmap_ex(8, 384, 384) }, + { 512, create_bitmap_ex(8, 512, 512) } + }; +} + + ////////////////////////////////////////////////////////////////////////////////////////// // Method: Create ////////////////////////////////////////////////////////////////////////////////////////// @@ -314,15 +334,7 @@ int SceneMan::LoadScene(std::string sceneName, bool placeObjects, bool placeUnit int SceneMan::ReadProperty(const std::string_view &propName, Reader &reader) { - if (propName == "AddScene") - g_PresetMan.GetEntityPreset(reader); - else if (propName == "AddTerrain") - g_PresetMan.GetEntityPreset(reader); - else if (propName == "AddTerrainDebris") - g_PresetMan.GetEntityPreset(reader); - else if (propName == "AddTerrainObject") - g_PresetMan.GetEntityPreset(reader); - else if (propName == "AddMaterial") + if (propName == "AddMaterial") { // Get this before reading Object, since if it's the last one in its datafile, the stream will show the parent file instead string objectFilePath = reader.GetCurrentFilePath(); @@ -409,6 +421,10 @@ void SceneMan::Destroy() destroy_bitmap(m_pOrphanSearchBitmap); m_pOrphanSearchBitmap = 0; + for (const auto &[bitmapSize, bitmapPtr] : m_IntermediateSettlingBitmaps) { + destroy_bitmap(bitmapPtr); + } + Clear(); } @@ -583,7 +599,6 @@ unsigned char SceneMan::GetTerrMatter(int pixelX, int pixelY) // If it's still below or to the sides out of bounds after // what is supposed to be wrapped, shit is out of bounds. if (pixelX < 0 || pixelX >= pTMatBitmap->w || pixelY >= pTMatBitmap->h) -// return g_MaterialOutOfBounds; return g_MaterialAir; // If above terrain bitmap, return air material. @@ -1485,9 +1500,9 @@ bool SceneMan::IsUnseen(const int posX, const int posY, const int team) if (pUnseenLayer) { // Translate to the scaled unseen layer's coordinates - Vector scale = pUnseenLayer->GetScaleInverse(); - int scaledX = posX * scale.m_X; - int scaledY = posY * scale.m_Y; + Vector scale = pUnseenLayer->GetScaleFactor(); + int scaledX = posX / scale.m_X; + int scaledY = posY / scale.m_Y; return getpixel(pUnseenLayer->GetBitmap(), scaledX, scaledY) != g_MaskColor; } @@ -1510,9 +1525,9 @@ bool SceneMan::RevealUnseen(const int posX, const int posY, const int team) if (pUnseenLayer) { // Translate to the scaled unseen layer's coordinates - Vector scale = pUnseenLayer->GetScaleInverse(); - int scaledX = posX * scale.m_X; - int scaledY = posY * scale.m_Y; + Vector scale = pUnseenLayer->GetScaleFactor(); + int scaledX = posX / scale.m_X; + int scaledY = posY / scale.m_Y; // Make sure we're actually revealing an unseen pixel that is ON the bitmap! int pixel = getpixel(pUnseenLayer->GetBitmap(), scaledX, scaledY); @@ -1549,9 +1564,9 @@ bool SceneMan::RestoreUnseen(const int posX, const int posY, const int team) if (pUnseenLayer) { // Translate to the scaled unseen layer's coordinates - Vector scale = pUnseenLayer->GetScaleInverse(); - int scaledX = posX * scale.m_X; - int scaledY = posY * scale.m_Y; + Vector scale = pUnseenLayer->GetScaleFactor(); + int scaledX = posX / scale.m_X; + int scaledY = posY / scale.m_Y; // Make sure we're actually revealing an unseen pixel that is ON the bitmap! int pixel = getpixel(pUnseenLayer->GetBitmap(), scaledX, scaledY); @@ -1588,11 +1603,11 @@ void SceneMan::RevealUnseenBox(const int posX, const int posY, const int width, if (pUnseenLayer) { // Translate to the scaled unseen layer's coordinates - Vector scale = pUnseenLayer->GetScaleInverse(); - int scaledX = posX * scale.m_X; - int scaledY = posY * scale.m_Y; - int scaledW = width * scale.m_X; - int scaledH = height * scale.m_Y; + Vector scale = pUnseenLayer->GetScaleFactor(); + int scaledX = posX / scale.m_X; + int scaledY = posY / scale.m_Y; + int scaledW = width / scale.m_X; + int scaledH = height / scale.m_Y; // Fill the box rectfill(pUnseenLayer->GetBitmap(), scaledX, scaledY, scaledX + scaledW, scaledY + scaledH, g_MaskColor); @@ -1615,11 +1630,11 @@ void SceneMan::RestoreUnseenBox(const int posX, const int posY, const int width, if (pUnseenLayer) { // Translate to the scaled unseen layer's coordinates - Vector scale = pUnseenLayer->GetScaleInverse(); - int scaledX = posX * scale.m_X; - int scaledY = posY * scale.m_Y; - int scaledW = width * scale.m_X; - int scaledH = height * scale.m_Y; + Vector scale = pUnseenLayer->GetScaleFactor(); + int scaledX = posX / scale.m_X; + int scaledY = posY / scale.m_Y; + int scaledW = width / scale.m_X; + int scaledH = height / scale.m_Y; // Fill the box rectfill(pUnseenLayer->GetBitmap(), scaledX, scaledY, scaledX + scaledW, scaledY + scaledH, g_BlackColor); @@ -2836,55 +2851,6 @@ Vector SceneMan::MovePointToGround(const Vector &from, int maxAltitude, int accu } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: StructuralCalc -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the structural integrity of the Terrain during a set time -// and turns structurally unsound areas into MovableObject:s. - -void SceneMan::StructuralCalc(unsigned long calcTime) { - -// TODO: Develop this!") - return; - - - if (calcTime <= 0) - return; - // Pad the time a little for FPS smoothness. - calcTime -= 1; - m_CalcTimer.Reset(); - - SLTerrain *pTerrain = g_SceneMan.GetTerrain(); - BITMAP *pColBitmap = pTerrain->GetFGColorBitmap(); - BITMAP *pMatBitmap = pTerrain->GetMaterialBitmap(); - BITMAP *pStructBitmap = pTerrain->GetStructuralBitmap(); - int posX, posY, height = pColBitmap->h, width = pColBitmap->w; - - // Lock all bitmaps involved, outside the loop. - acquire_bitmap(pColBitmap); - acquire_bitmap(pMatBitmap); - acquire_bitmap(pStructBitmap); - - // Preprocess bottom row to have full support. - for (posX = width - 1; posX >= 0; --posX) - putpixel(pStructBitmap, posX, height - 1, 255); - - // Start on the second row from bottom. - for (posY = height - 2; posY >= 0 && !m_CalcTimer.IsPastSimMS(calcTime); --posY) { - for (posX = width - 1; posX >= 0; --posX) { - getpixel(pColBitmap, posX, posY); - getpixel(pMatBitmap, posX, posY); - getpixel(pStructBitmap, posX, posY); - } - } - - // Unlock all bitmaps involved. - release_bitmap(pColBitmap); - release_bitmap(pMatBitmap); - release_bitmap(pStructBitmap); -} - - ////////////////////////////////////////////////////////////////////////////////////////// // Method: IsWithinBounds ////////////////////////////////////////////////////////////////////////////////////////// @@ -3279,199 +3245,99 @@ int SceneMan::WrapBox(const Box &wrapBox, list &outputList) return addedTimes; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddTerrainObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Takes TerrainObject and applies it to the terrain -// OWNERSHIP NOT TRANSFERED! - -bool SceneMan::AddTerrainObject(TerrainObject *pObject) -{ - if (!pObject) - return false; - - bool result = m_pCurrentScene->GetTerrain()->ApplyObject(pObject); - if (result) - { - Vector corner = pObject->GetPos() + pObject->GetBitmapOffset(); - Box box = Box(corner, pObject->GetBitmapWidth(), pObject->GetBitmapHeight()); - - m_pCurrentScene->GetTerrain()->CleanAirBox(box, GetScene()->WrapsX(), GetScene()->WrapsY()); +bool SceneMan::AddSceneObject(SceneObject *sceneObject) { + bool result = false; + if (sceneObject) { + if (MovableObject *sceneObjectAsMovableObject = dynamic_cast(sceneObject)) { + return g_MovableMan.AddMO(sceneObjectAsMovableObject); + } else if (TerrainObject *sceneObjectAsTerrainObject = dynamic_cast(sceneObject)) { + result = sceneObjectAsTerrainObject->PlaceOnTerrain(m_pCurrentScene->GetTerrain()); + if (result) { + Box airBox(sceneObjectAsTerrainObject->GetPos() + sceneObjectAsTerrainObject->GetBitmapOffset(), static_cast(sceneObjectAsTerrainObject->GetBitmapWidth()), static_cast(sceneObjectAsTerrainObject->GetBitmapHeight())); + m_pCurrentScene->GetTerrain()->CleanAirBox(airBox, GetScene()->WrapsX(), GetScene()->WrapsY()); + } + } } + delete sceneObject; return result; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddSceneObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Takes any scene object and adds it to the scene in the appropriate way. -// If it's a TerrainObject, then it gets applied to the terrain, if it's -// an MO, it gets added to the correct type group in MovableMan. - -bool SceneMan::AddSceneObject(SceneObject *pObject) -{ - if (!pObject) - return false; - - // Find out what kind it is and apply accordingly - if (MovableObject *pMO = dynamic_cast(pObject)) - { - // No need to clean up here, AddMO takes ownership and takes care of it in either case - return g_MovableMan.AddMO(pMO); - } - else if (TerrainObject *pTO = dynamic_cast(pObject)) - { - bool result = m_pCurrentScene->GetTerrain()->ApplyObject(pTO); - // Have to clean up the added object here, since ApplyObject doesn't take ownership - delete pTO; - pObject = pTO = 0; - return result; - } - - delete pObject; - pObject = 0; - return false; -} - - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Update ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the state of this SceneMan. Supposed to be done every frame // before drawing. -void SceneMan::Update(int screen) -{ - if (m_pCurrentScene == nullptr) { - return; - } - - // Record screen was the last updated screen - m_LastUpdatedScreen = screen; - - // Update the scene, only if doing the first screen, since it only needs done once per update - if (screen == 0) - m_pCurrentScene->Update(); - - // Handy - SLTerrain *pTerrain = m_pCurrentScene->GetTerrain(); - - // Learn about the unseen layer, if any - int team = m_ScreenTeam[screen]; - SceneLayer *pUnseenLayer = team != Activity::NoTeam ? m_pCurrentScene->GetUnseenLayer(team) : 0; - - //////////////////////////////// - // Scrolling interpolation - - // Only adjust wrapping if this is the frame to be drawn - if (g_TimerMan.DrawnSimUpdate()) - { - // Adjust for wrapping, if the scrolltarget jumped a seam this frame, as reported by - // whatever client set it (the scrolltarget) this frame. This is to avoid big, - // scene-wide jumps in scrolling when traversing the seam. - if (m_TargetWrapped[screen]) - { - if (pTerrain->WrapsX()) - { - if (m_ScrollTarget[screen].m_X < (pTerrain->GetBitmap()->w / 2)) - { - m_Offset[screen].m_X -= pTerrain->GetBitmap()->w; - m_SeamCrossCount[screen][X] += 1; - } - else - { - m_Offset[screen].m_X += pTerrain->GetBitmap()->w; - m_SeamCrossCount[screen][X] -= 1; - } - } - - if (pTerrain->WrapsY()) - { - if (m_ScrollTarget[screen].m_Y < (pTerrain->GetBitmap()->h / 2)) - { - m_Offset[screen].m_Y -= pTerrain->GetBitmap()->h; - m_SeamCrossCount[screen][Y] += 1; - } - else - { - m_Offset[screen].m_Y += pTerrain->GetBitmap()->h; - m_SeamCrossCount[screen][Y] -= 1; - } - } - } - m_TargetWrapped[screen] = false; - } - - Vector oldOffset(m_Offset[screen]); - - // Get the offset target, since the scroll target is centered on the target in scene units. - Vector offsetTarget; - if (g_FrameMan.IsInMultiplayerMode()) - { - offsetTarget.m_X = m_ScrollTarget[screen].m_X - (g_FrameMan.GetPlayerFrameBufferWidth(screen) / 2); - offsetTarget.m_Y = m_ScrollTarget[screen].m_Y - (g_FrameMan.GetPlayerFrameBufferHeight(screen) / 2); - } - else - { - offsetTarget.m_X = m_ScrollTarget[screen].m_X - (g_FrameMan.GetResX() / (g_FrameMan.GetVSplit() ? 4 : 2)); - offsetTarget.m_Y = m_ScrollTarget[screen].m_Y - (g_FrameMan.GetResY() / (g_FrameMan.GetHSplit() ? 4 : 2)); +void SceneMan::Update(int screen) { + if (!m_pCurrentScene) { + return; } + m_LastUpdatedScreen = screen; - // Take the occlusion of the screens into account, - // so that the scrolltarget is still centered on the terrain-visible portion of the screen. - offsetTarget -= m_ScreenOcclusion[screen] / 2; - - if (offsetTarget.GetFloored() != m_Offset[screen].GetFloored()) - { - Vector scrollVec(offsetTarget - m_Offset[screen]); - // Figure out the scroll progress this frame, and cap it at 1.0 - float scrollProgress = m_ScrollSpeed[screen] * m_ScrollTimer[screen].GetElapsedRealTimeMS() * 0.05; - if (scrollProgress > 1.0) - scrollProgress = 1.0; -// TODO: Check if rounding is appropriate? - SetOffset(m_Offset[screen] + (scrollVec * scrollProgress).GetRounded(), screen); - } - - ///////////////////////////////// - // Apply offsets to SceneLayer:s + // Update the scene, only if doing the first screen, since it only needs done once per update. + if (screen == 0) { m_pCurrentScene->Update(); } - m_pMOColorLayer->SetOffset(m_Offset[screen]); - m_pMOIDLayer->SetOffset(m_Offset[screen]); + SLTerrain *terrain = m_pCurrentScene->GetTerrain(); - if (m_pDebugLayer) { m_pDebugLayer->SetOffset(m_Offset[screen]); } - - pTerrain->SetOffset(m_Offset[screen]); - pTerrain->Update(); + if (g_TimerMan.DrawnSimUpdate()) { + // Adjust for wrapping if the scroll target jumped a seam this frame, as reported by whatever screen set it (the scroll target) this frame. This is to avoid big, scene-wide jumps in scrolling when traversing the seam. + if (m_TargetWrapped[screen]) { + if (terrain->WrapsX()) { + int wrappingScrollDirection = (m_ScrollTarget[screen].GetFloorIntX() < (terrain->GetBitmap()->w / 2)) ? 1 : -1; + m_Offset[screen].SetX(m_Offset[screen].GetX() - (static_cast(terrain->GetBitmap()->w * wrappingScrollDirection))); + m_SeamCrossCount[screen][X] += wrappingScrollDirection; + } + if (terrain->WrapsY()) { + int wrappingScrollDirection = (m_ScrollTarget[screen].GetFloorIntY() < (terrain->GetBitmap()->h / 2)) ? 1 : -1; + m_Offset[screen].SetY(m_Offset[screen].GetY() - (static_cast(terrain->GetBitmap()->h * wrappingScrollDirection))); + m_SeamCrossCount[screen][Y] += wrappingScrollDirection; + } + } + m_TargetWrapped[screen] = false; + } + Vector oldOffset(m_Offset[screen]); + Vector offsetTarget; - // Scroll the unexplored/unseen layer, if there is one - if (pUnseenLayer) - { - // Update the unseen obstruction layer for this team's screen view - pUnseenLayer->SetOffset(m_Offset[screen]); - } + if (g_FrameMan.IsInMultiplayerMode()) { + offsetTarget.SetX(m_ScrollTarget[screen].GetX() - static_cast(g_FrameMan.GetPlayerFrameBufferWidth(screen) / 2)); + offsetTarget.SetY(m_ScrollTarget[screen].GetY() - static_cast(g_FrameMan.GetPlayerFrameBufferHeight(screen) / 2)); + } else { + offsetTarget.SetX(m_ScrollTarget[screen].GetX() - static_cast(g_FrameMan.GetResX() / (g_FrameMan.GetVSplit() ? 4 : 2))); + offsetTarget.SetY(m_ScrollTarget[screen].GetY() - static_cast(g_FrameMan.GetResY() / (g_FrameMan.GetHSplit() ? 4 : 2))); + } + // Take the occlusion of the screens into account so that the scroll target is still centered on the terrain-visible portion of the screen. + offsetTarget -= (m_ScreenOcclusion[screen] / 2); - // Background layers may scroll in fractions of the real offset, and need special care to avoid jumping after having traversed wrapped edges - // Reconstruct and give them the total offset, not taking any wrappings into account - Vector offsetUnwrapped = m_Offset[screen]; - offsetUnwrapped.m_X += pTerrain->GetBitmap()->w * m_SeamCrossCount[screen][X]; - offsetUnwrapped.m_Y += pTerrain->GetBitmap()->h * m_SeamCrossCount[screen][Y]; + if (offsetTarget.GetFloored() != m_Offset[screen].GetFloored()) { + Vector scrollVec(offsetTarget - m_Offset[screen]); + float scrollProgress = std::min(1.0F, static_cast(m_ScrollSpeed[screen] * m_ScrollTimer[screen].GetElapsedRealTimeMS() * 0.05F)); + SetOffset(m_Offset[screen] + (scrollVec * scrollProgress), screen); + } - for (list::iterator itr = m_pCurrentScene->GetBackLayers().begin(); itr != m_pCurrentScene->GetBackLayers().end(); ++itr) - (*itr)->SetOffset(offsetUnwrapped); + m_pMOColorLayer->SetOffset(m_Offset[screen]); + m_pMOIDLayer->SetOffset(m_Offset[screen]); + if (m_pDebugLayer) { m_pDebugLayer->SetOffset(m_Offset[screen]); } - // Calculate delta offset. - m_DeltaOffset[screen] = m_Offset[screen] - oldOffset; + terrain->SetOffset(m_Offset[screen]); + terrain->Update(); - // Reset the timer so we can know the real time diff next frame - m_ScrollTimer[screen].Reset(); + // Background layers may scroll in fractions of the real offset and need special care to avoid jumping after having traversed wrapped edges, so they need the total offset without taking wrapping into account. + Vector unwrappedOffset(m_Offset[screen].GetX() + static_cast(terrain->GetBitmap()->w * m_SeamCrossCount[screen][X]), m_Offset[screen].GetY() + static_cast(terrain->GetBitmap()->h * m_SeamCrossCount[screen][Y])); + for (SLBackground *backgroundLayer : m_pCurrentScene->GetBackLayers()) { + backgroundLayer->SetOffset(unwrappedOffset); + backgroundLayer->Update(); + } + // Update the unseen obstruction layer for this team's screen view, if there is one. + if (SceneLayer *unseenLayer = (m_ScreenTeam[screen] != Activity::NoTeam) ? m_pCurrentScene->GetUnseenLayer(m_ScreenTeam[screen]) : nullptr) { unseenLayer->SetOffset(m_Offset[screen]); } - // Clean the color layer of the Terrain - if (m_CleanTimer.GetElapsedSimTimeMS() > CLEANAIRINTERVAL) - { - pTerrain->CleanAir(); - m_CleanTimer.Reset(); - } + if (m_CleanTimer.GetElapsedSimTimeMS() > CLEANAIRINTERVAL) { + terrain->CleanAir(); + m_CleanTimer.Reset(); + } + m_DeltaOffset[screen] = m_Offset[screen] - oldOffset; + m_ScrollTimer[screen].Reset(); } @@ -3481,81 +3347,60 @@ void SceneMan::Update(int screen) // Description: Draws this SceneMan's current graphical representation to a // BITMAP of choice. -void SceneMan::Draw(BITMAP *pTargetBitmap, BITMAP *pTargetGUIBitmap, const Vector &targetPos, bool skipSkybox, bool skipTerrain) -{ - if (m_pCurrentScene == nullptr) { - return; - } - // Handy - SLTerrain *pTerrain = m_pCurrentScene->GetTerrain(); - - // Learn about the unseen layer, if any - int team = m_ScreenTeam[m_LastUpdatedScreen]; - SceneLayer *pUnseenLayer = team != Activity::NoTeam ? m_pCurrentScene->GetUnseenLayer(team) : 0; - - // Set up the target box to draw to on the target bitmap, if it is larger than the scene in either dimension - Box targetBox(Vector(0, 0), pTargetBitmap->w, pTargetBitmap->h); - - if (!pTerrain->WrapsX() && pTargetBitmap->w > GetSceneWidth()) - { - targetBox.m_Corner.m_X = (pTargetBitmap->w - GetSceneWidth()) / 2; - targetBox.m_Width = GetSceneWidth(); - } - if (!pTerrain->WrapsY() && pTargetBitmap->h > GetSceneHeight()) - { - targetBox.m_Corner.m_Y = (pTargetBitmap->h - GetSceneHeight()) / 2; - targetBox.m_Height = GetSceneHeight(); - } +void SceneMan::Draw(BITMAP *targetBitmap, BITMAP *targetGUIBitmap, const Vector &targetPos, bool skipBackgroundLayers, bool skipTerrain) { + if (!m_pCurrentScene) { + return; + } + SLTerrain *terrain = m_pCurrentScene->GetTerrain(); + // Set up the target box to draw to on the target bitmap, if it is larger than the scene in either dimension. + Box targetBox(Vector(), static_cast(targetBitmap->w), static_cast(targetBitmap->h)); - switch (m_LayerDrawMode) - { - case g_LayerTerrainMatter: - pTerrain->SetToDrawMaterial(true); - pTerrain->Draw(pTargetBitmap, targetBox); - break; - case g_LayerMOID: - m_pMOIDLayer->Draw(pTargetBitmap, targetBox); - break; - // Draw normally - default: - if (skipSkybox) - { + if (!terrain->WrapsX() && targetBitmap->w > GetSceneWidth()) { + targetBox.SetCorner(Vector(static_cast((targetBitmap->w - GetSceneWidth()) / 2), targetBox.GetCorner().GetY())); + targetBox.SetWidth(static_cast(GetSceneWidth())); + } + if (!terrain->WrapsY() && targetBitmap->h > GetSceneHeight()) { + targetBox.SetCorner(Vector(targetBox.GetCorner().GetX(), static_cast((targetBitmap->h - GetSceneHeight()) / 2))); + targetBox.SetHeight(static_cast(GetSceneHeight())); + } - } - else - { - // Background Layers - for (list::reverse_iterator itr = m_pCurrentScene->GetBackLayers().rbegin(); itr != m_pCurrentScene->GetBackLayers().rend(); ++itr) - (*itr)->Draw(pTargetBitmap, targetBox); + switch (m_LayerDrawMode) { + case LayerDrawMode::g_LayerTerrainMatter: + terrain->SetLayerToDraw(SLTerrain::LayerType::MaterialLayer); + terrain->Draw(targetBitmap, targetBox); + break; + case LayerDrawMode::g_LayerMOID: + m_pMOIDLayer->Draw(targetBitmap, targetBox); + break; + default: + if (!skipBackgroundLayers) { + for (std::list::reverse_iterator backgroundLayer = m_pCurrentScene->GetBackLayers().rbegin(); backgroundLayer != m_pCurrentScene->GetBackLayers().rend(); ++backgroundLayer) { + (*backgroundLayer)->Draw(targetBitmap, targetBox); + } } + if (!skipTerrain) { + terrain->SetLayerToDraw(SLTerrain::LayerType::BackgroundLayer); + terrain->Draw(targetBitmap, targetBox); + } + m_pMOColorLayer->Draw(targetBitmap, targetBox); - if (!skipTerrain) - // Terrain background - pTerrain->DrawBackground(pTargetBitmap, targetBox); - // Movables' color layer - m_pMOColorLayer->Draw(pTargetBitmap, targetBox); - // Terrain foreground - pTerrain->SetToDrawMaterial(false); - if (!skipTerrain) - pTerrain->Draw(pTargetBitmap, targetBox); - - // Obscure unexplored/unseen areas - if (pUnseenLayer && !g_FrameMan.IsInMultiplayerMode()) - { - // Draw the unseen obstruction layer so it obscures the team's view - pUnseenLayer->DrawScaled(pTargetBitmap, targetBox); - } + if (!skipTerrain) { + terrain->SetLayerToDraw(SLTerrain::LayerType::ForegroundLayer); + terrain->Draw(targetBitmap, targetBox); + } + if (!g_FrameMan.IsInMultiplayerMode()) { + int team = m_ScreenTeam[m_LastUpdatedScreen]; + if (SceneLayer *unseenLayer = (team != Activity::NoTeam) ? m_pCurrentScene->GetUnseenLayer(team) : nullptr) { unseenLayer->Draw(targetBitmap, targetBox); } + } - // Actor and gameplay HUDs and GUIs - g_MovableMan.DrawHUD(pTargetGUIBitmap, targetPos, m_LastUpdatedScreen); - g_PrimitiveMan.DrawPrimitives(m_LastUpdatedScreen, pTargetGUIBitmap, targetPos); -// g_ActivityMan.GetActivity()->Draw(pTargetBitmap, targetPos, m_LastUpdatedScreen); - g_ActivityMan.GetActivity()->DrawGUI(pTargetGUIBitmap, targetPos, m_LastUpdatedScreen); + g_MovableMan.DrawHUD(targetGUIBitmap, targetPos, m_LastUpdatedScreen); + g_PrimitiveMan.DrawPrimitives(m_LastUpdatedScreen, targetGUIBitmap, targetPos); + g_ActivityMan.GetActivity()->DrawGUI(targetGUIBitmap, targetPos, m_LastUpdatedScreen); -// std::snprintf(str, sizeof(str), "Normal Layer Draw Mode\nHit M to cycle modes"); + if (m_pDebugLayer) { m_pDebugLayer->Draw(targetBitmap, targetBox); } - if (m_pDebugLayer) { m_pDebugLayer->Draw(pTargetBitmap, targetBox); } - } + break; + } } @@ -3604,4 +3449,16 @@ void SceneMan::ClearCurrentScene() { m_pCurrentScene = nullptr; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +BITMAP * SceneMan::GetIntermediateBitmapForSettlingIntoTerrain(int moDiameter) const { + int bitmapSizeNeeded = static_cast(std::ceil(static_cast(moDiameter) / 16.0F)) * 16; + for (const auto &[bitmapSize, bitmapPtr] : m_IntermediateSettlingBitmaps) { + if (std::min(bitmapSize, bitmapSizeNeeded) >= bitmapSizeNeeded) { + return bitmapPtr; + } + } + return m_IntermediateSettlingBitmaps.back().second; +} + } // namespace RTE diff --git a/Managers/SceneMan.h b/Managers/SceneMan.h index 6c58b91a1..bb4b3679e 100644 --- a/Managers/SceneMan.h +++ b/Managers/SceneMan.h @@ -44,20 +44,6 @@ enum LayerDrawMode g_LayerMOID }; -enum -{ - REGULAR_MAT_OFFSET = 8, - g_MaterialAir = 0, - g_MaterialDefault = 1, - g_MaterialOutOfBounds = 1, - g_MaterialCavity = 1, - g_MaterialGold = 2, - g_MaterialGrass = 128, - g_MaterialFlesh = 145, - g_MaterialSand = 8, - g_MaterialDoor = 181 -}; - #define SCENEGRIDSIZE 24 #define SCENESNAPSIZE 12 #define MAXORPHANRADIUS 11 @@ -135,6 +121,12 @@ class SceneMan : public Singleton, public Serializable { ~SceneMan() { Destroy(); } + /// + /// Makes the SceneMan object ready for use. + /// + void Initialize() const; + + ////////////////////////////////////////////////////////////////////////////////////////// // Method: Create ////////////////////////////////////////////////////////////////////////////////////////// @@ -1344,17 +1336,6 @@ class SceneMan : public Singleton, public Serializable { Vector MovePointToGround(const Vector &from, int maxAltitude = 0, int accuracy = 0); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: StructuralCalc -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the structural integrity of the Terrain during a set time -// and turns structurally unsound areas into MovableObject:s. -// Arguments: The amount of time in ms to use for these calculations this frame. -// Return value: None. - - void StructuralCalc(unsigned long calcTime); - - ////////////////////////////////////////////////////////////////////////////////////////// // Method: IsWithinBounds ////////////////////////////////////////////////////////////////////////////////////////// @@ -1545,17 +1526,6 @@ class SceneMan : public Singleton, public Serializable { int WrapBox(const Box &wrapBox, std::list &outputList); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddTerrainObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Takes TerrainObject and applies it to the terrain -// OWNERSHIP NOT TRANSFERED! -// Arguments: TerrainObject to add. -// Return value: True on success. - - bool AddTerrainObject(TerrainObject *pObject); - - ////////////////////////////////////////////////////////////////////////////////////////// // Method: AddSceneObject ////////////////////////////////////////////////////////////////////////////////////////// @@ -1591,7 +1561,7 @@ class SceneMan : public Singleton, public Serializable { // is located. // Return value: None. - void Draw(BITMAP *pTargetBitmap, BITMAP *pTargetGUIBitmap, const Vector &targetPos = Vector(), bool skipSkybox = false, bool skipTerrain = false); + void Draw(BITMAP *targetBitmap, BITMAP *targetGUIBitmap, const Vector &targetPos = Vector(), bool skipBackgroundLayers = false, bool skipTerrain = false); ////////////////////////////////////////////////////////////////////////////////////////// @@ -1647,6 +1617,13 @@ class SceneMan : public Singleton, public Serializable { void RegisterTerrainChange(int x, int y, int w, int h, unsigned char color, bool back); + /// + /// Gets an intermediate bitmap that is used for drawing a settled MovableObject into the terrain. + /// + /// The diameter of the MovableObject to calculate the required bitmap size. + /// Pointer to the temp BITMAP of the appropriate size. Ownership is NOT transferred! + BITMAP * GetIntermediateBitmapForSettlingIntoTerrain(int moDiameter) const; + // Struct to register terrain change events struct TerrainChange { @@ -1668,6 +1645,7 @@ class SceneMan : public Singleton, public Serializable { protected: + static std::vector> m_IntermediateSettlingBitmaps; //!< Intermediate bitmaps of different sizes that are used to draw settled MovableObjects into the terrain. // Default Scene name to load if nothing else is specified std::string m_DefaultSceneName; diff --git a/Managers/SettingsMan.cpp b/Managers/SettingsMan.cpp index 752d630b5..cff485b79 100644 --- a/Managers/SettingsMan.cpp +++ b/Managers/SettingsMan.cpp @@ -42,6 +42,7 @@ namespace RTE { m_RecommendedMOIDCount = 240; m_SimplifiedCollisionDetection = false; + m_SceneBackgroundAutoScaleMode = 1; m_SkipIntro = false; m_ShowToolTips = true; @@ -171,6 +172,8 @@ namespace RTE { reader >> m_RecommendedMOIDCount; } else if (propName == "SimplifiedCollisionDetection") { reader >> m_SimplifiedCollisionDetection; + } else if (propName == "SceneBackgroundAutoScaleMode") { + SetSceneBackgroundAutoScaleMode(std::stoi(reader.ReadPropValue())); } else if (propName == "EnableParticleSettling") { reader >> g_MovableMan.m_SettlingEnabled; } else if (propName == "EnableMOSubtraction") { @@ -345,6 +348,7 @@ namespace RTE { writer.NewPropertyWithValue("DisableLuaJIT", g_LuaMan.m_DisableLuaJIT); writer.NewPropertyWithValue("RecommendedMOIDCount", m_RecommendedMOIDCount); writer.NewPropertyWithValue("SimplifiedCollisionDetection", m_SimplifiedCollisionDetection); + writer.NewPropertyWithValue("SceneBackgroundAutoScaleMode", m_SceneBackgroundAutoScaleMode); writer.NewPropertyWithValue("EnableParticleSettling", g_MovableMan.m_SettlingEnabled); writer.NewPropertyWithValue("EnableMOSubtraction", g_MovableMan.m_MOSubtractionEnabled); writer.NewPropertyWithValue("DeltaTime", g_TimerMan.GetDeltaTimeSecs()); diff --git a/Managers/SettingsMan.h b/Managers/SettingsMan.h index 63f77efe7..075c6f3a7 100644 --- a/Managers/SettingsMan.h +++ b/Managers/SettingsMan.h @@ -68,6 +68,18 @@ namespace RTE { /// /// Whether simplified collision detection is enabled or not. bool SimplifiedCollisionDetection() const { return m_SimplifiedCollisionDetection; } + + /// + /// Gets the Scene background layer auto-scaling mode. + /// + /// The Scene background layer auto-scaling mode. 0 for off, 1 for fit screen dimensions and 2 for always upscaled to x2. + int GetSceneBackgroundAutoScaleMode() const { return m_SceneBackgroundAutoScaleMode; } + + /// + /// Sets the Scene background layer auto-scaling mode. + /// + /// The new Scene background layer auto-scaling mode. 0 for off, 1 for fit screen dimensions and 2 for always upscaled to x2. + void SetSceneBackgroundAutoScaleMode(int newMode) { m_SceneBackgroundAutoScaleMode = std::clamp(newMode, 0, 2); } #pragma endregion #pragma region Gameplay Settings @@ -457,6 +469,7 @@ namespace RTE { int m_RecommendedMOIDCount; //!< Recommended max MOID's before removing actors from scenes. bool m_SimplifiedCollisionDetection; //!< Whether simplified collision detection (reduced MOID layer sampling) is enabled. + int m_SceneBackgroundAutoScaleMode; //!< Scene background layer auto-scaling mode. 0 for off, 1 for fit screen dimensions and 2 for always upscaled to x2. bool m_SkipIntro; //!< Whether to play the intro of the game or skip directly to the main menu. bool m_ShowToolTips; //!< Whether ToolTips are enabled or not. diff --git a/Menus/AreaPickerGUI.cpp b/Menus/AreaPickerGUI.cpp index c99b1ebbe..22a35be80 100644 --- a/Menus/AreaPickerGUI.cpp +++ b/Menus/AreaPickerGUI.cpp @@ -17,6 +17,7 @@ #include "PresetMan.h" #include "ActivityMan.h" #include "UInputMan.h" +#include "SceneMan.h" #include "GUI.h" #include "AllegroBitmap.h" diff --git a/Menus/AssemblyEditorGUI.cpp b/Menus/AssemblyEditorGUI.cpp index 62480d0c1..02be26a2d 100644 --- a/Menus/AssemblyEditorGUI.cpp +++ b/Menus/AssemblyEditorGUI.cpp @@ -20,6 +20,7 @@ #include "SceneEditor.h" #include "UInputMan.h" +#include "Deployment.h" #include "Controller.h" #include "SceneObject.h" #include "MOSprite.h" diff --git a/Menus/ScenarioGUI.cpp b/Menus/ScenarioGUI.cpp index 30c52a5d5..ef62bfd65 100644 --- a/Menus/ScenarioGUI.cpp +++ b/Menus/ScenarioGUI.cpp @@ -96,7 +96,7 @@ namespace RTE { m_StartActivityConfigButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonStartActivityConfig")); m_DefaultScenePreview.Create(ContentFile("Base.rte/GUIs/DefaultPreview.png"), 5); - m_DefaultScenePreview.SetSpriteAnimMode(MOSprite::SpriteAnimMode::ALWAYSLOOP); + m_DefaultScenePreview.SetSpriteAnimMode(SpriteAnimMode::ALWAYSLOOP); m_DefaultScenePreview.SetSpriteAnimDuration(200); m_DefaultScenePreview.SetPos(Vector(static_cast(m_ScenePreviewImageBox->GetXPos() + (m_ScenePreviewImageBox->GetWidth() / 2)), static_cast(m_ScenePreviewImageBox->GetYPos() + (m_ScenePreviewImageBox->GetHeight() / 2)))); diff --git a/Menus/SceneEditorGUI.cpp b/Menus/SceneEditorGUI.cpp index 4dd98ad35..d77d26a59 100644 --- a/Menus/SceneEditorGUI.cpp +++ b/Menus/SceneEditorGUI.cpp @@ -1026,9 +1026,12 @@ void SceneEditorGUI::Update() // Deduct the cost from team funds g_ActivityMan.GetActivity()->ChangeTeamFunds(-m_pCurrentObject->GetTotalValue(m_NativeTechModule, m_ForeignCostMult), m_pController->GetTeam()); - g_SceneMan.GetTerrain()->ApplyTerrainObject(pTO); + pTO->PlaceOnTerrain(g_SceneMan.GetTerrain()); g_SceneMan.GetTerrain()->CleanAir(); - g_SceneMan.GetTerrain()->RegisterTerrainChange(pTO); + + Vector terrainObjectPos = pTO->GetPos() + pTO->GetBitmapOffset(); + if (pTO->HasBGColorBitmap()) { g_SceneMan.RegisterTerrainChange(terrainObjectPos.GetFloorIntX(), terrainObjectPos.GetFloorIntY(), pTO->GetBitmapWidth(), pTO->GetBitmapHeight(), ColorKeys::g_MaskColor, true); } + if (pTO->HasFGColorBitmap()) { g_SceneMan.RegisterTerrainChange(terrainObjectPos.GetFloorIntX(), terrainObjectPos.GetFloorIntY(), pTO->GetBitmapWidth(), pTO->GetBitmapHeight(), ColorKeys::g_MaskColor, false); } // TODO: Make IsBrain function to see if one was placed if (pTO->GetPresetName() == "Brain Vault") diff --git a/Menus/SettingsMiscGUI.cpp b/Menus/SettingsMiscGUI.cpp index 6ecf849da..d68523be1 100644 --- a/Menus/SettingsMiscGUI.cpp +++ b/Menus/SettingsMiscGUI.cpp @@ -6,6 +6,8 @@ #include "GUI.h" #include "GUICollectionBox.h" #include "GUICheckbox.h" +#include "GUILabel.h" +#include "GUISlider.h" namespace RTE { @@ -31,6 +33,12 @@ namespace RTE { m_UseMonospaceConsoleFontCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxUseMonospaceConsoleFont")); m_UseMonospaceConsoleFontCheckbox->SetCheck(g_ConsoleMan.GetConsoleUseMonospaceFont()); + + m_SceneBackgroundAutoScaleLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelSceneBackgroundAutoScaleSetting")); + UpdateSceneBackgroundAutoScaleLabel(); + + m_SceneBackgroundAutoScaleSlider = dynamic_cast(m_GUIControlManager->GetControl("SliderSceneBackgroundAutoScale")); + m_SceneBackgroundAutoScaleSlider->SetValue(g_SettingsMan.GetSceneBackgroundAutoScaleMode()); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -40,6 +48,22 @@ namespace RTE { m_MiscSettingsBox->SetEnabled(enable); } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SettingsMiscGUI::UpdateSceneBackgroundAutoScaleLabel() { + switch (g_SettingsMan.GetSceneBackgroundAutoScaleMode()) { + case 1: + m_SceneBackgroundAutoScaleLabel->SetText("Stretch to fit screen"); + break; + case 2: + m_SceneBackgroundAutoScaleLabel->SetText("Always upscaled"); + break; + default: + m_SceneBackgroundAutoScaleLabel->SetText("Disabled"); + break; + } + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsMiscGUI::HandleInputEvents(GUIEvent &guiEvent) { @@ -56,6 +80,9 @@ namespace RTE { g_SettingsMan.MeasureModuleLoadTime(m_MeasureLoadTimeCheckbox->GetCheck()); } else if (guiEvent.GetControl() == m_UseMonospaceConsoleFontCheckbox) { g_ConsoleMan.SetConsoleUseMonospaceFont(m_UseMonospaceConsoleFontCheckbox->GetCheck()); + } else if (guiEvent.GetControl() == m_SceneBackgroundAutoScaleSlider) { + g_SettingsMan.SetSceneBackgroundAutoScaleMode(m_SceneBackgroundAutoScaleSlider->GetValue()); + UpdateSceneBackgroundAutoScaleLabel(); } } } diff --git a/Menus/SettingsMiscGUI.h b/Menus/SettingsMiscGUI.h index 6f759514d..3c4d354eb 100644 --- a/Menus/SettingsMiscGUI.h +++ b/Menus/SettingsMiscGUI.h @@ -6,6 +6,8 @@ namespace RTE { class GUIControlManager; class GUICollectionBox; class GUICheckbox; + class GUILabel; + class GUISlider; class GUIEvent; /// @@ -51,6 +53,15 @@ namespace RTE { GUICheckbox *m_ShowAdvancedPerfStatsCheckbox; GUICheckbox *m_MeasureLoadTimeCheckbox; GUICheckbox *m_UseMonospaceConsoleFontCheckbox; + GUILabel *m_SceneBackgroundAutoScaleLabel; + GUISlider *m_SceneBackgroundAutoScaleSlider; + +#pragma region Misc Settings Handling + /// + /// Updates the Scene background auto-scale label according to the setting. + /// + void UpdateSceneBackgroundAutoScaleLabel(); +#pragma endregion // Disallow the use of some implicit methods. SettingsMiscGUI(const SettingsMiscGUI &reference) = delete; diff --git a/Menus/TitleScreen.cpp b/Menus/TitleScreen.cpp index 56567e586..c675e06cb 100644 --- a/Menus/TitleScreen.cpp +++ b/Menus/TitleScreen.cpp @@ -42,7 +42,6 @@ namespace RTE { m_SectionProgress = 0; m_ScrollOffset.Reset(); - m_BackdropScrollRatio = 1.0F / 3.0F; m_PlanetViewScrollOffsetY = 325 + m_PlanetRadius - (static_cast(g_FrameMan.GetResY()) / 2); m_FinishedPlayingIntro = false; @@ -70,7 +69,7 @@ namespace RTE { void TitleScreen::Create(AllegroScreen *guiScreen) { CreateTitleElements(); - m_IntroScrollStartOffsetY = (static_cast(m_Nebula.GetBitmap()->h) / m_BackdropScrollRatio) - (static_cast(g_FrameMan.GetResY()) / m_BackdropScrollRatio); + m_IntroScrollStartOffsetY = (static_cast(m_Nebula.GetBitmap()->h) / m_Nebula.GetScrollRatio().GetY()) - (static_cast(g_FrameMan.GetResY()) / m_Nebula.GetScrollRatio().GetY()); m_ScrollOffset.SetXY(0, m_IntroScrollStartOffsetY); m_StationOrbitTimer.SetElapsedRealTimeS(15); @@ -103,6 +102,7 @@ namespace RTE { m_Moon.Create(ContentFile("Base.rte/GUIs/Title/Moon.png")); m_Station.Create(ContentFile("Base.rte/GUIs/Title/Station.png")); m_Nebula.Create(ContentFile("Base.rte/GUIs/Title/Nebula.png"), false, Vector(), false, false, Vector(0, -1.0F)); + m_Nebula.SetScrollRatio(Vector(-1.0F, 1.0F / 3.0F)); set_write_alpha_blender(); draw_trans_sprite(m_PreGameLogoText.GetSpriteFrame(0), ContentFile("Base.rte/GUIs/Title/Intro/PreTitleAlpha.png").GetAsBitmap(), 0, 0); @@ -532,15 +532,14 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TitleScreen::DrawTitleScreenScene() { - Vector nebulaPos(0, m_ScrollOffset.GetY() * m_BackdropScrollRatio); - if (m_Nebula.GetBitmap()->w != g_FrameMan.GetResX()) { nebulaPos.SetX(static_cast((m_Nebula.GetBitmap()->w - g_FrameMan.GetResX()) / 2)); } Box nebulaTargetBox; - m_Nebula.Draw(g_FrameMan.GetBackBuffer32(), nebulaTargetBox, nebulaPos); + m_Nebula.SetOffset(Vector(static_cast((g_FrameMan.GetResX() - m_Nebula.GetBitmap()->w) / 2), m_ScrollOffset.GetY())); + m_Nebula.Draw(g_FrameMan.GetBackBuffer32(), nebulaTargetBox, true); for (const Star &star : m_BackdropStars) { int intensity = star.Intensity + RandomNum(0, (star.Size == Star::StarSize::StarSmall) ? 35 : 70); set_screen_blender(intensity, intensity, intensity, intensity); - int starPosY = static_cast(star.Position.GetY() - (m_ScrollOffset.GetY() * (m_BackdropScrollRatio * ((star.Size == Star::StarSize::StarSmall) ? 0.8F : 1.0F)))); + int starPosY = static_cast(star.Position.GetY() - (m_ScrollOffset.GetY() * (m_Nebula.GetScrollRatio().GetY() * ((star.Size == Star::StarSize::StarSmall) ? 0.8F : 1.0F)))); draw_trans_sprite(g_FrameMan.GetBackBuffer32(), star.Bitmap, star.Position.GetFloorIntX(), starPosY); } diff --git a/Menus/TitleScreen.h b/Menus/TitleScreen.h index d92693f1a..6c9ef6305 100644 --- a/Menus/TitleScreen.h +++ b/Menus/TitleScreen.h @@ -179,7 +179,6 @@ namespace RTE { float m_SectionProgress; //!< Progress made on a section, from 0.0 to 1.0. Vector m_ScrollOffset; //!< The scrolling position of the whole title screen scene. - float m_BackdropScrollRatio; //!< The scrolling ratio of the whole title screen scene background (nebula and stars). float m_PlanetViewScrollOffsetY; //!< The title screen scene scroll position when in planet overview menus. bool m_FinishedPlayingIntro; //!< Whether the intro sequence finished playing. diff --git a/RTEA.vcxproj b/RTEA.vcxproj index 5fa446fb0..b155fcd42 100644 --- a/RTEA.vcxproj +++ b/RTEA.vcxproj @@ -542,7 +542,9 @@ + + @@ -713,7 +715,9 @@ + + diff --git a/RTEA.vcxproj.filters b/RTEA.vcxproj.filters index 1631e1000..00cd55fb5 100644 --- a/RTEA.vcxproj.filters +++ b/RTEA.vcxproj.filters @@ -537,6 +537,12 @@ Lua + + Entities + + + Entities + @@ -1035,6 +1041,12 @@ Lua + + Entities + + + Entities + diff --git a/System/Atom.cpp b/System/Atom.cpp index 15db3907c..4d547e9ad 100644 --- a/System/Atom.cpp +++ b/System/Atom.cpp @@ -886,7 +886,7 @@ namespace RTE { if (m_Material->GetStickiness() >= RandomNum() && velocity.GetLargest() > 0.5F) { // SPLAT, so update position, apply to terrain and delete, and stop traveling m_OwnerMO->SetPos(Vector(intPos[X], intPos[Y])); - g_SceneMan.GetTerrain()->ApplyMovableObject(m_OwnerMO); + m_OwnerMO->DrawToTerrain(g_SceneMan.GetTerrain()); m_OwnerMO->SetToDelete(true); m_LastHit.Terminate[HITOR] = hit[dom] = hit[sub] = true; break; diff --git a/System/Constants.h b/System/Constants.h index b387c037d..ee7e1c99d 100644 --- a/System/Constants.h +++ b/System/Constants.h @@ -46,8 +46,31 @@ namespace RTE { g_NoMOID = 255 }; - enum DotGlowColor { NoDot = 0, YellowDot, RedDot, BlueDot }; - enum TransparencyPreset { LessTrans = 0, HalfTrans, MoreTrans }; + enum MaterialColorKeys { + g_MaterialAir = 0, + g_MaterialOutOfBounds = 1, + g_MaterialCavity = 1, + g_MaterialGold = 2, + g_MaterialSand = 8, + g_MaterialGrass = 128, + g_MaterialDoor = 181 + }; + + enum DotGlowColor { NoDot, YellowDot, RedDot, BlueDot }; + enum TransparencyPreset { LessTrans, HalfTrans, MoreTrans }; + + enum SpriteAnimMode { + NOANIM, + ALWAYSLOOP, + ALWAYSRANDOM, + ALWAYSPINGPONG, + LOOPWHENACTIVE, + LOOPWHENOPENCLOSE, + PINGPONGOPENCLOSE, + OVERLIFETIME, + ONCOLLIDE, + SpriteAnimModeCount + }; // GUI colors #define c_GUIColorWhite makecol(255, 255, 255) diff --git a/System/Entity.h b/System/Entity.h index 5386d269c..56e44ec82 100644 --- a/System/Entity.h +++ b/System/Entity.h @@ -52,19 +52,13 @@ namespace RTE { /// Whether to draw the colors, or own material property, or to clear the corresponding non-key-color pixels of the Entity being drawn with key-color pixels on the target. /// enum DrawMode { - g_DrawColor = 0, + g_DrawColor, g_DrawMaterial, - g_DrawAir, - g_DrawMask, g_DrawWhite, g_DrawMOID, g_DrawNoMOID, g_DrawDoor, - g_DrawDebug, - g_DrawLess, g_DrawTrans, - g_DrawRedTrans, - g_DrawScreen, g_DrawAlpha }; diff --git a/System/PathFinder.cpp b/System/PathFinder.cpp index c174ac075..8b47b2dce 100644 --- a/System/PathFinder.cpp +++ b/System/PathFinder.cpp @@ -1,4 +1,6 @@ #include "PathFinder.h" +#include "Scene.h" +#include "SceneMan.h" namespace RTE { @@ -180,7 +182,7 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PathFinder::RecalculateAreaCosts(const std::list &boxList) { + void PathFinder::RecalculateAreaCosts(const std::deque &boxList) { Box box; // Go through all the boxes and see if any of the node centers are inside each for (const Box &boxListEntry : boxList) { @@ -293,6 +295,12 @@ namespace RTE { } } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + float PathFinder::CostAlongLine(const Vector &start, const Vector &end) { + return g_SceneMan.CastMaxStrengthRay(start, end, 0); + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PathFinder::UpdateNodeCosts(PathNode *node) { diff --git a/System/PathFinder.h b/System/PathFinder.h index fa93115e4..870b2e4cb 100644 --- a/System/PathFinder.h +++ b/System/PathFinder.h @@ -2,13 +2,14 @@ #define _RTEPATHFINDER_ #include "Box.h" -#include "Scene.h" #include "System/MicroPather/micropather.h" using namespace micropather; namespace RTE { + class Scene; + /// /// Contains everything related to a node on the path grid used by PathFinder. /// @@ -110,10 +111,10 @@ namespace RTE { void RecalculateAllCosts(); /// - /// Recalculates the costs between all the nodes touching a list of specific rectangular areas (which will be wrapped). Also resets the pather itself. + /// Recalculates the costs between all the nodes touching a deque of specific rectangular areas (which will be wrapped). Also resets the pather itself. /// - /// The list of Boxes representing the updated areas. - void RecalculateAreaCosts(const std::list &boxList); + /// The deque of Boxes representing the updated areas. + void RecalculateAreaCosts(const std::deque &boxList); /// /// Implementation of the abstract interface of Graph. @@ -164,7 +165,7 @@ namespace RTE { /// Origin point. /// Destination point. /// The cost value. - float CostAlongLine(const Vector &start, const Vector &end) { return g_SceneMan.CastMaxStrengthRay(start, end, 0); } + float CostAlongLine(const Vector &start, const Vector &end); /// /// Helper function for updating all the values of cost edges going out from a specific node.