Skip to content

Commit

Permalink
Feature: register new lua functions DrawBuildSquare and `DrawBuildS…
Browse files Browse the repository at this point in the history
…quareArray` to show build squares on custom building commands
  • Loading branch information
Aurelien Lambert committed Oct 27, 2024
1 parent 19b0efc commit fdd3e11
Show file tree
Hide file tree
Showing 5 changed files with 321 additions and 1 deletion.
114 changes: 114 additions & 0 deletions rts/Lua/LuaUnsyncedCtrl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ bool LuaUnsyncedCtrl::PushEntries(lua_State* L)
REGISTER_LUA_CFUNC(AddWorldUnit);

REGISTER_LUA_CFUNC(DrawUnitCommands);
REGISTER_LUA_CFUNC(DrawBuildSquare);
REGISTER_LUA_CFUNC(DrawBuildSquareArray);

REGISTER_LUA_CFUNC(SetTeamColor);

Expand Down Expand Up @@ -3382,6 +3384,118 @@ int LuaUnsyncedCtrl::SetBuildFacing(lua_State* L)
}


static bool ParseBuildSquareOptions(
lua_State* L,
LuaBuildSquareOptions& opts,
const char* caller,
const int idx
) {
if (lua_istable(L, idx)) {
for (lua_pushnil(L); lua_next(L, idx) != 0; lua_pop(L, 1)) {
// "key" = value (table format of CommandNotify)
// ignore the "coded" key; not a boolean value
if (lua_israwstring(L, -2)) {
if (lua_isboolean(L, -1)) {
const bool value = lua_toboolean(L, -1);
switch (hashString(lua_tostring(L, -2))) {
case hashString("unbuildable"):
opts.unbuildable = value;
break;
}
} else if (lua_istable(L, -1)) {
float color[4];
LuaUtils::ParseFloatArray(L, -1, &color[0], 4);
switch (hashString(lua_tostring(L, -2))) {
case hashString("defaultColor"):
opts.defaultColor = &color[0];
break;
case hashString("buildableColor"):
opts.buildableColor = &color[0];
break;
case hashString("unbuildableColor"):
opts.unbuildableColor = &color[0];
break;
case hashString("featureColor"):
opts.featureColor = &color[0];
break;
case hashString("illegalColor"):
opts.illegalColor = &color[0];
break;
}
}
}
}
} else if (!lua_isnil(L, idx)) {
luaL_error(L, "%s(): bad options-argument type", caller);
return false;
}
return true;
}


/***
*
* @function Spring.DrawBuildSquare
* @tparam cmdSpec cmd
* @bool[opt] unbuildable
* @tparam[opt] rgb buildableColor
* @tparam[opt] rgb unbuildableColor
* @tparam[opt] rgb featureColor
* @tparam[opt] rgb illegalColor
* @treturn nil
*/
int LuaUnsyncedCtrl::DrawBuildSquare(lua_State* L)
{
const int args = lua_gettop(L); // number of arguments

Command cmd = LuaUtils::ParseCommand(L, __func__, 1);

LuaBuildSquareOptions opts;
if (args >= 2) {
ParseBuildSquareOptions(L, opts, __func__, 2);
}

if (cmd.GetID() < 0) {
unitDrawer->AddLuaBuildSquare(BuildInfo(cmd), opts);
};

return 0;
}


/***
*
* @function Spring.DrawBuildSquareArray
* @tparam {cmdSpec,...} cmdArray
* @bool[opt] unbuildable
* @tparam[opt] rgb buildableColor
* @tparam[opt] rgb unbuildableColor
* @tparam[opt] rgb featureColor
* @tparam[opt] rgb illegalColor
* @treturn nil
*/
int LuaUnsyncedCtrl::DrawBuildSquareArray(lua_State* L)
{
const int args = lua_gettop(L); // number of arguments

vector<Command> commands;
LuaUtils::ParseCommandArray(L, __func__, 1, commands);

LuaBuildSquareOptions opts;
if (args >= 2) {
ParseBuildSquareOptions(L, opts, __func__, 2);
}

for (Command cmd: commands) {
if (cmd.GetID() < 0) {
unitDrawer->AddLuaBuildSquare(BuildInfo(cmd), opts);
};
}

return 0;
}


