Skip to content
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
3 changes: 3 additions & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,9 @@ This page lists all the individual contributions to the project by their author.
- Fixed an issue where parachute units would die upon landing if bridges were destroyed during their descent
- Custom hover vehicles shutdown drowning death
- SHP turret vehicles support the use of `*tur.shp` files
- DeployFire supports buildings
- `OmniFire` supports buildings with `Turret=yes`
- Fixed an issue where setting a production building as `Primary` could cause it to enter an unload state
- **NetsuNegi**:
- Forbidding parallel AI queues by type
- Jumpjet crash speed fix when crashing onto building
Expand Down
1 change: 1 addition & 0 deletions Phobos.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\Ext\Building\Hooks.Unload.cpp" />
<ClCompile Include="src\Ext\Cell\Hooks.cpp" />
<ClCompile Include="src\Ext\Event\Body.cpp" />
<ClCompile Include="src\Ext\Infantry\Hooks.cpp" />
Expand Down
1 change: 1 addition & 0 deletions docs/Fixed-or-Improved-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ This page describes all ingame logics that are fixed or improved in Phobos witho
- Fixed units with Fly, Jumpjet or Rocket locomotors destroyed while crashing off-map never being fully cleaned up, permanently blocking production slots and counting towards unit limits.
- Fixed a desync due to an inconsistent shroud state caused by `GapGenerator` and `SpySat` interaction.
- Now miners will no longer withdraw from the Harvest mission due to mineral depletion and will periodically attempt to return to work.
- Fixed an issue where setting a production building as `Primary` could cause it to enter an unload state.

## Fixes / interactions with other extensions

