diff --git a/Phobos.vcxproj b/Phobos.vcxproj
index 819c7037b0..87ee4f3c6a 100644
--- a/Phobos.vcxproj
+++ b/Phobos.vcxproj
@@ -32,6 +32,7 @@
+
diff --git a/README.md b/README.md
index 327226c265..169229e17b 100644
--- a/README.md
+++ b/README.md
@@ -63,7 +63,7 @@ Credits
- wiktorderelf - overhauled Unicode font
- Thrifinesma (Uranusian) - Mind Control enhancement, custom warhead splash list, harvesters counter, promoted spawns, shields, overhauled Unicode font, help with docs
- SEC-SOME (secsome) - debug info dump hotkey, refactoring & porting of Ares helper code, introducing more Ares-derived stuff, disguise removal warhead, Mind Control removal warhead, Mind Control enhancement, shields
-- Otamaa (BoredEXE) - help with CellSpread, ported and fixed custom RadType code , Disable spesific ElectricBolt bolt
+- Otamaa (BoredEXE) - help with CellSpread, ported and fixed custom RadType code, togglable ElectricBolt bolts, customizable Chrono Locomotor properties per TechnoClass
- E1 Elite - TileSet 255 and above bridge repair fix
- FS-21 - Dump Object Info enhancements, Powered.KillSpawns, Spawner.LimitRange, ScriptType Actions 71, 72 & 73, MC deployer fixes, help with docs
- AutoGavy - interceptor logic, warhead critical damage system
diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md
index 415ff0cc4a..ff71d7d8e4 100644
--- a/docs/Fixed-or-Improved-Logics.md
+++ b/docs/Fixed-or-Improved-Logics.md
@@ -57,6 +57,30 @@ SpawnsTiberium.Range=1 ; integer, radius in cells
SpawnsTiberium.GrowthStage=3 ; single int / comma-sep. range
```
+## TechnoType
+
+### Customizable Teleport/Chrono Locomotor settings per TechnoType
+
+
+*Chrono Legionere and Ronco (hero) from [YR:New War] (https://www.moddb.com/mods/yuris-revenge-new-war)*
+
+- You can now specify Teleport/Chrono Locomotor settings per TechnoType to override default rules values. Unfilled values default to values in `[General]`.
+- Only applicable to Techno that have Teleport/Chrono Locomotor attached.
+
+In `rulesmd.ini`:
+```ini
+[SOMETECHNO] ; TechnoType
+WarpOut= ; Anim (played when Techno warping out)
+WarpIn= ; Anim (played when Techno warping in)
+WarpAway= ; Anim (played when Techno chronowarped by chronosphere)
+ChronoTrigger= ; boolean, if yes then delay varies by distance, if no it is a constant
+ChronoDistanceFactor= ; integer, amount to divide the distance to destination by to get the warped out delay
+ChronoMinimumDelay= ; integer, the minimum delay for teleporting, no matter how short the distance
+ChronoRangeMinimum= ; integer, can be used to set a small range within which the delay is constant
+ChronoDelay= ; integer, delay after teleport for chronosphere
+
+```
+
## Weapons
### Togglable ElectricBolt visuals
diff --git a/docs/_static/images/cust-Chrono.gif b/docs/_static/images/cust-Chrono.gif
new file mode 100644
index 0000000000..68ba3dff56
Binary files /dev/null and b/docs/_static/images/cust-Chrono.gif differ
diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp
index b373d0e066..0f8b58eb87 100644
--- a/src/Ext/TechnoType/Body.cpp
+++ b/src/Ext/TechnoType/Body.cpp
@@ -117,6 +117,17 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
this->Shield_HitAnim.Read(exINI, pSection, "Shield.HitAnim");
this->Shield_BracketDelta.Read(exINI, pSection, "Shield.BracketDelta");
+ //
+ this->WarpOut.Read(exINI, pSection, "WarpOut");
+ this->WarpIn.Read(exINI, pSection, "WarpIn");
+ this->WarpAway.Read(exINI, pSection, "WarpAway");
+ this->ChronoTrigger.Read(exINI, pSection, "ChronoTrigger");
+ this->ChronoDistanceFactor.Read(exINI, pSection, "ChronoDistanceFactor");
+ this->ChronoMinimumDelay.Read(exINI, pSection, "ChronoMinimumDelay");
+ this->ChronoRangeMinimum.Read(exINI, pSection, "ChronoRangeMinimum");
+ this->ChronoDelay.Read(exINI, pSection, "ChronoDelay");
+
+
// Ares 0.A
this->GroupAs.Read(pINI, pSection, "GroupAs");
@@ -158,6 +169,14 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm)
.Process(this->Shield_BreakAnim)
.Process(this->Shield_RespawnAnim)
.Process(this->Shield_HitAnim)
+ .Process(this->WarpOut)
+ .Process(this->WarpIn)
+ .Process(this->WarpAway)
+ .Process(this->ChronoTrigger)
+ .Process(this->ChronoDistanceFactor)
+ .Process(this->ChronoMinimumDelay)
+ .Process(this->ChronoRangeMinimum)
+ .Process(this->ChronoDelay)
;
}
void TechnoTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm)
diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h
index 0731acfb2d..a6386ba53a 100644
--- a/src/Ext/TechnoType/Body.h
+++ b/src/Ext/TechnoType/Body.h
@@ -45,6 +45,15 @@ class TechnoTypeExt
Nullable Shield_RespawnAnim;
Nullable Shield_HitAnim;
+ Nullable WarpOut;
+ Nullable WarpIn;
+ Nullable WarpAway;
+ Nullable ChronoTrigger;
+ Nullable ChronoDistanceFactor;
+ Nullable ChronoMinimumDelay;
+ Nullable ChronoRangeMinimum;
+ Nullable ChronoDelay;
+
ExtData(TechnoTypeClass* OwnerObject) : Extension(OwnerObject),
HealthBar_Hide(false),
UIDescription(),
@@ -74,7 +83,15 @@ class TechnoTypeExt
Shield_IdleAnim(),
Shield_BreakAnim(),
Shield_RespawnAnim(),
- Shield_HitAnim()
+ Shield_HitAnim(),
+ WarpOut(),
+ WarpIn(),
+ WarpAway(),
+ ChronoTrigger(),
+ ChronoDistanceFactor(),
+ ChronoMinimumDelay(),
+ ChronoRangeMinimum(),
+ ChronoDelay()
{ }
virtual ~ExtData() = default;
diff --git a/src/Ext/TechnoType/Hooks.Teleport.cpp b/src/Ext/TechnoType/Hooks.Teleport.cpp
new file mode 100644
index 0000000000..09cd876caf
--- /dev/null
+++ b/src/Ext/TechnoType/Hooks.Teleport.cpp
@@ -0,0 +1,99 @@
+#include "Body.h"
+#include
+#include
+
+#define GET_LOCO(reg_Loco) \
+ GET(ILocomotion *, Loco, reg_Loco); \
+ TeleportLocomotionClass *pLocomotor = static_cast(Loco); \
+ TechnoTypeClass *pType = pLocomotor->LinkedTo->GetTechnoType(); \
+ TechnoTypeExt::ExtData *pExt = TechnoTypeExt::ExtMap.Find(pType);
+
+DEFINE_HOOK(719439, TeleportLocomotionClass_ILocomotion_Process_WarpoutAnim, 6)
+{
+ GET_LOCO(ESI);
+
+ R->EDX(pExt->WarpOut.Get(RulesClass::Instance->WarpOut));
+
+ return 0x71943F;
+}
+
+DEFINE_HOOK(719788, TeleportLocomotionClass_ILocomotion_Process_WarpInAnim, 6)
+{
+ GET_LOCO(ESI);
+
+ R->EDX(pExt->WarpIn.Get(RulesClass::Instance->WarpOut));
+
+ return 0x71978E;
+}
+
+DEFINE_HOOK(71986A, TeleportLocomotionClass_ILocomotion_Process_WarpAway, 6)
+{
+ GET_LOCO(ESI);
+
+ R->ECX(pExt->WarpAway.Get(RulesClass::Instance->WarpOut));
+
+ return 0x719870;
+}
+
+DEFINE_HOOK(7194D0, TeleportLocomotionClass_ILocomotion_Process_ChronoTrigger, 6)
+{
+ GET_LOCO(ESI);
+
+ R->AL(pExt->ChronoTrigger.Get(RulesClass::Instance->ChronoTrigger));
+
+ return 0x7194D6;
+}
+
+DEFINE_HOOK(7194E3, TeleportLocomotionClass_ILocomotion_Process_ChronoDistanceFactor, 6)
+{
+ GET_LOCO(ESI);
+ GET(int, val, EAX);
+
+ auto factor = pExt->ChronoDistanceFactor.Get(RulesClass::Instance->ChronoDistanceFactor);
+ factor = factor == 0 ? 1 : factor; //fix factor 0 crash by force it to 1 (Vanilla bug)
+
+ //IDIV
+ R->EAX(val / factor);
+ R->EDX(val % factor);
+
+ return 0x7194E9;
+}
+
+DEFINE_HOOK(719519, TeleportLocomotionClass_ILocomotion_Process_ChronoMinimumDelay, 6)
+{
+ GET_LOCO(ESI);
+
+ R->EBX(pExt->ChronoMinimumDelay.Get(RulesClass::Instance->ChronoMinimumDelay));
+
+ return 0x71951F;
+}
+
+DEFINE_HOOK(719562, TeleportLocomotionClass_ILocomotion_Process_ChronoMinimumDelay2, 6)
+{
+ GET_LOCO(ESI);
+
+ R->ECX(pExt->ChronoMinimumDelay.Get(RulesClass::Instance->ChronoMinimumDelay));
+
+ return 0x719568;
+}
+
+DEFINE_HOOK(719555, TeleportLocomotionClass_ILocomotion_Process_ChronoRangeMinimum, 6)
+{
+ GET_LOCO(ESI);
+ GET(int, comparator, EDX);
+
+ auto factor = pExt->ChronoRangeMinimum.Get(RulesClass::Instance->ChronoRangeMinimum);
+
+ return comparator < factor ? 0x71955D : 0x719576;
+}
+
+DEFINE_HOOK(71997B, TeleportLocomotionClass_ILocomotion_Process_ChronoDelay, 6)
+{
+ GET_LOCO(ESI);
+
+ R->ECX(pExt->ChronoDelay.Get(RulesClass::Instance->ChronoDelay));
+
+ return 0x719981;
+}
+
+#undef GET_LOCO
\ No newline at end of file