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
1 change: 1 addition & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ This page lists all the individual contributions to the project by their author.
- Fast access structure
- Iron Curtain/Custom Tint Support for SHP Turreted Vehicles
- Reactivate unused trigger events 2, 53, and 54
- Map Action 511, 609, 610
- **NetsuNegi**:
- Forbidding parallel AI queues by type
- Jumpjet crash speed fix when crashing onto building
Expand Down
47 changes: 47 additions & 0 deletions docs/AI-Scripting-and-Mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,19 @@ ID=ActionCount,[Action1],510,0,0,[MCVRedeploy],0,0,0,A,[ActionX]
...
```

### `511` Undeploy Building to Waypoint

- Undeploy specific BuildingTypes into VehicleTypes and move them to a specific Waypoint.
- If `<All>` is entered for the Building Type here, then undeploy all BuildingTypes.

In `mycampaign.map`:
```ini
[Actions]
...
ID=ActionCount,[Action1],511,-10,[BuildingTypesID],[HouseIndex],0,0,0,[WaypointIndex],[ActionX]
...
```

### `606` Edit Hate-Value

- Edit the hate-value that trigger houses to other houses.
Expand Down Expand Up @@ -707,6 +720,40 @@ ID=ActionCount,[Action1],608,0,0,[HouseIndex],0,0,0,A,[ActionX]
...
```

### `609` Set Radar Mode

- Change the current radar mode of the trigger house.

In `mycampaign.map`:
```ini
[Actions]
...
ID=ActionCount,[Action1],609,0,0,[RadarMode],0,0,0,A,[ActionX]
...
```

- The possible argument values are:

