Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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 @@ -763,3 +763,4 @@ This page lists all the individual contributions to the project by their author.
- **RAZER**:
- Wall overlay unit sell exploit fix
- Multiplayer gamespeed fix for RealTimeTimers
- Rebuild **X** keyboard commands
2 changes: 2 additions & 0 deletions Phobos.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
<ClCompile Include="src\Blowfish\Hooks.Blowfish.cpp" />
<ClCompile Include="src\Ext\Cell\Body.cpp" />
<ClCompile Include="src\Ext\House\Hooks.ForceEnemy.cpp" />
<ClCompile Include="src\Commands\BuildLastOfTab.cpp" />
<ClCompile Include="src\Commands\ToggleSWSidebar.cpp" />
<ClCompile Include="src\Ext\Sidebar\SWSidebar\SWColumnClass.cpp" />
<ClCompile Include="src\Ext\Sidebar\SWSidebar\SWSidebarClass.cpp" />
Expand Down Expand Up @@ -132,6 +133,7 @@
<ClCompile Include="src\Ext\House\Body.cpp" />
<ClCompile Include="src\Ext\House\Hooks.cpp" />
<ClCompile Include="src\Ext\House\Hooks.AINavalProduction.cpp" />
<ClCompile Include="src\Ext\House\Hooks.BuildLastTab.cpp" />
<ClCompile Include="src\Ext\House\Hooks.UnitFromFactory.cpp" />
<ClCompile Include="src\Ext\ParticleSystemType\Body.cpp" />
<ClCompile Include="src\Ext\RadSite\Body.cpp" />
Expand Down
12 changes: 12 additions & 0 deletions docs/User-Interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,18 @@ For this command to work in multiplayer - you need to use a version of [YRpp spa
- Switches on/off [Task subtitles' label in the middle of the screen](#task-subtitles-display-in-the-middle-of-the-screen).
- For localization add `TXT_TOGGLE_MESSAGE` and `TXT_TOGGLE_MESSAGE_DESC` into your `.csf` file.

### `[ ]` Rebuild Structure
- Re-queue the last produced Power/Resource/Tech Building you placed.

### `[ ]` Rebuild Defense
- Re-queue the last produced Defensive Building you placed.

### `[ ]` Rebuild Infantry
- Re-queue the last produced Infantry you built.

### `[ ]` Rebuild Vehicle
- Re-queue the last produced Vehicle you built.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it would be best to handle Aircraft and Ship separately as well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

So 3 potential keybinds for last tab? Vehicle / navy / aircraft?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Implemented this change. Let me know what you think

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So 3 potential keybinds for last tab? Vehicle / navy / aircraft?

Yeah. In short, the key should bind to the production line instead of the tab.
In addition, the old code should be modified to ensure consistency (i.e., set the shortcut keys according to the production line instead of the tab).


## Loading screen

- PCX files can now be used as loadscreen images.
Expand Down
1 change: 1 addition & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,7 @@ Fixes / interactions with other extensions:
- Fixed the issue that technos cannot spawn survivors due to non-probabilistic reasons when the tech type was destroyed (by NetsuNegi)
- Fixed the bug that vehicle survivor can spawn on wrong position when transport has been destroyed (by NetsuNegi)
- Fixed the bug that building with `Explodes=yes` use Ares's rubble logic will cause it's owner cannot defeat normally (by NetsuNegi)
- Four new keyboard commands under `Interface`: `Rebuild Structure`, `Rebuild Defense`, `Rebuild Infantry` and `Rebuild Vehicle` (by RAZER)
```

### 0.4.0.3
Expand Down
104 changes: 104 additions & 0 deletions src/Commands/BuildLastOfTab.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#include "BuildLastOfTab.h"

#include <Utilities/GeneralUtils.h>
#include <Ext/House/Body.h>
#include <HouseClass.h>
#include <EventClass.h>
#include <SidebarClass.h>

static constexpr const char* BuildLastTabNames[4] =
{
"RebuildStructure",
"RebuildDefense",
"RebuildInfantry",
"RebuildVehicle",
};

static constexpr const char* BuildLastTabDescKeys[4] =
{
"RebuildStructure_Desc",
"RebuildDefense_Desc",
"RebuildInfantry_Desc",
"RebuildVehicle_Desc",
};

static constexpr const wchar_t* BuildLastTabUINames[4] =
{
L"Rebuild Structure",
L"Rebuild Defense",
L"Rebuild Infantry",
L"Rebuild Vehicle",
};

static constexpr const wchar_t* BuildLastTabUIDescs[4] =
{
L"Re-queue the last produced Power/Resources building.",
L"Re-queue the last produced Defense/Combat building.",
L"Re-queue the last produced Infantry unit.",
L"Re-queue the last produced Vehicle or Aircraft.",
};

template<int TabIndex>
const char* BuildLastOfTabCommandClass<TabIndex>::GetName() const
{
return BuildLastTabNames[TabIndex];
}

template<int TabIndex>
const wchar_t* BuildLastOfTabCommandClass<TabIndex>::GetUIName() const
{
return GeneralUtils::LoadStringUnlessMissing(BuildLastTabNames[TabIndex], BuildLastTabUINames[TabIndex]);
}

template<int TabIndex>
const wchar_t* BuildLastOfTabCommandClass<TabIndex>::GetUICategory() const
{
return CATEGORY_INTERFACE;
}

template<int TabIndex>
const wchar_t* BuildLastOfTabCommandClass<TabIndex>::GetUIDescription() const
{
static_assert(TabIndex >= 0 && TabIndex < 4, "TabIndex out of range");
return GeneralUtils::LoadStringUnlessMissing(BuildLastTabDescKeys[TabIndex], BuildLastTabUIDescs[TabIndex]);
}

template<int TabIndex>
void BuildLastOfTabCommandClass<TabIndex>::Execute(WWKey eInput) const
{
auto const pPlayer = HouseClass::CurrentPlayer;
if (!pPlayer)
return;

auto const pExt = HouseExt::ExtMap.Find(pPlayer);
if (!pExt)
return;

auto const typeIndex = pExt->LastBuiltPerTab[TabIndex];
if (typeIndex < 0)
return;

// Focus the sidebar to the corresponding tab.
if (SidebarClass::Instance.IsSidebarActive)
{
SidebarClass::Instance.ActiveTabIndex = TabIndex;
SidebarClass::Instance.SidebarNeedsRepaint();
}

auto const rtti = pExt->LastBuiltRTTIPerTab[TabIndex];
auto const isNaval = pExt->LastBuiltIsNavalPerTab[TabIndex];

EventClass::OutList.Add(EventClass(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I am pretty sure this bypasses all the legality checks?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It will get rejected by the house if the house can't build it. That said, what we did in Vinifera is check if it's present on th sidebar as an icon as a shortcut.

pPlayer->ArrayIndex,
EventType::Produce,
static_cast<int>(rtti),
typeIndex,
isNaval ? TRUE : FALSE
));
}

// Explicit instantiations
template class BuildLastOfTabCommandClass<0>;
template class BuildLastOfTabCommandClass<1>;
template class BuildLastOfTabCommandClass<2>;
template class BuildLastOfTabCommandClass<3>;
16 changes: 16 additions & 0 deletions src/Commands/BuildLastOfTab.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include "Commands.h"

// Re-queue the last produced item from a specific production tab.
// TabIndex: 0 = Power/Infrastructure, 1 = Defense/Combat, 2 = Infantry, 3 = Vehicles/Aircraft
template<int TabIndex>
class BuildLastOfTabCommandClass : public CommandClass
{
public:
virtual const char* GetName() const override;
virtual const wchar_t* GetUIName() const override;
virtual const wchar_t* GetUICategory() const override;
virtual const wchar_t* GetUIDescription() const override;
virtual void Execute(WWKey eInput) const override;
};
5 changes: 5 additions & 0 deletions src/Commands/Commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "ToggleSWSidebar.h"
#include "FireTacticalSW.h"
#include "ToggleMessageList.h"
#include "BuildLastOfTab.h"

#include <CCINIClass.h>

Expand All @@ -28,6 +29,10 @@ DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6)
MakeCommand<ToggleDesignatorRangeCommandClass>();
MakeCommand<ToggleMessageListCommandClass>();
MakeCommand<ToggleSWSidebar>();
MakeCommand<BuildLastOfTabCommandClass<0>>();
MakeCommand<BuildLastOfTabCommandClass<1>>();
MakeCommand<BuildLastOfTabCommandClass<2>>();
MakeCommand<BuildLastOfTabCommandClass<3>>();

if (Phobos::Config::SuperWeaponSidebarCommands)
{
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 @@ -696,6 +696,9 @@ void HouseExt::ExtData::Serialize(T& Stm)
.Process(this->RestrictedFactoryPlants)
.Process(this->LastBuiltNavalVehicleType)
.Process(this->ProducingNavalUnitTypeIndex)
.Process(this->LastBuiltPerTab)
.Process(this->LastBuiltRTTIPerTab)
.Process(this->LastBuiltIsNavalPerTab)
.Process(this->CombatAlertTimer)
.Process(this->NumAirpads_NonMFB)
.Process(this->NumBarracks_NonMFB)
Expand Down
9 changes: 9 additions & 0 deletions src/Ext/House/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ class HouseExt
int LastBuiltNavalVehicleType;
int ProducingNavalUnitTypeIndex;

// Per-tab last-built tracking for the Build Last commands.
// Indexed by sidebar tab: 0=Power/Infra, 1=Combat, 2=Infantry, 3=Vehicles/Aircraft.
int LastBuiltPerTab[4];
AbstractType LastBuiltRTTIPerTab[4];
bool LastBuiltIsNavalPerTab[4];

// Factories that exist but don't count towards multiple factory bonus.
int NumAirpads_NonMFB;
int NumBarracks_NonMFB;
Expand Down Expand Up @@ -85,6 +91,9 @@ class HouseExt
, RestrictedFactoryPlants {}
, LastBuiltNavalVehicleType { -1 }
, ProducingNavalUnitTypeIndex { -1 }
, LastBuiltPerTab { -1, -1, -1, -1 }
, LastBuiltRTTIPerTab { AbstractType::None, AbstractType::None, AbstractType::None, AbstractType::None }
, LastBuiltIsNavalPerTab { false, false, false, false }
, CombatAlertTimer {}
, NumAirpads_NonMFB { 0 }
, NumBarracks_NonMFB { 0 }
Expand Down
42 changes: 42 additions & 0 deletions src/Ext/House/Hooks.BuildLastTab.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include "Body.h"

#include <SidebarClass.h>
#include <BuildingTypeClass.h>
#include <TechnoClass.h>

// Hook into HouseClass::RegisterObjectGain_FromFactory (0x4FB6B0).
// Fires when a unit or building is delivered from a factory.
// At this point: EDI = HouseClass*, EBP = TechnoClass* (the object being delivered).
// We track the delivered type per sidebar tab so the "Build Last" commands can re-queue it.

DEFINE_HOOK(0x4FB6C2, HouseClass_RegisterObjectGain_TrackLastBuiltTab, 0x7)
{
GET(HouseClass* const, pThis, EDI);
GET(TechnoClass* const, pTechno, EBP);

if (pThis != HouseClass::CurrentPlayer)
return 0;

auto const pType = pTechno->GetTechnoType();
if (!pType)
return 0;

auto const absType = pType->WhatAmI();
auto const isNaval = pType->Naval;

auto buildCat = BuildCat::DontCare;
if (absType == AbstractType::BuildingType)
buildCat = static_cast<BuildingTypeClass const*>(pType)->BuildCat;

int const tabIdx = SidebarClass::GetObjectTabIdx(absType, buildCat, isNaval);

if (tabIdx >= 0 && tabIdx < 4)
{
auto const pExt = HouseExt::ExtMap.Find(pThis);
pExt->LastBuiltPerTab[tabIdx] = pType->GetArrayIndex();
pExt->LastBuiltRTTIPerTab[tabIdx] = absType;
pExt->LastBuiltIsNavalPerTab[tabIdx] = isNaval;
}

return 0;
}