Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix static ghost behaviours #1831

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions rts/Lua/LuaSyncedCtrl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "Rendering/Env/GrassDrawer.h"
#include "Rendering/Env/IGroundDecalDrawer.h"
#include "Rendering/Models/IModelParser.h"
#include "Rendering/Units/UnitDrawer.h"
#include "Sim/Features/Feature.h"
#include "Sim/Features/FeatureDef.h"
#include "Sim/Features/FeatureDefHandler.h"
Expand Down Expand Up @@ -181,6 +182,7 @@ bool LuaSyncedCtrl::PushEntries(lua_State* L)
REGISTER_LUA_CFUNC(SetUnitStealth);
REGISTER_LUA_CFUNC(SetUnitSonarStealth);
REGISTER_LUA_CFUNC(SetUnitSeismicSignature);
REGISTER_LUA_CFUNC(SetUnitLeavesGhost);
REGISTER_LUA_CFUNC(SetUnitAlwaysVisible);
REGISTER_LUA_CFUNC(SetUnitUseAirLos);
REGISTER_LUA_CFUNC(SetUnitMetalExtraction);
Expand Down Expand Up @@ -2684,6 +2686,35 @@ int LuaSyncedCtrl::SetUnitSeismicSignature(lua_State* L)
return 0;
}

/***
* @function Spring.SetUnitLeavesGhost
*
* Change the unit leavesGhost attribute.
*
* Controls unit having static radar ghosts.
*
* @number unitID
* @bool leavesGhost
* @bool[opt] leaveDeadGhost leave a dead ghost behind if disabling and the unit had a live static ghost.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good if leaving a dead ghost was a standalone function. That way SetUnitLeavesGhost is atomic and you can leave dead ghosts around in other circumstances at will.

* @treturn nil
*/
int LuaSyncedCtrl::SetUnitLeavesGhost(lua_State* L)
{
if (!gameSetup->ghostedBuildings)
return 0;

CUnit* unit = ParseUnit(L, __func__, 1);

if (unit == nullptr)
return 0;

bool prevValue = unit->leavesGhost;
unit->SetLeavesGhost(luaL_checkboolean(L, 2));
if (prevValue != unit->leavesGhost)
unitDrawer->UnitLeavesGhostChanged(unit, luaL_optboolean(L, 3, false));
Comment on lines +2713 to +2714
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two lines should probably be part of unit->SetLeavesGhost since otherwise unit drawer can fail to be notified of a change.

return 0;
}

/***
* @function Spring.SetUnitAlwaysVisible
* @number unitID
Expand Down
1 change: 1 addition & 0 deletions rts/Lua/LuaSyncedCtrl.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class LuaSyncedCtrl
static int SetUnitStealth(lua_State* L);
static int SetUnitSonarStealth(lua_State* L);
static int SetUnitSeismicSignature(lua_State* L);
static int SetUnitLeavesGhost(lua_State* L);
static int SetUnitAlwaysVisible(lua_State* L);
static int SetUnitUseAirLos(lua_State* L);
static int SetUnitResourcing(lua_State* L);
Expand Down
17 changes: 17 additions & 0 deletions rts/Lua/LuaSyncedRead.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ bool LuaSyncedRead::PushEntries(lua_State* L)
REGISTER_LUA_CFUNC(GetUnitArmored);
REGISTER_LUA_CFUNC(GetUnitIsActive);
REGISTER_LUA_CFUNC(GetUnitIsCloaked);
REGISTER_LUA_CFUNC(GetUnitLeavesGhost);
REGISTER_LUA_CFUNC(GetUnitSelfDTime);
REGISTER_LUA_CFUNC(GetUnitStockpile);
REGISTER_LUA_CFUNC(GetUnitSensorRadius);
Expand Down Expand Up @@ -3850,6 +3851,22 @@ int LuaSyncedRead::GetUnitSeismicSignature(lua_State* L)
return 1;
}

/***
*
* @function Spring.GetUnitLeavesGhost
* @number unitID
* @treturn nil|number
*/
int LuaSyncedRead::GetUnitLeavesGhost(lua_State* L)
{
const CUnit* const unit = ParseAllyUnit(L, __func__, 1);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably be allowed for enemy units too, since that is whose ghosts you're interested in.

if (unit == nullptr)
return 0;

lua_pushboolean(L, unit->leavesGhost);
return 1;
}