| *Argument* | *Description* |
| :--------: | :-----------------------------------------------------------------------: |
| 0 | Normal mode, requires buildings that provide radar and sufficient power |
| 1 | Change to [FreeRadar](https://modenc.renegadeprojects.com/FreeRadar) mode |
| 2 | Force enable radar |
| 3 | Force disable radar |

### `610` Set house's `TeamDelays` value

- Set the `TeamDelays` value of the trigger's house.
- If this value is less than 0, then use the value of `[General] -> TeamDelays`.

In `mycampaign.map`:
```ini
[Actions]
...
ID=ActionCount,[Action1],610,0,0,[Number],0,0,0,A,[ActionX]
...
```

### `800-802` Display Banner

- Display a 'banner' at a fixed location that is relative to the screen.
Expand Down
20 changes: 12 additions & 8 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,17 +185,20 @@ HideShakeEffects=false ; boolean
[ActionsRA2]
41=Play animation at a waypoint...,0,25,69,0,0,0,1,0,0,[LONG DESC].,0,1,41
125=Build at...,-10,47,0,65,0,0,1,0,0,[LONG DESC],0,1,125
500=Save game,-4,13,0,0,0,0,0,0,0,[LONG DESC],0,1,500,1
501=Edit variable,0,56,55,6,54,0,0,0,0,[LONG DESC],0,1,501,1
502=Generate random number,0,56,57,58,54,0,0,0,0,[LONG DESC],0,1,502,1
503=Print variable value,0,56,54,0,0,0,0,0,0,[LONG DESC],0,1,503,0
504=Binary operation,0,56,55,60,54,59,0,0,0,[LONG DESC],0,1,504,1
500=Save game (Phobos),-4,13,0,0,0,0,0,0,0,[LONG DESC],0,1,500,1
501=Edit variable (Phobos),0,56,55,6,54,0,0,0,0,[LONG DESC],0,1,501,1
502=Generate random number (Phobos),0,56,57,58,54,0,0,0,0,[LONG DESC],0,1,502,1
503=Print variable value (Phobos),0,56,54,0,0,0,0,0,0,[LONG DESC],0,1,503,0
504=Binary operation (Phobos),0,56,55,60,54,59,0,0,0,[LONG DESC],0,1,504,1
505=Fire Super Weapon at specified location (Phobos),0,0,20,2,21,22,0,0,0,Launch a Super Weapon from [SuperWeaponTypes] list at a specified location. House=-1 means random target that isn't neutral. House=-2 means the first neutral house. House=-3 means random human target. Coordinate X=-1 means random. Coordinate Y=-1 means random,0,1,505
506=Fire Super Weapon at specified waypoint (Phobos),0,0,20,2,30,0,0,0,0,Launch a Super Weapon from [SuperWeaponTypes] list at a specified waypoint. House=-1 means random target that isn't neutral. House=-2 means the first neutral house. House=-3 means random human target. Coordinate X=-1 means random. Coordinate Y=-1 means random,0,1,506
510=Toggle MCV Redeployablility (Phobos),0,0,15,0,0,0,0,0,0, Set MCVRedeploys to the given value,0,1,510
606=Edit hate-value (Phobos),0,2,55,6,0,0,0,0,0, Edit the hate-value that trigger houses to other houses. -1 works for all houses.,0,1,606
607=Clear hate-value (Phobos),0,2,0,0,0,0,0,0,0, Clear the hate-value that trigger houses to other houses. -1 works for all houses.,0,1,607
608=Set force enemy (Phobos),0,0,2,0,0,0,0,0,0, Force an enemy, it will not change with the change of hate-value. -1 will remove the forced enemy, -2 will never have any enemies.,0,1,608
511=Building Type undeploy at... (Phobos),-10,47,2,0,0,0,1,0,0,Recycle the building type into a vehicle and move it to the specified waypoint. If the type is `<All>`, recycle all buildings.,0,1,511
606=Edit hate-value... (Phobos),0,2,55,6,0,0,0,0,0, Edit the hate-value that trigger houses to other houses. -1 works for all houses.,0,1,606
607=Clear hate-value... (Phobos),0,2,0,0,0,0,0,0,0, Clear the hate-value that trigger houses to other houses. -1 works for all houses.,0,1,607
608=Set force enemy... (Phobos),0,0,2,0,0,0,0,0,0, Force an enemy, it will not change with the change of hate-value. -1 will remove the forced enemy, -2 will never have any enemies.,0,1,608
609=Set radar mode... (Phobos),0,0,15,0,0,0,0,0,0, Trigger's house can modify the current radar mode. 0 for requires full-power and building, 1 for free radar, 2 for forced enable, 3 for forced disable.,0,1,609
610=Set team delay... (Phobos),0,0,6,0,0,0,0,0,0, Trigger's house can customize TeamDelay. When the value is less than 0 in `[General]>TeamDelays`.,0,1,610
800=Display banner and local variable (Phobos),-4,101,104,102,103,3,0,0,0,Draw banner on screen and replace banner with same ID,0,1,800
801=Display banner and global variable (Phobos),-4,101,104,102,103,35,0,0,0,Draw banner on screen and replace banner with same ID,0,1,801
802=Delete banner (Phobos),0,104,0,0,0,0,0,0,0,Delete banner with ID,0,1,802
Expand Down Expand Up @@ -472,6 +475,7 @@ New:
- [Script Action 14004 for forcing all new actions to target only the main owner's enemy](AI-Scripting-and-Mapping.md#force-global-onlytargethouseenemy-value-in-teams-for-new-attack-move-actions-introduced-by-phobos) (by FS-21)
- [Allow merging AOE damage to buildings into one](New-or-Enhanced-Logics.md#allow-merging-aoe-damage-to-buildings-into-one) (by CrimRecya)
- [Allow customizing whether to synchronously change the owner of the RadioLink-linked units when the owner of a building changes](Fixed-or-Improved-Logics.md#custom-whether-to-synchronously-change-the-owner-of-the-radiolink-linked-units-when-the-owner-of-a-building-changes) (by TaranDahl)
- Map Action [`511` Undeploy Building to Waypoint](AI-Scripting-and-Mapping.md#undeploy-building-to-waypoint), [`609` Set Radar Mode](AI-Scripting-and-Mapping.md#set-radar-mode), [`610` Set house's `TeamDelays` value](AI-Scripting-and-Mapping.md#set-house-s-teamdelays-value) (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
3 changes: 3 additions & 0 deletions src/Ext/House/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,9 @@ void HouseExt::ExtData::Serialize(T& Stm)
.Process(this->ForceEnemyIndex)
.Process(this->ForceOnlyTargetHouseEnemy)
.Process(this->ForceOnlyTargetHouseEnemyMode)
.Process(this->TeamDelay)
.Process(this->FreeRadar)
.Process(this->ForceRadar)
;
}

Expand Down
6 changes: 6 additions & 0 deletions src/Ext/House/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ class HouseExt
std::vector<SWExt> SuperExts;

int ForceEnemyIndex;
int TeamDelay;
bool FreeRadar;
bool ForceRadar;

ExtData(HouseClass* OwnerObject) : Extension<HouseClass>(OwnerObject)
, PowerPlantEnhancers {}
Expand Down Expand Up @@ -99,6 +102,9 @@ class HouseExt
, ForceEnemyIndex(-1)
, ForceOnlyTargetHouseEnemy { false }
, ForceOnlyTargetHouseEnemyMode { -1 }
, TeamDelay(-1)
, FreeRadar(false)
, ForceRadar(false)
{ }

bool OwnsLimboDeliveredBuilding(BuildingClass* pBuilding) const;
Expand Down
45 changes: 45 additions & 0 deletions src/Ext/House/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -483,3 +483,48 @@ DEFINE_HOOK(0x4FD8F7, HouseClass_UpdateAI_OnLastLegs, 0x10)

return ret;
}

DEFINE_HOOK(0x4F8ACC, HouseClass_Update_ResetTeamDelay, 0x6)
{
enum { ResetTeamDelay = 0x4F8AD5 };

GET(HouseClass*, pThis, ESI);

const int teamDelay = HouseExt::ExtMap.Find(pThis)->TeamDelay;

if (teamDelay >= 0)
{
R->ECX(teamDelay);
return ResetTeamDelay;
}

return 0;
}

DEFINE_HOOK(0x508E17, HouseClass_UpdateRadar_FreeRadar, 0x8)
{
enum { ForceRadar = 0x508F2F, Continue = 0x508E4A };

GET(HouseClass*, pThis, ECX);

auto const pExt = HouseExt::ExtMap.Find(pThis);
const bool freeRadar = pExt->FreeRadar;

if (pExt->ForceRadar)
{
R->Stack(STACK_OFFSET(0x1C, -0xC), freeRadar);
return ForceRadar;
}
else if (pThis->PowerBlackoutTimer.InProgress())
{
R->Stack(STACK_OFFSET(0x1C, -0xC), false);
return ForceRadar;
}
else if (freeRadar)
{
R->Stack(STACK_OFFSET(0x1C, -0xC), true);
return ForceRadar;
}

return Continue;
}
7 changes: 7 additions & 0 deletions src/Ext/Scenario/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <SessionClass.h>
#include <VeinholeMonsterClass.h>

#include <Ext/House/Body.h>

std::unique_ptr<ScenarioExt::ExtData> ScenarioExt::Data = nullptr;

bool ScenarioExt::CellParsed = false;
Expand Down Expand Up @@ -90,6 +92,11 @@ void ScenarioExt::Remove(ScenarioClass* pThis)
void ScenarioExt::LoadFromINIFile(ScenarioClass* pThis, CCINIClass* pINI)
{
Data->LoadFromINI(pINI);

for (auto const pHouse : HouseClass::Array)
{
HouseExt::ExtMap.Find(pHouse)->FreeRadar = ScenarioClass::Instance->FreeRadar;
}
}

void ScenarioExt::ExtData::UpdateAutoDeathObjectsInLimbo()
Expand Down
153 changes: 153 additions & 0 deletions src/Ext/TAction/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,19 @@ bool TActionExt::Execute(TActionClass* pThis, HouseClass* pHouse, ObjectClass* p

case PhobosTriggerAction::ToggleMCVRedeploy:
return TActionExt::ToggleMCVRedeploy(pThis, pHouse, pObject, pTrigger, location);
case PhobosTriggerAction::UndeployToWaypoint:
return TActionExt::UndeployToWaypoint(pThis, pHouse, pObject, pTrigger, location);

case PhobosTriggerAction::EditAngerNode:
return TActionExt::EditAngerNode(pThis, pHouse, pObject, pTrigger, location);
case PhobosTriggerAction::ClearAngerNode:
return TActionExt::ClearAngerNode(pThis, pHouse, pObject, pTrigger, location);
case PhobosTriggerAction::SetForceEnemy:
return TActionExt::SetForceEnemy(pThis, pHouse, pObject, pTrigger, location);
case PhobosTriggerAction::SetFreeRadar:
return TActionExt::SetFreeRadar(pThis, pHouse, pObject, pTrigger, location);
case PhobosTriggerAction::SetTeamDelay:
return TActionExt::SetTeamDelay(pThis, pHouse, pObject, pTrigger, location);

case PhobosTriggerAction::CreateBannerLocal:
return TActionExt::CreateBannerLocal(pThis, pHouse, pObject, pTrigger, location);
Expand Down Expand Up @@ -384,6 +390,100 @@ bool TActionExt::ToggleMCVRedeploy(TActionClass* pThis, HouseClass* pHouse, Obje
return true;
}

bool TActionExt::UndeployToWaypoint(TActionClass* const pThis, HouseClass* const pHouse, ObjectClass* const pObject, TriggerClass* const pTrigger, const CellStruct& location)
{
const auto& nCell = ScenarioExt::Global()->Waypoints[pThis->Waypoint];
CellClass* const pCell = MapClass::Instance.TryGetCellAt(nCell);

if (!pCell)
return true;

HouseClass* vHouse = nullptr;
const int houseIndex = pThis->Param3;

if (houseIndex >= 0)
{
vHouse = HouseClass::Index_IsMP(houseIndex)
? HouseClass::FindByIndex(houseIndex) : HouseClass::FindByCountryIndex(houseIndex);
}

if (!vHouse)
return true;

const char* buildingName = pThis->TechnoID;
bool allBuilding = false;
BuildingTypeClass* pBuildingType = nullptr;

if (!strcmp(buildingName, "<All>"))
{
allBuilding = true;
}
else
{
pBuildingType = BuildingTypeClass::Find(buildingName);
}

if (!allBuilding && !pBuildingType)
return true;

const auto& limboDelivereds = HouseExt::ExtMap.Find(vHouse)->OwnedLimboDeliveredBuildings;
const bool existLimboBuilding = !limboDelivereds.empty();

// Thanks to chaserli for the relevant code!
// There should be a more perfect way to do this, but I don't know how.
auto canUndeploy = [&](BuildingClass* const pBuilding)
{
auto const pType = pBuilding->Type;

if (!pType->UndeploysInto
|| pBuilding->Owner != vHouse
|| (!allBuilding && pType != pBuildingType)
|| pBuilding->CurrentMission == Mission::Selling
|| !pBuilding->IsAlive || pBuilding->Health <= 0 || pBuilding->InLimbo)
{
return false;
}

// verify whether the building's source is LimboDelivery.
if (existLimboBuilding)
{
for (auto const pLimboBuilding : limboDelivereds)
{
if (pLimboBuilding == pBuilding)
return false;
}
}

if (pType->ConstructionYard)
{
// Conyards can't undeploy if MCVRedeploy=no
if (!GameModeOptionsClass::Instance.MCVRedeploy)
return false;
// or MindControlledBy YURIX (why? for balance?)
if (!RulesExt::Global()->AllowDeployControlledMCV && pBuilding->MindControlledBy)
return false;
}

return true;
};

for (const auto pBuilding : BuildingClass::Array)
{
if (!canUndeploy(pBuilding))
continue;

// Why does having this allow it to undeploy?
// Why don't vehicles move when waypoints are placed off the map?

const bool old = std::exchange(VocClass::VoicesEnabled, false);
pBuilding->SetArchiveTarget(pCell);
pBuilding->Sell(true);
VocClass::VoicesEnabled = old;
}

return true;
}

bool TActionExt::EditAngerNode(TActionClass* pThis, HouseClass* pHouse, ObjectClass* pObject, TriggerClass* pTrigger, CellStruct const& location)
{
if (pHouse->AngerNodes.Count <= 0)
Expand Down Expand Up @@ -519,6 +619,59 @@ bool TActionExt::SetForceEnemy(TActionClass* pThis, HouseClass* pHouse, ObjectCl
return true;
}

bool TActionExt::SetFreeRadar(TActionClass* const pThis, HouseClass* const pHouse, ObjectClass* const pObject, TriggerClass* const pTrigger, const CellStruct& location)
{
if (pHouse->IsControlledByHuman())
{
auto const pHouseExt = HouseExt::ExtMap.Find(pHouse);

switch (pThis->Param3)
{
case 1:
pHouseExt->FreeRadar = true;
pHouseExt->ForceRadar = false;
break;
case 2:
pHouseExt->FreeRadar = true;
pHouseExt->ForceRadar = true;
break;
case 3:
pHouseExt->FreeRadar = false;
pHouseExt->ForceRadar = true;
break;
default:
pHouseExt->FreeRadar = false;
pHouseExt->ForceRadar = false;
break;
}

pHouse->RecheckRadar = true;
}

return true;
}

bool TActionExt::SetTeamDelay(TActionClass* const pThis, HouseClass* const pHouse, ObjectClass* const pObject, TriggerClass* const pTrigger, const CellStruct& location)
{
const int value = pThis->Param3;
const int timer = value < 0 ? RulesClass::Instance->TeamDelays.Items[static_cast<int>(pHouse->AIDifficulty)] : value;
HouseExt::ExtMap.Find(pHouse)->TeamDelay = value;

auto& Timer = pHouse->TeamDelayTimer;
const int time = std::min(Timer.GetTimeLeft(), timer);

if (Timer.StartTime == -1 && Timer.TimeLeft != 0 && time > 0)
{
Timer.TimeLeft = time;
}
else if (Timer.InProgress())
{
Timer.Start(time);
}

return true;
}

static void CreateOrReplaceBanner(TActionClass* pTAction, bool isGlobal)
{
const auto pBannerType = BannerTypeClass::Find(pTAction->Text);
Expand Down
Loading