Expand Down
12 changes: 12 additions & 0 deletions docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,18 @@ Adjacent.Disallowed.ProhibitDistance=0 ; integer, cell offset
NoBuildAreaOnBuildup=false ; boolean
```

### DeployFire supports

- Building types now also support using `DeployFire` and `DeployFireWeapon`.
- If a building has other configurations such as `Factory`, `GapGenerator`, or `Passengers`, it will prioritize executing those deployment actions instead.
- `DeployFireDelay` specifies the frame interval at which deployed weapons attempt to fire. If the weapon is unavailable due to `ROF`, `CanTarget`, or other reasons, the deployed weapon will not fire.

In `rulesmd.ini`:
```ini
[SOMEBUILDING] ; BuildingType, with DeployFire=yes
DeployFireDelay= ; integer, default value ranges from 14 to 16
```

### 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
3 changes: 3 additions & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,8 @@ New:
- [Allow customize jumpjet properties on warhead](Fixed-or-Improved-Logics.md#customizing-locomotor-warhead) (by NetsuNegi)
- Customize effects range of power plant enhancer (by NetsuNegi)
- Allow each side to customize the color when the proportion of working miners is higher than `HarvesterCounter.ConditionYellow` (by Noble_Fish)
- [DeployFire supports buildings](New-or-Enhanced-Logics.md#deployfire-supports) (By FlyStar)
- `OmniFire` supports buildings with `Turret=yes` (by FlyStar)

Vanilla fixes:
- Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya)
Expand Down Expand Up @@ -637,6 +639,7 @@ Vanilla fixes:
- Miners back to work when ore regenerated (by TaranDahl)
- [Allow disable an over-optimization in targeting](Fixed-or-Improved-Logics.md#allow-disable-an-over-optimization-in-targeting) (by TaranDahl)
- Extra threat (by TaranDahl)
- Fixed an issue where setting a production building as `Primary` could cause it to enter an unload state (by FlyStar)

Phobos fixes:
- Fixed the bug that `AllowAirstrike=no` cannot completely prevent air strikes from being launched against it (by NetsuNegi)
Expand Down
118 changes: 118 additions & 0 deletions src/Ext/Building/Hooks.Unload.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#include "Body.h"

DEFINE_HOOK(0x447358, BuildingClass_MouseOverObject_DeployFire, 0x6)
{
GET(BuildingTypeClass* const, pType, EAX);

return pType->DeployFire ? 0x4472EC : 0;
}

DEFINE_HOOK(0x443459, BuildingClass_ObjectClickedAction_DeployFire, 0x6)
{
GET(BuildingClass*, pThis, EBX);
enum { SkipGameCode = 0x443568, SkipFactory = 0x4434F2 };

auto const pType = pThis->Type;

// Perhaps Factory should not allow the use of DeployFire.
if (pType->Factory != AbstractType::None)
{
if (!pThis->IsPrimaryFactory)
{
// Do not enter unloading tasks simultaneously, as this may prevent buildings from producing units in a timely manner.
pThis->ClickedEvent(EventType::Primary);
return SkipGameCode;
}
}
else if (pType->DeployFire)
{
pThis->ClickedMission(Mission::Unload, pThis, nullptr, nullptr);
return SkipGameCode;
}

return SkipFactory;
}

DEFINE_HOOK(0x44E371, BuildingClass_Mission_Unload_DeployFire, 0x6)
{
GET(BuildingClass* const, pThis, EBP);
enum { SkipGameCode = 0x44E37F };

auto const pType = pThis->Type;

if (!pType->GapGenerator && pType->DeployFire)
{
auto const pCell = pThis->GetCell();

if (pThis->Target != pCell)
pThis->SetTarget(pCell);

const int deployFireWeapon = pType->DeployFireWeapon;
const int weaponIndex = deployFireWeapon >= 0 ? deployFireWeapon : pThis->SelectWeapon(pCell);
const FireError fireError = pThis->GetFireError(pCell, weaponIndex, true);

if (fireError == FireError::ILLEGAL)
{
// Do not allow the building to remain in the Unload task indefinitely.
pThis->QueueMission(Mission::Guard, false);
pThis->NextMission();

return SkipGameCode;
}
else if (fireError == FireError::OK && pThis->Fire(pCell, weaponIndex))
{
auto const pWeapon = pThis->GetWeapon(weaponIndex)->WeaponType;

if (pWeapon->FireOnce)
{
// When Turret=yes, the Unload task may not be canceled, so this handling is performed.
pThis->QueueMission(Mission::Guard, false);
pThis->NextMission();

return SkipGameCode;
}
}

auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pType);
const int result = pTypeExt->DeployFireDelay.isset()
? pTypeExt->DeployFireDelay : (ScenarioClass::Instance->Random.RandomRanged(0, 2) + 14);

R->EBX(result);
return SkipGameCode;
}

return 0;
}

DEFINE_HOOK(0x730B09, DeployCommandClass_Execute_BuildingDeploy, 0x5)
{
for (const auto pObject : ObjectClass::CurrentObjects)
{
const AbstractFlags flags = pObject->AbstractFlags;

if (!(flags & AbstractFlags::Techno) || (flags & AbstractFlags::Foot))
continue;

const auto pBuilding = static_cast<BuildingClass*>(pObject);
auto const pType = pBuilding->Type;
const auto pHouse = pBuilding->Owner;

if (!pHouse->IsControlledByCurrentPlayer()
|| !pType->DeployFire || pType->Factory != AbstractType::None || pType->GapGenerator)
{
continue;
}

const Mission currentMission = pBuilding->CurrentMission;

if (currentMission == Mission::Construction || currentMission == Mission::Selling)
continue;

if (pBuilding->EMPLockRemaining > 0 || !pBuilding->WasOnline || pBuilding->BunkerLinkedItem)
continue;

pBuilding->ClickedMission(Mission::Unload, nullptr, nullptr, nullptr);
}

return 0;
}
5 changes: 4 additions & 1 deletion src/Ext/BuildingType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ void BuildingTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
this->Refinery_UseStorage.Read(exINI, pSection, "Refinery.UseStorage");
this->UndeploysInto_Sellable.Read(exINI, pSection, "UndeploysInto.Sellable");
this->BuildingRadioLink_SyncOwner.Read(exINI, pSection, "BuildingRadioLink.SyncOwner");

if (pThis->NumberOfDocks > 0)
{
std::optional<DirType> empty;
Expand All @@ -247,6 +247,8 @@ void BuildingTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)

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

this->DeployFireDelay.Read(exINI, pSection, "DeployFireDelay");

// Ares tag
this->SpyEffect_Custom.Read(exINI, pSection, "SpyEffect.Custom");
if (SuperWeaponTypeClass::Array.Count > 0)
Expand Down Expand Up @@ -378,6 +380,7 @@ void BuildingTypeExt::ExtData::Serialize(T& Stm)
.Process(this->HasPowerUpAnim)
.Process(this->UndeploysInto_Sellable)
.Process(this->BuildingRadioLink_SyncOwner)
.Process(this->DeployFireDelay)

// Ares 0.2
.Process(this->CloningFacility)
Expand Down
3 changes: 3 additions & 0 deletions src/Ext/BuildingType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ class BuildingTypeExt

Nullable<bool> BuildingRadioLink_SyncOwner;

Nullable<int> DeployFireDelay;

// Ares 0.2
Valueable<bool> CloningFacility;

Expand Down Expand Up @@ -183,6 +185,7 @@ class BuildingTypeExt
, HasPowerUpAnim {}
, UndeploysInto_Sellable { false }
, BuildingRadioLink_SyncOwner {}
, DeployFireDelay {}

// Ares 0.2
, CloningFacility { false }
Expand Down
12 changes: 12 additions & 0 deletions src/Ext/Techno/Hooks.Firing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,18 @@ DEFINE_HOOK(0x6FC7EB, TechnoClass_CanFire_InterceptBullet, 0x7)
return ContinueCheck;
}

DEFINE_HOOK(0x447FED, BuildingClass_CanFire_OmniFire, 0x7)
{
enum { SkipGameCode = 0x448052 };

GET(BuildingClass* const, pThis, ESI);
GET_STACK(const int, weaponIndex, STACK_OFFSET(0xC, 0x8));

auto const pWeapon = pThis->GetWeapon(weaponIndex)->WeaponType;

return pWeapon->OmniFire ? SkipGameCode : 0;
}

#pragma endregion

#pragma region TechnoClass_Fire
Expand Down
18 changes: 16 additions & 2 deletions src/Ext/Techno/Hooks.Misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -889,19 +889,33 @@ DEFINE_HOOK(0x4C7512, EventClass_Execute_StopCommand, 0x6)

if (auto const pUnit = abstract_cast<UnitClass*>(pThis))
{
auto const pType = pUnit->Type;

// issue #112 Make FireOnce=yes work on other TechnoType
// Author: Starkku
if (pUnit->CurrentMission == Mission::Unload && pUnit->Type->DeployFire && !pUnit->Type->IsSimpleDeployer)
if (pUnit->CurrentMission == Mission::Unload && pType->DeployFire && !pType->IsSimpleDeployer)
{
pUnit->SetTarget(nullptr);
pThis->QueueMission(Mission::Guard, true);
}

// Explicit stop command should reset subterranean harvester state machine.
auto const pExt = TechnoExt::ExtMap.Find(pUnit);
pExt->SubterraneanHarvStatus = 0;
pExt->SubterraneanHarvRallyPoint = nullptr;
}
else if (auto const pBuilding = abstract_cast<BuildingClass*, true>(pThis))
{
auto const pType = pBuilding->Type;

if (pBuilding->CurrentMission == Mission::Unload
&& pType->DeployFire && pType->Factory == AbstractType::None)
{
pBuilding->SetTarget(nullptr);
pBuilding->QueueMission(Mission::Guard, false);
pBuilding->NextMission();
}
}

return 0;
}
Expand Down
Loading