Index: binaries/data/config/default.cfg =================================================================== --- binaries/data/config/default.cfg +++ binaries/data/config/default.cfg @@ -61,6 +61,8 @@ ; if false, actors won't be rendered but anything entity will be. renderactors = true +maxcorpsecount = -1 ; When enabled, limits the number of corpses to the number specified. + watereffects=true ; When disabled, force usage of the fixed pipeline water. This is faster, but really, really ugly. waterfancyeffects = false waterrealdepth = true Index: binaries/data/mods/public/gui/options/options.json =================================================================== --- binaries/data/mods/public/gui/options/options.json +++ binaries/data/mods/public/gui/options/options.json @@ -592,6 +592,13 @@ "tooltip": "Units and Structures can be part of multiple control groups. This is useful to keep control groups for distinct armies and a control group for the entire army simultaneously." } ] + }, + { + "type": "number", + "label": "Max number of corpses", + "tooltip": "To save CPU workload, limits the number of corpses to display. -1 is unlimited.", + "config": "maxcorpsecount", + "min": -1 } ] } Index: binaries/data/mods/public/simulation/components/Health.js =================================================================== --- binaries/data/mods/public/simulation/components/Health.js +++ binaries/data/mods/public/simulation/components/Health.js @@ -323,7 +323,13 @@ if (resource) entCorpse = Engine.AddEntity("resource|" + templateName); else + { + let cmpUnitRenderer = Engine.QueryInterface(SYSTEM_ENTITY, IID_UnitRenderer); + if (!cmpUnitRenderer || !cmpUnitRenderer.CanAddCorpse()) + return; + entCorpse = Engine.AddLocalEntity("corpse|" + templateName); + } // Copy various parameters so it looks just like us. let cmpPositionCorpse = Engine.QueryInterface(entCorpse, IID_Position); Index: binaries/data/mods/public/simulation/components/tests/test_Health.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_Health.js +++ binaries/data/mods/public/simulation/components/tests/test_Health.js @@ -79,6 +79,12 @@ TS_ASSERT_EQUALS(cmpHealth.IsInjured(), false); TS_ASSERT_EQUALS(cmpHealth.IsUnhealable(), true); +let numberOfCorpses = 0; + +AddMock(SYSTEM_ENTITY, IID_UnitRenderer, { + "CanAddCorpse": () => { return numberOfCorpses < 1 }, +}); + // Check death. Engine.AddLocalEntity = function(template) { corpse_entity = template; @@ -98,10 +104,13 @@ return corpse_id; }; +let querySpy = new Spy(Engine, "AddLocalEntity"); change = cmpHealth.Reduce(50); - +// Fake the engine addition of the new corpse. +++numberOfCorpses; // Assert we create a corpse with the proper template. TS_ASSERT_EQUALS(corpse_entity, "corpse|test"); +TS_ASSERT_EQUALS(querySpy._called, 1); // Check that we are not marked as injured. TS_ASSERT_EQUALS(injured_flag, false); @@ -148,3 +157,10 @@ TS_ASSERT_EQUALS(cmpHealth.GetMaxHitpoints(), 50); TS_ASSERT_EQUALS(cmpHealth.IsInjured(), false); TS_ASSERT_EQUALS(cmpHealth.IsUnhealable(), true); + +// Test that a new local entity is not created if the config does not allow it. +cmpHealth = setEntityUp(); +// Kill the entity +change = cmpHealth.Reduce(80); +// It was only called the first time. +TS_ASSERT_EQUALS(querySpy._called, 1); Index: binaries/data/mods/public/simulation/templates/template_unit.xml =================================================================== --- binaries/data/mods/public/simulation/templates/template_unit.xml +++ binaries/data/mods/public/simulation/templates/template_unit.xml @@ -19,6 +19,7 @@ false + false 30.0 0.01 Index: source/renderer/RenderingOptions.h =================================================================== --- source/renderer/RenderingOptions.h +++ source/renderer/RenderingOptions.h @@ -100,6 +100,8 @@ OPTION(DisplayFrustum, bool); OPTION(DisplayShadowsFrustum, bool); + OPTION(MaxCorpseCount, int); + OPTION(RenderActors, bool); #undef OPTION_DEFAULT_SETTER Index: source/renderer/RenderingOptions.cpp =================================================================== --- source/renderer/RenderingOptions.cpp +++ source/renderer/RenderingOptions.cpp @@ -79,6 +79,7 @@ m_DisplayFrustum = false; m_DisplayShadowsFrustum = false; m_RenderActors = true; + m_MaxCorpseCount = -1; } void SRenderingOptions::ReadConfig() @@ -111,6 +112,8 @@ CFG_GET_VAL("forcealphatest", m_ForceAlphaTest); CFG_GET_VAL("gpuskinning", m_GPUSkinning); + CFG_GET_VAL("maxcorpsecount", m_MaxCorpseCount); + CFG_GET_VAL("renderactors", m_RenderActors); } Index: source/simulation2/components/CCmpDecay.cpp =================================================================== --- source/simulation2/components/CCmpDecay.cpp +++ source/simulation2/components/CCmpDecay.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2015 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -24,6 +24,7 @@ #include "ICmpPosition.h" #include "ICmpTerrain.h" +#include "ICmpUnitRenderer.h" #include "ICmpVisual.h" #include "ps/Profile.h" @@ -55,6 +56,7 @@ bool m_Active; bool m_ShipSink; + bool m_CreatesCorpse; float m_DelayTime; float m_SinkRate; float m_SinkAccel; @@ -75,6 +77,11 @@ "" "" "" + "" + "" + "" + "" + "" "" "" "" @@ -93,6 +100,7 @@ { m_Active = paramNode.GetChild("Active").ToBool(); m_ShipSink = paramNode.GetChild("SinkingAnim").ToBool(); + m_CreatesCorpse = paramNode.GetChild("CreatesCorpse").IsOk(); m_DelayTime = paramNode.GetChild("DelayTime").ToFixed().ToFloat(); m_SinkRate = paramNode.GetChild("SinkRate").ToFixed().ToFloat(); m_SinkAccel = paramNode.GetChild("SinkAccel").ToFixed().ToFloat(); @@ -109,10 +117,24 @@ if (m_Active) GetSimContext().GetComponentManager().DynamicSubscriptionNonsync(MT_Interpolate, this, true); + + if (m_Active && m_CreatesCorpse) + { + CmpPtr cmpUnitRenderer(GetSystemEntity()); + if (cmpUnitRenderer) + cmpUnitRenderer->AddCorpse(); + } } virtual void Deinit() { + if (m_Active && m_CreatesCorpse) + { + CmpPtr cmpUnitRenderer(GetSystemEntity()); + if (cmpUnitRenderer) + cmpUnitRenderer->RemoveCorpse(); + } + } virtual void Serialize(ISerializer& UNUSED(serialize)) Index: source/simulation2/components/CCmpUnitRenderer.cpp =================================================================== --- source/simulation2/components/CCmpUnitRenderer.cpp +++ source/simulation2/components/CCmpUnitRenderer.cpp @@ -119,6 +119,7 @@ std::vector m_UnitTagsFree; int m_FrameNumber; + int m_CorpseCount; float m_FrameOffset; bool m_EnableDebugOverlays; @@ -189,6 +190,21 @@ return &m_Units[tag.n - 1]; } + virtual void AddCorpse() + { + ++m_CorpseCount; + } + virtual void RemoveCorpse() + { + --m_CorpseCount; + } + + virtual bool CanAddCorpse() + { + int maxCorpses = g_RenderingOptions.GetMaxCorpseCount(); + return maxCorpses == -1 || m_CorpseCount < maxCorpses; + } + virtual tag_t AddUnit(CEntityHandle entity, CUnit* actor, const CBoundingSphere& boundsApprox, int flags) { ENSURE(actor != NULL); Index: source/simulation2/components/ICmpUnitRenderer.h =================================================================== --- source/simulation2/components/ICmpUnitRenderer.h +++ source/simulation2/components/ICmpUnitRenderer.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -49,7 +49,9 @@ virtual tag_t AddUnit(CEntityHandle entity, CUnit* unit, const CBoundingSphere& boundsApprox, int flags) = 0; virtual void RemoveUnit(tag_t tag) = 0; - + virtual void AddCorpse() = 0; + virtual void RemoveCorpse() = 0; + virtual bool CanAddCorpse() = 0; virtual void UpdateUnit(tag_t tag, CUnit* unit, const CBoundingSphere& boundsApprox) = 0; virtual void UpdateUnitPos(tag_t tag, bool inWorld, const CVector3D& pos0, const CVector3D& pos1) = 0; Index: source/simulation2/components/ICmpUnitRenderer.cpp =================================================================== --- source/simulation2/components/ICmpUnitRenderer.cpp +++ source/simulation2/components/ICmpUnitRenderer.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2014 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -23,4 +23,5 @@ BEGIN_INTERFACE_WRAPPER(UnitRenderer) DEFINE_INTERFACE_METHOD_1("SetDebugOverlay", void, ICmpUnitRenderer, SetDebugOverlay, bool) +DEFINE_INTERFACE_METHOD_0("CanAddCorpse", bool, ICmpUnitRenderer, CanAddCorpse) END_INTERFACE_WRAPPER(UnitRenderer)