/***
*
* @function Spring.GetUnitSelfDTime
Expand Down
1 change: 1 addition & 0 deletions rts/Lua/LuaSyncedRead.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ class LuaSyncedRead {
static int GetUnitIsActive(lua_State* L);
static int GetUnitIsCloaked(lua_State* L);
static int GetUnitSeismicSignature(lua_State* L);
static int GetUnitLeavesGhost(lua_State* L);
static int GetUnitSelfDTime(lua_State* L);
static int GetUnitStockpile(lua_State* L);
static int GetUnitSensorRadius(lua_State* L);
Expand Down
1 change: 1 addition & 0 deletions rts/Lua/LuaUnitDefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,7 @@ ADD_BOOL("canAttackWater", canAttackWater); // CUSTOM
ADD_FLOAT("seismicSignature", ud.seismicSignature);
ADD_BOOL("stealth", ud.stealth);
ADD_BOOL("sonarStealth", ud.sonarStealth);
ADD_BOOL("leavesGhost", ud.leavesGhost);

ADD_FLOAT("mass", ud.mass);

Expand Down
1 change: 1 addition & 0 deletions rts/Rendering/Units/UnitDrawer.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class CUnitDrawer : public CModelDrawerBase<CUnitDrawerData, CUnitDrawer>
static const std::vector<CUnit*>& GetUnsortedUnits() { return modelDrawerData->GetUnsortedObjects(); }

static void ClearPreviousDrawFlags() { modelDrawerData->ClearPreviousDrawFlags(); }
static void UnitLeavesGhostChanged(const CUnit* unit, const bool leaveDeadGhost) { modelDrawerData->UnitLeavesGhostChanged(unit, leaveDeadGhost); }
public:
// DrawUnit*
virtual void DrawUnitNoTrans(const CUnit* unit, uint32_t preList, uint32_t postList, bool lodCall, bool noLuaCall) const = 0;
Expand Down
39 changes: 31 additions & 8 deletions rts/Rendering/Units/UnitDrawerData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ const icon::CIconData* CUnitDrawerData::GetUnitIcon(const CUnit* unit)
// use the unit's custom icon if we can currently see it,
// or have seen it before and did not lose contact since
bool unitVisible = ((losStatus & (LOS_INLOS | LOS_INRADAR)) && ((losStatus & prevMask) == prevMask));
unitVisible |= gameSetup->ghostedBuildings && unit->unitDef->IsBuildingUnit() && (losStatus & LOS_PREVLOS);
unitVisible |= unit->leavesGhost && (losStatus & LOS_PREVLOS);
const bool customIcon = (unitVisible || gu->spectatingFullView);

if (customIcon)
Expand Down Expand Up @@ -577,16 +577,17 @@ void CUnitDrawerData::RenderUnitCreated(const CUnit* unit, int cloaked)
UpdateUnitIcon(unit, false, false);
}

void CUnitDrawerData::RenderUnitDestroyed(const CUnit* unit)
bool CUnitDrawerData::UpdateUnitGhosts(const CUnit* unit, const bool addNewGhost)
{
RECOIL_DETAILED_TRACY_ZONE;
if (!gameSetup->ghostedBuildings)
return false;

bool addedOwnAllyTeam = false;
CUnit* u = const_cast<CUnit*>(unit);

const UnitDef* unitDef = unit->unitDef;
const UnitDef* decoyDef = unitDef->decoyDef;

const bool addNewGhost = unitDef->IsBuildingUnit() && gameSetup->ghostedBuildings;

// TODO - make ghosted buildings per allyTeam - so they are correctly dealt with
// when spectating
GhostSolidObject* gso = nullptr;
Expand Down Expand Up @@ -615,10 +616,23 @@ void CUnitDrawerData::RenderUnitDestroyed(const CUnit* unit)
// (the ref-counter saves us come deletion time)
savedData.deadGhostBuildings[allyTeam][gsoModel->type].push_back(gso);
gso->IncRef();
u->losStatus[allyTeam] &= ~LOS_PREVLOS;
if (allyTeam == gu->myAllyTeam)
addedOwnAllyTeam = true;

}

spring::VectorErase(savedData.liveGhostBuildings[allyTeam][MDL_TYPE(u)], u);
}
return addedOwnAllyTeam;
}

void CUnitDrawerData::RenderUnitDestroyed(const CUnit* unit)
{
RECOIL_DETAILED_TRACY_ZONE;
CUnit* u = const_cast<CUnit*>(unit);

UpdateUnitGhosts(unit, unit->leavesGhost);

DelObject(unit, true);
UpdateUnitIcon(unit, false, true);
Expand All @@ -640,7 +654,7 @@ void CUnitDrawerData::UnitEnteredLos(const CUnit* unit, int allyTeam)
RECOIL_DETAILED_TRACY_ZONE;
CUnit* u = const_cast<CUnit*>(unit); //cleanup

if (gameSetup->ghostedBuildings && unit->unitDef->IsBuildingUnit())
if (unit->leavesGhost)
spring::VectorErase(savedData.liveGhostBuildings[allyTeam][MDL_TYPE(unit)], u);

if (allyTeam != gu->myAllyTeam)
Expand All @@ -654,7 +668,7 @@ void CUnitDrawerData::UnitLeftLos(const CUnit* unit, int allyTeam)
RECOIL_DETAILED_TRACY_ZONE;
CUnit* u = const_cast<CUnit*>(unit); //cleanup

if (gameSetup->ghostedBuildings && unit->unitDef->IsBuildingUnit())
if (unit->leavesGhost)
spring::VectorInsertUnique(savedData.liveGhostBuildings[allyTeam][MDL_TYPE(unit)], u, true);

if (allyTeam != gu->myAllyTeam)
Expand All @@ -663,6 +677,15 @@ void CUnitDrawerData::UnitLeftLos(const CUnit* unit, int allyTeam)
UpdateUnitIcon(unit, false, false);
}

void CUnitDrawerData::UnitLeavesGhostChanged(const CUnit* unit, const bool leaveDeadGhost)
{
if (unit->leavesGhost)
return;

if (UpdateUnitGhosts(unit, leaveDeadGhost))
UpdateUnitIcon(unit, false, true);
}

void CUnitDrawerData::PlayerChanged(int playerNum)
{
RECOIL_DETAILED_TRACY_ZONE;
Expand All @@ -677,4 +700,4 @@ void CUnitDrawerData::PlayerChanged(int playerNum)
// force an erase (no-op) followed by an insert
UpdateUnitIcon(unit, true, false);
}
}
}
2 changes: 2 additions & 0 deletions rts/Rendering/Units/UnitDrawerData.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ class CUnitDrawerData : public CUnitDrawerDataBase {
void UnitLeftLos(const CUnit* unit, int allyTeam) override;

void PlayerChanged(int playerNum) override;
bool UpdateUnitGhosts(const CUnit* unit, const bool addNewGhost);
void UnitLeavesGhostChanged(const CUnit* unit, const bool leaveDeadGhost);
public:
class TempDrawUnit {
CR_DECLARE_STRUCT(TempDrawUnit)
Expand Down
11 changes: 10 additions & 1 deletion rts/Sim/Units/Unit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,8 @@ void CUnit::PreInit(const UnitLoadParams& params)
wantCloak |= unitDef->startCloaked;
decloakDistance = unitDef->decloakDistance;

leavesGhost = gameSetup->ghostedBuildings && unitDef->leavesGhost;

flankingBonusMode = unitDef->flankingBonusMode;
flankingBonusDir = unitDef->flankingBonusDir;
flankingBonusMobility = unitDef->flankingBonusMobilityAdd * 1000;
Expand Down Expand Up @@ -543,6 +545,11 @@ void CUnit::ForcedMove(const float3& newPos)
}


void CUnit::SetLeavesGhost(bool newLeavesGhost)
{
leavesGhost = newLeavesGhost;
}


float3 CUnit::GetErrorVector(int argAllyTeam) const
{
Expand All @@ -555,7 +562,7 @@ float3 CUnit::GetErrorVector(int argAllyTeam) const
const int atSightMask = losStatus[argAllyTeam];

const int isVisible = 2 * ((atSightMask & LOS_INLOS ) != 0 || teamHandler.Ally(argAllyTeam, allyteam)); // in LOS or allied, no error
const int seenGhost = 4 * ((atSightMask & LOS_PREVLOS) != 0 && gameSetup->ghostedBuildings && unitDef->IsBuildingUnit()); // seen ghosted building, no error
const int seenGhost = 4 * ((atSightMask & LOS_PREVLOS) != 0 && leavesGhost); // seen ghosted immobiles, no error
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ghosts are no longer tied to being an immobile.

Suggested change
const int seenGhost = 4 * ((atSightMask & LOS_PREVLOS) != 0 && leavesGhost); // seen ghosted immobiles, no error
const int seenGhost = 4 * ((atSightMask & LOS_PREVLOS) != 0 && leavesGhost); // seen ghost, no error

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unsure about this, since this is just for ghosts of "generally immobile" units.

At least I don't see how this kind of ghosts could have a meaning in the game unless the unit has some kind of "generally immobile" assumption, even if it can sometimes be broken, like when a building is moved with some exception like being transported.

Mobile ones have a different kind of ghost.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you point to the different kind of ghost for mobiles? Maybe handling for the two could be combined somehow.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, looks like the mobile ones don't get drawn by engine itself, for bar it looks like it goes through unit_ghostradar_gl4.

Copy link
Collaborator Author

@saurtron saurtron Jan 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, looks like the mobile ones don't get drawn by engine itself, for bar it looks like it goes through unit_ghostradar_gl4.

I suspect they could use the same path liveGhostBuildings are using tho, they probably doing it for nothing

const int isOnRadar = 8 * ((atSightMask & LOS_INRADAR) != 0 ); // current radar contact

float errorMult = 0.0f;
Expand Down Expand Up @@ -3013,6 +3020,8 @@ CR_REG_METADATA(CUnit, (
CR_MEMBER(isCloaked),
CR_MEMBER(decloakDistance),

CR_MEMBER(leavesGhost),

CR_MEMBER(lastTerrainType),
CR_MEMBER(curTerrainType),

Expand Down
5 changes: 4 additions & 1 deletion rts/Sim/Units/Unit.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ class CUnit : public CSolidObject
unsigned short CalcLosStatus(int allyTeam);
void UpdateLosStatus(int allyTeam);

void SetLeavesGhost(bool newLeavesGhost);

void UpdateWeapons();
void UpdateWeaponVectors();

Expand Down Expand Up @@ -548,7 +550,8 @@ class CUnit : public CSolidObject
bool isCloaked = false;
// true if the unit currently wants to be cloaked
bool wantCloak = false;

// true if the unit leaves static ghosts
bool leavesGhost = false;

// unsynced vars
bool noMinimap = false;
Expand Down
3 changes: 3 additions & 0 deletions rts/Sim/Units/UnitDef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ UnitDef::UnitDef()
, seismicSignature(0.0f)
, stealth(false)
, sonarStealth(false)
, leavesGhost(false)
, buildRange3D(false)
, buildDistance(16.0f) // 16.0f is the minimum distance between two 1x1 units
, buildSpeed(0.0f)
Expand Down Expand Up @@ -658,6 +659,8 @@ UnitDef::UnitDef(const LuaTable& udTable, const std::string& unitName, int id)
if (IsImmobileUnit())
CreateYardMap(udTable.GetString("yardMap", ""));

leavesGhost = udTable.GetBool("leavesGhost", IsBuildingUnit());

decalDef.Parse(udTable);

canDropFlare = udTable.GetBool("canDropFlare", false);
Expand Down
1 change: 1 addition & 0 deletions rts/Sim/Units/UnitDef.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ struct UnitDef: public SolidObjectDef
float seismicSignature;
bool stealth;
bool sonarStealth;
bool leavesGhost;

bool buildRange3D;
float buildDistance;
Expand Down