From 0f48f16c4a2dda3e2357dc08ad6493b21d031ffa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=88=AA=E5=91=B3=E9=BA=BB=E9=85=B1?=
<93972760+TaranDahl@users.noreply.github.com>
Date: Wed, 7 Jan 2026 16:32:00 +0800
Subject: [PATCH 1/3] core
---
CREDITS.md | 1 +
Phobos.vcxproj | 2 ++
docs/User-Interface.md | 12 ++++++++++
docs/Whats-New.md | 1 +
src/Ext/Event/Body.cpp | 40 ++++++++++++++++++++++++++-----
src/Ext/Event/Body.h | 37 +++++++++++++++++++++-------
src/Ext/House/Body.cpp | 1 +
src/Ext/House/Body.h | 3 +++
src/Ext/House/Hooks.cpp | 53 +++++++++++++++++++++++++++++++++++++++++
src/Ext/Rules/Body.cpp | 3 +++
src/Ext/Rules/Body.h | 4 ++++
11 files changed, 142 insertions(+), 15 deletions(-)
diff --git a/CREDITS.md b/CREDITS.md
index db73b2649c..bce4653df0 100644
--- a/CREDITS.md
+++ b/CREDITS.md
@@ -662,6 +662,7 @@ This page lists all the individual contributions to the project by their author.
- Toggle per-target warhead effects apply timing
- Extra range for chasing and pre-firing
- Fix an issue that rockets do not consider the destination altitude during climbing
+ - Replace vanilla repairing with togglable auto repairing
- **solar-III (凤九歌)**
- Target scanning delay customization (documentation)
- Skip target scanning function calling for unarmed technos (documentation)
diff --git a/Phobos.vcxproj b/Phobos.vcxproj
index 2def0db9a8..afe15b208e 100644
--- a/Phobos.vcxproj
+++ b/Phobos.vcxproj
@@ -19,6 +19,7 @@
+
@@ -218,6 +219,7 @@
+
diff --git a/docs/User-Interface.md b/docs/User-Interface.md
index 28212091ca..7657fe8488 100644
--- a/docs/User-Interface.md
+++ b/docs/User-Interface.md
@@ -700,6 +700,18 @@ In `rulesmd.ini`:
Sidebar.ProducingProgress.Offset=0,0 ; X,Y, pixels relative to default
```
+### Replace vanilla repairing with togglable auto repairing
+
+- Now you can replace the vanilla repair method with a togglable auto-repair.
+ - Pressing repair button or hotkey will no longer change your mouse, but will toggle your auto-repair state on/off.
+ - When auto-repair state is toggled off, buildings will stop repairing.
+
+ In `rulesmd.ini`:
+```ini
+[General]
+ExtendedPlayerRepair=false ; boolean
+```
+
### Specify Sidebar style
- It's now possible to switch hardcoded sidebar button coords to use GDI sidebar coords by setting `Sidebar.GDIPosition`. Defaults to true for first side, false for all others.
diff --git a/docs/Whats-New.md b/docs/Whats-New.md
index 10c5a6a3ec..3bf417950b 100644
--- a/docs/Whats-New.md
+++ b/docs/Whats-New.md
@@ -480,6 +480,7 @@ New:
- Allow techno type considered as other type when recruiting techno for teams (by NetsuNegi)
- 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)
- Fixed an issue that rockets do not consider the destination altitude during climbing (by TaranDahl)
+- [Replace vanilla repairing with togglable auto repairing](User-Interface.md#replace-vanilla-repairing-with-togglable-auto-repairing) (by TaranDahl)
Vanilla fixes:
- Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya)
diff --git a/src/Ext/Event/Body.cpp b/src/Ext/Event/Body.cpp
index c4c471c55d..c05290f29b 100644
--- a/src/Ext/Event/Body.cpp
+++ b/src/Ext/Event/Body.cpp
@@ -1,8 +1,12 @@
-/*
+
#include "Body.h"
+#include
+
#include
#include
+#include
+#include
bool EventExt::AddEvent()
{
@@ -13,8 +17,8 @@ void EventExt::RespondEvent()
{
switch (this->Type)
{
- case EventTypeExt::Sample:
- // Place the handler here
+ case EventTypeExt::TogglePlayerAutoRepair:
+ this->RespondToTogglePlayerAutoRepair();
break;
}
}
@@ -23,8 +27,8 @@ size_t EventExt::GetDataSize(EventTypeExt type)
{
switch (type)
{
- case EventTypeExt::Sample:
- return sizeof(EventExt::Sample);
+ case EventTypeExt::TogglePlayerAutoRepair:
+ return sizeof(EventExt::TogglePlayerAutoRepair);
}
return 0;
@@ -35,6 +39,30 @@ bool EventExt::IsValidType(EventTypeExt type)
return (type >= EventTypeExt::FIRST && type <= EventTypeExt::LAST);
}
+void EventExt::RespondToTogglePlayerAutoRepair()
+{
+ if (this->HouseIndex >= HouseClass::Array.Count)
+ return;
+
+ if (!RulesExt::Global()->ExtendedPlayerRepair)
+ return;
+
+ auto pHouse = HouseClass::Array.GetItem(this->HouseIndex);
+ auto pHouseExt = HouseExt::ExtMap.Find(pHouse);
+ pHouseExt->PlayerAutoRepair = !pHouseExt->PlayerAutoRepair;
+
+ if (HouseClass::CurrentPlayer == pHouse)
+ {
+ SidebarClass::Instance.SidebarNeedsRedraw = true;
+ auto pButton = &Make_Global(0xB0B3A0);
+
+ if (pHouseExt->PlayerAutoRepair)
+ pButton->TurnOn();
+ else
+ pButton->TurnOff();
+ }
+}
+
// hooks
DEFINE_HOOK(0x4C6CC8, Networking_RespondToEvent, 0x5)
@@ -98,4 +126,4 @@ DEFINE_HOOK(0x64C30E, sub_64BDD0_GetEventSize2, 0x6)
return 0;
}
-*/
+
diff --git a/src/Ext/Event/Body.h b/src/Ext/Event/Body.h
index a9d37294e6..d4f81096e4 100644
--- a/src/Ext/Event/Body.h
+++ b/src/Ext/Event/Body.h
@@ -1,18 +1,20 @@
#pragma once
-/*
+
#include
#include
+#include
+
enum class EventTypeExt : uint8_t
{
// Vanilla game used Events from 0x00 to 0x2F
// CnCNet reserved Events from 0x30 to 0x3F
// Ares used Events 0x60 and 0x61
- Sample = 0x40, // Sample event, remove it when Phobos needs its own events
+ TogglePlayerAutoRepair = 0x40,
- FIRST = Sample,
- LAST = Sample
+ FIRST = TogglePlayerAutoRepair,
+ LAST = TogglePlayerAutoRepair
};
#pragma pack(push, 1)
@@ -27,15 +29,32 @@ class EventExt
{
char DataBuffer[104];
- struct Sample
- {
- char DataBuffer[104];
- } Sample;
+ struct TogglePlayerAutoRepair
+ { } TogglePlayerAutoRepair;
};
bool AddEvent();
void RespondEvent();
+ EventExt(EventTypeExt type)
+ {
+ this->Type = type;
+ this->IsExecuted = false;
+ this->HouseIndex = (char)HouseClass::CurrentPlayer->ArrayIndex;
+ this->Frame = Unsorted::CurrentFrame;
+
+ switch (type)
+ {
+ case EventTypeExt::TogglePlayerAutoRepair:
+ this->TogglePlayerAutoRepair = {};
+ break;
+ default:
+ this->TogglePlayerAutoRepair = {};
+ break;
+ }
+ }
+ void RespondToTogglePlayerAutoRepair();
+
static size_t GetDataSize(EventTypeExt type);
static bool IsValidType(EventTypeExt type);
};
@@ -43,4 +62,4 @@ class EventExt
static_assert(sizeof(EventExt) == 111);
static_assert(offsetof(EventExt, DataBuffer) == 7);
#pragma pack(pop)
-*/
+
diff --git a/src/Ext/House/Body.cpp b/src/Ext/House/Body.cpp
index 3adeb78310..6cbcd54496 100644
--- a/src/Ext/House/Body.cpp
+++ b/src/Ext/House/Body.cpp
@@ -688,6 +688,7 @@ void HouseExt::ExtData::Serialize(T& Stm)
.Process(this->TeamDelay)
.Process(this->FreeRadar)
.Process(this->ForceRadar)
+ .Process(this->PlayerAutoRepair)
;
}
diff --git a/src/Ext/House/Body.h b/src/Ext/House/Body.h
index 8d1e60518c..1bd619f404 100644
--- a/src/Ext/House/Body.h
+++ b/src/Ext/House/Body.h
@@ -72,6 +72,8 @@ class HouseExt
bool FreeRadar;
bool ForceRadar;
+ bool PlayerAutoRepair;
+
ExtData(HouseClass* OwnerObject) : Extension(OwnerObject)
, PowerPlantEnhancers {}
, OwnedLimboDeliveredBuildings {}
@@ -105,6 +107,7 @@ class HouseExt
, TeamDelay(-1)
, FreeRadar(false)
, ForceRadar(false)
+ , PlayerAutoRepair(false)
{ }
bool OwnsLimboDeliveredBuilding(BuildingClass* pBuilding) const;
diff --git a/src/Ext/House/Hooks.cpp b/src/Ext/House/Hooks.cpp
index 0489338586..e024a81b5e 100644
--- a/src/Ext/House/Hooks.cpp
+++ b/src/Ext/House/Hooks.cpp
@@ -4,6 +4,8 @@
#include
#include "Ext/Techno/Body.h"
#include "Ext/Building/Body.h"
+#include
+
#include
DEFINE_HOOK(0x508C30, HouseClass_UpdatePower_UpdateCounter, 0x5)
@@ -528,3 +530,54 @@ DEFINE_HOOK(0x508E17, HouseClass_UpdateRadar_FreeRadar, 0x8)
return Continue;
}
+
+#pragma region PlayerAutoRepair
+
+DEFINE_HOOK(0x536FA0, ToggleRepariModeCommandClass_Execute_PlayerAutoRepair, 0x7)
+{
+ if (!RulesExt::Global()->ExtendedPlayerRepair)
+ return 0;
+
+ EventExt(EventTypeExt::TogglePlayerAutoRepair).AddEvent();
+ return 0x536FAC;
+}
+
+DEFINE_HOOK(0x6A78F6, SidebarClass_Update_ToggleRepair, 0x9)
+{
+ if (!RulesExt::Global()->ExtendedPlayerRepair)
+ MapClass::Instance.SetRepairMode(-1);
+ else
+ EventExt(EventTypeExt::TogglePlayerAutoRepair).AddEvent();
+ return 0x6A78FF;
+}
+
+DEFINE_HOOK(0x6A7AE1, SidebarClass_Update_RepairButton, 0x6)
+{
+ if (!RulesExt::Global()->ExtendedPlayerRepair)
+ return 0;
+
+ R->AL(HouseExt::ExtMap.Find(HouseClass::CurrentPlayer)->PlayerAutoRepair);
+ return 0x6A7AE7;
+}
+
+DEFINE_HOOK(0x45063F, BuildingClass_UpdateRepairSell_PlayerAutoRepair, 0x6)
+{
+ enum { CanAutoRepair = 0x450659, CanNotAutoRepair = 0x450813 };
+
+ if (!RulesExt::Global()->ExtendedPlayerRepair)
+ return 0;
+
+ GET(BuildingClass*, pThis, ESI);
+
+ if (HouseExt::ExtMap.Find(pThis->Owner)->PlayerAutoRepair)
+ {
+ return CanAutoRepair;
+ }
+ else
+ {
+ pThis->SetRepairState(0);
+ return CanNotAutoRepair;
+ }
+}
+
+#pragma endregion
diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp
index df18a5bd60..128507991d 100644
--- a/src/Ext/Rules/Body.cpp
+++ b/src/Ext/Rules/Body.cpp
@@ -350,6 +350,8 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI)
this->ExtraRange_Prefiring.Read(exINI, GameStrings::General, "ExtraRange.Prefiring");
this->ExtraRange_Prefiring_IncludeBurst.Read(exINI, GameStrings::General, "ExtraRange.Prefiring.IncludeBurst");
+ this->ExtendedPlayerRepair.Read(exINI, GameStrings::General, "ExtendedPlayerRepair");
+
// Section AITargetTypes
int itemsCount = pINI->GetKeyCount("AITargetTypes");
for (int i = 0; i < itemsCount; ++i)
@@ -634,6 +636,7 @@ void RulesExt::ExtData::Serialize(T& Stm)
.Process(this->ExtraRange_FirerMoving)
.Process(this->ExtraRange_Prefiring)
.Process(this->ExtraRange_Prefiring_IncludeBurst)
+ .Process(this->ExtendedPlayerRepair)
;
}
diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h
index 6d0b6404ab..4a51cb4bc9 100644
--- a/src/Ext/Rules/Body.h
+++ b/src/Ext/Rules/Body.h
@@ -298,6 +298,8 @@ class RulesExt
Valueable ExtraRange_Prefiring_IncludeBurst;
Valueable ApplyPerTargetEffectsOnDetonate;
+
+ Valueable ExtendedPlayerRepair;
ExtData(RulesClass* OwnerObject) : Extension(OwnerObject)
, Storage_TiberiumIndex { -1 }
@@ -536,6 +538,8 @@ class RulesExt
, ExtraRange_FirerMoving { Leptons(0) }
, ExtraRange_Prefiring { Leptons(0) }
, ExtraRange_Prefiring_IncludeBurst { true }
+
+ , ExtendedPlayerRepair { false }
{ }
virtual ~ExtData() = default;
From 04a36210b7b644554b73cf311c141e4d91dac466 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=88=AA=E5=91=B3=E9=BA=BB=E9=85=B1?=
<93972760+TaranDahl@users.noreply.github.com>
Date: Wed, 7 Jan 2026 22:46:20 +0800
Subject: [PATCH 2/3] update
---
src/Ext/Event/Body.cpp | 10 ++++++++++
src/Ext/Event/Body.h | 18 +-----------------
src/Ext/House/Hooks.cpp | 4 ++--
3 files changed, 13 insertions(+), 19 deletions(-)
diff --git a/src/Ext/Event/Body.cpp b/src/Ext/Event/Body.cpp
index c05290f29b..7a159ad0d4 100644
--- a/src/Ext/Event/Body.cpp
+++ b/src/Ext/Event/Body.cpp
@@ -23,6 +23,16 @@ void EventExt::RespondEvent()
}
}
+void EventExt::RaiseTogglePlayerAutoRepair()
+{
+ EventExt eventExt {};
+ eventExt.Type = EventTypeExt::TogglePlayerAutoRepair;
+ eventExt.HouseIndex = (char)HouseClass::CurrentPlayer->ArrayIndex; // not used
+ eventExt.Frame = Unsorted::CurrentFrame;
+ eventExt.AddEvent();
+ Debug::LogGame("Adding event TOGGLE_PLAYER_AUTOREPAIR\n");
+}
+
size_t EventExt::GetDataSize(EventTypeExt type)
{
switch (type)
diff --git a/src/Ext/Event/Body.h b/src/Ext/Event/Body.h
index d4f81096e4..b0e3b279b7 100644
--- a/src/Ext/Event/Body.h
+++ b/src/Ext/Event/Body.h
@@ -36,23 +36,7 @@ class EventExt
bool AddEvent();
void RespondEvent();
- EventExt(EventTypeExt type)
- {
- this->Type = type;
- this->IsExecuted = false;
- this->HouseIndex = (char)HouseClass::CurrentPlayer->ArrayIndex;
- this->Frame = Unsorted::CurrentFrame;
-
- switch (type)
- {
- case EventTypeExt::TogglePlayerAutoRepair:
- this->TogglePlayerAutoRepair = {};
- break;
- default:
- this->TogglePlayerAutoRepair = {};
- break;
- }
- }
+ static void RaiseTogglePlayerAutoRepair();
void RespondToTogglePlayerAutoRepair();
static size_t GetDataSize(EventTypeExt type);
diff --git a/src/Ext/House/Hooks.cpp b/src/Ext/House/Hooks.cpp
index e024a81b5e..eb710358d3 100644
--- a/src/Ext/House/Hooks.cpp
+++ b/src/Ext/House/Hooks.cpp
@@ -538,7 +538,7 @@ DEFINE_HOOK(0x536FA0, ToggleRepariModeCommandClass_Execute_PlayerAutoRepair, 0x7
if (!RulesExt::Global()->ExtendedPlayerRepair)
return 0;
- EventExt(EventTypeExt::TogglePlayerAutoRepair).AddEvent();
+ EventExt::RaiseTogglePlayerAutoRepair();
return 0x536FAC;
}
@@ -547,7 +547,7 @@ DEFINE_HOOK(0x6A78F6, SidebarClass_Update_ToggleRepair, 0x9)
if (!RulesExt::Global()->ExtendedPlayerRepair)
MapClass::Instance.SetRepairMode(-1);
else
- EventExt(EventTypeExt::TogglePlayerAutoRepair).AddEvent();
+ EventExt::RaiseTogglePlayerAutoRepair();
return 0x6A78FF;
}
From 460a742b8e4fef63477aa5f1a23534fc4bedd5cd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=88=AA=E5=91=B3=E9=BA=BB=E9=85=B1?=
<93972760+TaranDahl@users.noreply.github.com>
Date: Wed, 7 Jan 2026 23:01:37 +0800
Subject: [PATCH 3/3] Update Hooks.cpp
---
src/Ext/House/Hooks.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/Ext/House/Hooks.cpp b/src/Ext/House/Hooks.cpp
index eb710358d3..5377434fa0 100644
--- a/src/Ext/House/Hooks.cpp
+++ b/src/Ext/House/Hooks.cpp
@@ -575,7 +575,8 @@ DEFINE_HOOK(0x45063F, BuildingClass_UpdateRepairSell_PlayerAutoRepair, 0x6)
}
else
{
- pThis->SetRepairState(0);
+ if (pThis->IsBeingRepaired)
+ pThis->SetRepairState(0);
return CanNotAutoRepair;
}
}