diff --git a/rts/Lua/LuaUnsyncedCtrl.cpp b/rts/Lua/LuaUnsyncedCtrl.cpp index 125838451c..90ed4652fe 100644 --- a/rts/Lua/LuaUnsyncedCtrl.cpp +++ b/rts/Lua/LuaUnsyncedCtrl.cpp @@ -171,6 +171,7 @@ bool LuaUnsyncedCtrl::PushEntries(lua_State* L) REGISTER_LUA_CFUNC(AddWorldUnit); REGISTER_LUA_CFUNC(DrawUnitCommands); + REGISTER_LUA_CFUNC(DrawBuildSquare); REGISTER_LUA_CFUNC(SetTeamColor); @@ -3382,6 +3383,54 @@ int LuaUnsyncedCtrl::SetBuildFacing(lua_State* L) } +/*** + * + * @function Spring.DrawBuildSquare + * @tparam {number,...} unitArray + * @tparam {cmdSpec,...} cmdArray + * @treturn {bool,...} canBuild + */ +int LuaUnsyncedCtrl::DrawBuildSquare(lua_State* L) +{ + vector unitIDs; + ParseUnitArray(L, __func__, 1, unitIDs); + + vector commands; + LuaUtils::ParseCommandArray(L, __func__, 2, commands); + + vector canBuildVector(commands.size()); + + bool isQueued = !commands.empty() && (commands[0].GetOpts() & SHIFT_KEY); + vector conflicting; + + for (int i=0; i= 0) continue; + + if (isQueued) { + conflicting.clear(); + for (const int unitID: unitIDs) { + const CUnit* su = unitHandler.GetUnit(unitID); + const CCommandAI* cai = su->commandAI; + vector overlap = cai->GetOverlapQueued(cmd); + conflicting.insert(conflicting.end(), overlap.begin(), overlap.end()); + } + } + + canBuildVector[i] = unitDrawer->AddLuaBuildSquare(BuildInfo(cmd), conflicting); + } + + lua_createtable(L, canBuildVector.size(), 0); + for (int i=0; i& commands) const { RECOIL_DETAILED_TRACY_ZONE; - //TODO: make this a lua callin! glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -1333,6 +1332,192 @@ bool CUnitDrawerLegacy::ShowUnitBuildSquare(const BuildInfo& buildInfo, const st return canBuild; } +bool CUnitDrawerLegacy::AddLuaBuildSquare(const BuildInfo& buildInfo, const std::vector& commands) { + CFeature* feature = nullptr; + + struct BuildCache { + uint64_t key; + int createFrame; + bool canBuild; + std::vector buildableSquares; // buildable squares + std::vector featureSquares; // occupied squares + std::vector illegalSquares; // non-buildable squares + }; + + static std::vector buildCache; + + const float3& pos = buildInfo.pos; + + uint64_t hashKey = spring::LiteHash(pos); + hashKey = spring::hash_combine(spring::LiteHash(buildInfo.buildFacing), hashKey); + /* + for (const auto& cmd : commands) { + const BuildInfo bc(cmd); + spring::hash_combine(spring::LiteHash(bc), hash); + } + */ + + // the chosen number here is arbitrary, feel free to fine balance. + static constexpr int CACHE_VALIDITY_PERIOD = GAME_SPEED / 5; + std::erase_if(buildCache, [](const BuildCache& bc) { + return gs->frameNum - bc.createFrame >= CACHE_VALIDITY_PERIOD; + }); + + const float x1 = std::floor(pos.x - (buildInfo.GetXSize() * 0.5f * SQUARE_SIZE)); + const float x2 = x1 + (buildInfo.GetXSize() * SQUARE_SIZE); + const float z1 = std::floor(pos.z - (buildInfo.GetZSize() * 0.5f * SQUARE_SIZE)); + const float z2 = z1 + (buildInfo.GetZSize() * SQUARE_SIZE); + const float h = CGameHelper::GetBuildHeight(pos, buildInfo.def, false); + + luaBuildPos.push_back(float3{x1, h, z1}); + luaBuildPos.push_back(float3{x2, h, z2}); + + auto buildCacheItem = std::find_if(buildCache.begin(), buildCache.end(), [hashKey](const BuildCache& bc) { + return bc.key == hashKey; + }); + + if (buildCacheItem == buildCache.end()) { + buildCache.emplace_back(); + buildCacheItem = buildCache.end()-1; + + buildCacheItem->key = hashKey; + buildCacheItem->createFrame = gs->frameNum; + buildCacheItem->canBuild = CGameHelper::TestUnitBuildSquare( + buildInfo, + feature, + -1, + false, + &buildCacheItem->buildableSquares, + &buildCacheItem->featureSquares, + &buildCacheItem->illegalSquares, + &commands + ); + } + + bool canBuild = buildCacheItem->canBuild; + if (canBuild) { + luaBuildableSquares.insert(luaBuildableSquares.end(), + buildCacheItem->buildableSquares.begin(), + buildCacheItem->buildableSquares.end()); + } else { + luaUnbuildableSquares.insert(luaUnbuildableSquares.end(), + buildCacheItem->buildableSquares.begin(), + buildCacheItem->buildableSquares.end()); + } + luaFeatureSquares.insert(luaFeatureSquares.end(), + buildCacheItem->featureSquares.begin(), + buildCacheItem->featureSquares.end()); + luaIllegalSquares.insert(luaIllegalSquares.end(), + buildCacheItem->illegalSquares.begin(), + buildCacheItem->illegalSquares.end()); + + return canBuild; +} + +void CUnitDrawerLegacy::ShowLuaBuildSquare() { + if (luaBuildableSquares.empty() && luaUnbuildableSquares.empty() && luaFeatureSquares.empty() && luaIllegalSquares.empty()) { + return; + } + + RECOIL_DETAILED_TRACY_ZONE; + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_TEXTURE_2D); + //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + + static constexpr std::array buildColorT = { 0.0f, 0.9f, 0.0f, 0.7f }; + static constexpr std::array buildColorF = { 0.9f, 0.8f, 0.0f, 0.7f }; + static constexpr std::array featureColor = { 0.9f, 0.8f, 0.0f, 0.7f }; + static constexpr std::array illegalColor = { 0.9f, 0.0f, 0.0f, 0.7f }; + + static auto& rb = RenderBuffer::GetTypedRenderBuffer(); + rb.AssertSubmission(); + + auto& sh = rb.GetShader(); + + sh.Enable(); + + const float* color = &buildColorT[0]; + for (const auto& buildableSquare : luaBuildableSquares) { + rb.AddQuadLines( + { buildableSquare , color }, + { buildableSquare + float3(SQUARE_SIZE, 0, 0 ), color }, + { buildableSquare + float3(SQUARE_SIZE, 0, SQUARE_SIZE), color }, + { buildableSquare + float3(0 , 0, SQUARE_SIZE), color } + ); + } + + color = &buildColorF[0]; + for (const auto& unbuildableSquare : luaUnbuildableSquares) { + rb.AddQuadLines( + { unbuildableSquare , color }, + { unbuildableSquare + float3(SQUARE_SIZE, 0, 0 ), color }, + { unbuildableSquare + float3(SQUARE_SIZE, 0, SQUARE_SIZE), color }, + { unbuildableSquare + float3(0 , 0, SQUARE_SIZE), color } + ); + } + + color = &featureColor[0]; + for (const auto& featureSquare : luaFeatureSquares) { + rb.AddQuadLines( + { featureSquare , color }, + { featureSquare + float3(SQUARE_SIZE, 0, 0 ), color }, + { featureSquare + float3(SQUARE_SIZE, 0, SQUARE_SIZE), color }, + { featureSquare + float3(0 , 0, SQUARE_SIZE), color } + ); + } + + color = &illegalColor[0]; + for (const auto& illegalSquare : luaIllegalSquares) { + rb.AddQuadLines( + { illegalSquare , color }, + { illegalSquare + float3(SQUARE_SIZE, 0, 0 ), color }, + { illegalSquare + float3(SQUARE_SIZE, 0, SQUARE_SIZE), color }, + { illegalSquare + float3(0 , 0, SQUARE_SIZE), color } + ); + } + rb.Submit(GL_LINES); + + for (int i=0; i& buildIcons) const { RECOIL_DETAILED_TRACY_ZONE; diff --git a/rts/Rendering/Units/UnitDrawer.h b/rts/Rendering/Units/UnitDrawer.h index c38350e694..860e4b6681 100644 --- a/rts/Rendering/Units/UnitDrawer.h +++ b/rts/Rendering/Units/UnitDrawer.h @@ -77,6 +77,9 @@ class CUnitDrawer : public CModelDrawerBase bool ShowUnitBuildSquare(const BuildInfo& buildInfo) const { return ShowUnitBuildSquare(buildInfo, std::vector()); } virtual bool ShowUnitBuildSquare(const BuildInfo& buildInfo, const std::vector& commands) const = 0; + virtual bool AddLuaBuildSquare(const BuildInfo& buildInfo, const std::vector& commands) = 0; + virtual void ShowLuaBuildSquare() = 0; + virtual void DrawBuildIcons(const std::vector& buildIcons) const = 0; protected: static bool ShouldDrawOpaqueUnit(CUnit* u, uint8_t thisPassMask); @@ -144,6 +147,8 @@ class CUnitDrawerLegacy : public CUnitDrawerBase { void DrawIndividualDefAlpha(const SolidObjectDef* objectDef, int teamID, bool rawState, bool toScreen = false) const override; bool ShowUnitBuildSquare(const BuildInfo& buildInfo, const std::vector& commands) const override; + bool AddLuaBuildSquare(const BuildInfo& buildInfo, const std::vector& commands) override; + void ShowLuaBuildSquare() override; void DrawBuildIcons(const std::vector& buildIcons) const override; void DrawUnitMiniMapIcons() const override; @@ -185,7 +190,11 @@ class CUnitDrawerLegacy : public CUnitDrawerBase { void PopIndividualOpaqueState(const S3DModel* model, int teamID, bool deferredPass) const; void PopIndividualAlphaState(const S3DModel* model, int teamID, bool deferredPass) const; protected: - + std::vector luaBuildableSquares; + std::vector luaUnbuildableSquares; + std::vector luaFeatureSquares; + std::vector luaIllegalSquares; + std::vector luaBuildPos; }; class CUnitDrawerFFP final : public CUnitDrawerLegacy {}; diff --git a/rts/Rendering/WorldDrawer.cpp b/rts/Rendering/WorldDrawer.cpp index ea01c6ef78..deb81cc632 100644 --- a/rts/Rendering/WorldDrawer.cpp +++ b/rts/Rendering/WorldDrawer.cpp @@ -462,6 +462,7 @@ void CWorldDrawer::DrawMiscObjects() const mouse->DrawSelectionBox(); guihandler->DrawMapStuff(false); + unitDrawer->ShowLuaBuildSquare(); if (globalRendering->drawMapMarks && !game->hideInterface) { inMapDrawerView->Draw();