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 @@ -412,6 +412,9 @@ This page lists all the individual contributions to the project by their author.
- Fix an issue where miners affected by `Passengers/DeployFire` were unable to unload minerals
- Fix an issue where mining vehicles could not move after leaving a tank bunker
- Fixed the bug in AI scripts 56 and 57 that forced the launch of superweapons with index numbers 3 and 4
- DeployFire supports buildings
- Fixed an issue where `OmniFire` was ineffective on 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
2 changes: 2 additions & 0 deletions docs/Fixed-or-Improved-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,8 @@ This page describes all ingame logics that are fixed or improved in Phobos witho
- Fixed the bug that techno in attack move will move to target if it cannot attack it.
- Fixed the bug in AI scripts 56 and 57 that forced the launch of superweapons with index numbers 3 and 4.
- Buildings with `NeedsEngineer=true` are now considered to have threat value of 0 under ownership of `MultiplayPassive=true` houses regardless of their `ThreatPosed` value.
- Fixed an issue where `OmniFire` was ineffective on buildings with `Turret=yes`.
- 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 @@ -609,6 +609,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 @@ -543,6 +543,7 @@ New:
- Allow jumpjet climbing ignore building height (by TaranDahl)
- Allow draw SuperWeapon timer as percentage (by NetsuNegi)
- Customize particle system of parasite logic (by NetsuNegi)
- [DeployFire supports buildings](New-or-Enhanced-Logics.md#deployfire-supports) (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 @@ -602,6 +603,8 @@ Vanilla fixes:
- Fixed the bug that techno in attack move will move to target if it cannot attack it (by NetsuNegi)
- Fixed the bug in AI scripts 56 and 57 that forced the launch of superweapons with index numbers 3 and 4 (by FlyStar)
- Buildings with `NeedsEngineer=true` are now considered to have threat value of 0 under ownership of `MultiplayPassive=true` houses regardless of their `ThreatPosed` value (by Starkku)
- Fixed an issue where `OmniFire` was ineffective on buildings with `Turret=yes` (by FlyStar)
- 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 @@ -201,7 +201,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 @@ -226,6 +226,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 @@ -353,6 +355,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 @@ -103,6 +103,8 @@ class BuildingTypeExt

Nullable<bool> BuildingRadioLink_SyncOwner;

Nullable<int> DeployFireDelay;

// Ares 0.2
Valueable<bool> CloningFacility;

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

// Ares 0.2
, CloningFacility { false }
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 @@ -874,19 +874,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
12 changes: 12 additions & 0 deletions src/Misc/Hooks.BugFixes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3022,3 +3022,15 @@ DEFINE_HOOK(0x6EA870, TeamClass_LiberateMember_Start, 0x6)
pMember->RecruitableB = true;
return 0;
}

DEFINE_HOOK(0x447FED, BuildingClass_GetFireError_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;
}