Skip to content
1 change: 1 addition & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,7 @@ This page lists all the individual contributions to the project by their author.
- Jumpjet Climbing Logic Enhancement
- Fix for pathfinding crashes on big maps due to too small pathfinding node buffer
- Fix an issue that units' `LaserTrails` will always lags behind by one frame
- Customized bib & impassable rows & weapons factory direction
- **Ollerus**:
- Build limit group enhancement
- Customizable rocker amplitude
Expand Down
23 changes: 23 additions & 0 deletions docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,29 @@ Adjacent.Disallowed= ; List of BuildingTypes
NoBuildAreaOnBuildup=false ; boolean
```

### Customized bib & impassable rows & weapons factory direction

- Now you can use `ExtendedWeaponsFactory` to remove the hard coding of `WeaponsFactory` position and direction, and adjust the position of the generated unit and the direction of the unit's exit separately through the original `ExitCoord` and the newly added `WeaponsFactory.Dir`. Similarly, the directions of `Bib` and `NumberImpassableRows` can also be modified through `Bib.Dir` and `NumberImpassableRows.Dir` respectively.

In `rulesmd.ini`:
```ini
[General]
ExtendedWeaponsFactory=false ; boolean

[SOMEBUILDING] ; BuildingType
Bib.Dir=2 ; integer
NumberImpassableRows.Dir=2 ; integer
WeaponsFactory.Dir=2 ; integer
```

```{note}
- The available directions are:
- 0 - North (top right)
- 2 - East (bottom right)
- 4 - South (bottom left)
- 6 - West (top left)
```

### Destroyable pathfinding obstacles

- It is possible to make buildings be considered pathfinding obstacles that can be destroyed by setting `IsDestroyableBlockage` to true. What this does is make the building be considered impassable and impenetrable pathfinding obstacle to every unit that is not flying or have appropriate `MovementZone` (ones that allow destroyable obstacles to be overcome, e.g `(Infantry|Amphibious)Destroyer`) akin to wall overlays and TerrainTypes.
Expand Down
1 change: 1 addition & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ New:
- [Customize hardcoded projectile initial facing behavior](Fixed-or-Improved-Logics.md#customizing-initial-facing-behavior) (by Starkku)
- Health bar permanently displayed (by FlyStar)
- [`IsSimpleDeployer` facing customization & directional deploy animations](Fixed-or-Improved-Logics.md#issimpleDeployer-facing-and-animation-customization) (by Starkku)
- Customized bib & impassable rows & weapons factory direction (by CrimRecya)

Vanilla fixes:
- Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya)
Expand Down
61 changes: 56 additions & 5 deletions src/Ext/Building/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,10 +360,56 @@ void BuildingExt::KickOutStuckUnits(BuildingClass* pThis)

auto cell = CellClass::Coord2Cell(buffer);

bool upward = false;
short* pCur = nullptr;
short start = 0; // door

const auto pType = pThis->Type;
const short start = static_cast<short>(pThis->Location.X / Unsorted::LeptonsPerCell + pType->GetFoundationWidth() - 2); // door
const short end = cell.X; // exit
cell.X = start;

switch (RulesExt::Global()->ExtendedWeaponsFactory ? BuildingTypeExt::ExtMap.Find(pType)->WeaponsFactory_Dir.Get() : 2)
{

case 0: // North -> left+down/++Y
{
upward = false;
pCur = &cell.Y;
start = static_cast<short>(pThis->Location.Y / Unsorted::LeptonsPerCell + 1);
break;
}

case 2: // East -> left+up/--X
{
upward = true;
pCur = &cell.X;
start = static_cast<short>(pThis->Location.X / Unsorted::LeptonsPerCell + pType->GetFoundationWidth() - 2);
break;
}

case 4: // South -> right+up/--Y
{
upward = true;
pCur = &cell.Y;
start = static_cast<short>(pThis->Location.Y / Unsorted::LeptonsPerCell + pType->GetFoundationHeight(false) - 2);
break;
}

case 6: // West -> right+down/++X
{
upward = false;
pCur = &cell.X;
start = static_cast<short>(pThis->Location.X / Unsorted::LeptonsPerCell + 1);
break;
}

default: // Invalid direction
{
return;
}

}

const short end = *pCur; // exit
*pCur = start;
auto pCell = MapClass::Instance.GetCellAt(cell);

while (true)
Expand All @@ -372,7 +418,12 @@ void BuildingExt::KickOutStuckUnits(BuildingClass* pThis)
{
if (const auto pUnit = abstract_cast<UnitClass*, true>(pObject))
{
if (pThis->Owner != pUnit->Owner || pUnit->Locomotor->Destination() != CoordStruct::Empty)
if (pThis->Owner != pUnit->Owner)
continue;

const auto pLocoDest = pUnit->Locomotor->Destination();

if (pLocoDest != CoordStruct::Empty && pLocoDest != pUnit->Location)
continue;

const auto height = pUnit->GetHeight();
Expand All @@ -386,7 +437,7 @@ void BuildingExt::KickOutStuckUnits(BuildingClass* pThis)
}
}

if (--cell.X < end)
if (upward ? (--(*pCur) < end) : (++(*pCur) > end))
return; // no stuck

pCell = MapClass::Instance.GetCellAt(cell);
Expand Down
45 changes: 19 additions & 26 deletions src/Ext/Building/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,25 +208,6 @@ DEFINE_HOOK(0x44D455, BuildingClass_Mission_Missile_EMPulseBulletWeapon, 0x8)

#pragma region KickOutStuckUnits

DEFINE_HOOK(0x44955D, BuildingClass_WeaponFactoryOutsideBusy_WeaponFactoryCell, 0x6)
{
enum { NotBusy = 0x44969B };

GET(BuildingClass* const, pThis, ESI);

const auto pLink = pThis->GetNthLink();

if (!pLink)
return NotBusy;

const auto pLinkType = pLink->GetTechnoType();

if (pLinkType->JumpJet && pLinkType->BalloonHover)
return NotBusy;

return 0;
}

// Attempt to kick the stuck unit out again by setting the destination
DEFINE_HOOK(0x44E202, BuildingClass_Mission_Unload_CheckStuck, 0x6)
{
Expand All @@ -240,17 +221,20 @@ DEFINE_HOOK(0x44E202, BuildingClass_Mission_Unload_CheckStuck, 0x6)
if (const auto pUnit = abstract_cast<UnitClass*>(pThis->GetNthLink()))
{
// Detecting movement status
if (pUnit->Locomotor->Destination() == CoordStruct::Empty)
const auto pLocoDest = pUnit->Locomotor->Destination();

if (pLocoDest == CoordStruct::Empty || pLocoDest == pUnit->Location)
{
// Evacuate the congestion at the entrance
reinterpret_cast<void(__thiscall*)(BuildingClass*)>(0x449540)(pThis);
const auto pType = pThis->Type;
const auto cell = pThis->GetMapCoords() + pType->FoundationOutside[10];
const auto door = cell - CellStruct { 1, 0 };
const auto pDest = MapClass::Instance.GetCellAt(door);
const auto cell = BuildingTypeExt::GetWeaponFactoryDoor(pThis);
const auto pDest = MapClass::Instance.GetCellAt(cell);

// Hover units may stop one cell behind their destination, should forcing them to advance one more cell
pUnit->SetDestination((pUnit->Destination != pDest ? pDest : MapClass::Instance.GetCellAt(cell)), true);
// Hover units may stop one cell behind their destination
if (pUnit->Destination != pDest)
pUnit->SetDestination(pDest, true);
else
pUnit->Locomotor->Move_To(CellClass::Cell2Coord(cell, Unsorted::LevelHeight * pDest->Level));
}
}

Expand All @@ -262,6 +246,15 @@ DEFINE_HOOK(0x44E260, BuildingClass_Mission_Unload_KickOutStuckUnits, 0x7)
{
GET(BuildingClass*, pThis, EBP);

if (!pThis->IsTether)
{
if (const auto pLink = pThis->GetNthLink())
{
pThis->SendCommand(RadioCommand::NotifyUnlink, pLink);
pLink->Scatter(pThis->GetCoords(), true, true);
}
}

BuildingExt::KickOutStuckUnits(pThis);

return 0;
Expand Down
56 changes: 56 additions & 0 deletions src/Ext/BuildingType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,52 @@ void BuildingTypeExt::PlayBunkerSound(BuildingClass const* pThis, bool buildUp)
VocClass::PlayAt(nSound, pThis->Location);
}

CellStruct BuildingTypeExt::GetWeaponFactoryDoor(BuildingClass* pThis)
{
auto cell = pThis->GetMapCoords();
auto buffer = CoordStruct::Empty;
pThis->GetExitCoords(&buffer, 0);
const auto pType = pThis->Type;

switch (RulesExt::Global()->ExtendedWeaponsFactory ? BuildingTypeExt::ExtMap.Find(pType)->WeaponsFactory_Dir.Get() : 2)
{

case 0:
{
cell.X = static_cast<short>(buffer.X / Unsorted::LeptonsPerCell);
break;
}

case 2:
{
cell.X += static_cast<short>(pType->GetFoundationWidth() - 1);
cell.Y = static_cast<short>(buffer.Y / Unsorted::LeptonsPerCell);
break;
}

case 4:
{
cell.X = static_cast<short>(buffer.X / Unsorted::LeptonsPerCell);
cell.Y += static_cast<short>(pType->GetFoundationHeight(false) - 1);
break;
}

case 6:
{
cell.Y = static_cast<short>(buffer.Y / Unsorted::LeptonsPerCell);
break;
}

default:
{
break;
}

}

return cell;
}

int BuildingTypeExt::CountOwnedNowWithDeployOrUpgrade(BuildingTypeClass* pType, HouseClass* pHouse)
{
const auto upgrades = BuildingTypeExt::GetUpgradesAmount(pType, pHouse);
Expand Down Expand Up @@ -219,6 +265,13 @@ void BuildingTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)

this->Refinery_UseNormalActiveAnim.Read(exArtINI, pArtSection, "Refinery.UseNormalActiveAnim");

this->Bib_Dir.Read(exINI, pSection, "Bib.Dir");
this->Bib_Dir = Math::max(0, this->Bib_Dir) & 6; // Only accept 0,2,4,6
this->NumberImpassableRows_Dir.Read(exINI, pSection, "NumberImpassableRows.Dir");
this->NumberImpassableRows_Dir = Math::max(0, this->NumberImpassableRows_Dir) & 6; // Only accept 0,2,4,6
this->WeaponsFactory_Dir.Read(exINI, pSection, "WeaponsFactory.Dir");
this->WeaponsFactory_Dir = Math::max(0, this->WeaponsFactory_Dir) & 6; // Only accept 0,2,4,6

// Ares tag
this->SpyEffect_Custom.Read(exINI, pSection, "SpyEffect.Custom");
if (SuperWeaponTypeClass::Array.Count > 0)
Expand Down Expand Up @@ -334,6 +387,9 @@ void BuildingTypeExt::ExtData::Serialize(T& Stm)
.Process(this->BuildingRepairedSound)
.Process(this->Refinery_UseNormalActiveAnim)
.Process(this->HasPowerUpAnim)
.Process(this->Bib_Dir)
.Process(this->NumberImpassableRows_Dir)
.Process(this->WeaponsFactory_Dir)
;
}

Expand Down
9 changes: 8 additions & 1 deletion src/Ext/BuildingType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ class BuildingTypeExt

ValueableVector<bool> HasPowerUpAnim;

Valueable<int> Bib_Dir;
Valueable<int> NumberImpassableRows_Dir;
Valueable<int> WeaponsFactory_Dir;

ExtData(BuildingTypeClass* OwnerObject) : Extension<BuildingTypeClass>(OwnerObject)
, PowersUp_Owner { AffectedHouse::Owner }
, PowersUp_Buildings {}
Expand Down Expand Up @@ -161,6 +165,9 @@ class BuildingTypeExt
, BuildingRepairedSound {}
, Refinery_UseNormalActiveAnim { false }
, HasPowerUpAnim {}
, Bib_Dir { 2 }
, NumberImpassableRows_Dir { 2 }
, WeaponsFactory_Dir { 2 }
{ }

// Ares 0.A functions
Expand Down Expand Up @@ -198,7 +205,7 @@ class BuildingTypeExt
static bool SaveGlobals(PhobosStreamWriter& Stm);

static void PlayBunkerSound(BuildingClass const* pThis, bool buildUp = false);

static CellStruct GetWeaponFactoryDoor(BuildingClass* pThis);
static int GetEnhancedPower(BuildingClass* pBuilding, HouseClass* pHouse);
static bool CanUpgrade(BuildingClass* pBuilding, BuildingTypeClass* pUpgradeType, HouseClass* pUpgradeOwner);
static int CountOwnedNowWithDeployOrUpgrade(BuildingTypeClass* pBuilding, HouseClass* pHouse);
Expand Down
Loading
Loading