/******************************************************************************
* UI
* @section ui
Expand Down
2 changes: 2 additions & 0 deletions rts/Lua/LuaUnsyncedCtrl.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ class LuaUnsyncedCtrl {
static int AddWorldUnit(lua_State* L);

static int DrawUnitCommands(lua_State* L);
static int DrawBuildSquare(lua_State* L);
static int DrawBuildSquareArray(lua_State* L);

static int SetTeamColor(lua_State* L);

Expand Down
184 changes: 183 additions & 1 deletion rts/Rendering/Units/UnitDrawer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "Sim/Units/UnitHandler.h"

#include "System/EventHandler.h"
#include "System/Color.h"
#include "System/Config/ConfigHandler.h"
//#include "System/FileSystem/FileHandler.h"

Expand Down Expand Up @@ -1183,7 +1184,6 @@ void CUnitDrawerLegacy::DrawIndividualDefAlpha(const SolidObjectDef* objectDef,
bool CUnitDrawerLegacy::ShowUnitBuildSquare(const BuildInfo& buildInfo, const std::vector<Command>& 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);
Expand Down Expand Up @@ -1333,6 +1333,188 @@ bool CUnitDrawerLegacy::ShowUnitBuildSquare(const BuildInfo& buildInfo, const st
return canBuild;
}

static inline bool IsZeroColor(SColor color) {
return color.r == 0 && color.g == 0 && color.b == 0 && color.a == 0;
}

void CUnitDrawerLegacy::AddLuaBuildSquare(const BuildInfo& buildInfo, LuaBuildSquareOptions& opts) {
CFeature* feature = nullptr;

struct BuildCacheSquare {
float3 pos;
int state; // 0 = buildable, 1 = feature, 2 = illegal
};

struct BuildCache {
uint64_t key;
int createFrame;
bool canBuild;
std::vector<BuildCacheSquare> squares;
};

static std::vector<BuildCache> buildCache;

const float3& pos = buildInfo.pos;

uint64_t hashKey = spring::LiteHash(buildInfo.def->id);
hashKey = spring::hash_combine(spring::LiteHash(pos), hashKey);
hashKey = spring::hash_combine(spring::LiteHash(buildInfo.buildFacing), hashKey);

// 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);

if (h < 0.0f) {
luaBuildPos.push_back({x1, h, z1});
luaBuildPos.push_back({x2, h, z2});
}

std::vector<float3> squares[3];
static std::vector<Command> emptyCommands;

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,
&squares[0],
&squares[1],
&squares[2],
&emptyCommands
);
size_t size = 0;
for (int index=0, state=0; state<3; state++) {
size += squares[state].size();
}
buildCacheItem->squares.resize(size);
for (int index=0, state=0; state<3; state++) {
for (float3 pos: squares[state]) {
buildCacheItem->squares[index] = BuildCacheSquare{
pos: pos,
state: state,
};
index++;
}
}
}

static constexpr SColor nullColor(0, 0, 0, 0);
static constexpr SColor buildColorT(0.0f, 0.9f, 0.0f, 0.7f);
static constexpr SColor buildColorF(0.9f, 0.8f, 0.0f, 0.7f);
static constexpr SColor featureColor(0.9f, 0.8f, 0.0f, 0.7f);
static constexpr SColor illegalColor(0.9f, 0.0f, 0.0f, 0.7f);

bool canBuild = buildCacheItem->canBuild;
SColor stateColors[3];
if (buildCacheItem->canBuild && !opts.unbuildable) {
stateColors[0] =
!IsZeroColor(opts.buildableColor) ? opts.buildableColor :
!IsZeroColor(opts.defaultColor) ? opts.defaultColor :
buildColorT;
} else {
stateColors[0] =
!IsZeroColor(opts.unbuildableColor) ? opts.unbuildableColor :
!IsZeroColor(opts.defaultColor) ? opts.defaultColor :
buildColorF;
}
stateColors[1] =
!IsZeroColor(opts.featureColor) ? opts.featureColor :
!IsZeroColor(opts.defaultColor) ? opts.defaultColor :
featureColor;
stateColors[2] =
!IsZeroColor(opts.illegalColor) ? opts.illegalColor :
!IsZeroColor(opts.defaultColor) ? opts.defaultColor :
illegalColor;

for (auto square: buildCacheItem->squares) {
luaBuildSquares.push_back(LuaBuildSquare{
pos: square.pos,
color: stateColors[square.state],
});
}
}

void CUnitDrawerLegacy::ShowLuaBuildSquare() {
if (luaBuildSquares.empty() && luaBuildPos.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 auto& rb = RenderBuffer::GetTypedRenderBuffer<VA_TYPE_C>();
rb.AssertSubmission();

auto& sh = rb.GetShader();

sh.Enable();

for (const auto &square: luaBuildSquares) {
rb.AddQuadLines(
{ square.pos , square.color },
{ square.pos + float3(SQUARE_SIZE, 0, 0 ), square.color },
{ square.pos + float3(SQUARE_SIZE, 0, SQUARE_SIZE), square.color },
{ square.pos + float3(0 , 0, SQUARE_SIZE), square.color }
);
}
rb.Submit(GL_LINES);

static constexpr SColor startColor(0, 0, 255, 128);
static constexpr SColor endColor(0, 128, 255, 255);

for (int i=0; i<luaBuildPos.size(); i+=2) {
float3 pos1 = luaBuildPos[i];
float3 pos2 = luaBuildPos[i+1];
float x1 = pos1.x, x2 = pos2.x;
float z1 = pos1.z, z2 = pos2.z;
float h = pos1.y;

rb.AddVertex({ float3(x1, h, z1), startColor }); rb.AddVertex({ float3(x1, 0.f, z1), endColor });
rb.AddVertex({ float3(x1, h, z2), startColor }); rb.AddVertex({ float3(x1, 0.f, z2), endColor });
rb.AddVertex({ float3(x2, h, z2), startColor }); rb.AddVertex({ float3(x2, 0.f, z2), endColor });
rb.AddVertex({ float3(x2, h, z1), startColor }); rb.AddVertex({ float3(x2, 0.f, z1), endColor });
rb.Submit(GL_LINES);

rb.AddVertex({ float3(x1, 0.0f, z1), endColor });
rb.AddVertex({ float3(x1, 0.0f, z2), endColor });
rb.AddVertex({ float3(x2, 0.0f, z2), endColor });
rb.AddVertex({ float3(x2, 0.0f, z1), endColor });
rb.Submit(GL_LINE_LOOP);
}

sh.Disable();


glEnable(GL_DEPTH_TEST);
//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
// glDisable(GL_BLEND);

luaBuildSquares.clear();
luaBuildPos.clear();
}

void CUnitDrawerLegacy::DrawBuildIcons(const std::vector<CCursorIcons::BuildIcon>& buildIcons) const
{
RECOIL_DETAILED_TRACY_ZONE;
Expand Down
21 changes: 21 additions & 0 deletions rts/Rendering/Units/UnitDrawer.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
#include "Rendering/Units/UnitDrawerData.h"
#include "Rendering/GL/LightHandler.h"
#include "Game/UI/CursorIcons.h"
#include "System/Color.h"
#include "System/type2.h"
#include "Sim/Units/CommandAI/Command.h"

class CSolidObject;
class CUnit;
struct S3DModel;
struct SolidObjectDef;
struct LuaBuildSquareOptions;

namespace Shader { struct IProgramObject; }

Expand Down Expand Up @@ -77,6 +79,9 @@ class CUnitDrawer : public CModelDrawerBase<CUnitDrawerData, CUnitDrawer>
bool ShowUnitBuildSquare(const BuildInfo& buildInfo) const { return ShowUnitBuildSquare(buildInfo, std::vector<Command>()); }
virtual bool ShowUnitBuildSquare(const BuildInfo& buildInfo, const std::vector<Command>& commands) const = 0;

virtual void AddLuaBuildSquare(const BuildInfo& buildInfo, LuaBuildSquareOptions& opts) = 0;
virtual void ShowLuaBuildSquare() = 0;

virtual void DrawBuildIcons(const std::vector<CCursorIcons::BuildIcon>& buildIcons) const = 0;
protected:
static bool ShouldDrawOpaqueUnit(CUnit* u, uint8_t thisPassMask);
Expand Down Expand Up @@ -144,6 +149,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<Command>& commands) const override;
void AddLuaBuildSquare(const BuildInfo& buildInfo, LuaBuildSquareOptions& opts) override;
void ShowLuaBuildSquare() override;
void DrawBuildIcons(const std::vector<CCursorIcons::BuildIcon>& buildIcons) const override;

void DrawUnitMiniMapIcons() const override;
Expand Down Expand Up @@ -185,7 +192,21 @@ 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:
struct LuaBuildSquare {
float3 pos;
SColor color;
};
std::vector<LuaBuildSquare> luaBuildSquares;
std::vector<float3> luaBuildPos;
};

struct LuaBuildSquareOptions {
bool unbuildable = false;
SColor defaultColor = SColor::Zero;
SColor buildableColor = SColor::Zero;
SColor unbuildableColor = SColor::Zero;
SColor featureColor = SColor::Zero;
SColor illegalColor = SColor::Zero;
};

class CUnitDrawerFFP final : public CUnitDrawerLegacy {};
Expand Down
Loading

0 comments on commit fdd3e11

Please sign in to comment.