Combine Workshop addon and fix prison startup

Merge owned systems into one addon while preserving resource identities. Gate player admission on the replicated prison scene, restore the original framed door, and hold enemy AI until escape. Correct scenario branding and author metadata, include publishing assets, and retain regression and native verification evidence.
This commit is contained in:
2026-09-20 16:42:52 +02:00
parent 7d94953b53
commit cab998fb64
513 changed files with 18940 additions and 862 deletions
-12
View File
@@ -1,12 +0,0 @@
GameProject {
ID "ERR_Ai"
GUID "5192138CB9D5B911"
TITLE "Escapists Rereintroduced - Ai"
Dependencies {
"58D0FB3206B6F859"
"2528466DE220D91C"
"595F2BF2F44836FB"
"629B2BA37EFFD577"
"5E524E4FEECCA92B"
}
}
-12
View File
@@ -1,12 +0,0 @@
GameProject {
ID "ERR_Chat"
GUID "FA804F411BDDF2C6"
TITLE "Escapists Rereintroduced - Chat"
Dependencies {
"58D0FB3206B6F859"
"2528466DE220D91C"
"5192138CB9D5B911"
"D31039C7C53E348A"
"9AAEE9041D79809E"
}
}
@@ -1,8 +1,8 @@
SCR_MissionHeader {
World "{B6C516F86302C679}Worlds/ERR_Everon.ent"
SystemsConfig "{C50579A2EC48E8EF}Configs/Systems/MissionSystems.conf"
m_sName "Rereintroduced: Everon (development)"
m_sAuthor "Rereintroduced contributors"
m_sName "Escapists Reintroduced — Everon"
m_sAuthor "Jakub Jim Žáček"
m_sDescription "Independent escape prototype. Development scenario; runtime validation in progress."
m_sGameMode "Escape"
m_iPlayerCount 12
+4 -4
View File
@@ -1,11 +1,11 @@
SCR_MissionHeader {
World "{B6C516F86302C679}Worlds/ERR_Everon.ent"
SystemsConfig "{C50579A2EC48E8EF}Configs/Systems/MissionSystems.conf"
m_sName "Rereintroduced: Everon Base (development)"
m_sAuthor "Rereintroduced contributors"
m_sDescription "Independent base escape mission using vanilla Reforger. Companion addons optional. Development scenario."
m_sName "Escapists Reintroduced — Everon"
m_sAuthor "Jakub Jim Žáček"
m_sDescription "Legacy diagnostic header for the integrated Escapists Reintroduced mod."
m_sGameMode "Escape"
m_iPlayerCount 12
m_eSaveTypes 0
m_bShowInScenarioMenu 1
m_bShowInScenarioMenu 0
}
@@ -1,4 +1,5 @@
SCR_AIGroup : "{000CD338713F2B5A}Prefabs/AI/Groups/Group_Base.et" {
ID "781266D87A305AA9"
m_faction "USSR"
m_bSpawnImmediately 0
m_aUnitPrefabSlots {
+1
View File
@@ -1,4 +1,5 @@
GenericEntity {
ID "B7DDF3A8A8C387D1"
components {
ERR_LocationComponent "{C17CA14398A1CD7E}" { m_Kind 2 }
RplComponent "{0BF5BAAA5FBFEECE}" { Streamable Disabled }
@@ -1,4 +1,5 @@
SCR_FactionManager : "{A242612865F2A48E}Prefabs/MP/Managers/Factions/FactionManager_Base.et" {
ID "9D8B772E349A5ED5"
Factions {
SCR_Faction : "{5C67A1DDE0044806}Configs/ERR/Factions/US.conf" {
m_sFactionBackground "{F8AC305446D5D480}UI/Textures/DeployMenu/Loadout-BG/Loadout_US_BG.imageset"
+1
View File
@@ -1,4 +1,5 @@
GenericEntity {
ID "8F6EFB31A8BEAB20"
components {
MeshObject "{A557F6DC012E5101}" {
Object "{268DBDABB3C94AD7}Assets/Props/Military/Sandbags/Sandbag_01_short.xob"
+2 -1
View File
@@ -1,4 +1,5 @@
ERR_GameMode : "{0F307326459A1395}Prefabs/MP/Modes/GameMode_Base.et" {
ID "17B3CC49610F4ADE"
components {
SCR_GameOverScreenManagerComponent "{56DEC646298AD219}" {
m_GameOverScreenConfig SCR_GameOverScreenConfig "{56DEC6462FFDD82D}" : "{F6633922A25F9441}Configs/ERR/GameOverScreens.conf" {}
@@ -10,5 +11,5 @@ ERR_GameMode : "{0F307326459A1395}Prefabs/MP/Modes/GameMode_Base.et" {
m_SpawnLogic ERR_SpawnLogic "{79AD722B2EFD595F}" {}
}
}
m_bUseSpawnPreload 0
m_bUseSpawnPreload 1
}
@@ -1,4 +1,5 @@
GenericEntity : "{33B2DFDCD0EBA3DB}Prefabs/Items/Equipment/Kits/RepairKit_01/RepairKit_01_wrench.et" {
ID "D4FD26777E598970"
components {
InventoryItemComponent "{5222EB4D0C73006B}" {
Attributes SCR_ItemAttributeCollection "{5222EB4D0A2B466B}" {
@@ -1,4 +1,5 @@
SCR_AIGroup : "{8DE0C0830FE0C33D}Prefabs/Groups/OPFOR/Group_USSR_Base.et" {
ID "2550CE58718BC634"
m_aUnitPrefabSlots {
"{86E565387A634103}Prefabs/Characters/Campaign/Final/OPFOR/USSR_Army/Regular/Campaign_USSR_Player_MortarCrew.et"
"{86E565387A634103}Prefabs/Characters/Campaign/Final/OPFOR/USSR_Army/Regular/Campaign_USSR_Player_MortarCrew.et"
@@ -1,4 +1,5 @@
GenericEntity : "{6D56FED1E55A8F84}Prefabs/Items/Misc/IntelligenceFolder_E_01/IntelligenceFolder_E_01.et" {
ID "7F01099866C4E054"
components {
ERR_DocumentComponent "{E4598D0BE2C251A3}" {}
}
+1
View File
@@ -1,4 +1,5 @@
SCR_AIGroup : "{E552DABF3636C2AD}Prefabs/Groups/OPFOR/Group_USSR_RifleSquad.et" {
ID "B31BA06A7D5A2E37"
m_aUnitPrefabSlots {
"{5436629450D8387A}Prefabs/Characters/Factions/OPFOR/USSR_Army/Character_USSR_SL.et"
"{612F43A4D5AE765F}Prefabs/Characters/Factions/OPFOR/USSR_Army/Character_USSR_RTO.et"
+8 -3
View File
@@ -1,4 +1,5 @@
GenericEntity {
ID "287E3009E495A14F"
components {
ERR_LocationComponent "{3B60CBEB3C04333C}" { m_Kind 0 }
Hierarchy "{30ADADE77D1EC3B8}" {}
@@ -72,7 +73,7 @@ GenericEntity {
}
GenericEntity : "{A67420C1FB91856E}Prefabs/ERR/PrisonWall.et" {
ID "2735B52246DD6213"
coords -3 0 -6
coords -3.72 0 -6.04
angles 0 0 0
}
GenericEntity : "{A67420C1FB91856E}Prefabs/ERR/PrisonWall.et" {
@@ -80,10 +81,14 @@ GenericEntity {
coords 3 0 -6
angles 0 0 0
}
GenericEntity : "{A67420C1FB91856E}Prefabs/ERR/PrisonWall.et" {
ID "48971F16E1F34412"
coords 0.72 0 -6.04
angles 0 0 0
}
GenericEntity : "{C3FDC4DBCD58C2F5}Prefabs/ERR/PrisonDoor.et" {
ID "54D8F96ECC743335"
scale 1.5
coords 0 -0.1 -6
coords 0 0 -6
angles 0 0 0
}
DecalEntity : "{4AD6EB80A8B1FA3A}Prefabs/Decals/Dirt/Decal_Dirt_Patch_03.et" {
+34 -12
View File
@@ -1,17 +1,39 @@
GenericEntity : "{34B08027DBDE322A}Prefabs/Structures/BuildingParts/Doors/Door_MetalGate_01_Short_LEFT_EXT_ST_B.et" {
GenericEntity : "{87CADB5ADC989993}Prefabs/Structures/BuildingParts/Doors/DoorSet_Base.et" {
ID "779BF460CE27BEF7"
components {
DoorComponent "{221C667A9F7D4B69}" {
DoorAction ERR_PrisonDoorAction "{50888D3646A61640}" {
ParentContextList { "SCR_DoorUserAction" }
}
MeshObject "{61B367470698533A}" {
Object "{E3BC73BFBC7AA497}Assets/Structures/BuildingsParts/Doors/DoorFrame_door_metal_A_RIGHT_EXT_B.xob"
}
ActionsManagerComponent "{F32B667C3C823800}" {
ActionContexts { UserActionContext "{50888D37F529F720}" { ContextName "SCR_DoorUserAction" Radius 2 } }
additionalActions {
ERR_UnlockPrisonAction "{CA7918FEA177BFB8}" {
ParentContextList { "SCR_DoorUserAction" }
UIInfo SCR_ActionUIInfo "{F734C81E11AA6BEF}" { Name "Pick prison lock" }
Duration 4
RplComponent "{82AC07E74327438F}" {}
}
{
GenericEntity : "{67F17696FF278AE0}Prefabs/Structures/BuildingParts/Doors/Door_Metal_A_RIGHT_EXT_COV_B.et" {
ID "A14F618EC4C14ECB"
components {
Hierarchy "{7642683606C70747}" { PivotID "socket_door" }
DoorComponent "{221C667A9F7D4B69}" {
DoorAction ERR_PrisonDoorAction "{50888D3646A61640}" {
ParentContextList { "SCR_DoorUserAction" }
}
}
ActionsManagerComponent "{F32B667C3C823800}" {
ActionContexts {
UserActionContext "{50888D37F529F720}" {
ContextName "SCR_DoorUserAction"
Radius 2
}
}
additionalActions {
ERR_UnlockPrisonAction "{CA7918FEA177BFB8}" {
ParentContextList { "SCR_DoorUserAction" }
UIInfo SCR_ActionUIInfo "{F734C81E11AA6BEF}" {
Name "Pick prison lock"
m_sIconName "tools"
}
Duration 4
"Sort Priority" -1
}
}
}
}
}
+1
View File
@@ -1,4 +1,5 @@
GenericEntity {
ID "124B3922375D4E58"
components {
Hierarchy "{4F20A90EDBD625A4}" {}
MeshObject "{4F20A90EDBD625A1}" { Object "{0B96D139FEBCA006}Assets/Structures/Walls/Concrete/ConcreteWall_01_3m.xob" }
+1
View File
@@ -1,4 +1,5 @@
SCR_ChimeraCharacter : "{836E7E39AAC5888B}Prefabs/Characters/Factions/BLUFOR/US_Army/Character_US_Base.et" {
ID "33B18E530DB189C6"
components {
ActionsManagerComponent "{520EA1D2F659CE02}" {
additionalActions {
+1
View File
@@ -1,4 +1,5 @@
GenericEntity {
ID "83D9280A866F0092"
components {
MeshObject "{111C06CB740ED195}" { Object "{77C9FCF3FB1246E9}Assets/Items/Equipment/Radios/Radio_R107M/Radio_R107M_01_item.xob" }
RigidBody "{4C174A44D07B91BA}" { ModelGeometry 1 Static 1 }
@@ -1,4 +1,5 @@
SCR_ManualCamera {
ID "634904BCEDA1A285"
m_bInputEnabled 0
m_bStartAtEntity 1
m_aComponents {
+1
View File
@@ -1,4 +1,5 @@
GenericEntity : "{EBBC859CCD94D96D}Prefabs/Props/EmptyStorages/USSR_AmmoBox_empty.et" {
ID "53884C37C0729BF6"
components {
MeshObject "{6530845A09309E37}" {
Object "{819ED3266EE5113B}Assets/Props/Military/AmmoBox/EquipmentBoxWooden_01/EquipmentBoxWooden_Weapon_01.xob"
+1
View File
@@ -1,4 +1,5 @@
GenericEntity {
ID "11E225C720271170"
components {
ERR_LocationComponent "{8E7CFEA92C1811F0}" { m_Kind 4 }
RplComponent "{98A4A31435A9CA04}" { Streamable Disabled }
@@ -1,4 +1,5 @@
Vehicle : "{259EE7B78C51B624}Prefabs/Vehicles/Wheeled/UAZ469/UAZ469.et" {
ID "E0C2C9683B1B68D4"
components {
ERR_VehicleSecurityComponent "{E51E9F624DB28C37}" {
m_LockAnchor -0.95 1.05 0.3
@@ -1,4 +1,5 @@
GenericEntity : "{780AAF0A51AEE61A}Prefabs/Vehicles/Wheeled/UAZ469/VehParts/Seats/UAZ469_seats_rear.et" {
ID "32BA78DE126813F5"
components {
SCR_BaseCompartmentManagerComponent "{2CD4A765A7D52986}" {
CompartmentSlots {
@@ -280,7 +280,7 @@ class ERR_CasualtyLoadingService
ERR_CasualtyLoadTicket ticket = Ticket(controller, nonce);
if (!ticket || !ticket.Committed || ticket.OwnerReported) return;
ticket.OwnerReported = true;
if (!accepted) Print("Rereintroduced [casualty]: owner declined loading; observing physical outcome until expiry.");
if (!accepted) Print("Reintroduced [casualty]: owner declined loading; observing physical outcome until expiry.");
// Neither positive nor negative client acknowledgements release a reserved
// seat while an authorized owner command could still be in flight.
}
@@ -70,7 +70,7 @@ class ERR_DiscoveryService
ERR_LocationComponent chosen = eligible.GetRandomElement();
if (!chosen.Discover(Math.RandomInt(0, 100), m_Mode.Settings().Get("intelRevealChancePercent"))) continue;
m_Spent.Insert(folder);
PrintFormat("Rereintroduced [intel]: player %1 recovered folder; location=%2 knowledge=%3", GetGame().GetPlayerManager().GetPlayerIdFromControlledEntity(player), chosen.Id(), chosen.Knowledge());
PrintFormat("Reintroduced [intel]: player %1 recovered folder; location=%2 knowledge=%3", GetGame().GetPlayerManager().GetPlayerIdFromControlledEntity(player), chosen.Id(), chosen.Knowledge());
}
}
@@ -1,4 +1,4 @@
[ComponentEditorProps(category: "Rereintroduced", description: "Physical document identifying one authored objective")]
[ComponentEditorProps(category: "Reintroduced", description: "Physical document identifying one authored objective")]
class ERR_DocumentComponentClass : ScriptComponentClass {}
class ERR_DocumentComponent : ScriptComponent
{
@@ -108,7 +108,7 @@ class ERR_EnvironmentService
m_DateCode = dateCode;
m_Profile = profile;
m_Started = true;
PrintFormat("Rereintroduced [environment]: date=%1 weather=%2", Date(dateCode), ProfileName(profile));
PrintFormat("Reintroduced [environment]: date=%1 weather=%2", Date(dateCode), ProfileName(profile));
return true;
}
bool Started() { return m_Started && !m_Stopped; }
@@ -41,7 +41,7 @@ class ERR_ExtractionPlacement
protected ERR_PrepareResult Fail(string reason)
{
m_Failure = reason; m_Result = ERR_PrepareResult.FAILED;
Print("Rereintroduced [extraction placement]: " + reason, LogLevel.ERROR);
Print("Reintroduced [extraction placement]: " + reason, LogLevel.ERROR);
return m_Result;
}
protected void Diagnostic(bool ready)
@@ -98,7 +98,7 @@ class ERR_ExtractionPlacement
if (Stable(m_StableSince, m_LastObservation, now))
{
if (!QualificationAllowed(m_Result == ERR_PrepareResult.READY, now, m_Deadline)) return Fail("initial aircraft stable qualification missed the 30-second preparation deadline");
if (m_Result != ERR_PrepareResult.READY) Print("Rereintroduced [extraction placement]: native supported aircraft stable for one second, fuel present, exactly one initial wake");
if (m_Result != ERR_PrepareResult.READY) Print("Reintroduced [extraction placement]: native supported aircraft stable for one second, fuel present, exactly one initial wake");
m_Result = ERR_PrepareResult.READY;
m_LastObservation = now;
return m_Result;
@@ -106,7 +106,7 @@ class ERR_ForceLedger
// formation cannot replace its casualties with a fresh full-strength unit.
if (m_Records.Count() >= 128 || m_Sources.Count() >= 128)
{
if (!m_CapLogged) Print("Rereintroduced [ledger]: 128-formation registration cap reached.");
if (!m_CapLogged) Print("Reintroduced [ledger]: 128-formation registration cap reached.");
m_CapLogged = true;
return null;
}
@@ -153,7 +153,7 @@ class ERR_GameMode : SCR_BaseGameMode
if (!Replication.IsServer() || m_Phase != ERR_Phase.PREPARING || !m_PreparationError.IsEmpty()) return;
m_PreparationError = detail;
Replication.BumpMe();
Print("Rereintroduced: scenario preparation failed; spawning remains disabled: " + detail, LogLevel.ERROR);
Print("Reintroduced: scenario preparation failed; spawning remains disabled: " + detail, LogLevel.ERROR);
StopServices();
ERR_SiteService sites = Sites();
if (sites) sites.Rollback();
@@ -184,7 +184,7 @@ class ERR_GameMode : SCR_BaseGameMode
{
if (existing.Id() == location.Id())
{
Print("Rereintroduced: duplicate location ID " + location.Id(), LogLevel.ERROR);
Print("Reintroduced: duplicate location ID " + location.Id(), LogLevel.ERROR);
return;
}
}
@@ -222,7 +222,7 @@ class ERR_GameMode : SCR_BaseGameMode
{
m_ForceSerial++;
ERR_ForceRecord record = m_Forces.Register("formation-" + m_ForceSerial.ToString(), ERR_ForceKind.GARRISON, group.GetOrigin(), strength, null, group, GetGame().GetWorld().GetWorldTime());
if (!record) { Print("Rereintroduced: force ledger refused physical formation", LogLevel.ERROR); return; }
if (!record) { Print("Reintroduced: force ledger refused physical formation", LogLevel.ERROR); return; }
}
}
if (m_Command) m_Command.RegisterGroup(group);
@@ -259,10 +259,19 @@ class ERR_GameMode : SCR_BaseGameMode
if (!Replication.IsServer() || !m_PreparationError.IsEmpty() || !ERR_MissionRules.CanTransition(m_Phase, phase)) return false;
ERR_Phase previous = m_Phase;
m_Phase = phase;
if (previous == ERR_Phase.PRISON && phase == ERR_Phase.RADIO_SEARCH)
{
// Dormant patrols must not catch up the time spent waiting in prison.
for (int i = 0; i < m_Forces.Count(); i++)
{
ERR_ForceRecord force = m_Forces.At(i);
if (force) force.LastSimulatedAt = GetWorld().GetWorldTime();
}
}
RefreshRadioFeedback();
if (ERR_MissionRules.Terminal(phase)) PublishEscapeResults();
Replication.BumpMe();
PrintFormat("Rereintroduced [mission]: phase %1 -> %2", previous, phase);
PrintFormat("Reintroduced [mission]: phase %1 -> %2", previous, phase);
m_OnPhaseChanged.Invoke(previous, phase);
if (ERR_MissionRules.Terminal(phase))
{
@@ -451,7 +460,7 @@ class ERR_GameMode : SCR_BaseGameMode
if (m_Command) m_Command.Maintain(now);
// Maintenance can finish or stop the round; do not resume tactical work.
if (!Active()) return;
TickForces(now);
if (m_Phase >= ERR_Phase.RADIO_SEARCH) TickForces(now);
if (GetGame().GetPlayerManager().GetPlayerCount() == 0)
{
m_NoSurvivorsSince = -1;
@@ -476,7 +485,7 @@ class ERR_GameMode : SCR_BaseGameMode
SCR_PlayerController controller = SCR_PlayerController.Cast(GetGame().GetPlayerManager().GetPlayerController(id));
if (controller) controller.ERR_UpdateObserverServer();
}
if (m_Command) m_Command.Tick(now);
if (m_Command && m_Phase >= ERR_Phase.RADIO_SEARCH) m_Command.Tick(now);
m_OnTick.Invoke(now);
}
@@ -67,7 +67,7 @@ class ERR_LocalAdmission
m_Selected = true;
if (m_Key.IsNull()) { m_Key = UUID.GenV4(); m_LocalKey = true; }
if (m_Key.IsNull()) { m_Failed = true; return UUID.NULL_UUID; }
if (m_LocalKey) Print("Rereintroduced [admission]: sole offline controller uses a generated local-round roster key; backend identity and permissions unchanged.");
if (m_LocalKey) Print("Reintroduced [admission]: sole offline controller uses a generated local-round roster key; backend identity and permissions unchanged.");
return m_Key;
}
bool Validate()
@@ -1,6 +1,6 @@
enum ERR_LocationKind { PRISON, RADIO, EXTRACTION, OUTPOST, DEPOT }
[ComponentEditorProps(category: "Rereintroduced", description: "Server-owned location with replicated discovery state")]
[ComponentEditorProps(category: "Reintroduced", description: "Server-owned location with replicated discovery state")]
class ERR_LocationComponentClass : ScriptComponentClass {}
class ERR_LocationComponent : ScriptComponent
{
@@ -99,11 +99,21 @@ class ERR_NativePrisonBreachProbe
if (gate) door = BaseDoorComponent.Cast(gate.FindComponent(BaseDoorComponent));
if (!Check(door && !escape.GateLost() && Math.AbsFloat(door.GetNormalizedDoorState()) < 0.01 && !door.IsOpening() && m_Mode.Phase() == ERR_Phase.PRISON && m_Transitions == 0, "direct AI/player PerformAction cannot open locked native gate or advance progression")) return -1;
SCR_DestructionMultiPhaseComponent damage = SCR_DestructionMultiPhaseComponent.Cast(gate.FindComponent(SCR_DestructionMultiPhaseComponent));
if (!Check(damage && damage.GetDefaultHitZone(), "actual gate has its inherited multiphase destruction component and hit zone")) return -1;
PrintFormat("ERR PRISON BREACH DAMAGE: health=%1 enabled=%2 threshold=%3", damage.GetHealth(), damage.IsDamageHandlingEnabled(), damage.GetTotalDestructionThreshold());
vector hit[3]; hit[0] = gate.GetOrigin(); hit[1] = "0 0 1"; hit[2] = "0 0 -1";
SCR_DamageContext context = new SCR_DamageContext(EDamageType.TRUE, 100000, hit, gate, damage.GetDefaultHitZone(), Instigator.CreateInstigator(m_Player), null, -1, -1);
damage.HandleDamage(context);
if (damage)
{
if (!Check(damage.GetDefaultHitZone() != null, "destructible gate has its native hit zone")) return -1;
PrintFormat("ERR PRISON BREACH DAMAGE: health=%1 enabled=%2 threshold=%3", damage.GetHealth(), damage.IsDamageHandlingEnabled(), damage.GetTotalDestructionThreshold());
vector hit[3]; hit[0] = gate.GetOrigin(); hit[1] = "0 0 1"; hit[2] = "0 0 -1";
SCR_DamageContext context = new SCR_DamageContext(EDamageType.TRUE, 100000, hit, gate, damage.GetDefaultHitZone(), Instigator.CreateInstigator(m_Player), null, -1, -1);
damage.HandleDamage(context);
}
else
{
// The original framed metal door is not destructible. Exercise actual
// entity loss explicitly; this is not evidence of a damage-driven breach.
Print("ERR PRISON BREACH: deleting non-destructible original metal door for gate-loss fixture");
SCR_EntityHelper.DeleteEntityAndChildren(gate);
}
m_Step = 2; m_Wait = now + 1500;
return 0;
}
@@ -116,7 +126,7 @@ class ERR_NativePrisonBreachProbe
bool refusedLate = !late.Registered() && !late.GateLost();
late.Stop();
if (!Check(refusedLate, "fresh registration after native gate loss stays unregistered and cannot grant escape")) return -1;
if (!Check(m_Mode.Phase() == ERR_Phase.PRISON && m_Transitions == 0, "native gate destruction alone cannot advance the round")) return -1;
if (!Check(m_Mode.Phase() == ERR_Phase.PRISON && m_Transitions == 0, "native gate loss alone cannot advance the round")) return -1;
Move("1.5 0 -60");
m_Step = 3; m_Wait = now + 2500;
return 0;

Some files were not shown because too many files have changed in this diff Show More