Add bounded aircraft control and verify current packaged runtime closure
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
{
|
||||
"command.json": "39664d6eddeef0998bfebdd6f2f69157f417b63f9592ee49a8ca44964261ae9f",
|
||||
"isolation-preflight.json": "d39e14b6fbbbc0f7937772130c63e35a0bc23787a4d2f8918b3e4eabeacab09b",
|
||||
"profile/logs/logs_2026-09-19_13-14-58/console.log": "ccc80fb88a4df681329bc4ab9e01b453b49de998593b49bd8c270e8afb22e8d8",
|
||||
"profile/logs/logs_2026-09-19_13-14-58/crash.log": "10b059e914171e6df560767b24f5c04c680b867f3a6cc72fadc2408f4e5102f7",
|
||||
"profile/logs/logs_2026-09-19_13-14-58/error.log": "e4dae58ce0ca06fbb95c881857aefc3e7e7e777fb24653bb473fd511717090fa",
|
||||
"profile/logs/logs_2026-09-19_13-14-58/script.log": "b1bdb0efb02459d8cd0422d5d778e834a51f15cf078c4a8a523850d703f66dc2",
|
||||
"profile/profile/.save/settings/ReforgerEngineSettings.conf": "7b72f3e6b6c9c47643a213914e5dcb40964a2d43328178a740bb512904157cb7",
|
||||
"profile/profile/.save/settings/ReforgerGameSettings.conf": "1656680640c178239490ffbf9b3ddae04d3ac0993cd84471c0d27b28b76abf13",
|
||||
"result.json": "d577ba940786dedbcdbd720c2e08305240d9ed775104b949eccfd040886ad371",
|
||||
"review.json": "a3efd25c6f9f96da0184e272a8d20e0cf65f82405b5410495278da9b0d993a8d",
|
||||
"runner.py": "213a5a1dafede017301f750d074c2432f7199d8fa92f3c3be5846afa654b499b",
|
||||
"source/addons/ai/Prefabs/ERR/MortarCrew.et": "05d3e7353ff9fe5523218a1487b31b7b742b0be8adbcb060c7099978cf008bce",
|
||||
"source/addons/ai/Prefabs/ERR/MortarCrew.et.meta": "fdfd4fcd21a67c3d698fa4aadc4e5c76e665cebf4be86013bc389e493fab63cc",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_AircraftFlight.c": "96b20b7e78612fb26befa6e26635be80dbf9ca089384db4a7839f5064ab06ccc",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_AircraftFlightEnvelope.c": "8125cef61fb283d0d7c4dd2df3c04b062b448f1101fd48dd53f4f4097df5fef3",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_HQ.c": "7ee8e7a3282fd566ec330c94854b353b2c6178e9b964f7b4d5e197e4d0b7dee9",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_HQJournal.c": "b3e402cfe92d415f37f666045afb062e6e11670ba03a0ca6f2d9fd4fc3520ac1",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_HQNetwork.c": "92ff2caca52e94c2cc9642dfc8509ce6e9349b257012bade8650bace9231128c",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_HQObservation.c": "36c978ec5f052213ac1236e1398d85063efbfd7ef60fe04f1fc11447fefe1d68",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_HQReport.c": "c4d16ccb571db4d9444c0a8d2be7318845d25ff90238b6d304e96a9ea19f1a08",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_HelicopterAvailability.c": "60eaef2f6d4b2fd44861c5a4434ef97e732e40a5ff7401e2da451ed2dd9c2762",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_MortarAimSolution.c": "59e8343d09f7a44871d8d3335d6ad1d02216abb5deed30d38b5ec9ba3f2c0649",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_MortarBattery.c": "a4eed96d17f239cbf353863ebe40d2282aeacd7a4d63a7b2f0ecfb3d93a2e2b2",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_MortarCourierTransfer.c": "968e58ca700b4a11e5e04a5c159696f514a0b1bce531210172b913e495f7e2b7",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_MortarFiring.c": "7aec51c83972610d1bcbca58b84b14f16f59952d402b12c3d88e7192a3e2ee90",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_MortarLogistics.c": "e1ffb25fba384b449de576a5502a93d875c3f52e8da1941bbf590ad9845f9730",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_MortarPrediction.c": "470dd196ed64800c697d24c09d1344bacd2502587fac76af4ee0edae2563092d",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_MortarReadiness.c": "2fabe8f812fd333cea7199d1eb722c807fcc5adb285c618956bd54d31fa27ce3",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_MortarRefresh.c": "52ae8d9d6cad92f42d68c634091b80a474d2666b6c06d63a8c30a3fab71494c4",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_MortarReportLedger.c": "42a96040b63e56677353e55164ae23031c90f70b4d6673a9f412ad7b19e636ae",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_MortarSalvo.c": "697c01f479df6c505a41d984a29c02002e3052114dafa2ec6252ae55e2979a2b",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_MortarSupplyTransfer.c": "34f7eaa01dc68e97aae8c1177b7f8fd6c15d5a41c6dc86de377f00b3613a8e77",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_MortarSystem.c": "4edefc8530cc6eec7a5e1df1d0b7d56e6801186f23c61331ce0e8075b8281efb",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_NativeAircraftControlProbe.c": "e501301e683a7e36ad5a0cb56bc87a590c0c9071c9fc5c579502fb1c7a0ff143",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_NativeMortarCourierProbe.c": "740c8bebbd4128a9311b1167d1058549475d6b46f81c9b6c0d19650f4b50616f",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_NativeMortarFiringProbe.c": "ee570d21bede8baebefc799399712d9582b3e30ebda7299f6c941777f02ad2a2",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_NativeMortarSalvoProbe.c": "5fd7bb8953f6850ad79e646c0c286153108a10ee2d5ef397ccb65a07f8d2ed7c",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_NativeMortarTransferProbe.c": "399c21940ef10777b46fdb3e905430ce43404ce1de2f4a6bb62f9c61c1a0689c",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_NativePatrolHQProbe.c": "4c04aa3e1b6b000747da8e2a919a6501107880fd041134c019f516665bb6f758",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_SettlementSafety.c": "e5c0ebc95521a9a33a3f7d9cca6c551dda862a08b7af20b017877aba9584795b",
|
||||
"source/addons/ai/Scripts/Game/ERR/ERR_SupportScenario.c": "71757018ec9716c4dc24f62a292a214919d3fd5daed3c16e03e56abd208e9f03",
|
||||
"source/addons/ai/addon.gproj": "5f559db8f526700db11e6ae763f5245f19b7006fc68c56e4c09fb6f940bdf2a1",
|
||||
"source/addons/chat/Missions/ERR_Everon.conf": "76f3ac0d21b585654beac7ddd68e3c8cbdd66dbfe909b17b04838fa813a7c29b",
|
||||
"source/addons/chat/Missions/ERR_Everon.conf.meta": "0a23c98bf83873536c65fa17d38dea33a1d7b9e4ab210f90633ff688abcc551f",
|
||||
"source/addons/chat/Scripts/Game/ERR/ERR_AdminCommands.c": "d7b4ed95fc32767911936cb0fcc60763233f2c31afd34a6a0fa998b39cec9f5c",
|
||||
"source/addons/chat/Scripts/Game/ERR/ERR_NativeHQResponseProof.c": "0f8b09fd8753234c91fd6baaca0e2f812e409ae1c4be8f340b648ae59b69c2f3",
|
||||
"source/addons/chat/Scripts/Game/ERR/ERR_NativeMortarProbe.c": "ddb892141500c192c0787b011d57dd8c07dd8c9a3ca5fdc407f6c271897bde0a",
|
||||
"source/addons/chat/Scripts/Game/ERR/ERR_NativeRadioProbe.c": "84524163c63402755b4f834f74dfe6d8e5c397dc26031f532593c6f79f5a93b1",
|
||||
"source/addons/chat/Scripts/Game/ERR/ERR_NativeSmoke.c": "32501d011d459e3c2d01077ef3eeb0ce8c364985e1aadfe8e8c022ba4b2054ef",
|
||||
"source/addons/chat/Scripts/Game/ERRChat/ERR_OrdinaryScenarioLaunchChat.c": "dda715c9691d366e35fa2ad5e390dee1971651f137a5a9f9aeb23dcbe53ec87e",
|
||||
"source/addons/chat/Scripts/WorkbenchGame/ERR/ERR_ValidateWorldPlugin.c": "81c92d408ce88cc9f98d6053f1d0c863427693ba748f00df9f1bf8079015ef94",
|
||||
"source/addons/chat/addon.gproj": "ea1587e0174cf795334d306c71d99b06797fc891427cd57b6cab6e21cf0e4e0e",
|
||||
"source/addons/core/Configs/ERR/EveronSites.conf": "ade92cc8d2b14a84ac378bbbb52608b351dca65d359dd7a315d64a145fa4b46f",
|
||||
"source/addons/core/Configs/ERR/EveronSites.conf.meta": "d5fe68025ef4e7c1e16082cf5873b7c7172eacbfaef4ab29b7e0e3c98f8dca56",
|
||||
"source/addons/core/Configs/ERR/Factions/MissionVehicles.conf": "5f74f4a7089c7e7b54ad62463159ce8f0bab25a6b85268dc3872ea6001e048fb",
|
||||
"source/addons/core/Configs/ERR/Factions/MissionVehicles.conf.meta": "00c368b6271d3440cc8bf627b03a384d247635723923fc5ad2104b8b4007b035",
|
||||
"source/addons/core/Configs/ERR/Factions/US.conf": "4c3da063abb6e3ee39a6e4e0c5a9ffd5eae9d209d58470ea18b45dba5528db1b",
|
||||
"source/addons/core/Configs/ERR/Factions/US.conf.meta": "a05469aef028e9e17eba545e81e2af2887b59f0fef17dac51a23c7b8155e381b",
|
||||
"source/addons/core/Configs/ERR/Factions/USSR.conf": "407ea27f80a64c5184c089d43cdd0bdabfc5974c84f1d2a245081fdf2afa3f30",
|
||||
"source/addons/core/Configs/ERR/Factions/USSR.conf.meta": "057397d97096346791b51b7b13fbe18af218bcf78e9984d9c01b49a2403de611",
|
||||
"source/addons/core/Configs/ERR/GameOverScreens.conf": "45231061911a1462d45fb6db0fecc2b0793f17b5ae71017174954ff4fbd20236",
|
||||
"source/addons/core/Configs/ERR/GameOverScreens.conf.meta": "eb654b745c0b97b41c991694569ef4877a3c2315e0826f9556337cd68690843e",
|
||||
"source/addons/core/Configs/ERR/VanillaFactions.conf": "db890b61b48ee15484041fd2245a862d9fa9e7ba21b1229c281ff37c448a35b1",
|
||||
"source/addons/core/Configs/ERR/VanillaFactions.conf.meta": "ac77725c0c21974991ef47a466b88122e939de8b46fd23e68889e7674cbb2bfd",
|
||||
"source/addons/core/Missions/ERR_Everon_Base.conf": "1e6a0e9a756d15495201f71121626890f01efe6b00154c561672d4ec157960bd",
|
||||
"source/addons/core/Missions/ERR_Everon_Base.conf.meta": "9b16267c644d815bcf98a3a3aaff30b93d7224fb24d3a2bac66730a28011f317",
|
||||
"source/addons/core/Prefabs/ERR/Extraction.et": "1aa286ef253c5410f09fa641e4be91a7be8ec834487a7fbb74cfb45cab60ee1e",
|
||||
"source/addons/core/Prefabs/ERR/Extraction.et.meta": "c2a838633001e2e3a202d39cb33aa744ebcd996017ad032359069641b5d13853",
|
||||
"source/addons/core/Prefabs/ERR/FactionManager.et": "425f916b890019c537f6b2f72b1af9e7be54b0a476320da969e1f313a7ac5ea5",
|
||||
"source/addons/core/Prefabs/ERR/FactionManager.et.meta": "9bf53d5e4bcbe60dd1a4ece1409fc23c349055c860708620f5f6e9716290a97d",
|
||||
"source/addons/core/Prefabs/ERR/FieldCover.et": "20f8095e69dbfe3db74ff19078e528ea75405362b335e03560ccd61f8df2e7f3",
|
||||
"source/addons/core/Prefabs/ERR/FieldCover.et.meta": "83461ffa76244e1d69f20b053a9741ccdf7d127009182bd540a7e5ba6150c3d9",
|
||||
"source/addons/core/Prefabs/ERR/GameMode.et": "385c97a5b8067091d92aa60c2d4fcf02002cf606b95523866ead65c96b8c0029",
|
||||
"source/addons/core/Prefabs/ERR/GameMode.et.meta": "106769cd34525c9d440057072836cb7401a6752148c40edd7fe61c03bd58328c",
|
||||
"source/addons/core/Prefabs/ERR/ObjectiveDocument.et": "2e72270bf186654af12d2c3ff9bf940690b03fb17fca44f873c1fde5d11384c0",
|
||||
"source/addons/core/Prefabs/ERR/ObjectiveDocument.et.meta": "eaba9903be147b98a1106e2273e2270773df73b9b22f2cd4e20136a8e281382e",
|
||||
"source/addons/core/Prefabs/ERR/Patrol.et": "c5685a8bc311cc5563fb3334beec9408d3d7a1069152fddeeb7df4f1b7f9e6ce",
|
||||
"source/addons/core/Prefabs/ERR/Patrol.et.meta": "7c5da568fa2fcf0709ee2959253cb068d7c7ed7c2467af3b247a7e70efd96b69",
|
||||
"source/addons/core/Prefabs/ERR/Prison.et": "dcd3f4dfd453303d026e68b64f7de6125bd3390822d8432dd95a3710a5b3df89",
|
||||
"source/addons/core/Prefabs/ERR/Prison.et.meta": "2e426ef3de68f4107907854b51e818bc11d84649a01c538e3f18dd9ebec766bb",
|
||||
"source/addons/core/Prefabs/ERR/PrisonDoor.et": "fc66be9d36f1f01084de59f1b0a1e8817f9e46547ead33d4fafaac0107446c40",
|
||||
"source/addons/core/Prefabs/ERR/PrisonDoor.et.meta": "b8f3db52bf186426026668fbb9d633c3cbd4d438ab301fba880398f6a0e781c2",
|
||||
"source/addons/core/Prefabs/ERR/PrisonWall.et": "8009d19365ae72a4749d7ad4fa04662575b06c65c6bf06507d380d57258b5eed",
|
||||
"source/addons/core/Prefabs/ERR/PrisonWall.et.meta": "0294bc5dbb56f90f8153d62459bb0a6ad91d76a479812bbec9c7db182fdb58d6",
|
||||
"source/addons/core/Prefabs/ERR/Prisoner.et": "198b5badabb12f218f86df7eb4dc268fd830769dc3de7eef6362cfaee4d6fc8d",
|
||||
"source/addons/core/Prefabs/ERR/Prisoner.et.meta": "974278ba49fafeed70b619a0067bbc70ae4d534be441c812ce2bef4a5fb3c022",
|
||||
"source/addons/core/Prefabs/ERR/Radio.et": "b4b5cfe125b39019855816c9a1c508ca4d65d435b5546fdaa6c1fa3c8c9b1165",
|
||||
"source/addons/core/Prefabs/ERR/Radio.et.meta": "bc91137b68ba550217914fb8892bb44d7e1920e4a1086f3e506623832a44212f",
|
||||
"source/addons/core/Prefabs/ERR/SpectatorCamera.et": "b5b87d5b7b707f3745741aebe7c923067c8a74f8fbb2a4711834bab03bb5a812",
|
||||
"source/addons/core/Prefabs/ERR/SpectatorCamera.et.meta": "2a16558f93915e31908a674509e9c89976470f78a84a1174a64b94b68b8f77ad",
|
||||
"source/addons/core/Prefabs/ERR/SupplyCache.et": "bccf7afbaae97cfcdd78dd6dbbf4be5b2c045d348f50fe4581962e556c0044d7",
|
||||
"source/addons/core/Prefabs/ERR/SupplyCache.et.meta": "2f5de3a4aab1e9d0e35521f48dc57b95bd6e449c383dec4f1c258e10295904cb",
|
||||
"source/addons/core/Prefabs/ERR/SupplySite.et": "dc5decce5cabf39b6116e8794ec3960cb1e35761854fdbe69740da118787ded4",
|
||||
"source/addons/core/Prefabs/ERR/SupplySite.et.meta": "b41944b12eb3528ff7a3ac94258b18c66ef161aa6a38894aee300a1ac4798630",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_Actions.c": "cabb21413c90b252d29708b62228d0679c51c76e39ee0e483953ad7061b2b9d6",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_Actors.c": "b3c5aabb05f7cf35167bc09b5db51a4ffd89a46658805d6eedbfb80eee30273d",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_AdminService.c": "9985747d5ddaeb91bf27937836a6c369f8dcb353263b77a49212ec28c10ba4eb",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_CasualtyGroundSupport.c": "26f0c7ebe8563bb022f7c383674e7d8997b0cc35c7ea321d39d74f2493c1ed9d",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_CasualtyLoadingGameMode.c": "f4cf19440583ac24346831a2d5f1d7f734c031e46dc266c499972329dd0533cf",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_CasualtyLoadingOwner.c": "6346a05ddda030f7d9b6f026f977d5f018ee16002fa519e8c49dcd700c323dc7",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_CasualtyLoadingService.c": "93fe23d9e1da596886c33cf76bad92b0906ff5cdd8b5b21d58906e9270246201",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_DiscoveryMap.c": "7553c8cc1388cca3404994e36f96946c6abd1fefb08cef2fbb3ea1a62b22861b",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_DiscoveryService.c": "2793bb888566dd975e5bbf93fdb47a22e3b0f33a68d04992191cf6428ae942a0",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_DocumentComponent.c": "17bb76b80ebd3357e46d553c44674f602c7bd6901c92aec53ac8214194aa4ff2",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_EnvironmentService.c": "867e9f47adfb7a9819a8b58498688ff86127282cb89cf2529ae81f73e529e1c5",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_EscapeGameOverScreenInfo.c": "b1e1afb7795cb77c0bad94c85abf705b26b9dc2a9400905d80eafd56bdd009d3",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_ExtractionPlacement.c": "c30222ca25cc0e91dec4077e6b82b37a9edafcf2cbebd0be5b6bd8656b53adfe",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_ExtractionService.c": "91357099b4e0401196b67d48963be54be30e56bdba57cdac67dc20b8ebb883e9",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_FactionCatalog.c": "a5a8c53acf21259e14f59cb919535910f30dabff5a9179c664bc6a7a4d744d6e",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_GameMode.c": "41f2247a45252eaacb580aa5ebb5a85ecfa41e0f940eb0d2c8ea5a3f6c35058e",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_InitialGuardRules.c": "8c9d6b1dc4c2af96284e34f487eebb5a221091016ef108006dec60b4e3836558",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_Inventory.c": "b63e679d5b650b63a7a91dfc3f42a62f4be58b0338f6df3313e66d29a93bebf6",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_ItemStationTeardown.c": "102e61216e86e47ad243cc19f6f66da1fcd8cc77f8799354ceaeb4a6dc74622e",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_LoadCasualtyAction.c": "5d868fb065a1a0fea7d5abfc81d1d8be96823815535f2ffd0dc4ff60d8eae794",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_LocalAdmission.c": "b0e824c6880cbf9b1f368dc7ada9907bde78b22b948d419b6c861323f32258be",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_Location.c": "d2035f56ded35743312ec6ca91a67f8623a466c91255e1893c570edf0954ff24",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_MissionRules.c": "f7abc38ad8fdc375ce7fa63a65f0bade027d22b37e09de5e3f94ba8c3c63cc41",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeAdminSettingsProbe.c": "6a4df33a2b0e6b6aeed944c6895961e658d4f0db9aed9b8433a43a451fa35cae",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeBaseSmoke.c": "4c58290868fe4b669ff0f05835e9e7626766c13eab9633a721e0ebe5c551428e",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeCasualtyGroundProbe.c": "41f40db3735f8aa535c0061458d9892dcfe972222b8a17238e67b65633c46bc3",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeCasualtyLoadProbe.c": "94c533deea654ec546df3f1ebb7cd213e6cd79b1125d99bab5303ef8e87b018b",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeCasualtyRemoveProbe.c": "4cfc06f6d43b709b63caae714155ffc562502e884f49a9825623c6f46972ad36",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeCasualtyServiceProbe.c": "02a9d70d1f4ba20326df199ba41489cfbae12b7bb0aab441361421cee38ad9ea",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeContractsProbe.c": "d1556df885171cf967e96eef5d8981a2e9ee4e3938b7d32fdd0426f647bbabc0",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeDefinitionProbe.c": "c07048341396443ddf5307e340e2087254d65f0bddf8f7d93e6b6bd7f03ca360",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeDiscoveryMapProbe.c": "9ed32d391d08d937ff4f9f33bd11c3a204ce9a2230c2d72b6761e0320b31f24f",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeEnvironmentProbe.c": "8b1d110717aa5f07bfb8fe633847eab519109e3a8a4100970a487be05c053418",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeEnvironmentRestartProbe.c": "7a836a206381b8e8de24c92baf141beb30575723675dd6948d738169ed26b507",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeExtractionProbe.c": "4b09385f73206de004f4f3c38a6192fd658a0d2012482dc5a84dff1599ceb972",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeFactionCatalogProbe.c": "ee11486cf77da9070506ccc0cfd6e94240c5387c8be455bf864934a8fef65f78",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeFlightFootprint.c": "a8862a04ac784426f431e2e9cd3f1f19f820b38a081674d29549b8b00277c72d",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeFlightInputDiagnostics.c": "e231dcbdaefe81beb3bd587cb73be8fb3d3184bc86d18dbaa30a43ca4ba5dcc4",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeGuardCancelProbe.c": "bfa1c65cb039e19c3d1891e4a3fd33469e974c97cc7f42128efc31a8f587d514",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeItemStationTeardownProbe.c": "028588a49fa9207087cd27ec9396979cd932daa071e2490bd0262cd71dae30d9",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeLifecycleProbe.c": "b613a5dbe81c2032fd02d31070ae5631166d14d0b02cdbec3b63b782f0bbc115",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeLocalAdmissionProbe.c": "29c860ca3e51cdaa463f3c23a98ff45ee0499ce9bbc322046e0efbed587f1e09",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeMedicalProbe.c": "43fa51ffbcf9dd0cd8e905a1135bcf0756651da3af34677f3c92e7d264ed70b6",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeObjectiveLossProbe.c": "6b80fd317a8c6e8583bd4337870838a86251e3b8a681d0aa07e8b52e22abfc48",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeObjectiveProbe.c": "71fbd2479a70048f83d8b262ad1452f427be2c0afc8b071ef13bff477b9dc29b",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativePatrolProbe.c": "02bd9fdf5454db7dd4c82240efe0d947bf8d6488ace42809c3aef6505cb6266d",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativePoweredFlightProbe.c": "7ae5d6f85bbbebec214c66bfdf6f53b93c802f109fcd5bcfbad82c5d61d6cb9f",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativePrisonBreachProbe.c": "22d0923cd69ba591e783011d424c97c86520272b70da9aa6a6d15558464c264c",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativePrisonerProbe.c": "e2d1ffe5d269add55124601ff0d1dc032a398c7d51010736e6f0e4cbeb38ae86",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeProgressionIdentityProbe.c": "1d3fe20bce761a1082964bca9bad9ce79d1fa8b04db9b5053bae98da380ae560",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeRadioDefenceProbe.c": "cad54f0b43cefd72df74697dfc7e93725ff0acac6d311e963ce4f817428c272c",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeRecoveryProbe.c": "4de6868cd3e76afb80cf0ae5dac1e35d2c417838f640c2f18aa3343217eea2f2",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeResultProbe.c": "b9a117eef2d5f65fbd792041a2b3e59e38e201bda8e0df8c503e1a1242cfc415",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeSiteSurvey.c": "ac0203753ade0cdff6d483132797a80e4520f14546c3f9dea288281cd4cd9c4f",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeStationTeardownDiagnostic.c": "172248540932174341882861bb78a9720a6cf89981fcdf9dffe63f7fd4023f2c",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeTimeProbe.c": "c93d049785971e51246fcc07cf24804270a752a655505f2c38e48c80df5255ae",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeTransportProbe.c": "63ec0c5ce698114daa6314b2a6e3c3e98e76094117c477f9d0d072260686d193",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_NativeTransportSurvey.c": "3e94493bf54d15be2bceb106ad18873e14e6755af09c05f07db7fb26fb7adff5",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_ObjectiveDetails.c": "628ebd166dcb5544c2d18dce489619984887ee18296cf2446fc6d894f89dd5bf",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_ObjectiveText.c": "d1740ddbe8a4bac04aeb0ffc68c2957bc807cbb0a197afd515f0fcb742aac04f",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_Objectives.c": "88d95a52fcba103483ea751571e1afeae0e6fc82115ed50d6fc195424726960f",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_Observer.c": "f417f503dcb66b4d5224d49046ab47c08fa14255d5c4af02a85047096069da33",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_OrdinaryScenarioLaunch.c": "11541755401af8da68dc004d9a8e2a49578d617d4dec63b1d6e5a75b00b9c3ff",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_PlayerAdminCommands.c": "a084d7dc097dfcde3d6cd9c2e326b9e2f8f2a519f77b30c4f56ab7a1429aa721",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_PlayerEscapeResult.c": "3124ab4d2d6d471409a47aba0af528790734df53243c03fe0cdac3e407047c14",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_PlayerGuide.c": "2c9c365b88651f8b12618443638bf52c04e497221592bd483c33447e30468f36",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_PlayerRoster.c": "c038dac0f749bd29ebc35a4e952904b41a34417ba738904d5efcd1833a263e0b",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_PrisonEscapeService.c": "ed6286342ed12cc9a3df618474f5647c7ac36bc65336be7a5bec908dee04421d",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_RadioDefenceService.c": "5d347cf1a3b021074d560aad8db774828b797e685b63ddff3d28f55781e33ea8",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_RadioFeedback.c": "545262e65e9b3645d9e514a0521e442fe08f0c2e4e7c83ba638c5105d09818db",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_RecoveryService.c": "ba9ffb93679012c20a665636ef6b201cbe7d8d14fdf53e8cd3eb12ac7248943f",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_Resources.c": "d0169aac6fd0c38928c47a9002ded0fd8ba558ea3314a600e792d307ac1ca3ee",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_ScenarioComponent.c": "abba03af68323d5715ba1e2497fc66f3c706e6a6a983c3a386228abdfddeec89",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_Settings.c": "b9390548936b386e0211d487c1b76b65410fdd2e339cd8ecd9585ef4f4ffed31",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_SiteDefinition.c": "b422feeaca90084ea2bee5cde94dc9c6fcae542749b55d95f7226f922a6a68ac",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_SiteDiscoveryRules.c": "50f7b3ff4f32bde37ce5ae2be7f598fc4dd8cc08f45914f6e3260d31c84baef2",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_SiteService.c": "6bc9a0b5aed326822807f05e17459d0fc2da10b709e8168972b4e40fd09f87cb",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_SpawnFinalization.c": "2e295b0f225208c5414728ba83333e5e0ab6eef3a708ce39eb1dfce5400c94c9",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_SpawnLogic.c": "ed88279cbeb3e5e6daf8471763df87a251881f550632eddadd08153594bbd1a1",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_SpawnPlacement.c": "55323dcb330c23d49dc8fa3fde86a3ae3958c0b6563d6c3cd0960da88333f93d",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_Supplies.c": "752e8a56b0b8cde33cf532032cb0a203553017b686faac2e19bee06f7a9c0713",
|
||||
"source/addons/core/Scripts/Game/ERR/ERR_TimeService.c": "d9f481b7da01e08d2c982a948c18ba6bde76889d7cc9d26d510708f4ba479453",
|
||||
"source/addons/core/Scripts/GameLib/ERR/ERR_DiagnosticNoSplash.c": "0ea1b402f8927b6d68bf6f7e5caa2967f7e3a39147634faab147796b791933b5",
|
||||
"source/addons/core/Scripts/WorkbenchGame/ERR/ERR_ValidateBaseWorldPlugin.c": "7f44aa64ea7b4f8b3640a37a4b781c43bf6adb80e4a3a702d9491131698056f6",
|
||||
"source/addons/core/Worlds/ERR_Everon.ent": "22324bc7eb0d80dec8a1f3bd61a9df9201b72f749f4e751766b0c9d0b7d46b87",
|
||||
"source/addons/core/Worlds/ERR_Everon.ent.meta": "19d1903ca72baa326b4724a6791d0da1104a1e53f3f31dc1ca41a856efd68e83",
|
||||
"source/addons/core/Worlds/ERR_Everon_Layers/default.layer": "17105538007199bfc419aa04a64debaf2da0a7b77a1dc3ba983e92bf747c862e",
|
||||
"source/addons/core/addon.gproj": "231274e6b62302f32c65f9ff31de2ce8a9e51e1c8d12c87deeec82ae1f40de46",
|
||||
"source/addons/intel/Scripts/Game/ERR/ERR_IntelService.c": "5e167630a76437fd731fbb7a62a41381ea71509f3adb8a6b9c77f6816384861a",
|
||||
"source/addons/intel/addon.gproj": "d0ced242cefb220a5cccd6779d61fda9bc55581701b9f78bcf13c09d92ddbcbe",
|
||||
"source/addons/map/Materials/ERRM_MapRenderTarget.emat": "9877c5b175c5f30dd80bea36c711b21ab38219aa7075adf9ae79932e1d4c76bc",
|
||||
"source/addons/map/Materials/ERRM_MapRenderTarget.emat.meta": "5ee1a705f8011a44020ca2691f5ccc17d44bf596fef9a36e98d1f267eb58fab0",
|
||||
"source/addons/map/Prefabs/Items/Equipment/ERRM_SharedFieldMap.et": "30dee8e9eab459ea32a7d835a9fb89d43df47708057cab829f067b25a24cd581",
|
||||
"source/addons/map/Prefabs/Items/Equipment/ERRM_SharedFieldMap.et.meta": "59093e9072c50d99c86da7609c87541016da5bcf92b98d99db4c524e1c8e0e8d",
|
||||
"source/addons/map/Scripts/Game/ERR/ERR_ScenarioMap.c": "75fa1d742129ef7f33c005cd48f56db7f49ee7bde3c5428532b0b501b032b0a3",
|
||||
"source/addons/map/Scripts/Game/ERRMap/ERRM_NativeMapOwnershipProbe.c": "32418602f085e3edba7343f42a6a691630b533be262002f29248e8ae87a38254",
|
||||
"source/addons/map/Scripts/Game/ERRMap/ERRM_SharedMapComponent.c": "3a10f6ea25bf88a400bc3e6c339823433845c5db4f2f423835ccf99a1b1a843f",
|
||||
"source/addons/map/UI/layouts/ERRMap/SharedMapRenderTarget.layout": "49a2da4181b94d42174f58c7f9b6bf2207116c99427d61ff1c212092ac7db4d2",
|
||||
"source/addons/map/UI/layouts/ERRMap/SharedMapRenderTarget.layout.meta": "5c846445cc089e2e08899ba703059d4adb805ff30ab5a16409a3cd59718cbd54",
|
||||
"source/addons/map/addon.gproj": "1b371e5e01ea9fa46e76570fecb8aaa3a9298e34cba7ccca8a6bdb5ca86eb46a",
|
||||
"source-manifest-after.json": "86b8c474e8240667a5f5b7a0d19cdf5e26aa12be78e27e96b50d331b639648d5",
|
||||
"source-manifest.json": "86b8c474e8240667a5f5b7a0d19cdf5e26aa12be78e27e96b50d331b639648d5",
|
||||
"stdout.log": "64963f62de592e2b38b3485cecaf81f0707eb55c7ba8a91aa4f2818fa370a6cb"
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
{
|
||||
"command": [
|
||||
"bwrap",
|
||||
"--unshare-net",
|
||||
"--unshare-pid",
|
||||
"--unshare-ipc",
|
||||
"--unshare-uts",
|
||||
"--die-with-parent",
|
||||
"--cap-drop",
|
||||
"ALL",
|
||||
"--clearenv",
|
||||
"--ro-bind",
|
||||
"/",
|
||||
"/",
|
||||
"--tmpfs",
|
||||
"/home",
|
||||
"--tmpfs",
|
||||
"/tmp",
|
||||
"--tmpfs",
|
||||
"/run",
|
||||
"--proc",
|
||||
"/proc",
|
||||
"--dev",
|
||||
"/dev",
|
||||
"--ro-bind",
|
||||
"/tmp/erri-dedicated-runtime/server",
|
||||
"/tmp/runtime",
|
||||
"--bind",
|
||||
"/tmp/erri-dedicated-chat-20260919-1314/source",
|
||||
"/tmp/source",
|
||||
"--bind",
|
||||
"/tmp/erri-dedicated-chat-20260919-1314",
|
||||
"/tmp/work",
|
||||
"--setenv",
|
||||
"HOME",
|
||||
"/tmp/work/home",
|
||||
"--setenv",
|
||||
"TMPDIR",
|
||||
"/tmp/work/tmp",
|
||||
"--setenv",
|
||||
"XDG_CONFIG_HOME",
|
||||
"/tmp/work/home/.config",
|
||||
"--setenv",
|
||||
"XDG_DATA_HOME",
|
||||
"/tmp/work/home/.local/share",
|
||||
"--setenv",
|
||||
"XDG_CACHE_HOME",
|
||||
"/tmp/work/home/.cache",
|
||||
"--setenv",
|
||||
"PATH",
|
||||
"/usr/bin:/bin",
|
||||
"--setenv",
|
||||
"LANG",
|
||||
"C.UTF-8",
|
||||
"--chdir",
|
||||
"/tmp/runtime",
|
||||
"/usr/bin/python3",
|
||||
"-c",
|
||||
"import json,os,socket,sys\nstate={\"netns\":os.readlink(\"/proc/self/ns/net\"),\"interfaces\":socket.if_nameindex(),\"routes\":open(\"/proc/net/route\").read(),\"ipv6Routes\":open(\"/proc/net/ipv6_route\").read()}\nprint(\"ERR ISOLATION \"+json.dumps(state),flush=True)\nassert state[\"netns\"] != sys.argv[1], \"network namespace was not isolated\"\nassert [name for _,name in state[\"interfaces\"]] == [\"lo\"], \"non-loopback interface present\"\nassert len(state[\"routes\"].splitlines()) == 1, \"IPv4 route present\"\nassert all(line.split()[-1] == \"lo\" for line in state[\"ipv6Routes\"].splitlines()), \"non-loopback IPv6 route present\"\nif len(sys.argv)>2: os.execv(sys.argv[2],sys.argv[2:])\n",
|
||||
"net:[4026531833]",
|
||||
"/tmp/runtime/ArmaReforgerServer",
|
||||
"-profile",
|
||||
"/tmp/work/profile",
|
||||
"-noBackend",
|
||||
"-noSound",
|
||||
"-noSplash",
|
||||
"-noThrow",
|
||||
"-bindIP",
|
||||
"127.0.0.1",
|
||||
"-bindPort",
|
||||
"2001",
|
||||
"-maxFPS",
|
||||
"30",
|
||||
"-logStats",
|
||||
"5000",
|
||||
"-addonsDir",
|
||||
"/tmp/source/addons",
|
||||
"-addons",
|
||||
"FA804F411BDDF2C6",
|
||||
"-server",
|
||||
"{C1E20667DCE324ED}Missions/ERR_Everon.conf"
|
||||
],
|
||||
"engine": [
|
||||
"/tmp/runtime/ArmaReforgerServer",
|
||||
"-profile",
|
||||
"/tmp/work/profile",
|
||||
"-noBackend",
|
||||
"-noSound",
|
||||
"-noSplash",
|
||||
"-noThrow",
|
||||
"-bindIP",
|
||||
"127.0.0.1",
|
||||
"-bindPort",
|
||||
"2001",
|
||||
"-maxFPS",
|
||||
"30",
|
||||
"-logStats",
|
||||
"5000",
|
||||
"-addonsDir",
|
||||
"/tmp/source/addons",
|
||||
"-addons",
|
||||
"FA804F411BDDF2C6",
|
||||
"-server",
|
||||
"{C1E20667DCE324ED}Missions/ERR_Everon.conf"
|
||||
],
|
||||
"seconds": 90,
|
||||
"route": "header",
|
||||
"project": "chat",
|
||||
"binarySHA256": "91fdf333ec49c266d309be358aabab4244a93a2f9cfa11853a600e026104001f",
|
||||
"serverLaunched": false,
|
||||
"realPlayers": 0,
|
||||
"multiplayerProven": false,
|
||||
"scope": "successor chat empty-server startup"
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"command": [
|
||||
"bwrap",
|
||||
"--unshare-net",
|
||||
"--unshare-pid",
|
||||
"--unshare-ipc",
|
||||
"--unshare-uts",
|
||||
"--die-with-parent",
|
||||
"--cap-drop",
|
||||
"ALL",
|
||||
"--clearenv",
|
||||
"--ro-bind",
|
||||
"/",
|
||||
"/",
|
||||
"--tmpfs",
|
||||
"/home",
|
||||
"--tmpfs",
|
||||
"/tmp",
|
||||
"--tmpfs",
|
||||
"/run",
|
||||
"--proc",
|
||||
"/proc",
|
||||
"--dev",
|
||||
"/dev",
|
||||
"--ro-bind",
|
||||
"/tmp/erri-dedicated-runtime/server",
|
||||
"/tmp/runtime",
|
||||
"--bind",
|
||||
"/tmp/erri-dedicated-chat-20260919-1314/source",
|
||||
"/tmp/source",
|
||||
"--bind",
|
||||
"/tmp/erri-dedicated-chat-20260919-1314",
|
||||
"/tmp/work",
|
||||
"--setenv",
|
||||
"HOME",
|
||||
"/tmp/work/home",
|
||||
"--setenv",
|
||||
"TMPDIR",
|
||||
"/tmp/work/tmp",
|
||||
"--setenv",
|
||||
"XDG_CONFIG_HOME",
|
||||
"/tmp/work/home/.config",
|
||||
"--setenv",
|
||||
"XDG_DATA_HOME",
|
||||
"/tmp/work/home/.local/share",
|
||||
"--setenv",
|
||||
"XDG_CACHE_HOME",
|
||||
"/tmp/work/home/.cache",
|
||||
"--setenv",
|
||||
"PATH",
|
||||
"/usr/bin:/bin",
|
||||
"--setenv",
|
||||
"LANG",
|
||||
"C.UTF-8",
|
||||
"--chdir",
|
||||
"/tmp/runtime",
|
||||
"/usr/bin/python3",
|
||||
"-c",
|
||||
"import json,os,socket,sys\nstate={\"netns\":os.readlink(\"/proc/self/ns/net\"),\"interfaces\":socket.if_nameindex(),\"routes\":open(\"/proc/net/route\").read(),\"ipv6Routes\":open(\"/proc/net/ipv6_route\").read()}\nprint(\"ERR ISOLATION \"+json.dumps(state),flush=True)\nassert state[\"netns\"] != sys.argv[1], \"network namespace was not isolated\"\nassert [name for _,name in state[\"interfaces\"]] == [\"lo\"], \"non-loopback interface present\"\nassert len(state[\"routes\"].splitlines()) == 1, \"IPv4 route present\"\nassert all(line.split()[-1] == \"lo\" for line in state[\"ipv6Routes\"].splitlines()), \"non-loopback IPv6 route present\"\nif len(sys.argv)>2: os.execv(sys.argv[2],sys.argv[2:])\n",
|
||||
"net:[4026531833]"
|
||||
],
|
||||
"exit": 0,
|
||||
"stdout": "ERR ISOLATION {\"netns\": \"net:[4026534801]\", \"interfaces\": [[1, \"lo\"]], \"routes\": \"Iface\\tDestination\\tGateway \\tFlags\\tRefCnt\\tUse\\tMetric\\tMask\\t\\tMTU\\tWindow\\tIRTT \\n\", \"ipv6Routes\": \"00000000000000000000000000000000 00 00000000000000000000000000000000 00 00000000000000000000000000000000 ffffffff 00000001 00000000 00200200 lo\\n00000000000000000000000000000001 80 00000000000000000000000000000000 00 00000000000000000000000000000000 00000000 00000002 00000000 80200001 lo\\n00000000000000000000000000000000 00 00000000000000000000000000000000 00 00000000000000000000000000000000 ffffffff 00000001 00000000 00200200 lo\\n\"}\n",
|
||||
"stderr": ""
|
||||
}
|
||||
+304
@@ -0,0 +1,304 @@
|
||||
---------------------------------------------
|
||||
Log /tmp/work/profile/logs/logs_2026-09-19_13-14-58/console.log started at 2026-09-19 13:14:58 (2026-09-19 11:14:58 UTC)
|
||||
|
||||
13:14:58.032 ENGINE : FileSystem: Adding relative directory '/tmp/work/profile/logs/logs_2026-09-19_13-14-58' to filesystem under name logs
|
||||
13:14:58.032 ENGINE : FileSystem: Adding relative directory '/tmp/work/profile/profile' to filesystem under name profile
|
||||
13:14:58.032 ENGINE : FileSystem: Current working directory: '/tmp/runtime/'
|
||||
13:14:58.033 ENGINE : Initializing engine, version 192142
|
||||
13:14:58.033 ENGINE : CLI Params: -profile /tmp/work/profile -noBackend -noSound -noSplash -noThrow -bindIP 127.0.0.1 -bindPort 2001 -maxFPS 30 -logStats 5000 -addonsDir /tmp/source/addons -addons FA804F411BDDF2C6 -server {C1E20667DCE324ED}Missions/ERR_Everon.conf
|
||||
13:14:58.034 ENGINE : Addon dirs:
|
||||
13:14:58.034 ENGINE : dir: '/tmp/source/addons'
|
||||
13:14:58.034 ENGINE : dir: './addons'
|
||||
13:14:58.034 ENGINE : dir: '/tmp/work/profile/addons'
|
||||
13:14:58.034 ENGINE : Available addons:
|
||||
13:14:58.034 ENGINE : gproj: '/tmp/source/addons/ai/addon.gproj' guid: '5192138CB9D5B911'
|
||||
13:14:58.034 ENGINE : gproj: '/tmp/source/addons/chat/addon.gproj' guid: 'FA804F411BDDF2C6'
|
||||
13:14:58.034 ENGINE : gproj: '/tmp/source/addons/core/addon.gproj' guid: '2528466DE220D91C'
|
||||
13:14:58.034 ENGINE : gproj: '/tmp/source/addons/intel/addon.gproj' guid: 'D31039C7C53E348A'
|
||||
13:14:58.034 ENGINE : gproj: '/tmp/source/addons/map/addon.gproj' guid: '9AAEE9041D79809E'
|
||||
13:14:58.034 ENGINE : gproj: './addons/data/ArmaReforger.gproj' guid: '58D0FB3206B6F859' (packed)
|
||||
13:14:58.034 ENGINE : gproj: './addons/core/core.gproj' guid: '5614BBCCBB55ED1C' (packed)
|
||||
13:14:58.034 ENGINE : FileSystem: Adding relative directory '/tmp/source/addons/chat' to filesystem under name ERR_Chat
|
||||
13:14:58.034 ENGINE : FileSystem: Adding relative directory '/tmp/source/addons/map' to filesystem under name ERR_Map
|
||||
13:14:58.034 ENGINE : FileSystem: Adding relative directory '/tmp/source/addons/intel' to filesystem under name ERR_Intel
|
||||
13:14:58.034 ENGINE : FileSystem: Adding relative directory '/tmp/source/addons/ai' to filesystem under name ERR_Ai
|
||||
13:14:58.034 ENGINE : FileSystem: Adding relative directory '/tmp/source/addons/core' to filesystem under name ERR_Core
|
||||
13:14:58.123 ENGINE : FileSystem: Adding package './addons/data/' (pak count: 6) to filesystem under name ArmaReforger
|
||||
13:14:58.123 ENGINE : FileSystem: Adding package './addons/core/' (pak count: 1) to filesystem under name core
|
||||
13:14:58.123 ENGINE : CPU vendor : AuthenticAMD
|
||||
13:14:58.123 ENGINE : CPU type : 2
|
||||
13:14:58.123 ENGINE : CPU name : AMD Ryzen 7 3800XT 8-Core Processor
|
||||
13:14:58.123 ENGINE : CPU cache info : Unknown cache
|
||||
13:14:58.123 ENGINE : CPU count logical : 16
|
||||
13:14:58.123 ENGINE : CPU count physical : 8
|
||||
13:14:58.123 ENGINE : CPU features : mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 popcnt aesni pclmulqdq osxsave avx sha avx2
|
||||
13:14:58.123 RESOURCES : ResourceDB: loading cache (id=0 name=ERR_Chat path=/tmp/source/addons/chat/resourceDatabase.rdb)
|
||||
13:14:58.124 RESOURCES (W): ResourceDB: could not open the cache file '/tmp/source/addons/chat/resourceDatabase.rdb'
|
||||
13:14:58.124 RESOURCES : ResourceDB: loading cache (id=1 name=ERR_Map path=/tmp/source/addons/map/resourceDatabase.rdb)
|
||||
13:14:58.124 RESOURCES (W): ResourceDB: could not open the cache file '/tmp/source/addons/map/resourceDatabase.rdb'
|
||||
13:14:58.124 RESOURCES : ResourceDB: loading cache (id=2 name=ERR_Intel path=/tmp/source/addons/intel/resourceDatabase.rdb)
|
||||
13:14:58.124 RESOURCES (W): ResourceDB: could not open the cache file '/tmp/source/addons/intel/resourceDatabase.rdb'
|
||||
13:14:58.124 RESOURCES : ResourceDB: loading cache (id=3 name=ERR_Ai path=/tmp/source/addons/ai/resourceDatabase.rdb)
|
||||
13:14:58.124 RESOURCES (W): ResourceDB: could not open the cache file '/tmp/source/addons/ai/resourceDatabase.rdb'
|
||||
13:14:58.124 RESOURCES : ResourceDB: loading cache (id=4 name=ERR_Core path=/tmp/source/addons/core/resourceDatabase.rdb)
|
||||
13:14:58.124 RESOURCES (W): ResourceDB: could not open the cache file '/tmp/source/addons/core/resourceDatabase.rdb'
|
||||
13:14:58.124 RESOURCES : ResourceDB: loading cache (id=5 name=ArmaReforger path=./addons/data/resourceDatabase.rdb)
|
||||
13:14:58.253 RESOURCES : ResourceDB: loading cache (id=6 name=core path=./addons/core/resourceDatabase.rdb)
|
||||
13:14:58.254 RESOURCES : ResourceDB: loading cache (id=7 name=profile path=/tmp/work/profile/profile/resourceDatabase.rdb)
|
||||
13:14:58.254 RESOURCES : ResourceDB: loading cache (id=8 name=logs path=/tmp/work/profile/logs/logs_2026-09-19_13-14-58/resourceDatabase.rdb)
|
||||
13:14:58.394 ENGINE : GameProject load
|
||||
13:14:58.394 ENGINE : Loaded addons:
|
||||
13:14:58.394 ENGINE : gproj: './addons/core/core.gproj' guid: '5614BBCCBB55ED1C'
|
||||
13:14:58.394 ENGINE : gproj: './addons/data/ArmaReforger.gproj' guid: '58D0FB3206B6F859'
|
||||
13:14:58.394 ENGINE : gproj: '/tmp/source/addons/core/addon.gproj' guid: '2528466DE220D91C'
|
||||
13:14:58.394 ENGINE : gproj: '/tmp/source/addons/ai/addon.gproj' guid: '5192138CB9D5B911'
|
||||
13:14:58.394 ENGINE : gproj: '/tmp/source/addons/intel/addon.gproj' guid: 'D31039C7C53E348A'
|
||||
13:14:58.394 ENGINE : gproj: '/tmp/source/addons/map/addon.gproj' guid: '9AAEE9041D79809E'
|
||||
13:14:58.394 ENGINE : gproj: '/tmp/source/addons/chat/addon.gproj' guid: 'FA804F411BDDF2C6'
|
||||
13:14:58.395 ENGINE : Loading custom engine user config '$profile:.save/settings/ReforgerEngineSettings.conf'
|
||||
13:14:58.395 ENGINE : No Enfusion settings file loaded. Continuing with defaults
|
||||
13:14:58.395 RENDER : Basic screen setup:
|
||||
13:14:58.395 RENDER : Back buffer width x height: 0x0
|
||||
13:14:58.395 RENDER : Back buffer format : TEXFMT_UNKNOWN (0)
|
||||
13:14:58.395 RENDER : MSAA : none
|
||||
13:14:58.395 RENDER : GPU_DUMMY (0xFFFF) (Enfusion dummy render) with 1 GPU detected, VRAM 128MB (fast 128MB), shared 128MB, driver version Unknown version
|
||||
13:14:58.395 RENDER : Detected output devices:
|
||||
13:14:58.395 RENDER : * 1 device/s connected to Dummy:
|
||||
13:14:58.395 RENDER : Dummy output (Dummy device for our dummy render.), 1920x1080 (offset 0x0), 60Hz
|
||||
13:14:58.395 RENDER : Concurrent frames limit 2
|
||||
13:14:58.395 ENGINE : Job system settings: coreCount=16, shortJobContextCount=16, shortJobThreadCount=15, longJobThreadCount=7
|
||||
13:14:58.408 PROFILING : Settings changed took: 0.025000 ms
|
||||
13:14:58.411 ENGINE : Initializing inputs.
|
||||
13:14:58.419 NETWORK : Initializing networking.
|
||||
13:14:58.419 NETWORK : Using new GameRplScheduler.
|
||||
13:14:58.419 SCRIPT : SCRIPT : Initializing scripts
|
||||
13:14:58.419 SCRIPT : SCRIPT : ScriptProjectManager init
|
||||
13:14:58.419 PROFILING : ScriptProjectManager init took: 0.009000 ms
|
||||
13:14:58.440 PROFILING : Initializing scripts took: 20.561000 ms
|
||||
13:14:58.440 BACKEND : Backend Services are disabled.
|
||||
13:14:58.440 ENGINE : Enfusion engine successfully created.
|
||||
13:14:58.442 GUI : Using default language (en_us)
|
||||
13:14:58.442 GUI : Loading 'en_us' localization file.
|
||||
13:14:58.442 INIT : INIT : Loading StringTable
|
||||
13:14:58.463 PROFILING : Loading StringTable took: 20.762000 ms
|
||||
13:14:58.463 SCRIPT : SCRIPT : Compiling GameLib scripts
|
||||
13:14:58.496 SCRIPT : Module: GameLib; loaded 465x files; 607x classes; used 654K of static memory; defines: "PLATFORM_LINUX,ENF_ENABLE_VHC_OVERRIDE,ENF_ENABLE_RPLCOMPONENT_IN_GAMECODE"; CRC32: 4507af04
|
||||
13:14:58.496 PROFILING : Compiling GameLib scripts took: 33.527000 ms
|
||||
13:14:58.496 SCRIPT : SCRIPT : Compiling Game scripts
|
||||
13:14:59.284 SCRIPT : Compiling Game scripts
|
||||
13:14:59.284 SCRIPT (W): @"scripts/Game/Editor/Components/EditableEntity/SCR_EditableEntityComponent.c,785": 'GetPlayerPlatformKind' is obsolete
|
||||
13:14:59.284 SCRIPT (W): @"scripts/Game/Editor/Components/EditableEntity/SCR_EditableEntityComponent.c,786": 'GetPlayerPlatformId' is obsolete
|
||||
13:14:59.510 SCRIPT (W): @"scripts/Game/Workshop/SCR_AddonManager.c,876": 'GetRefCount' is obsolete: Will be removed
|
||||
13:14:59.536 SCRIPT (W): @"scripts/Game/Campaign/SCR_PlayerProfileManagerComponent.c,142": 'PlayerData' is obsolete
|
||||
13:14:59.624 SCRIPT (W): @"scripts/Game/UI/Backend/CareerBackend.c,159": 'GetDSSession' is obsolete
|
||||
13:14:59.624 SCRIPT (W): @"scripts/Game/UI/Backend/CareerBackend.c,162": 'RoomID' is obsolete
|
||||
13:14:59.624 SCRIPT (W): @"scripts/Game/UI/Backend/CareerBackend.c,197": 'GetDSSession' is obsolete
|
||||
13:14:59.624 SCRIPT (W): @"scripts/Game/UI/Backend/CareerBackend.c,201": 'RoomID' is obsolete
|
||||
13:14:59.706 SCRIPT (W): @"scripts/Game/DataCollection/SCR_DataCollectorComponent.c,481": 'GetStorage' is obsolete
|
||||
13:14:59.706 SCRIPT (W): @"scripts/Game/DataCollection/SCR_DataCollectorComponent.c,483": 'GetOnlineWritePrivilege' is obsolete
|
||||
13:14:59.707 SCRIPT (W): @"scripts/Game/Utilities/SCR_PlayerIdentityUtils.c,20": 'GetPlayerIdentityId' is obsolete
|
||||
13:14:59.744 SCRIPT (W): @"scripts/Game/UI/Menu/MainMenuUI.c,643": 'GetNotifyCount' is obsolete: Will be substitued by Notification Api.
|
||||
13:14:59.744 SCRIPT (W): @"scripts/Game/UI/Menu/MainMenuUI.c,645": 'GetNotifyItem' is obsolete: Will be substitued by Notification Api.
|
||||
13:14:59.799 SCRIPT (W): @"scripts/Game/DataCollection/PlayerData/SCR_PlayerData.c,210": 'PlayerData' is obsolete
|
||||
13:14:59.906 SCRIPT : Module: Game; loaded 5660x files; 11004x classes; used 25508K of static memory; defines: "PLATFORM_LINUX,ENF_ENABLE_VHC_OVERRIDE,ENF_ENABLE_RPLCOMPONENT_IN_GAMECODE,ENABLE_BASE_DESTRUCTION,BUDGET_OPTIMIZATION_CHECKS,DEBUG,RESPAWN_TIMER_COMPONENT_DEBUG,DEBUG_CONFIGURABLE_DIALOGS,DISABLE_WEAPON_SWITCHING,SCRIPTED_AIM_MODIFIER_DEBUG"; CRC32: 931a2d4d
|
||||
13:14:59.922 PROFILING : Compiling Game scripts took: 1425.231000 ms
|
||||
13:14:59.922 INIT : Creating game instance(ArmaReforgerScripted), version 1.8.0.13 built 2026-08-21 14:30:18 UTC.
|
||||
13:14:59.943 ENGINE : Game successfully created.
|
||||
13:15:00.028 PLATFORM : Save data from container 'settings' have been loaded
|
||||
13:15:00.127 PLATFORM : Save data from container 'sessions' have been loaded
|
||||
13:15:00.127 PLATFORM : System language differs from the saved one - using new language {new: 'en_us'; old: ''}
|
||||
13:15:00.127 PLATFORM : Save data transaction to 'settings' started.
|
||||
13:15:00.127 NETWORK : Starting local multiplayer server using command line args.
|
||||
13:15:00.127 RESOURCES : GetResourceObject @"{C1E20667DCE324ED}Missions/ERR_Everon.conf"
|
||||
13:15:00.127 DEFAULT : Config load @"{C1E20667DCE324ED}Missions/ERR_Everon.conf"
|
||||
13:15:00.127 RESOURCES (E): Wrong GUID/name for resource @"{B6C516F86302C679}Worlds/ERR_Everon.ent" in property "World"
|
||||
13:15:00.226 DEFAULT : [SaveGameManager] Starting new playthrough nr.0 '' for mission '{C1E20667DCE324ED}Missions/ERR_Everon.conf'.
|
||||
13:15:00.227 WORLD : WORLD : Entities load 'Worlds/ERR_Everon.ent'
|
||||
13:15:00.341 RESOURCES : GetResourceObject @"{58E62C5D17281925}Prefabs/Particles/Blasts/Blast_small_Invisible_Trigger.et"
|
||||
13:15:00.341 WORLD : Entity prefab load @"{58E62C5D17281925}Prefabs/Particles/Blasts/Blast_small_Invisible_Trigger.et"
|
||||
13:15:00.341 RESOURCES (E): Wrong GUID/name for resource @"{FF35BE9795BA3797}system/wbdata/Gizmo3D/sphere.xob" in property "Object"
|
||||
13:15:00.816 WORLD : Entities load 'Worlds/ERR_Everon.ent'
|
||||
13:15:00.816 WORLD : Subscene load @"{853E92315D1D9EFE}worlds/Eden/Eden.ent"
|
||||
13:15:00.816 RESOURCES : GetResourceObject @"{C8FAB83670BCF7C3}Prefabs/Structures/Cultural/Calvaries/CalvaryLarge_01.et"
|
||||
13:15:00.816 WORLD : Entity prefab load @"{C8FAB83670BCF7C3}Prefabs/Structures/Cultural/Calvaries/CalvaryLarge_01.et"
|
||||
13:15:00.816 RESOURCES (E): Wrong GUID/name for resource @"{45ECF6B1797D3CD9}system/wbdata/PreviewWindow/TransparentMat.emat" in property "AssignedMaterial"
|
||||
13:15:00.963 WORLD : Entity layer load @"Worlds/ERR_Everon_Layers/default.layer"
|
||||
13:15:00.963 RESOURCES (E): Wrong GUID/name for resource @"{978E045E53EF36C8}Prefabs/ERR/GameMode.et" in property "inherited-name"
|
||||
13:15:00.963 WORLD (E): Unknown class 'ERR_GameMode' at offset 73(0x49)
|
||||
13:15:00.963 RESOURCES (E): Wrong GUID/name for resource @"{5414041A15DC4B54}Prefabs/ERR/FactionManager.et" in property "inherited-name"
|
||||
13:15:00.963 RESOURCES : GetResourceObject @"{5414041A15DC4B54}Prefabs/ERR/FactionManager.et"
|
||||
13:15:00.963 WORLD : Entity prefab load @"{5414041A15DC4B54}Prefabs/ERR/FactionManager.et"
|
||||
13:15:00.963 RESOURCES (E): Wrong GUID/name for resource @"{5C67A1DDE0044806}Configs/ERR/Factions/US.conf" in property "inherited-name"
|
||||
13:15:00.964 RESOURCES : GetResourceObject @"{5C67A1DDE0044806}Configs/ERR/Factions/US.conf"
|
||||
13:15:00.964 DEFAULT : Config load @"{5C67A1DDE0044806}Configs/ERR/Factions/US.conf"
|
||||
13:15:00.964 RESOURCES (E): Wrong GUID/name for resource @"{E4785D3AF1D043C2}Configs/ERR/Factions/MissionVehicles.conf" in property "inherited-name"
|
||||
13:15:00.965 RESOURCES (E): Wrong GUID/name for resource @"{D7A5CFA62A36480D}Configs/ERR/Factions/USSR.conf" in property "inherited-name"
|
||||
13:15:00.965 RESOURCES : GetResourceObject @"{D7A5CFA62A36480D}Configs/ERR/Factions/USSR.conf"
|
||||
13:15:00.965 DEFAULT : Config load @"{D7A5CFA62A36480D}Configs/ERR/Factions/USSR.conf"
|
||||
13:15:00.965 RESOURCES (E): Wrong GUID/name for resource @"{E4785D3AF1D043C2}Configs/ERR/Factions/MissionVehicles.conf" in property "inherited-name"
|
||||
13:15:00.966 PROFILING : Entities load took: 739.507000 ms
|
||||
13:15:00.967 WORLD : WORLD : Game::LoadEntities
|
||||
13:15:00.967 WORLD : WORLD : Initializing world systems @"{C50579A2EC48E8EF}Configs/Systems/MissionSystems.conf"
|
||||
13:15:00.968 NETWORK : Enabled lag compensation, 29 frames with 100 FPS and 280 ms rewind limits.
|
||||
13:15:00.969 WORLD : WORLD : CreateEntities
|
||||
13:15:00.969 WORLD : WORLD : CreateEntities
|
||||
13:15:00.969 WORLD : WORLD : NotifyBeforeEntitiesCreated
|
||||
13:15:00.969 WORLD : Game::LoadEntities
|
||||
13:15:00.969 WORLD : CreateEntities
|
||||
13:15:00.969 WORLD : CreateEntities
|
||||
13:15:00.969 WORLD : NotifyBeforeEntitiesCreated
|
||||
13:15:00.969 RESOURCES : GetResourceObject '$ERR_Chat:Missions/ERR_Everon.conf'
|
||||
13:15:00.969 DEFAULT : Config load '$ERR_Chat:Missions/ERR_Everon.conf'
|
||||
13:15:00.969 RESOURCES (E): Wrong GUID/name for resource @"{B6C516F86302C679}Worlds/ERR_Everon.ent" in property "World"
|
||||
13:15:00.969 RESOURCES : GetResourceObject '$ERR_Core:Missions/ERR_Everon_Base.conf'
|
||||
13:15:00.969 DEFAULT : Config load '$ERR_Core:Missions/ERR_Everon_Base.conf'
|
||||
13:15:00.969 RESOURCES (E): Wrong GUID/name for resource @"{B6C516F86302C679}Worlds/ERR_Everon.ent" in property "World"
|
||||
13:15:00.970 RESOURCES : Loaded mission headers from 7 addon(s)!
|
||||
13:15:00.970 PROFILING : NotifyBeforeEntitiesCreated took: 1.522000 ms
|
||||
13:15:00.970 WORLD : WORLD : Wait for preloading data
|
||||
13:15:02.042 PROFILING : Wait for preloading data took: 1072.274000 ms
|
||||
13:15:02.105 ENTITY : Create entity @"ENTITY:2" ('Eden', GenericTerrainEntity) at <0.000000 0.000000 0.000000> @"{5AD924374E8C7AF6}Prefabs/World/Game/Eden/GenericTerrain_Eden.et"
|
||||
13:15:02.105 PROFILING : PROFILING : Terrain load
|
||||
13:15:03.298 PROFILING : Terrain load took: 1193.704000 ms
|
||||
13:15:05.817 ENTITY : Create entity @"ENTITY:610406" ('StaticModelEntity','Assets/Structures/Infrastructure/Towers/TransmitterTower_01/TransmitterTower_01_small.xob') at <1931.796021 81.777000 5138.725098> @"{6A004A8F0571D456}Prefabs/Structures/Infrastructure/Towers/TransmitterTower_01/TransmitterTower_01_small.et"
|
||||
13:15:05.817 ENTITY : CreateComponent 'ScriptedRadioComponent'
|
||||
13:15:05.817 DEFAULT (W): World doesn't contain RadioManagerEntity to support any BaseRadioComponent.
|
||||
13:15:07.939 ENTITY : Create entity @"ENTITY:1104061" ('StaticModelEntity') at <8831.291016 95.934265 2673.487061>
|
||||
13:15:07.939 ENTITY : EntityPrefabUse @"{C8FAB83670BCF7C3}Prefabs/Structures/Cultural/Calvaries/CalvaryLarge_01.et"
|
||||
13:15:07.939 RESOURCES : CreateComponentData @"{904345715F67BBAC}Assets/Structures/Cultural/Calvaries/CalvaryLarge_01/CalvaryLarge_01.xob"
|
||||
13:15:07.939 MATERIAL : Material load @"{45ECF6B1797D3CD9}system/wbdata/PreviewWindow/TransparentMat.emat"
|
||||
13:15:07.939 MATERIAL (E): Material file not found
|
||||
13:15:07.939 MATERIAL (E): Cannot load material @"system/wbdata/PreviewWindow/TransparentMat.emat", creating default one (class MatPBRBasic)
|
||||
13:15:10.631 WORLD : WORLD : NotifyBeforeEntitiesInitialized
|
||||
13:15:10.631 PROFILING : NotifyBeforeEntitiesInitialized took: 0.034000 ms
|
||||
13:15:10.631 WORLD : InitEntities 'world'
|
||||
13:15:10.631 ENTITY : Init entity @"ENTITY:3" ('SCR_MapEntity1', SCR_MapEntity) at <6400.000000 0.000000 6400.000000> @"{E1B88C66BF6CA1F9}Prefabs/World/Game/Eden/MapEntity_Everon.et"
|
||||
13:15:10.631 SCRIPT (W): SCR_MapEntity: Cannot get the size from terrain. Using default.
|
||||
13:15:11.148 ENTITY : Init entity @"ENTITY:2305843009213693955" ('Factions', SCR_FactionManager) at <0.000000 0.000000 0.000000> @"{5414041A15DC4B54}Prefabs/ERR/FactionManager.et"
|
||||
13:15:11.148 RPL (E): Attempting to insert an entity with invalid prefab resource GUID @"{5414041A15DC4B54}".
|
||||
13:15:11.149 SCRIPT (E): Virtual Machine Exception
|
||||
|
||||
Reason: NULL pointer to instance. Variable 'm_OnPlayerDisconnected'
|
||||
|
||||
Class: 'SCR_FactionManager'
|
||||
Entity name:'Factions' id:2305843009213693955
|
||||
|
||||
Function: 'EOnInit'
|
||||
Stack trace:
|
||||
scripts/Game/GameMode/FactionManager/SCR_FactionManager.c:439 Function EOnInit
|
||||
13:15:11.149 WORLD : WORLD : NotifyEntitiesInitialized
|
||||
13:15:11.149 TERRAIN : TERRAIN : Initializing road networks...
|
||||
13:15:11.151 PROFILING : Initializing road networks... took: 1.845000 ms
|
||||
13:15:11.151 DEFAULT : DEFAULT : WorldVolumesCollection
|
||||
13:15:11.207 PROFILING : WorldVolumesCollection took: 56.663000 ms
|
||||
13:15:11.207 INIT : INIT : SoundWorldInit
|
||||
13:15:11.215 PROFILING : SoundWorldInit took: 7.453000 ms
|
||||
13:15:11.215 PROFILING : NotifyEntitiesInitialized took: 66.060000 ms
|
||||
13:15:11.215 PROFILING : CreateEntities took: 10246.394000 ms
|
||||
13:15:11.215 PROFILING : CreateEntities took: 10246.400000 ms
|
||||
13:15:11.215 WORLD : WORLD : EOnActivate
|
||||
13:15:11.248 PROFILING : EOnActivate took: 33.366000 ms
|
||||
13:15:11.248 PROFILING : Game::LoadEntities took: 10280.968000 ms
|
||||
13:15:11.365 NETWORK : Starting RPL server, listening on address 0.0.0.0:2001, fastValidation=false
|
||||
13:15:11.841 DEFAULT : Entered online game state.
|
||||
13:15:11.842 PROFILING : Settings changed took: 0.491000 ms
|
||||
13:15:11.842 DEFAULT (W): No GameMode present in the world, using fallback logic!
|
||||
13:15:11.842 SCRIPT (E): 'SCR_BaseResupplySupportStationComponent' needs a entity catalog manager!
|
||||
13:15:11.842 SCRIPT (E): 'SCR_BaseResupplySupportStationComponent' needs a entity catalog manager!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:16.854 WORLD : UpdateEntities
|
||||
13:15:16.854 WORLD : Frame
|
||||
13:15:16.854 DEFAULT : FPS: 29.7, frame time (avg: 33.6 ms, min: 23.4 ms, max: 96.0 ms, mean: 33.3 ms, median: 33.5 ms), Mem: 985260 kB, Player: 0, AI: 0, AIChar: 0, Veh: 0 (0), Proj (S: 0, M: 0, G: 0 | 0), Streaming(Dynam: 509, Static: 57589, Disabled: 16 | New: 0, Del: 0, Bump: 16)
|
||||
13:15:21.854 WORLD : UpdateEntities
|
||||
13:15:21.854 WORLD : Frame
|
||||
13:15:21.854 DEFAULT : FPS: 30.0, frame time (avg: 33.3 ms, min: 31.0 ms, max: 35.2 ms, mean: 33.3 ms, median: 33.4 ms), Mem: 985260 kB, Player: 0, AI: 0, AIChar: 0, Veh: 0 (0), Proj (S: 0, M: 0, G: 0 | 0), Streaming(Dynam: 509, Static: 57589, Disabled: 16 | New: 0, Del: 0, Bump: 16)
|
||||
13:15:26.887 WORLD : UpdateEntities
|
||||
13:15:26.887 WORLD : Frame
|
||||
13:15:26.887 DEFAULT : FPS: 30.0, frame time (avg: 33.3 ms, min: 32.4 ms, max: 34.3 ms, mean: 33.3 ms, median: 33.4 ms), Mem: 985260 kB, Player: 0, AI: 0, AIChar: 0, Veh: 0 (0), Proj (S: 0, M: 0, G: 0 | 0), Streaming(Dynam: 509, Static: 57589, Disabled: 16 | New: 0, Del: 0, Bump: 16)
|
||||
13:15:31.920 WORLD : UpdateEntities
|
||||
13:15:31.920 WORLD : Frame
|
||||
13:15:31.920 DEFAULT : FPS: 30.0, frame time (avg: 33.3 ms, min: 30.8 ms, max: 36.3 ms, mean: 33.3 ms, median: 33.4 ms), Mem: 985260 kB, Player: 0, AI: 0, AIChar: 0, Veh: 0 (0), Proj (S: 0, M: 0, G: 0 | 0), Streaming(Dynam: 509, Static: 57589, Disabled: 16 | New: 0, Del: 0, Bump: 16)
|
||||
13:15:36.954 WORLD : UpdateEntities
|
||||
13:15:36.954 WORLD : Frame
|
||||
13:15:36.954 DEFAULT : FPS: 30.0, frame time (avg: 33.3 ms, min: 32.2 ms, max: 34.3 ms, mean: 33.3 ms, median: 33.4 ms), Mem: 985260 kB, Player: 0, AI: 0, AIChar: 0, Veh: 0 (0), Proj (S: 0, M: 0, G: 0 | 0), Streaming(Dynam: 509, Static: 57589, Disabled: 16 | New: 0, Del: 0, Bump: 16)
|
||||
13:15:41.987 WORLD : UpdateEntities
|
||||
13:15:41.987 WORLD : Frame
|
||||
13:15:41.987 DEFAULT : FPS: 30.0, frame time (avg: 33.3 ms, min: 32.4 ms, max: 34.3 ms, mean: 33.3 ms, median: 33.4 ms), Mem: 985260 kB, Player: 0, AI: 0, AIChar: 0, Veh: 0 (0), Proj (S: 0, M: 0, G: 0 | 0), Streaming(Dynam: 509, Static: 57589, Disabled: 16 | New: 0, Del: 0, Bump: 16)
|
||||
13:15:46.987 WORLD : UpdateEntities
|
||||
13:15:46.987 WORLD : Frame
|
||||
13:15:46.987 DEFAULT : FPS: 30.0, frame time (avg: 33.3 ms, min: 32.4 ms, max: 34.2 ms, mean: 33.3 ms, median: 33.4 ms), Mem: 985260 kB, Player: 0, AI: 0, AIChar: 0, Veh: 0 (0), Proj (S: 0, M: 0, G: 0 | 0), Streaming(Dynam: 509, Static: 57589, Disabled: 16 | New: 0, Del: 0, Bump: 16)
|
||||
13:15:52.021 WORLD : UpdateEntities
|
||||
13:15:52.021 WORLD : Frame
|
||||
13:15:52.021 DEFAULT : FPS: 30.0, frame time (avg: 33.3 ms, min: 32.3 ms, max: 34.4 ms, mean: 33.3 ms, median: 33.4 ms), Mem: 985260 kB, Player: 0, AI: 0, AIChar: 0, Veh: 0 (0), Proj (S: 0, M: 0, G: 0 | 0), Streaming(Dynam: 509, Static: 57589, Disabled: 16 | New: 0, Del: 0, Bump: 16)
|
||||
13:15:57.053 WORLD : UpdateEntities
|
||||
13:15:57.053 WORLD : Frame
|
||||
13:15:57.053 DEFAULT : FPS: 30.0, frame time (avg: 33.3 ms, min: 31.0 ms, max: 35.3 ms, mean: 33.3 ms, median: 33.5 ms), Mem: 985260 kB, Player: 0, AI: 0, AIChar: 0, Veh: 0 (0), Proj (S: 0, M: 0, G: 0 | 0), Streaming(Dynam: 509, Static: 57589, Disabled: 16 | New: 0, Del: 0, Bump: 16)
|
||||
13:16:02.054 WORLD : UpdateEntities
|
||||
13:16:02.054 WORLD : Frame
|
||||
13:16:02.054 DEFAULT : FPS: 30.0, frame time (avg: 33.3 ms, min: 31.5 ms, max: 34.6 ms, mean: 33.3 ms, median: 33.5 ms), Mem: 985260 kB, Player: 0, AI: 0, AIChar: 0, Veh: 0 (0), Proj (S: 0, M: 0, G: 0 | 0), Streaming(Dynam: 509, Static: 57589, Disabled: 16 | New: 0, Del: 0, Bump: 16)
|
||||
13:16:07.087 WORLD : UpdateEntities
|
||||
13:16:07.087 WORLD : Frame
|
||||
13:16:07.087 DEFAULT : FPS: 30.0, frame time (avg: 33.3 ms, min: 31.7 ms, max: 34.6 ms, mean: 33.3 ms, median: 33.5 ms), Mem: 985260 kB, Player: 0, AI: 0, AIChar: 0, Veh: 0 (0), Proj (S: 0, M: 0, G: 0 | 0), Streaming(Dynam: 509, Static: 57589, Disabled: 16 | New: 0, Del: 0, Bump: 16)
|
||||
13:16:12.120 WORLD : UpdateEntities
|
||||
13:16:12.120 WORLD : Frame
|
||||
13:16:12.120 DEFAULT : FPS: 30.0, frame time (avg: 33.3 ms, min: 32.4 ms, max: 34.7 ms, mean: 33.3 ms, median: 33.5 ms), Mem: 985260 kB, Player: 0, AI: 0, AIChar: 0, Veh: 0 (0), Proj (S: 0, M: 0, G: 0 | 0), Streaming(Dynam: 509, Static: 57589, Disabled: 16 | New: 0, Del: 0, Bump: 16)
|
||||
13:16:17.121 WORLD : UpdateEntities
|
||||
13:16:17.121 WORLD : Frame
|
||||
13:16:17.121 DEFAULT : FPS: 30.0, frame time (avg: 33.3 ms, min: 32.4 ms, max: 34.4 ms, mean: 33.3 ms, median: 33.5 ms), Mem: 985260 kB, Player: 0, AI: 0, AIChar: 0, Veh: 0 (0), Proj (S: 0, M: 0, G: 0 | 0), Streaming(Dynam: 509, Static: 57589, Disabled: 16 | New: 0, Del: 0, Bump: 16)
|
||||
13:16:22.154 WORLD : UpdateEntities
|
||||
13:16:22.154 WORLD : Frame
|
||||
13:16:22.154 DEFAULT : FPS: 30.0, frame time (avg: 33.3 ms, min: 32.4 ms, max: 33.9 ms, mean: 33.3 ms, median: 33.5 ms), Mem: 985260 kB, Player: 0, AI: 0, AIChar: 0, Veh: 0 (0), Proj (S: 0, M: 0, G: 0 | 0), Streaming(Dynam: 509, Static: 57589, Disabled: 16 | New: 0, Del: 0, Bump: 16)
|
||||
13:16:27.187 WORLD : UpdateEntities
|
||||
13:16:27.187 WORLD : Frame
|
||||
13:16:27.187 DEFAULT : FPS: 30.0, frame time (avg: 33.3 ms, min: 32.3 ms, max: 34.0 ms, mean: 33.3 ms, median: 33.5 ms), Mem: 985260 kB, Player: 0, AI: 0, AIChar: 0, Veh: 0 (0), Proj (S: 0, M: 0, G: 0 | 0), Streaming(Dynam: 509, Static: 57589, Disabled: 16 | New: 0, Del: 0, Bump: 16)
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
---------------------------------------------
|
||||
Log /tmp/work/profile/logs/logs_2026-09-19_13-14-58/crash.log started at 2026-09-19 13:15:11 (2026-09-19 11:15:11 UTC)
|
||||
|
||||
|
||||
|
||||
------------------------------------
|
||||
19.09 2026 13:15:11
|
||||
Virtual Machine Exception
|
||||
|
||||
Reason: NULL pointer to instance. Variable 'm_OnPlayerDisconnected'
|
||||
|
||||
Class: 'SCR_FactionManager'
|
||||
Entity name:'Factions' id:2305843009213693955
|
||||
|
||||
Function: 'EOnInit'
|
||||
Stack trace:
|
||||
scripts/Game/GameMode/FactionManager/SCR_FactionManager.c:439 Function EOnInit
|
||||
|
||||
Runtime mode
|
||||
CLI params: profile /tmp/work/profile noBackend noSound noSplash noThrow bindIP 127.0.0.1 bindPort 2001 maxFPS 30 logStats 5000 addonsDir /tmp/source/addons addons FA804F411BDDF2C6 server {C1E20667DCE324ED}Missions/ERR_Everon.conf
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
---------------------------------------------
|
||||
Log /tmp/work/profile/logs/logs_2026-09-19_13-14-58/error.log started at 2026-09-19 13:14:58 (2026-09-19 11:14:58 UTC)
|
||||
|
||||
13:14:58.124 RESOURCES (W): ResourceDB: could not open the cache file '/tmp/source/addons/chat/resourceDatabase.rdb'
|
||||
13:14:58.124 RESOURCES (W): ResourceDB: could not open the cache file '/tmp/source/addons/map/resourceDatabase.rdb'
|
||||
13:14:58.124 RESOURCES (W): ResourceDB: could not open the cache file '/tmp/source/addons/intel/resourceDatabase.rdb'
|
||||
13:14:58.124 RESOURCES (W): ResourceDB: could not open the cache file '/tmp/source/addons/ai/resourceDatabase.rdb'
|
||||
13:14:58.124 RESOURCES (W): ResourceDB: could not open the cache file '/tmp/source/addons/core/resourceDatabase.rdb'
|
||||
13:14:59.284 SCRIPT : Compiling Game scripts
|
||||
13:14:59.284 SCRIPT (W): @"scripts/Game/Editor/Components/EditableEntity/SCR_EditableEntityComponent.c,785": 'GetPlayerPlatformKind' is obsolete
|
||||
13:14:59.284 SCRIPT (W): @"scripts/Game/Editor/Components/EditableEntity/SCR_EditableEntityComponent.c,786": 'GetPlayerPlatformId' is obsolete
|
||||
13:14:59.510 SCRIPT (W): @"scripts/Game/Workshop/SCR_AddonManager.c,876": 'GetRefCount' is obsolete: Will be removed
|
||||
13:14:59.536 SCRIPT (W): @"scripts/Game/Campaign/SCR_PlayerProfileManagerComponent.c,142": 'PlayerData' is obsolete
|
||||
13:14:59.624 SCRIPT (W): @"scripts/Game/UI/Backend/CareerBackend.c,159": 'GetDSSession' is obsolete
|
||||
13:14:59.624 SCRIPT (W): @"scripts/Game/UI/Backend/CareerBackend.c,162": 'RoomID' is obsolete
|
||||
13:14:59.624 SCRIPT (W): @"scripts/Game/UI/Backend/CareerBackend.c,197": 'GetDSSession' is obsolete
|
||||
13:14:59.624 SCRIPT (W): @"scripts/Game/UI/Backend/CareerBackend.c,201": 'RoomID' is obsolete
|
||||
13:14:59.705 SCRIPT (W): @"scripts/Game/DataCollection/SCR_DataCollectorComponent.c,481": 'GetStorage' is obsolete
|
||||
13:14:59.706 SCRIPT (W): @"scripts/Game/DataCollection/SCR_DataCollectorComponent.c,483": 'GetOnlineWritePrivilege' is obsolete
|
||||
13:14:59.707 SCRIPT (W): @"scripts/Game/Utilities/SCR_PlayerIdentityUtils.c,20": 'GetPlayerIdentityId' is obsolete
|
||||
13:14:59.744 SCRIPT (W): @"scripts/Game/UI/Menu/MainMenuUI.c,643": 'GetNotifyCount' is obsolete: Will be substitued by Notification Api.
|
||||
13:14:59.744 SCRIPT (W): @"scripts/Game/UI/Menu/MainMenuUI.c,645": 'GetNotifyItem' is obsolete: Will be substitued by Notification Api.
|
||||
13:14:59.799 SCRIPT (W): @"scripts/Game/DataCollection/PlayerData/SCR_PlayerData.c,210": 'PlayerData' is obsolete
|
||||
13:15:00.127 RESOURCES : GetResourceObject @"{C1E20667DCE324ED}Missions/ERR_Everon.conf"
|
||||
13:15:00.127 DEFAULT : Config load @"{C1E20667DCE324ED}Missions/ERR_Everon.conf"
|
||||
13:15:00.127 RESOURCES (E): Wrong GUID/name for resource @"{B6C516F86302C679}Worlds/ERR_Everon.ent" in property "World"
|
||||
13:15:00.341 RESOURCES : GetResourceObject @"{58E62C5D17281925}Prefabs/Particles/Blasts/Blast_small_Invisible_Trigger.et"
|
||||
13:15:00.341 WORLD : Entity prefab load @"{58E62C5D17281925}Prefabs/Particles/Blasts/Blast_small_Invisible_Trigger.et"
|
||||
13:15:00.341 RESOURCES (E): Wrong GUID/name for resource @"{FF35BE9795BA3797}system/wbdata/Gizmo3D/sphere.xob" in property "Object"
|
||||
13:15:00.816 WORLD : Entities load 'Worlds/ERR_Everon.ent'
|
||||
13:15:00.816 WORLD : Subscene load @"{853E92315D1D9EFE}worlds/Eden/Eden.ent"
|
||||
13:15:00.816 RESOURCES : GetResourceObject @"{C8FAB83670BCF7C3}Prefabs/Structures/Cultural/Calvaries/CalvaryLarge_01.et"
|
||||
13:15:00.816 WORLD : Entity prefab load @"{C8FAB83670BCF7C3}Prefabs/Structures/Cultural/Calvaries/CalvaryLarge_01.et"
|
||||
13:15:00.816 RESOURCES (E): Wrong GUID/name for resource @"{45ECF6B1797D3CD9}system/wbdata/PreviewWindow/TransparentMat.emat" in property "AssignedMaterial"
|
||||
13:15:00.963 WORLD : Entity layer load @"Worlds/ERR_Everon_Layers/default.layer"
|
||||
13:15:00.963 RESOURCES (E): Wrong GUID/name for resource @"{978E045E53EF36C8}Prefabs/ERR/GameMode.et" in property "inherited-name"
|
||||
13:15:00.963 WORLD (E): Unknown class 'ERR_GameMode' at offset 73(0x49)
|
||||
13:15:00.963 RESOURCES (E): Wrong GUID/name for resource @"{5414041A15DC4B54}Prefabs/ERR/FactionManager.et" in property "inherited-name"
|
||||
13:15:00.963 RESOURCES : GetResourceObject @"{5414041A15DC4B54}Prefabs/ERR/FactionManager.et"
|
||||
13:15:00.963 WORLD : Entity prefab load @"{5414041A15DC4B54}Prefabs/ERR/FactionManager.et"
|
||||
13:15:00.963 RESOURCES (E): Wrong GUID/name for resource @"{5C67A1DDE0044806}Configs/ERR/Factions/US.conf" in property "inherited-name"
|
||||
13:15:00.964 RESOURCES : GetResourceObject @"{5C67A1DDE0044806}Configs/ERR/Factions/US.conf"
|
||||
13:15:00.964 DEFAULT : Config load @"{5C67A1DDE0044806}Configs/ERR/Factions/US.conf"
|
||||
13:15:00.964 RESOURCES (E): Wrong GUID/name for resource @"{E4785D3AF1D043C2}Configs/ERR/Factions/MissionVehicles.conf" in property "inherited-name"
|
||||
13:15:00.965 RESOURCES (E): Wrong GUID/name for resource @"{D7A5CFA62A36480D}Configs/ERR/Factions/USSR.conf" in property "inherited-name"
|
||||
13:15:00.965 RESOURCES : GetResourceObject @"{D7A5CFA62A36480D}Configs/ERR/Factions/USSR.conf"
|
||||
13:15:00.965 DEFAULT : Config load @"{D7A5CFA62A36480D}Configs/ERR/Factions/USSR.conf"
|
||||
13:15:00.965 RESOURCES (E): Wrong GUID/name for resource @"{E4785D3AF1D043C2}Configs/ERR/Factions/MissionVehicles.conf" in property "inherited-name"
|
||||
13:15:00.969 WORLD : Game::LoadEntities
|
||||
13:15:00.969 WORLD : CreateEntities
|
||||
13:15:00.969 WORLD : CreateEntities
|
||||
13:15:00.969 WORLD : NotifyBeforeEntitiesCreated
|
||||
13:15:00.969 RESOURCES : GetResourceObject '$ERR_Chat:Missions/ERR_Everon.conf'
|
||||
13:15:00.969 DEFAULT : Config load '$ERR_Chat:Missions/ERR_Everon.conf'
|
||||
13:15:00.969 RESOURCES (E): Wrong GUID/name for resource @"{B6C516F86302C679}Worlds/ERR_Everon.ent" in property "World"
|
||||
13:15:00.969 RESOURCES : GetResourceObject '$ERR_Core:Missions/ERR_Everon_Base.conf'
|
||||
13:15:00.969 DEFAULT : Config load '$ERR_Core:Missions/ERR_Everon_Base.conf'
|
||||
13:15:00.969 RESOURCES (E): Wrong GUID/name for resource @"{B6C516F86302C679}Worlds/ERR_Everon.ent" in property "World"
|
||||
13:15:05.817 ENTITY : Create entity @"ENTITY:610406" ('StaticModelEntity','Assets/Structures/Infrastructure/Towers/TransmitterTower_01/TransmitterTower_01_small.xob') at <1931.796021 81.777000 5138.725098> @"{6A004A8F0571D456}Prefabs/Structures/Infrastructure/Towers/TransmitterTower_01/TransmitterTower_01_small.et"
|
||||
13:15:05.817 ENTITY : CreateComponent 'ScriptedRadioComponent'
|
||||
13:15:05.817 DEFAULT (W): World doesn't contain RadioManagerEntity to support any BaseRadioComponent.
|
||||
13:15:07.939 ENTITY : Create entity @"ENTITY:1104061" ('StaticModelEntity') at <8831.291016 95.934265 2673.487061>
|
||||
13:15:07.939 ENTITY : EntityPrefabUse @"{C8FAB83670BCF7C3}Prefabs/Structures/Cultural/Calvaries/CalvaryLarge_01.et"
|
||||
13:15:07.939 RESOURCES : CreateComponentData @"{904345715F67BBAC}Assets/Structures/Cultural/Calvaries/CalvaryLarge_01/CalvaryLarge_01.xob"
|
||||
13:15:07.939 MATERIAL : Material load @"{45ECF6B1797D3CD9}system/wbdata/PreviewWindow/TransparentMat.emat"
|
||||
13:15:07.939 MATERIAL (E): Material file not found
|
||||
13:15:07.939 MATERIAL (E): Cannot load material @"system/wbdata/PreviewWindow/TransparentMat.emat", creating default one (class MatPBRBasic)
|
||||
13:15:10.631 WORLD : InitEntities 'world'
|
||||
13:15:10.631 ENTITY : Init entity @"ENTITY:3" ('SCR_MapEntity1', SCR_MapEntity) at <6400.000000 0.000000 6400.000000> @"{E1B88C66BF6CA1F9}Prefabs/World/Game/Eden/MapEntity_Everon.et"
|
||||
13:15:10.631 SCRIPT (W): SCR_MapEntity: Cannot get the size from terrain. Using default.
|
||||
13:15:11.148 ENTITY : Init entity @"ENTITY:2305843009213693955" ('Factions', SCR_FactionManager) at <0.000000 0.000000 0.000000> @"{5414041A15DC4B54}Prefabs/ERR/FactionManager.et"
|
||||
13:15:11.148 RPL (E): Attempting to insert an entity with invalid prefab resource GUID @"{5414041A15DC4B54}".
|
||||
13:15:11.149 SCRIPT (E): Virtual Machine Exception
|
||||
|
||||
Reason: NULL pointer to instance. Variable 'm_OnPlayerDisconnected'
|
||||
|
||||
Class: 'SCR_FactionManager'
|
||||
Entity name:'Factions' id:2305843009213693955
|
||||
|
||||
Function: 'EOnInit'
|
||||
Stack trace:
|
||||
scripts/Game/GameMode/FactionManager/SCR_FactionManager.c:439 Function EOnInit
|
||||
13:15:11.842 DEFAULT (W): No GameMode present in the world, using fallback logic!
|
||||
13:15:11.842 SCRIPT (E): 'SCR_BaseResupplySupportStationComponent' needs a entity catalog manager!
|
||||
13:15:11.842 SCRIPT (E): 'SCR_BaseResupplySupportStationComponent' needs a entity catalog manager!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
---------------------------------------------
|
||||
Log /tmp/work/profile/logs/logs_2026-09-19_13-14-58/script.log started at 2026-09-19 13:14:58 (2026-09-19 11:14:58 UTC)
|
||||
|
||||
13:14:58.419 SCRIPT : SCRIPT : Initializing scripts
|
||||
13:14:58.419 SCRIPT : SCRIPT : ScriptProjectManager init
|
||||
13:14:58.463 SCRIPT : SCRIPT : Compiling GameLib scripts
|
||||
13:14:58.496 SCRIPT : Module: GameLib; loaded 465x files; 607x classes; used 654K of static memory; defines: "PLATFORM_LINUX,ENF_ENABLE_VHC_OVERRIDE,ENF_ENABLE_RPLCOMPONENT_IN_GAMECODE"; CRC32: 4507af04
|
||||
13:14:58.496 SCRIPT : SCRIPT : Compiling Game scripts
|
||||
13:14:59.284 SCRIPT : Compiling Game scripts
|
||||
13:14:59.284 SCRIPT (W): @"scripts/Game/Editor/Components/EditableEntity/SCR_EditableEntityComponent.c,785": 'GetPlayerPlatformKind' is obsolete
|
||||
13:14:59.284 SCRIPT (W): @"scripts/Game/Editor/Components/EditableEntity/SCR_EditableEntityComponent.c,786": 'GetPlayerPlatformId' is obsolete
|
||||
13:14:59.510 SCRIPT (W): @"scripts/Game/Workshop/SCR_AddonManager.c,876": 'GetRefCount' is obsolete: Will be removed
|
||||
13:14:59.536 SCRIPT (W): @"scripts/Game/Campaign/SCR_PlayerProfileManagerComponent.c,142": 'PlayerData' is obsolete
|
||||
13:14:59.624 SCRIPT (W): @"scripts/Game/UI/Backend/CareerBackend.c,159": 'GetDSSession' is obsolete
|
||||
13:14:59.624 SCRIPT (W): @"scripts/Game/UI/Backend/CareerBackend.c,162": 'RoomID' is obsolete
|
||||
13:14:59.624 SCRIPT (W): @"scripts/Game/UI/Backend/CareerBackend.c,197": 'GetDSSession' is obsolete
|
||||
13:14:59.624 SCRIPT (W): @"scripts/Game/UI/Backend/CareerBackend.c,201": 'RoomID' is obsolete
|
||||
13:14:59.706 SCRIPT (W): @"scripts/Game/DataCollection/SCR_DataCollectorComponent.c,481": 'GetStorage' is obsolete
|
||||
13:14:59.706 SCRIPT (W): @"scripts/Game/DataCollection/SCR_DataCollectorComponent.c,483": 'GetOnlineWritePrivilege' is obsolete
|
||||
13:14:59.707 SCRIPT (W): @"scripts/Game/Utilities/SCR_PlayerIdentityUtils.c,20": 'GetPlayerIdentityId' is obsolete
|
||||
13:14:59.744 SCRIPT (W): @"scripts/Game/UI/Menu/MainMenuUI.c,643": 'GetNotifyCount' is obsolete: Will be substitued by Notification Api.
|
||||
13:14:59.744 SCRIPT (W): @"scripts/Game/UI/Menu/MainMenuUI.c,645": 'GetNotifyItem' is obsolete: Will be substitued by Notification Api.
|
||||
13:14:59.799 SCRIPT (W): @"scripts/Game/DataCollection/PlayerData/SCR_PlayerData.c,210": 'PlayerData' is obsolete
|
||||
13:14:59.906 SCRIPT : Module: Game; loaded 5660x files; 11004x classes; used 25508K of static memory; defines: "PLATFORM_LINUX,ENF_ENABLE_VHC_OVERRIDE,ENF_ENABLE_RPLCOMPONENT_IN_GAMECODE,ENABLE_BASE_DESTRUCTION,BUDGET_OPTIMIZATION_CHECKS,DEBUG,RESPAWN_TIMER_COMPONENT_DEBUG,DEBUG_CONFIGURABLE_DIALOGS,DISABLE_WEAPON_SWITCHING,SCRIPTED_AIM_MODIFIER_DEBUG"; CRC32: 931a2d4d
|
||||
13:15:10.631 SCRIPT (W): SCR_MapEntity: Cannot get the size from terrain. Using default.
|
||||
13:15:11.149 SCRIPT (E): Virtual Machine Exception
|
||||
|
||||
Reason: NULL pointer to instance. Variable 'm_OnPlayerDisconnected'
|
||||
|
||||
Class: 'SCR_FactionManager'
|
||||
Entity name:'Factions' id:2305843009213693955
|
||||
|
||||
Function: 'EOnInit'
|
||||
Stack trace:
|
||||
scripts/Game/GameMode/FactionManager/SCR_FactionManager.c:439 Function EOnInit
|
||||
13:15:11.842 SCRIPT (E): 'SCR_BaseResupplySupportStationComponent' needs a entity catalog manager!
|
||||
13:15:11.842 SCRIPT (E): 'SCR_BaseResupplySupportStationComponent' needs a entity catalog manager!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
13:15:11.847 SCRIPT (W): 'SCR_BaseSupportStationComponent' Support station on entity that exists in world when loaded has range but no 'SCR_SupportStationManagerComponent' availible on GameMode. So it won't work!
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
EngineUserSettings {
|
||||
GameProjectUserSettings GameProjectUserSettings {
|
||||
}
|
||||
GraphicsQualitySettings GraphicsQualitySettings {
|
||||
}
|
||||
GrassMaterialSettings GrassMaterialSettings {
|
||||
}
|
||||
InputDeviceUserSettings InputDeviceUserSettings {
|
||||
GamepadProfile InputProfileGamepad "{6A66B3BA49B3DB26}" {
|
||||
LeftThumbHorizontal InputProfileKey "{6A66B3BA49B3DB20}" {
|
||||
Transition CurveCubicSplineFloat "{6A66B3BA49B3DB21}" {
|
||||
}
|
||||
}
|
||||
LeftThumbVertical InputProfileKey "{6A66B3BA49B3DB23}" {
|
||||
Transition CurveCubicSplineFloat "{6A66B3BA49B228E5}" {
|
||||
}
|
||||
}
|
||||
RightThumbHorizontal InputProfileKey "{6A66B3BA49B254AB}" {
|
||||
Transition CurveCubicSplineFloat "{6A66B3BA49B254A4}" {
|
||||
}
|
||||
}
|
||||
RightThumbVertical InputProfileKey "{6A66B3BA49B2C432}" {
|
||||
Transition CurveCubicSplineFloat "{6A66B3BA49AD465D}" {
|
||||
}
|
||||
}
|
||||
LeftTrigger InputProfileKey "{6A66B3BA49AD5EA0}" {
|
||||
Transition CurveCubicSplineFloat "{6A66B3BA49AD9B6F}" {
|
||||
}
|
||||
}
|
||||
RightTrigger InputProfileKey "{6A66B3BA49AD9B6E}" {
|
||||
Transition CurveCubicSplineFloat "{6A66B3BA49ADFE10}" {
|
||||
}
|
||||
}
|
||||
}
|
||||
JoystickProfile InputProfileJoystick "{6A66B3BA49ADFE13}" {
|
||||
Axis00 InputProfileKey "{6A66B3BA49AC2CF5}" {
|
||||
Transition CurveCubicSplineFloat "{6A66B3BA49AC7F87}" {
|
||||
}
|
||||
}
|
||||
Axis01 InputProfileKey "{6A66B3BA49AC9E02}" {
|
||||
Transition CurveCubicSplineFloat "{6A66B3BA49ACC774}" {
|
||||
}
|
||||
}
|
||||
Axis02 InputProfileKey "{6A66B3BA49ACC775}" {
|
||||
Transition CurveCubicSplineFloat "{6A66B3BA49ACF67B}" {
|
||||
}
|
||||
}
|
||||
Axis03 InputProfileKey "{6A66B3BA49AF233F}" {
|
||||
Transition CurveCubicSplineFloat "{6A66B3BA49AF66E8}" {
|
||||
}
|
||||
}
|
||||
Axis04 InputProfileKey "{6A66B3BA49AFBED3}" {
|
||||
Transition CurveCubicSplineFloat "{6A66B3BA49AFEF1C}" {
|
||||
}
|
||||
}
|
||||
Axis05 InputProfileKey "{6A66B3BA49AE34D2}" {
|
||||
Transition CurveCubicSplineFloat "{6A66B3BA49AE6934}" {
|
||||
}
|
||||
}
|
||||
Axis06 InputProfileKey "{6A66B3BA49AE6933}" {
|
||||
Transition CurveCubicSplineFloat "{6A66B3BA49AE99CD}" {
|
||||
}
|
||||
}
|
||||
Axis07 InputProfileKey "{6A66B3BA49AEBC2D}" {
|
||||
Transition CurveCubicSplineFloat "{6A66B3BA49AEC52D}" {
|
||||
}
|
||||
}
|
||||
Axis08 InputProfileKey "{6A66B3BA49AEC514}" {
|
||||
Transition CurveCubicSplineFloat "{6A66B3BA49AED910}" {
|
||||
}
|
||||
}
|
||||
Axis09 InputProfileKey "{6A66B3BA49AEF812}" {
|
||||
Transition CurveCubicSplineFloat "{6A66B3BA49A97831}" {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MaterialSystemUserSettings MaterialSystemUserSettings {
|
||||
}
|
||||
PostprocessUserSettings PostprocessUserSettings {
|
||||
}
|
||||
ResourceManagerUserSettings ResourceManagerUserSettings {
|
||||
}
|
||||
TerrainGenMaterialSettings TerrainGenMaterialSettings {
|
||||
}
|
||||
UserInterfaceSettings UserInterfaceSettings {
|
||||
LanguageCode "en_us"
|
||||
SystemLanguageCode "en_us"
|
||||
}
|
||||
VideoUserSettings VideoUserSettings {
|
||||
WindowPosX -640
|
||||
WindowPosY -360
|
||||
}
|
||||
WaterPoolMaterialSettings WaterPoolMaterialSettings {
|
||||
}
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
GameUserSettings {
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
{
|
||||
"command": [
|
||||
"bwrap",
|
||||
"--unshare-net",
|
||||
"--unshare-pid",
|
||||
"--unshare-ipc",
|
||||
"--unshare-uts",
|
||||
"--die-with-parent",
|
||||
"--cap-drop",
|
||||
"ALL",
|
||||
"--clearenv",
|
||||
"--ro-bind",
|
||||
"/",
|
||||
"/",
|
||||
"--tmpfs",
|
||||
"/home",
|
||||
"--tmpfs",
|
||||
"/tmp",
|
||||
"--tmpfs",
|
||||
"/run",
|
||||
"--proc",
|
||||
"/proc",
|
||||
"--dev",
|
||||
"/dev",
|
||||
"--ro-bind",
|
||||
"/tmp/erri-dedicated-runtime/server",
|
||||
"/tmp/runtime",
|
||||
"--bind",
|
||||
"/tmp/erri-dedicated-chat-20260919-1314/source",
|
||||
"/tmp/source",
|
||||
"--bind",
|
||||
"/tmp/erri-dedicated-chat-20260919-1314",
|
||||
"/tmp/work",
|
||||
"--setenv",
|
||||
"HOME",
|
||||
"/tmp/work/home",
|
||||
"--setenv",
|
||||
"TMPDIR",
|
||||
"/tmp/work/tmp",
|
||||
"--setenv",
|
||||
"XDG_CONFIG_HOME",
|
||||
"/tmp/work/home/.config",
|
||||
"--setenv",
|
||||
"XDG_DATA_HOME",
|
||||
"/tmp/work/home/.local/share",
|
||||
"--setenv",
|
||||
"XDG_CACHE_HOME",
|
||||
"/tmp/work/home/.cache",
|
||||
"--setenv",
|
||||
"PATH",
|
||||
"/usr/bin:/bin",
|
||||
"--setenv",
|
||||
"LANG",
|
||||
"C.UTF-8",
|
||||
"--chdir",
|
||||
"/tmp/runtime",
|
||||
"/usr/bin/python3",
|
||||
"-c",
|
||||
"import json,os,socket,sys\nstate={\"netns\":os.readlink(\"/proc/self/ns/net\"),\"interfaces\":socket.if_nameindex(),\"routes\":open(\"/proc/net/route\").read(),\"ipv6Routes\":open(\"/proc/net/ipv6_route\").read()}\nprint(\"ERR ISOLATION \"+json.dumps(state),flush=True)\nassert state[\"netns\"] != sys.argv[1], \"network namespace was not isolated\"\nassert [name for _,name in state[\"interfaces\"]] == [\"lo\"], \"non-loopback interface present\"\nassert len(state[\"routes\"].splitlines()) == 1, \"IPv4 route present\"\nassert all(line.split()[-1] == \"lo\" for line in state[\"ipv6Routes\"].splitlines()), \"non-loopback IPv6 route present\"\nif len(sys.argv)>2: os.execv(sys.argv[2],sys.argv[2:])\n",
|
||||
"net:[4026531833]",
|
||||
"/tmp/runtime/ArmaReforgerServer",
|
||||
"-profile",
|
||||
"/tmp/work/profile",
|
||||
"-noBackend",
|
||||
"-noSound",
|
||||
"-noSplash",
|
||||
"-noThrow",
|
||||
"-bindIP",
|
||||
"127.0.0.1",
|
||||
"-bindPort",
|
||||
"2001",
|
||||
"-maxFPS",
|
||||
"30",
|
||||
"-logStats",
|
||||
"5000",
|
||||
"-addonsDir",
|
||||
"/tmp/source/addons",
|
||||
"-addons",
|
||||
"FA804F411BDDF2C6",
|
||||
"-server",
|
||||
"{C1E20667DCE324ED}Missions/ERR_Everon.conf"
|
||||
],
|
||||
"engine": [
|
||||
"/tmp/runtime/ArmaReforgerServer",
|
||||
"-profile",
|
||||
"/tmp/work/profile",
|
||||
"-noBackend",
|
||||
"-noSound",
|
||||
"-noSplash",
|
||||
"-noThrow",
|
||||
"-bindIP",
|
||||
"127.0.0.1",
|
||||
"-bindPort",
|
||||
"2001",
|
||||
"-maxFPS",
|
||||
"30",
|
||||
"-logStats",
|
||||
"5000",
|
||||
"-addonsDir",
|
||||
"/tmp/source/addons",
|
||||
"-addons",
|
||||
"FA804F411BDDF2C6",
|
||||
"-server",
|
||||
"{C1E20667DCE324ED}Missions/ERR_Everon.conf"
|
||||
],
|
||||
"seconds": 90,
|
||||
"route": "header",
|
||||
"project": "chat",
|
||||
"binarySHA256": "91fdf333ec49c266d309be358aabab4244a93a2f9cfa11853a600e026104001f",
|
||||
"serverLaunched": true,
|
||||
"realPlayers": 0,
|
||||
"multiplayerProven": false,
|
||||
"scope": "successor chat empty-server startup",
|
||||
"supervisorPid": 3553742,
|
||||
"startedUTC": "2026-09-19T11:14:57Z",
|
||||
"timeout": true,
|
||||
"exit": -15,
|
||||
"finishedUTC": "2026-09-19T11:16:27Z"
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"result": "failed-input-missing-resource-databases",
|
||||
"scope": "Isolated native Linux Chat attempt; no successful successor compilation/preparation, clients or multiplayer proof.",
|
||||
"source_files": 183,
|
||||
"source_unchanged": true,
|
||||
"input_resource_databases": [],
|
||||
"missing_database_warnings": [
|
||||
"/tmp/source/addons/chat/resourceDatabase.rdb",
|
||||
"/tmp/source/addons/map/resourceDatabase.rdb",
|
||||
"/tmp/source/addons/intel/resourceDatabase.rdb",
|
||||
"/tmp/source/addons/ai/resourceDatabase.rdb",
|
||||
"/tmp/source/addons/core/resourceDatabase.rdb"
|
||||
],
|
||||
"errors": [
|
||||
"13:15:00.127 RESOURCES (E): Wrong GUID/name for resource @\"{B6C516F86302C679}Worlds/ERR_Everon.ent\" in property \"World\"",
|
||||
"13:15:00.341 RESOURCES (E): Wrong GUID/name for resource @\"{FF35BE9795BA3797}system/wbdata/Gizmo3D/sphere.xob\" in property \"Object\"",
|
||||
"13:15:00.816 RESOURCES (E): Wrong GUID/name for resource @\"{45ECF6B1797D3CD9}system/wbdata/PreviewWindow/TransparentMat.emat\" in property \"AssignedMaterial\"",
|
||||
"13:15:00.963 RESOURCES (E): Wrong GUID/name for resource @\"{978E045E53EF36C8}Prefabs/ERR/GameMode.et\" in property \"inherited-name\"",
|
||||
"13:15:00.963 WORLD (E): Unknown class 'ERR_GameMode' at offset 73(0x49)",
|
||||
"13:15:00.963 RESOURCES (E): Wrong GUID/name for resource @\"{5414041A15DC4B54}Prefabs/ERR/FactionManager.et\" in property \"inherited-name\"",
|
||||
"13:15:00.963 RESOURCES (E): Wrong GUID/name for resource @\"{5C67A1DDE0044806}Configs/ERR/Factions/US.conf\" in property \"inherited-name\"",
|
||||
"13:15:00.964 RESOURCES (E): Wrong GUID/name for resource @\"{E4785D3AF1D043C2}Configs/ERR/Factions/MissionVehicles.conf\" in property \"inherited-name\"",
|
||||
"13:15:00.965 RESOURCES (E): Wrong GUID/name for resource @\"{D7A5CFA62A36480D}Configs/ERR/Factions/USSR.conf\" in property \"inherited-name\"",
|
||||
"13:15:00.965 RESOURCES (E): Wrong GUID/name for resource @\"{E4785D3AF1D043C2}Configs/ERR/Factions/MissionVehicles.conf\" in property \"inherited-name\"",
|
||||
"13:15:00.969 RESOURCES (E): Wrong GUID/name for resource @\"{B6C516F86302C679}Worlds/ERR_Everon.ent\" in property \"World\"",
|
||||
"13:15:00.969 RESOURCES (E): Wrong GUID/name for resource @\"{B6C516F86302C679}Worlds/ERR_Everon.ent\" in property \"World\"",
|
||||
"13:15:07.939 MATERIAL (E): Material file not found",
|
||||
"13:15:07.939 MATERIAL (E): Cannot load material @\"system/wbdata/PreviewWindow/TransparentMat.emat\", creating default one (class MatPBRBasic)",
|
||||
"13:15:11.148 RPL (E): Attempting to insert an entity with invalid prefab resource GUID @\"{5414041A15DC4B54}\".",
|
||||
"13:15:11.149 SCRIPT (E): Virtual Machine Exception",
|
||||
"13:15:11.842 SCRIPT (E): 'SCR_BaseResupplySupportStationComponent' needs a entity catalog manager!",
|
||||
"13:15:11.842 SCRIPT (E): 'SCR_BaseResupplySupportStationComponent' needs a entity catalog manager!"
|
||||
],
|
||||
"raw_console": "profile/logs/logs_2026-09-19_13-14-58/console.log",
|
||||
"multiplayer_proven": false,
|
||||
"release_qualified": false
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Bounded Linux server diagnostic: isolated network, frozen sources, no clients."""
|
||||
import argparse
|
||||
import fcntl
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import signal
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
PROBE = '''import json,os,socket,sys
|
||||
state={"netns":os.readlink("/proc/self/ns/net"),"interfaces":socket.if_nameindex(),"routes":open("/proc/net/route").read(),"ipv6Routes":open("/proc/net/ipv6_route").read()}
|
||||
print("ERR ISOLATION "+json.dumps(state),flush=True)
|
||||
assert state["netns"] != sys.argv[1], "network namespace was not isolated"
|
||||
assert [name for _,name in state["interfaces"]] == ["lo"], "non-loopback interface present"
|
||||
assert len(state["routes"].splitlines()) == 1, "IPv4 route present"
|
||||
assert all(line.split()[-1] == "lo" for line in state["ipv6Routes"].splitlines()), "non-loopback IPv6 route present"
|
||||
if len(sys.argv)>2: os.execv(sys.argv[2],sys.argv[2:])
|
||||
'''
|
||||
|
||||
|
||||
def manifest(source):
|
||||
result = {}
|
||||
for path in sorted(source.rglob('*')):
|
||||
if path.is_symlink():
|
||||
raise ValueError('Linked source entry is not allowed: ' + str(path))
|
||||
if path.is_file():
|
||||
result[str(path.relative_to(source))] = hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
return result
|
||||
|
||||
|
||||
def sandbox(runtime, source, run):
|
||||
return ['bwrap', '--unshare-net', '--unshare-pid', '--unshare-ipc', '--unshare-uts',
|
||||
'--die-with-parent', '--cap-drop', 'ALL', '--clearenv',
|
||||
'--ro-bind', '/', '/', '--tmpfs', '/home', '--tmpfs', '/tmp', '--tmpfs', '/run',
|
||||
'--proc', '/proc', '--dev', '/dev', '--ro-bind', str(runtime), '/tmp/runtime',
|
||||
'--bind', str(source), '/tmp/source', '--bind', str(run), '/tmp/work',
|
||||
'--setenv', 'HOME', '/tmp/work/home', '--setenv', 'TMPDIR', '/tmp/work/tmp',
|
||||
'--setenv', 'XDG_CONFIG_HOME', '/tmp/work/home/.config',
|
||||
'--setenv', 'XDG_DATA_HOME', '/tmp/work/home/.local/share',
|
||||
'--setenv', 'XDG_CACHE_HOME', '/tmp/work/home/.cache',
|
||||
'--setenv', 'PATH', '/usr/bin:/bin', '--setenv', 'LANG', 'C.UTF-8',
|
||||
'--chdir', '/tmp/runtime']
|
||||
|
||||
|
||||
PROJECTS = {
|
||||
'core': ('2528466DE220D91C', '{AD62322D5213C440}Missions/ERR_Everon_Base.conf'),
|
||||
'chat': ('FA804F411BDDF2C6', '{C1E20667DCE324ED}Missions/ERR_Everon.conf'),
|
||||
}
|
||||
WORLD = '{B6C516F86302C679}Worlds/ERR_Everon.ent'
|
||||
SYSTEMS = '{C50579A2EC48E8EF}Configs/Systems/MissionSystems.conf'
|
||||
|
||||
|
||||
def engine_args(route, project='core'):
|
||||
if route not in ('header', 'diag-world', 'vanilla-everon') or project not in PROJECTS:
|
||||
raise ValueError('Unsupported diagnostic route or project')
|
||||
if route == 'vanilla-everon' and project != 'core':
|
||||
raise ValueError('vanilla-everon cannot be combined with --project chat')
|
||||
args = ['/tmp/runtime/ArmaReforgerServer' + ('Diag' if route == 'diag-world' else ''),
|
||||
'-profile', '/tmp/work/profile', '-noBackend', '-noSound', '-noSplash', '-noThrow',
|
||||
'-bindIP', '127.0.0.1', '-bindPort', '2001', '-maxFPS', '30', '-logStats', '5000']
|
||||
if route == 'vanilla-everon':
|
||||
return args + ['-server', 'Missions/21_GM_Eden.conf']
|
||||
addon, header = PROJECTS[project]
|
||||
args += ['-addonsDir', '/tmp/source/addons', '-addons', addon]
|
||||
if route == 'header':
|
||||
return args + ['-server', header]
|
||||
return args + ['-server', WORLD, '-worldSystemsConfig', SYSTEMS]
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--source', type=Path, required=True)
|
||||
parser.add_argument('--runtime', type=Path, default=Path('/tmp/erri-dedicated-runtime/server'))
|
||||
parser.add_argument('--archive', type=Path, required=True)
|
||||
parser.add_argument('--route', choices=['header', 'diag-world', 'vanilla-everon'], default='header')
|
||||
parser.add_argument('--project', choices=['core', 'chat'], default='core',
|
||||
help='Core base (default) or Chat with all five declared successor addons')
|
||||
parser.add_argument('--seconds', type=int, default=90)
|
||||
args = parser.parse_args()
|
||||
if args.route == 'vanilla-everon' and args.project != 'core':
|
||||
parser.error('vanilla-everon cannot be combined with --project chat')
|
||||
if not 1 <= args.seconds <= 110:
|
||||
parser.error('Runtime bound must be 1..110 seconds (plus at most 10 seconds owned cleanup)')
|
||||
source, runtime, archive = args.source.resolve(), args.runtime.resolve(), args.archive.resolve()
|
||||
if not source.is_relative_to('/tmp') or not runtime.is_relative_to('/tmp') or not archive.is_relative_to('/tmp'):
|
||||
parser.error('Sources, runtime and archive must be isolated /tmp paths')
|
||||
if archive == source or source in archive.parents or archive in source.parents or archive == runtime or runtime in archive.parents or archive in runtime.parents:
|
||||
parser.error('Source, archive and runtime must not overlap')
|
||||
if archive.exists() or archive.is_symlink():
|
||||
parser.error('Archive must be new')
|
||||
origin = runtime.parent / 'runtime-origin.json'
|
||||
if not origin.is_file() or json.loads(origin.read_text()).get('app_id') != 1874900:
|
||||
parser.error('Stable private runtime origin missing')
|
||||
binary = runtime / Path(engine_args(args.route, args.project)[0]).name
|
||||
with binary.open('rb') as stream:
|
||||
elf = stream.read(20)
|
||||
if binary.is_symlink() or len(elf) != 20 or elf[:6] != b'\x7fELF\x02\x01' or int.from_bytes(elf[18:20], 'little') != 62:
|
||||
parser.error('Expected real native x86-64 ELF server')
|
||||
archive.mkdir(mode=0o700, parents=True)
|
||||
(archive / 'runner.py').write_bytes(Path(__file__).read_bytes())
|
||||
for child in ['home', 'profile', 'tmp']:
|
||||
(archive / child).mkdir()
|
||||
# Reject links before copytree follows them into the isolated source snapshot.
|
||||
manifest(source)
|
||||
frozen = archive / 'source'
|
||||
shutil.copytree(source, frozen, ignore=shutil.ignore_patterns('.git', '__pycache__', 'evidence', 'generated'))
|
||||
initial = manifest(frozen)
|
||||
(archive / 'source-manifest.json').write_text(json.dumps(initial, indent=2)+'\n')
|
||||
base = sandbox(runtime, frozen, archive)
|
||||
netns = os.readlink('/proc/self/ns/net')
|
||||
check = base + ['/usr/bin/python3', '-c', PROBE, netns]
|
||||
preflight = subprocess.run(check, capture_output=True, text=True, timeout=15)
|
||||
(archive / 'isolation-preflight.json').write_text(json.dumps({'command':check,'exit':preflight.returncode,'stdout':preflight.stdout,'stderr':preflight.stderr}, indent=2)+'\n')
|
||||
if preflight.returncode:
|
||||
print('Isolation refused; no server launched:', archive)
|
||||
return 2
|
||||
command = base + ['/usr/bin/python3', '-c', PROBE, netns] + engine_args(args.route, args.project)
|
||||
record = {'command': command, 'engine': engine_args(args.route, args.project), 'seconds':args.seconds,
|
||||
'route': args.route, 'project': None if args.route == 'vanilla-everon' else args.project,
|
||||
'binarySHA256':hashlib.sha256(binary.read_bytes()).hexdigest(),
|
||||
'serverLaunched':False,'realPlayers':0,'multiplayerProven':False,
|
||||
'scope':'vanilla Everon control; no successor loaded' if args.route == 'vanilla-everon' else f'successor {args.project} empty-server startup'}
|
||||
(archive / 'command.json').write_text(json.dumps(record, indent=2)+'\n')
|
||||
lock_fd = os.open('/tmp/erri-dedicated-native-diagnostic.lock', os.O_WRONLY | os.O_CREAT | os.O_NOFOLLOW, 0o600)
|
||||
with os.fdopen(lock_fd, 'a') as lock:
|
||||
fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
with (archive / 'stdout.log').open('wb') as log:
|
||||
process = subprocess.Popen(command, stdout=log, stderr=subprocess.STDOUT, start_new_session=True)
|
||||
record['serverLaunched'] = True
|
||||
record['supervisorPid'] = process.pid
|
||||
record['startedUTC'] = time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime())
|
||||
try:
|
||||
record['exit'] = process.wait(timeout=args.seconds)
|
||||
record['timeout'] = False
|
||||
except subprocess.TimeoutExpired:
|
||||
record['timeout'] = True
|
||||
finally:
|
||||
if process.poll() is None:
|
||||
os.killpg(process.pid, signal.SIGTERM)
|
||||
try: process.wait(timeout=5)
|
||||
except subprocess.TimeoutExpired:
|
||||
os.killpg(process.pid, signal.SIGKILL)
|
||||
process.wait(timeout=5)
|
||||
record['exit'] = process.returncode
|
||||
record['finishedUTC'] = time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime())
|
||||
(archive / 'result.json').write_text(json.dumps(record, indent=2)+'\n')
|
||||
(archive / 'source-manifest-after.json').write_text(json.dumps(manifest(frozen), indent=2)+'\n')
|
||||
print('Diagnostic archived; inspect actual compilation/header/systems/errors:',archive)
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main())
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
{
|
||||
"addons/ai/Prefabs/ERR/MortarCrew.et": "05d3e7353ff9fe5523218a1487b31b7b742b0be8adbcb060c7099978cf008bce",
|
||||
"addons/ai/Prefabs/ERR/MortarCrew.et.meta": "fdfd4fcd21a67c3d698fa4aadc4e5c76e665cebf4be86013bc389e493fab63cc",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_AircraftFlight.c": "96b20b7e78612fb26befa6e26635be80dbf9ca089384db4a7839f5064ab06ccc",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_AircraftFlightEnvelope.c": "8125cef61fb283d0d7c4dd2df3c04b062b448f1101fd48dd53f4f4097df5fef3",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_HQ.c": "7ee8e7a3282fd566ec330c94854b353b2c6178e9b964f7b4d5e197e4d0b7dee9",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_HQJournal.c": "b3e402cfe92d415f37f666045afb062e6e11670ba03a0ca6f2d9fd4fc3520ac1",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_HQNetwork.c": "92ff2caca52e94c2cc9642dfc8509ce6e9349b257012bade8650bace9231128c",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_HQObservation.c": "36c978ec5f052213ac1236e1398d85063efbfd7ef60fe04f1fc11447fefe1d68",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_HQReport.c": "c4d16ccb571db4d9444c0a8d2be7318845d25ff90238b6d304e96a9ea19f1a08",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_HelicopterAvailability.c": "60eaef2f6d4b2fd44861c5a4434ef97e732e40a5ff7401e2da451ed2dd9c2762",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarAimSolution.c": "59e8343d09f7a44871d8d3335d6ad1d02216abb5deed30d38b5ec9ba3f2c0649",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarBattery.c": "a4eed96d17f239cbf353863ebe40d2282aeacd7a4d63a7b2f0ecfb3d93a2e2b2",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarCourierTransfer.c": "968e58ca700b4a11e5e04a5c159696f514a0b1bce531210172b913e495f7e2b7",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarFiring.c": "7aec51c83972610d1bcbca58b84b14f16f59952d402b12c3d88e7192a3e2ee90",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarLogistics.c": "e1ffb25fba384b449de576a5502a93d875c3f52e8da1941bbf590ad9845f9730",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarPrediction.c": "470dd196ed64800c697d24c09d1344bacd2502587fac76af4ee0edae2563092d",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarReadiness.c": "2fabe8f812fd333cea7199d1eb722c807fcc5adb285c618956bd54d31fa27ce3",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarRefresh.c": "52ae8d9d6cad92f42d68c634091b80a474d2666b6c06d63a8c30a3fab71494c4",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarReportLedger.c": "42a96040b63e56677353e55164ae23031c90f70b4d6673a9f412ad7b19e636ae",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarSalvo.c": "697c01f479df6c505a41d984a29c02002e3052114dafa2ec6252ae55e2979a2b",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarSupplyTransfer.c": "34f7eaa01dc68e97aae8c1177b7f8fd6c15d5a41c6dc86de377f00b3613a8e77",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarSystem.c": "4edefc8530cc6eec7a5e1df1d0b7d56e6801186f23c61331ce0e8075b8281efb",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_NativeAircraftControlProbe.c": "e501301e683a7e36ad5a0cb56bc87a590c0c9071c9fc5c579502fb1c7a0ff143",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_NativeMortarCourierProbe.c": "740c8bebbd4128a9311b1167d1058549475d6b46f81c9b6c0d19650f4b50616f",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_NativeMortarFiringProbe.c": "ee570d21bede8baebefc799399712d9582b3e30ebda7299f6c941777f02ad2a2",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_NativeMortarSalvoProbe.c": "5fd7bb8953f6850ad79e646c0c286153108a10ee2d5ef397ccb65a07f8d2ed7c",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_NativeMortarTransferProbe.c": "399c21940ef10777b46fdb3e905430ce43404ce1de2f4a6bb62f9c61c1a0689c",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_NativePatrolHQProbe.c": "4c04aa3e1b6b000747da8e2a919a6501107880fd041134c019f516665bb6f758",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_SettlementSafety.c": "e5c0ebc95521a9a33a3f7d9cca6c551dda862a08b7af20b017877aba9584795b",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_SupportScenario.c": "71757018ec9716c4dc24f62a292a214919d3fd5daed3c16e03e56abd208e9f03",
|
||||
"addons/ai/addon.gproj": "5f559db8f526700db11e6ae763f5245f19b7006fc68c56e4c09fb6f940bdf2a1",
|
||||
"addons/chat/Missions/ERR_Everon.conf": "76f3ac0d21b585654beac7ddd68e3c8cbdd66dbfe909b17b04838fa813a7c29b",
|
||||
"addons/chat/Missions/ERR_Everon.conf.meta": "0a23c98bf83873536c65fa17d38dea33a1d7b9e4ab210f90633ff688abcc551f",
|
||||
"addons/chat/Scripts/Game/ERR/ERR_AdminCommands.c": "d7b4ed95fc32767911936cb0fcc60763233f2c31afd34a6a0fa998b39cec9f5c",
|
||||
"addons/chat/Scripts/Game/ERR/ERR_NativeHQResponseProof.c": "0f8b09fd8753234c91fd6baaca0e2f812e409ae1c4be8f340b648ae59b69c2f3",
|
||||
"addons/chat/Scripts/Game/ERR/ERR_NativeMortarProbe.c": "ddb892141500c192c0787b011d57dd8c07dd8c9a3ca5fdc407f6c271897bde0a",
|
||||
"addons/chat/Scripts/Game/ERR/ERR_NativeRadioProbe.c": "84524163c63402755b4f834f74dfe6d8e5c397dc26031f532593c6f79f5a93b1",
|
||||
"addons/chat/Scripts/Game/ERR/ERR_NativeSmoke.c": "32501d011d459e3c2d01077ef3eeb0ce8c364985e1aadfe8e8c022ba4b2054ef",
|
||||
"addons/chat/Scripts/Game/ERRChat/ERR_OrdinaryScenarioLaunchChat.c": "dda715c9691d366e35fa2ad5e390dee1971651f137a5a9f9aeb23dcbe53ec87e",
|
||||
"addons/chat/Scripts/WorkbenchGame/ERR/ERR_ValidateWorldPlugin.c": "81c92d408ce88cc9f98d6053f1d0c863427693ba748f00df9f1bf8079015ef94",
|
||||
"addons/chat/addon.gproj": "ea1587e0174cf795334d306c71d99b06797fc891427cd57b6cab6e21cf0e4e0e",
|
||||
"addons/core/Configs/ERR/EveronSites.conf": "ade92cc8d2b14a84ac378bbbb52608b351dca65d359dd7a315d64a145fa4b46f",
|
||||
"addons/core/Configs/ERR/EveronSites.conf.meta": "d5fe68025ef4e7c1e16082cf5873b7c7172eacbfaef4ab29b7e0e3c98f8dca56",
|
||||
"addons/core/Configs/ERR/Factions/MissionVehicles.conf": "5f74f4a7089c7e7b54ad62463159ce8f0bab25a6b85268dc3872ea6001e048fb",
|
||||
"addons/core/Configs/ERR/Factions/MissionVehicles.conf.meta": "00c368b6271d3440cc8bf627b03a384d247635723923fc5ad2104b8b4007b035",
|
||||
"addons/core/Configs/ERR/Factions/US.conf": "4c3da063abb6e3ee39a6e4e0c5a9ffd5eae9d209d58470ea18b45dba5528db1b",
|
||||
"addons/core/Configs/ERR/Factions/US.conf.meta": "a05469aef028e9e17eba545e81e2af2887b59f0fef17dac51a23c7b8155e381b",
|
||||
"addons/core/Configs/ERR/Factions/USSR.conf": "407ea27f80a64c5184c089d43cdd0bdabfc5974c84f1d2a245081fdf2afa3f30",
|
||||
"addons/core/Configs/ERR/Factions/USSR.conf.meta": "057397d97096346791b51b7b13fbe18af218bcf78e9984d9c01b49a2403de611",
|
||||
"addons/core/Configs/ERR/GameOverScreens.conf": "45231061911a1462d45fb6db0fecc2b0793f17b5ae71017174954ff4fbd20236",
|
||||
"addons/core/Configs/ERR/GameOverScreens.conf.meta": "eb654b745c0b97b41c991694569ef4877a3c2315e0826f9556337cd68690843e",
|
||||
"addons/core/Configs/ERR/VanillaFactions.conf": "db890b61b48ee15484041fd2245a862d9fa9e7ba21b1229c281ff37c448a35b1",
|
||||
"addons/core/Configs/ERR/VanillaFactions.conf.meta": "ac77725c0c21974991ef47a466b88122e939de8b46fd23e68889e7674cbb2bfd",
|
||||
"addons/core/Missions/ERR_Everon_Base.conf": "1e6a0e9a756d15495201f71121626890f01efe6b00154c561672d4ec157960bd",
|
||||
"addons/core/Missions/ERR_Everon_Base.conf.meta": "9b16267c644d815bcf98a3a3aaff30b93d7224fb24d3a2bac66730a28011f317",
|
||||
"addons/core/Prefabs/ERR/Extraction.et": "1aa286ef253c5410f09fa641e4be91a7be8ec834487a7fbb74cfb45cab60ee1e",
|
||||
"addons/core/Prefabs/ERR/Extraction.et.meta": "c2a838633001e2e3a202d39cb33aa744ebcd996017ad032359069641b5d13853",
|
||||
"addons/core/Prefabs/ERR/FactionManager.et": "425f916b890019c537f6b2f72b1af9e7be54b0a476320da969e1f313a7ac5ea5",
|
||||
"addons/core/Prefabs/ERR/FactionManager.et.meta": "9bf53d5e4bcbe60dd1a4ece1409fc23c349055c860708620f5f6e9716290a97d",
|
||||
"addons/core/Prefabs/ERR/FieldCover.et": "20f8095e69dbfe3db74ff19078e528ea75405362b335e03560ccd61f8df2e7f3",
|
||||
"addons/core/Prefabs/ERR/FieldCover.et.meta": "83461ffa76244e1d69f20b053a9741ccdf7d127009182bd540a7e5ba6150c3d9",
|
||||
"addons/core/Prefabs/ERR/GameMode.et": "385c97a5b8067091d92aa60c2d4fcf02002cf606b95523866ead65c96b8c0029",
|
||||
"addons/core/Prefabs/ERR/GameMode.et.meta": "106769cd34525c9d440057072836cb7401a6752148c40edd7fe61c03bd58328c",
|
||||
"addons/core/Prefabs/ERR/ObjectiveDocument.et": "2e72270bf186654af12d2c3ff9bf940690b03fb17fca44f873c1fde5d11384c0",
|
||||
"addons/core/Prefabs/ERR/ObjectiveDocument.et.meta": "eaba9903be147b98a1106e2273e2270773df73b9b22f2cd4e20136a8e281382e",
|
||||
"addons/core/Prefabs/ERR/Patrol.et": "c5685a8bc311cc5563fb3334beec9408d3d7a1069152fddeeb7df4f1b7f9e6ce",
|
||||
"addons/core/Prefabs/ERR/Patrol.et.meta": "7c5da568fa2fcf0709ee2959253cb068d7c7ed7c2467af3b247a7e70efd96b69",
|
||||
"addons/core/Prefabs/ERR/Prison.et": "dcd3f4dfd453303d026e68b64f7de6125bd3390822d8432dd95a3710a5b3df89",
|
||||
"addons/core/Prefabs/ERR/Prison.et.meta": "2e426ef3de68f4107907854b51e818bc11d84649a01c538e3f18dd9ebec766bb",
|
||||
"addons/core/Prefabs/ERR/PrisonDoor.et": "fc66be9d36f1f01084de59f1b0a1e8817f9e46547ead33d4fafaac0107446c40",
|
||||
"addons/core/Prefabs/ERR/PrisonDoor.et.meta": "b8f3db52bf186426026668fbb9d633c3cbd4d438ab301fba880398f6a0e781c2",
|
||||
"addons/core/Prefabs/ERR/PrisonWall.et": "8009d19365ae72a4749d7ad4fa04662575b06c65c6bf06507d380d57258b5eed",
|
||||
"addons/core/Prefabs/ERR/PrisonWall.et.meta": "0294bc5dbb56f90f8153d62459bb0a6ad91d76a479812bbec9c7db182fdb58d6",
|
||||
"addons/core/Prefabs/ERR/Prisoner.et": "198b5badabb12f218f86df7eb4dc268fd830769dc3de7eef6362cfaee4d6fc8d",
|
||||
"addons/core/Prefabs/ERR/Prisoner.et.meta": "974278ba49fafeed70b619a0067bbc70ae4d534be441c812ce2bef4a5fb3c022",
|
||||
"addons/core/Prefabs/ERR/Radio.et": "b4b5cfe125b39019855816c9a1c508ca4d65d435b5546fdaa6c1fa3c8c9b1165",
|
||||
"addons/core/Prefabs/ERR/Radio.et.meta": "bc91137b68ba550217914fb8892bb44d7e1920e4a1086f3e506623832a44212f",
|
||||
"addons/core/Prefabs/ERR/SpectatorCamera.et": "b5b87d5b7b707f3745741aebe7c923067c8a74f8fbb2a4711834bab03bb5a812",
|
||||
"addons/core/Prefabs/ERR/SpectatorCamera.et.meta": "2a16558f93915e31908a674509e9c89976470f78a84a1174a64b94b68b8f77ad",
|
||||
"addons/core/Prefabs/ERR/SupplyCache.et": "bccf7afbaae97cfcdd78dd6dbbf4be5b2c045d348f50fe4581962e556c0044d7",
|
||||
"addons/core/Prefabs/ERR/SupplyCache.et.meta": "2f5de3a4aab1e9d0e35521f48dc57b95bd6e449c383dec4f1c258e10295904cb",
|
||||
"addons/core/Prefabs/ERR/SupplySite.et": "dc5decce5cabf39b6116e8794ec3960cb1e35761854fdbe69740da118787ded4",
|
||||
"addons/core/Prefabs/ERR/SupplySite.et.meta": "b41944b12eb3528ff7a3ac94258b18c66ef161aa6a38894aee300a1ac4798630",
|
||||
"addons/core/Scripts/Game/ERR/ERR_Actions.c": "cabb21413c90b252d29708b62228d0679c51c76e39ee0e483953ad7061b2b9d6",
|
||||
"addons/core/Scripts/Game/ERR/ERR_Actors.c": "b3c5aabb05f7cf35167bc09b5db51a4ffd89a46658805d6eedbfb80eee30273d",
|
||||
"addons/core/Scripts/Game/ERR/ERR_AdminService.c": "9985747d5ddaeb91bf27937836a6c369f8dcb353263b77a49212ec28c10ba4eb",
|
||||
"addons/core/Scripts/Game/ERR/ERR_CasualtyGroundSupport.c": "26f0c7ebe8563bb022f7c383674e7d8997b0cc35c7ea321d39d74f2493c1ed9d",
|
||||
"addons/core/Scripts/Game/ERR/ERR_CasualtyLoadingGameMode.c": "f4cf19440583ac24346831a2d5f1d7f734c031e46dc266c499972329dd0533cf",
|
||||
"addons/core/Scripts/Game/ERR/ERR_CasualtyLoadingOwner.c": "6346a05ddda030f7d9b6f026f977d5f018ee16002fa519e8c49dcd700c323dc7",
|
||||
"addons/core/Scripts/Game/ERR/ERR_CasualtyLoadingService.c": "93fe23d9e1da596886c33cf76bad92b0906ff5cdd8b5b21d58906e9270246201",
|
||||
"addons/core/Scripts/Game/ERR/ERR_DiscoveryMap.c": "7553c8cc1388cca3404994e36f96946c6abd1fefb08cef2fbb3ea1a62b22861b",
|
||||
"addons/core/Scripts/Game/ERR/ERR_DiscoveryService.c": "2793bb888566dd975e5bbf93fdb47a22e3b0f33a68d04992191cf6428ae942a0",
|
||||
"addons/core/Scripts/Game/ERR/ERR_DocumentComponent.c": "17bb76b80ebd3357e46d553c44674f602c7bd6901c92aec53ac8214194aa4ff2",
|
||||
"addons/core/Scripts/Game/ERR/ERR_EnvironmentService.c": "867e9f47adfb7a9819a8b58498688ff86127282cb89cf2529ae81f73e529e1c5",
|
||||
"addons/core/Scripts/Game/ERR/ERR_EscapeGameOverScreenInfo.c": "b1e1afb7795cb77c0bad94c85abf705b26b9dc2a9400905d80eafd56bdd009d3",
|
||||
"addons/core/Scripts/Game/ERR/ERR_ExtractionPlacement.c": "c30222ca25cc0e91dec4077e6b82b37a9edafcf2cbebd0be5b6bd8656b53adfe",
|
||||
"addons/core/Scripts/Game/ERR/ERR_ExtractionService.c": "91357099b4e0401196b67d48963be54be30e56bdba57cdac67dc20b8ebb883e9",
|
||||
"addons/core/Scripts/Game/ERR/ERR_FactionCatalog.c": "a5a8c53acf21259e14f59cb919535910f30dabff5a9179c664bc6a7a4d744d6e",
|
||||
"addons/core/Scripts/Game/ERR/ERR_GameMode.c": "41f2247a45252eaacb580aa5ebb5a85ecfa41e0f940eb0d2c8ea5a3f6c35058e",
|
||||
"addons/core/Scripts/Game/ERR/ERR_InitialGuardRules.c": "8c9d6b1dc4c2af96284e34f487eebb5a221091016ef108006dec60b4e3836558",
|
||||
"addons/core/Scripts/Game/ERR/ERR_Inventory.c": "b63e679d5b650b63a7a91dfc3f42a62f4be58b0338f6df3313e66d29a93bebf6",
|
||||
"addons/core/Scripts/Game/ERR/ERR_ItemStationTeardown.c": "102e61216e86e47ad243cc19f6f66da1fcd8cc77f8799354ceaeb4a6dc74622e",
|
||||
"addons/core/Scripts/Game/ERR/ERR_LoadCasualtyAction.c": "5d868fb065a1a0fea7d5abfc81d1d8be96823815535f2ffd0dc4ff60d8eae794",
|
||||
"addons/core/Scripts/Game/ERR/ERR_LocalAdmission.c": "b0e824c6880cbf9b1f368dc7ada9907bde78b22b948d419b6c861323f32258be",
|
||||
"addons/core/Scripts/Game/ERR/ERR_Location.c": "d2035f56ded35743312ec6ca91a67f8623a466c91255e1893c570edf0954ff24",
|
||||
"addons/core/Scripts/Game/ERR/ERR_MissionRules.c": "f7abc38ad8fdc375ce7fa63a65f0bade027d22b37e09de5e3f94ba8c3c63cc41",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeAdminSettingsProbe.c": "6a4df33a2b0e6b6aeed944c6895961e658d4f0db9aed9b8433a43a451fa35cae",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeBaseSmoke.c": "4c58290868fe4b669ff0f05835e9e7626766c13eab9633a721e0ebe5c551428e",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeCasualtyGroundProbe.c": "41f40db3735f8aa535c0061458d9892dcfe972222b8a17238e67b65633c46bc3",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeCasualtyLoadProbe.c": "94c533deea654ec546df3f1ebb7cd213e6cd79b1125d99bab5303ef8e87b018b",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeCasualtyRemoveProbe.c": "4cfc06f6d43b709b63caae714155ffc562502e884f49a9825623c6f46972ad36",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeCasualtyServiceProbe.c": "02a9d70d1f4ba20326df199ba41489cfbae12b7bb0aab441361421cee38ad9ea",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeContractsProbe.c": "d1556df885171cf967e96eef5d8981a2e9ee4e3938b7d32fdd0426f647bbabc0",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeDefinitionProbe.c": "c07048341396443ddf5307e340e2087254d65f0bddf8f7d93e6b6bd7f03ca360",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeDiscoveryMapProbe.c": "9ed32d391d08d937ff4f9f33bd11c3a204ce9a2230c2d72b6761e0320b31f24f",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeEnvironmentProbe.c": "8b1d110717aa5f07bfb8fe633847eab519109e3a8a4100970a487be05c053418",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeEnvironmentRestartProbe.c": "7a836a206381b8e8de24c92baf141beb30575723675dd6948d738169ed26b507",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeExtractionProbe.c": "4b09385f73206de004f4f3c38a6192fd658a0d2012482dc5a84dff1599ceb972",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeFactionCatalogProbe.c": "ee11486cf77da9070506ccc0cfd6e94240c5387c8be455bf864934a8fef65f78",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeFlightFootprint.c": "a8862a04ac784426f431e2e9cd3f1f19f820b38a081674d29549b8b00277c72d",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeFlightInputDiagnostics.c": "e231dcbdaefe81beb3bd587cb73be8fb3d3184bc86d18dbaa30a43ca4ba5dcc4",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeGuardCancelProbe.c": "bfa1c65cb039e19c3d1891e4a3fd33469e974c97cc7f42128efc31a8f587d514",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeItemStationTeardownProbe.c": "028588a49fa9207087cd27ec9396979cd932daa071e2490bd0262cd71dae30d9",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeLifecycleProbe.c": "b613a5dbe81c2032fd02d31070ae5631166d14d0b02cdbec3b63b782f0bbc115",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeLocalAdmissionProbe.c": "29c860ca3e51cdaa463f3c23a98ff45ee0499ce9bbc322046e0efbed587f1e09",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeMedicalProbe.c": "43fa51ffbcf9dd0cd8e905a1135bcf0756651da3af34677f3c92e7d264ed70b6",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeObjectiveLossProbe.c": "6b80fd317a8c6e8583bd4337870838a86251e3b8a681d0aa07e8b52e22abfc48",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeObjectiveProbe.c": "71fbd2479a70048f83d8b262ad1452f427be2c0afc8b071ef13bff477b9dc29b",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativePatrolProbe.c": "02bd9fdf5454db7dd4c82240efe0d947bf8d6488ace42809c3aef6505cb6266d",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativePoweredFlightProbe.c": "7ae5d6f85bbbebec214c66bfdf6f53b93c802f109fcd5bcfbad82c5d61d6cb9f",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativePrisonBreachProbe.c": "22d0923cd69ba591e783011d424c97c86520272b70da9aa6a6d15558464c264c",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativePrisonerProbe.c": "e2d1ffe5d269add55124601ff0d1dc032a398c7d51010736e6f0e4cbeb38ae86",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeProgressionIdentityProbe.c": "1d3fe20bce761a1082964bca9bad9ce79d1fa8b04db9b5053bae98da380ae560",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeRadioDefenceProbe.c": "cad54f0b43cefd72df74697dfc7e93725ff0acac6d311e963ce4f817428c272c",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeRecoveryProbe.c": "4de6868cd3e76afb80cf0ae5dac1e35d2c417838f640c2f18aa3343217eea2f2",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeResultProbe.c": "b9a117eef2d5f65fbd792041a2b3e59e38e201bda8e0df8c503e1a1242cfc415",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeSiteSurvey.c": "ac0203753ade0cdff6d483132797a80e4520f14546c3f9dea288281cd4cd9c4f",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeStationTeardownDiagnostic.c": "172248540932174341882861bb78a9720a6cf89981fcdf9dffe63f7fd4023f2c",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeTimeProbe.c": "c93d049785971e51246fcc07cf24804270a752a655505f2c38e48c80df5255ae",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeTransportProbe.c": "63ec0c5ce698114daa6314b2a6e3c3e98e76094117c477f9d0d072260686d193",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeTransportSurvey.c": "3e94493bf54d15be2bceb106ad18873e14e6755af09c05f07db7fb26fb7adff5",
|
||||
"addons/core/Scripts/Game/ERR/ERR_ObjectiveDetails.c": "628ebd166dcb5544c2d18dce489619984887ee18296cf2446fc6d894f89dd5bf",
|
||||
"addons/core/Scripts/Game/ERR/ERR_ObjectiveText.c": "d1740ddbe8a4bac04aeb0ffc68c2957bc807cbb0a197afd515f0fcb742aac04f",
|
||||
"addons/core/Scripts/Game/ERR/ERR_Objectives.c": "88d95a52fcba103483ea751571e1afeae0e6fc82115ed50d6fc195424726960f",
|
||||
"addons/core/Scripts/Game/ERR/ERR_Observer.c": "f417f503dcb66b4d5224d49046ab47c08fa14255d5c4af02a85047096069da33",
|
||||
"addons/core/Scripts/Game/ERR/ERR_OrdinaryScenarioLaunch.c": "11541755401af8da68dc004d9a8e2a49578d617d4dec63b1d6e5a75b00b9c3ff",
|
||||
"addons/core/Scripts/Game/ERR/ERR_PlayerAdminCommands.c": "a084d7dc097dfcde3d6cd9c2e326b9e2f8f2a519f77b30c4f56ab7a1429aa721",
|
||||
"addons/core/Scripts/Game/ERR/ERR_PlayerEscapeResult.c": "3124ab4d2d6d471409a47aba0af528790734df53243c03fe0cdac3e407047c14",
|
||||
"addons/core/Scripts/Game/ERR/ERR_PlayerGuide.c": "2c9c365b88651f8b12618443638bf52c04e497221592bd483c33447e30468f36",
|
||||
"addons/core/Scripts/Game/ERR/ERR_PlayerRoster.c": "c038dac0f749bd29ebc35a4e952904b41a34417ba738904d5efcd1833a263e0b",
|
||||
"addons/core/Scripts/Game/ERR/ERR_PrisonEscapeService.c": "ed6286342ed12cc9a3df618474f5647c7ac36bc65336be7a5bec908dee04421d",
|
||||
"addons/core/Scripts/Game/ERR/ERR_RadioDefenceService.c": "5d347cf1a3b021074d560aad8db774828b797e685b63ddff3d28f55781e33ea8",
|
||||
"addons/core/Scripts/Game/ERR/ERR_RadioFeedback.c": "545262e65e9b3645d9e514a0521e442fe08f0c2e4e7c83ba638c5105d09818db",
|
||||
"addons/core/Scripts/Game/ERR/ERR_RecoveryService.c": "ba9ffb93679012c20a665636ef6b201cbe7d8d14fdf53e8cd3eb12ac7248943f",
|
||||
"addons/core/Scripts/Game/ERR/ERR_Resources.c": "d0169aac6fd0c38928c47a9002ded0fd8ba558ea3314a600e792d307ac1ca3ee",
|
||||
"addons/core/Scripts/Game/ERR/ERR_ScenarioComponent.c": "abba03af68323d5715ba1e2497fc66f3c706e6a6a983c3a386228abdfddeec89",
|
||||
"addons/core/Scripts/Game/ERR/ERR_Settings.c": "b9390548936b386e0211d487c1b76b65410fdd2e339cd8ecd9585ef4f4ffed31",
|
||||
"addons/core/Scripts/Game/ERR/ERR_SiteDefinition.c": "b422feeaca90084ea2bee5cde94dc9c6fcae542749b55d95f7226f922a6a68ac",
|
||||
"addons/core/Scripts/Game/ERR/ERR_SiteDiscoveryRules.c": "50f7b3ff4f32bde37ce5ae2be7f598fc4dd8cc08f45914f6e3260d31c84baef2",
|
||||
"addons/core/Scripts/Game/ERR/ERR_SiteService.c": "6bc9a0b5aed326822807f05e17459d0fc2da10b709e8168972b4e40fd09f87cb",
|
||||
"addons/core/Scripts/Game/ERR/ERR_SpawnFinalization.c": "2e295b0f225208c5414728ba83333e5e0ab6eef3a708ce39eb1dfce5400c94c9",
|
||||
"addons/core/Scripts/Game/ERR/ERR_SpawnLogic.c": "ed88279cbeb3e5e6daf8471763df87a251881f550632eddadd08153594bbd1a1",
|
||||
"addons/core/Scripts/Game/ERR/ERR_SpawnPlacement.c": "55323dcb330c23d49dc8fa3fde86a3ae3958c0b6563d6c3cd0960da88333f93d",
|
||||
"addons/core/Scripts/Game/ERR/ERR_Supplies.c": "752e8a56b0b8cde33cf532032cb0a203553017b686faac2e19bee06f7a9c0713",
|
||||
"addons/core/Scripts/Game/ERR/ERR_TimeService.c": "d9f481b7da01e08d2c982a948c18ba6bde76889d7cc9d26d510708f4ba479453",
|
||||
"addons/core/Scripts/GameLib/ERR/ERR_DiagnosticNoSplash.c": "0ea1b402f8927b6d68bf6f7e5caa2967f7e3a39147634faab147796b791933b5",
|
||||
"addons/core/Scripts/WorkbenchGame/ERR/ERR_ValidateBaseWorldPlugin.c": "7f44aa64ea7b4f8b3640a37a4b781c43bf6adb80e4a3a702d9491131698056f6",
|
||||
"addons/core/Worlds/ERR_Everon.ent": "22324bc7eb0d80dec8a1f3bd61a9df9201b72f749f4e751766b0c9d0b7d46b87",
|
||||
"addons/core/Worlds/ERR_Everon.ent.meta": "19d1903ca72baa326b4724a6791d0da1104a1e53f3f31dc1ca41a856efd68e83",
|
||||
"addons/core/Worlds/ERR_Everon_Layers/default.layer": "17105538007199bfc419aa04a64debaf2da0a7b77a1dc3ba983e92bf747c862e",
|
||||
"addons/core/addon.gproj": "231274e6b62302f32c65f9ff31de2ce8a9e51e1c8d12c87deeec82ae1f40de46",
|
||||
"addons/intel/Scripts/Game/ERR/ERR_IntelService.c": "5e167630a76437fd731fbb7a62a41381ea71509f3adb8a6b9c77f6816384861a",
|
||||
"addons/intel/addon.gproj": "d0ced242cefb220a5cccd6779d61fda9bc55581701b9f78bcf13c09d92ddbcbe",
|
||||
"addons/map/Materials/ERRM_MapRenderTarget.emat": "9877c5b175c5f30dd80bea36c711b21ab38219aa7075adf9ae79932e1d4c76bc",
|
||||
"addons/map/Materials/ERRM_MapRenderTarget.emat.meta": "5ee1a705f8011a44020ca2691f5ccc17d44bf596fef9a36e98d1f267eb58fab0",
|
||||
"addons/map/Prefabs/Items/Equipment/ERRM_SharedFieldMap.et": "30dee8e9eab459ea32a7d835a9fb89d43df47708057cab829f067b25a24cd581",
|
||||
"addons/map/Prefabs/Items/Equipment/ERRM_SharedFieldMap.et.meta": "59093e9072c50d99c86da7609c87541016da5bcf92b98d99db4c524e1c8e0e8d",
|
||||
"addons/map/Scripts/Game/ERR/ERR_ScenarioMap.c": "75fa1d742129ef7f33c005cd48f56db7f49ee7bde3c5428532b0b501b032b0a3",
|
||||
"addons/map/Scripts/Game/ERRMap/ERRM_NativeMapOwnershipProbe.c": "32418602f085e3edba7343f42a6a691630b533be262002f29248e8ae87a38254",
|
||||
"addons/map/Scripts/Game/ERRMap/ERRM_SharedMapComponent.c": "3a10f6ea25bf88a400bc3e6c339823433845c5db4f2f423835ccf99a1b1a843f",
|
||||
"addons/map/UI/layouts/ERRMap/SharedMapRenderTarget.layout": "49a2da4181b94d42174f58c7f9b6bf2207116c99427d61ff1c212092ac7db4d2",
|
||||
"addons/map/UI/layouts/ERRMap/SharedMapRenderTarget.layout.meta": "5c846445cc089e2e08899ba703059d4adb805ff30ab5a16409a3cd59718cbd54",
|
||||
"addons/map/addon.gproj": "1b371e5e01ea9fa46e76570fecb8aaa3a9298e34cba7ccca8a6bdb5ca86eb46a"
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
{
|
||||
"addons/ai/Prefabs/ERR/MortarCrew.et": "05d3e7353ff9fe5523218a1487b31b7b742b0be8adbcb060c7099978cf008bce",
|
||||
"addons/ai/Prefabs/ERR/MortarCrew.et.meta": "fdfd4fcd21a67c3d698fa4aadc4e5c76e665cebf4be86013bc389e493fab63cc",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_AircraftFlight.c": "96b20b7e78612fb26befa6e26635be80dbf9ca089384db4a7839f5064ab06ccc",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_AircraftFlightEnvelope.c": "8125cef61fb283d0d7c4dd2df3c04b062b448f1101fd48dd53f4f4097df5fef3",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_HQ.c": "7ee8e7a3282fd566ec330c94854b353b2c6178e9b964f7b4d5e197e4d0b7dee9",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_HQJournal.c": "b3e402cfe92d415f37f666045afb062e6e11670ba03a0ca6f2d9fd4fc3520ac1",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_HQNetwork.c": "92ff2caca52e94c2cc9642dfc8509ce6e9349b257012bade8650bace9231128c",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_HQObservation.c": "36c978ec5f052213ac1236e1398d85063efbfd7ef60fe04f1fc11447fefe1d68",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_HQReport.c": "c4d16ccb571db4d9444c0a8d2be7318845d25ff90238b6d304e96a9ea19f1a08",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_HelicopterAvailability.c": "60eaef2f6d4b2fd44861c5a4434ef97e732e40a5ff7401e2da451ed2dd9c2762",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarAimSolution.c": "59e8343d09f7a44871d8d3335d6ad1d02216abb5deed30d38b5ec9ba3f2c0649",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarBattery.c": "a4eed96d17f239cbf353863ebe40d2282aeacd7a4d63a7b2f0ecfb3d93a2e2b2",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarCourierTransfer.c": "968e58ca700b4a11e5e04a5c159696f514a0b1bce531210172b913e495f7e2b7",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarFiring.c": "7aec51c83972610d1bcbca58b84b14f16f59952d402b12c3d88e7192a3e2ee90",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarLogistics.c": "e1ffb25fba384b449de576a5502a93d875c3f52e8da1941bbf590ad9845f9730",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarPrediction.c": "470dd196ed64800c697d24c09d1344bacd2502587fac76af4ee0edae2563092d",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarReadiness.c": "2fabe8f812fd333cea7199d1eb722c807fcc5adb285c618956bd54d31fa27ce3",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarRefresh.c": "52ae8d9d6cad92f42d68c634091b80a474d2666b6c06d63a8c30a3fab71494c4",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarReportLedger.c": "42a96040b63e56677353e55164ae23031c90f70b4d6673a9f412ad7b19e636ae",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarSalvo.c": "697c01f479df6c505a41d984a29c02002e3052114dafa2ec6252ae55e2979a2b",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarSupplyTransfer.c": "34f7eaa01dc68e97aae8c1177b7f8fd6c15d5a41c6dc86de377f00b3613a8e77",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_MortarSystem.c": "4edefc8530cc6eec7a5e1df1d0b7d56e6801186f23c61331ce0e8075b8281efb",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_NativeAircraftControlProbe.c": "e501301e683a7e36ad5a0cb56bc87a590c0c9071c9fc5c579502fb1c7a0ff143",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_NativeMortarCourierProbe.c": "740c8bebbd4128a9311b1167d1058549475d6b46f81c9b6c0d19650f4b50616f",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_NativeMortarFiringProbe.c": "ee570d21bede8baebefc799399712d9582b3e30ebda7299f6c941777f02ad2a2",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_NativeMortarSalvoProbe.c": "5fd7bb8953f6850ad79e646c0c286153108a10ee2d5ef397ccb65a07f8d2ed7c",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_NativeMortarTransferProbe.c": "399c21940ef10777b46fdb3e905430ce43404ce1de2f4a6bb62f9c61c1a0689c",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_NativePatrolHQProbe.c": "4c04aa3e1b6b000747da8e2a919a6501107880fd041134c019f516665bb6f758",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_SettlementSafety.c": "e5c0ebc95521a9a33a3f7d9cca6c551dda862a08b7af20b017877aba9584795b",
|
||||
"addons/ai/Scripts/Game/ERR/ERR_SupportScenario.c": "71757018ec9716c4dc24f62a292a214919d3fd5daed3c16e03e56abd208e9f03",
|
||||
"addons/ai/addon.gproj": "5f559db8f526700db11e6ae763f5245f19b7006fc68c56e4c09fb6f940bdf2a1",
|
||||
"addons/chat/Missions/ERR_Everon.conf": "76f3ac0d21b585654beac7ddd68e3c8cbdd66dbfe909b17b04838fa813a7c29b",
|
||||
"addons/chat/Missions/ERR_Everon.conf.meta": "0a23c98bf83873536c65fa17d38dea33a1d7b9e4ab210f90633ff688abcc551f",
|
||||
"addons/chat/Scripts/Game/ERR/ERR_AdminCommands.c": "d7b4ed95fc32767911936cb0fcc60763233f2c31afd34a6a0fa998b39cec9f5c",
|
||||
"addons/chat/Scripts/Game/ERR/ERR_NativeHQResponseProof.c": "0f8b09fd8753234c91fd6baaca0e2f812e409ae1c4be8f340b648ae59b69c2f3",
|
||||
"addons/chat/Scripts/Game/ERR/ERR_NativeMortarProbe.c": "ddb892141500c192c0787b011d57dd8c07dd8c9a3ca5fdc407f6c271897bde0a",
|
||||
"addons/chat/Scripts/Game/ERR/ERR_NativeRadioProbe.c": "84524163c63402755b4f834f74dfe6d8e5c397dc26031f532593c6f79f5a93b1",
|
||||
"addons/chat/Scripts/Game/ERR/ERR_NativeSmoke.c": "32501d011d459e3c2d01077ef3eeb0ce8c364985e1aadfe8e8c022ba4b2054ef",
|
||||
"addons/chat/Scripts/Game/ERRChat/ERR_OrdinaryScenarioLaunchChat.c": "dda715c9691d366e35fa2ad5e390dee1971651f137a5a9f9aeb23dcbe53ec87e",
|
||||
"addons/chat/Scripts/WorkbenchGame/ERR/ERR_ValidateWorldPlugin.c": "81c92d408ce88cc9f98d6053f1d0c863427693ba748f00df9f1bf8079015ef94",
|
||||
"addons/chat/addon.gproj": "ea1587e0174cf795334d306c71d99b06797fc891427cd57b6cab6e21cf0e4e0e",
|
||||
"addons/core/Configs/ERR/EveronSites.conf": "ade92cc8d2b14a84ac378bbbb52608b351dca65d359dd7a315d64a145fa4b46f",
|
||||
"addons/core/Configs/ERR/EveronSites.conf.meta": "d5fe68025ef4e7c1e16082cf5873b7c7172eacbfaef4ab29b7e0e3c98f8dca56",
|
||||
"addons/core/Configs/ERR/Factions/MissionVehicles.conf": "5f74f4a7089c7e7b54ad62463159ce8f0bab25a6b85268dc3872ea6001e048fb",
|
||||
"addons/core/Configs/ERR/Factions/MissionVehicles.conf.meta": "00c368b6271d3440cc8bf627b03a384d247635723923fc5ad2104b8b4007b035",
|
||||
"addons/core/Configs/ERR/Factions/US.conf": "4c3da063abb6e3ee39a6e4e0c5a9ffd5eae9d209d58470ea18b45dba5528db1b",
|
||||
"addons/core/Configs/ERR/Factions/US.conf.meta": "a05469aef028e9e17eba545e81e2af2887b59f0fef17dac51a23c7b8155e381b",
|
||||
"addons/core/Configs/ERR/Factions/USSR.conf": "407ea27f80a64c5184c089d43cdd0bdabfc5974c84f1d2a245081fdf2afa3f30",
|
||||
"addons/core/Configs/ERR/Factions/USSR.conf.meta": "057397d97096346791b51b7b13fbe18af218bcf78e9984d9c01b49a2403de611",
|
||||
"addons/core/Configs/ERR/GameOverScreens.conf": "45231061911a1462d45fb6db0fecc2b0793f17b5ae71017174954ff4fbd20236",
|
||||
"addons/core/Configs/ERR/GameOverScreens.conf.meta": "eb654b745c0b97b41c991694569ef4877a3c2315e0826f9556337cd68690843e",
|
||||
"addons/core/Configs/ERR/VanillaFactions.conf": "db890b61b48ee15484041fd2245a862d9fa9e7ba21b1229c281ff37c448a35b1",
|
||||
"addons/core/Configs/ERR/VanillaFactions.conf.meta": "ac77725c0c21974991ef47a466b88122e939de8b46fd23e68889e7674cbb2bfd",
|
||||
"addons/core/Missions/ERR_Everon_Base.conf": "1e6a0e9a756d15495201f71121626890f01efe6b00154c561672d4ec157960bd",
|
||||
"addons/core/Missions/ERR_Everon_Base.conf.meta": "9b16267c644d815bcf98a3a3aaff30b93d7224fb24d3a2bac66730a28011f317",
|
||||
"addons/core/Prefabs/ERR/Extraction.et": "1aa286ef253c5410f09fa641e4be91a7be8ec834487a7fbb74cfb45cab60ee1e",
|
||||
"addons/core/Prefabs/ERR/Extraction.et.meta": "c2a838633001e2e3a202d39cb33aa744ebcd996017ad032359069641b5d13853",
|
||||
"addons/core/Prefabs/ERR/FactionManager.et": "425f916b890019c537f6b2f72b1af9e7be54b0a476320da969e1f313a7ac5ea5",
|
||||
"addons/core/Prefabs/ERR/FactionManager.et.meta": "9bf53d5e4bcbe60dd1a4ece1409fc23c349055c860708620f5f6e9716290a97d",
|
||||
"addons/core/Prefabs/ERR/FieldCover.et": "20f8095e69dbfe3db74ff19078e528ea75405362b335e03560ccd61f8df2e7f3",
|
||||
"addons/core/Prefabs/ERR/FieldCover.et.meta": "83461ffa76244e1d69f20b053a9741ccdf7d127009182bd540a7e5ba6150c3d9",
|
||||
"addons/core/Prefabs/ERR/GameMode.et": "385c97a5b8067091d92aa60c2d4fcf02002cf606b95523866ead65c96b8c0029",
|
||||
"addons/core/Prefabs/ERR/GameMode.et.meta": "106769cd34525c9d440057072836cb7401a6752148c40edd7fe61c03bd58328c",
|
||||
"addons/core/Prefabs/ERR/ObjectiveDocument.et": "2e72270bf186654af12d2c3ff9bf940690b03fb17fca44f873c1fde5d11384c0",
|
||||
"addons/core/Prefabs/ERR/ObjectiveDocument.et.meta": "eaba9903be147b98a1106e2273e2270773df73b9b22f2cd4e20136a8e281382e",
|
||||
"addons/core/Prefabs/ERR/Patrol.et": "c5685a8bc311cc5563fb3334beec9408d3d7a1069152fddeeb7df4f1b7f9e6ce",
|
||||
"addons/core/Prefabs/ERR/Patrol.et.meta": "7c5da568fa2fcf0709ee2959253cb068d7c7ed7c2467af3b247a7e70efd96b69",
|
||||
"addons/core/Prefabs/ERR/Prison.et": "dcd3f4dfd453303d026e68b64f7de6125bd3390822d8432dd95a3710a5b3df89",
|
||||
"addons/core/Prefabs/ERR/Prison.et.meta": "2e426ef3de68f4107907854b51e818bc11d84649a01c538e3f18dd9ebec766bb",
|
||||
"addons/core/Prefabs/ERR/PrisonDoor.et": "fc66be9d36f1f01084de59f1b0a1e8817f9e46547ead33d4fafaac0107446c40",
|
||||
"addons/core/Prefabs/ERR/PrisonDoor.et.meta": "b8f3db52bf186426026668fbb9d633c3cbd4d438ab301fba880398f6a0e781c2",
|
||||
"addons/core/Prefabs/ERR/PrisonWall.et": "8009d19365ae72a4749d7ad4fa04662575b06c65c6bf06507d380d57258b5eed",
|
||||
"addons/core/Prefabs/ERR/PrisonWall.et.meta": "0294bc5dbb56f90f8153d62459bb0a6ad91d76a479812bbec9c7db182fdb58d6",
|
||||
"addons/core/Prefabs/ERR/Prisoner.et": "198b5badabb12f218f86df7eb4dc268fd830769dc3de7eef6362cfaee4d6fc8d",
|
||||
"addons/core/Prefabs/ERR/Prisoner.et.meta": "974278ba49fafeed70b619a0067bbc70ae4d534be441c812ce2bef4a5fb3c022",
|
||||
"addons/core/Prefabs/ERR/Radio.et": "b4b5cfe125b39019855816c9a1c508ca4d65d435b5546fdaa6c1fa3c8c9b1165",
|
||||
"addons/core/Prefabs/ERR/Radio.et.meta": "bc91137b68ba550217914fb8892bb44d7e1920e4a1086f3e506623832a44212f",
|
||||
"addons/core/Prefabs/ERR/SpectatorCamera.et": "b5b87d5b7b707f3745741aebe7c923067c8a74f8fbb2a4711834bab03bb5a812",
|
||||
"addons/core/Prefabs/ERR/SpectatorCamera.et.meta": "2a16558f93915e31908a674509e9c89976470f78a84a1174a64b94b68b8f77ad",
|
||||
"addons/core/Prefabs/ERR/SupplyCache.et": "bccf7afbaae97cfcdd78dd6dbbf4be5b2c045d348f50fe4581962e556c0044d7",
|
||||
"addons/core/Prefabs/ERR/SupplyCache.et.meta": "2f5de3a4aab1e9d0e35521f48dc57b95bd6e449c383dec4f1c258e10295904cb",
|
||||
"addons/core/Prefabs/ERR/SupplySite.et": "dc5decce5cabf39b6116e8794ec3960cb1e35761854fdbe69740da118787ded4",
|
||||
"addons/core/Prefabs/ERR/SupplySite.et.meta": "b41944b12eb3528ff7a3ac94258b18c66ef161aa6a38894aee300a1ac4798630",
|
||||
"addons/core/Scripts/Game/ERR/ERR_Actions.c": "cabb21413c90b252d29708b62228d0679c51c76e39ee0e483953ad7061b2b9d6",
|
||||
"addons/core/Scripts/Game/ERR/ERR_Actors.c": "b3c5aabb05f7cf35167bc09b5db51a4ffd89a46658805d6eedbfb80eee30273d",
|
||||
"addons/core/Scripts/Game/ERR/ERR_AdminService.c": "9985747d5ddaeb91bf27937836a6c369f8dcb353263b77a49212ec28c10ba4eb",
|
||||
"addons/core/Scripts/Game/ERR/ERR_CasualtyGroundSupport.c": "26f0c7ebe8563bb022f7c383674e7d8997b0cc35c7ea321d39d74f2493c1ed9d",
|
||||
"addons/core/Scripts/Game/ERR/ERR_CasualtyLoadingGameMode.c": "f4cf19440583ac24346831a2d5f1d7f734c031e46dc266c499972329dd0533cf",
|
||||
"addons/core/Scripts/Game/ERR/ERR_CasualtyLoadingOwner.c": "6346a05ddda030f7d9b6f026f977d5f018ee16002fa519e8c49dcd700c323dc7",
|
||||
"addons/core/Scripts/Game/ERR/ERR_CasualtyLoadingService.c": "93fe23d9e1da596886c33cf76bad92b0906ff5cdd8b5b21d58906e9270246201",
|
||||
"addons/core/Scripts/Game/ERR/ERR_DiscoveryMap.c": "7553c8cc1388cca3404994e36f96946c6abd1fefb08cef2fbb3ea1a62b22861b",
|
||||
"addons/core/Scripts/Game/ERR/ERR_DiscoveryService.c": "2793bb888566dd975e5bbf93fdb47a22e3b0f33a68d04992191cf6428ae942a0",
|
||||
"addons/core/Scripts/Game/ERR/ERR_DocumentComponent.c": "17bb76b80ebd3357e46d553c44674f602c7bd6901c92aec53ac8214194aa4ff2",
|
||||
"addons/core/Scripts/Game/ERR/ERR_EnvironmentService.c": "867e9f47adfb7a9819a8b58498688ff86127282cb89cf2529ae81f73e529e1c5",
|
||||
"addons/core/Scripts/Game/ERR/ERR_EscapeGameOverScreenInfo.c": "b1e1afb7795cb77c0bad94c85abf705b26b9dc2a9400905d80eafd56bdd009d3",
|
||||
"addons/core/Scripts/Game/ERR/ERR_ExtractionPlacement.c": "c30222ca25cc0e91dec4077e6b82b37a9edafcf2cbebd0be5b6bd8656b53adfe",
|
||||
"addons/core/Scripts/Game/ERR/ERR_ExtractionService.c": "91357099b4e0401196b67d48963be54be30e56bdba57cdac67dc20b8ebb883e9",
|
||||
"addons/core/Scripts/Game/ERR/ERR_FactionCatalog.c": "a5a8c53acf21259e14f59cb919535910f30dabff5a9179c664bc6a7a4d744d6e",
|
||||
"addons/core/Scripts/Game/ERR/ERR_GameMode.c": "41f2247a45252eaacb580aa5ebb5a85ecfa41e0f940eb0d2c8ea5a3f6c35058e",
|
||||
"addons/core/Scripts/Game/ERR/ERR_InitialGuardRules.c": "8c9d6b1dc4c2af96284e34f487eebb5a221091016ef108006dec60b4e3836558",
|
||||
"addons/core/Scripts/Game/ERR/ERR_Inventory.c": "b63e679d5b650b63a7a91dfc3f42a62f4be58b0338f6df3313e66d29a93bebf6",
|
||||
"addons/core/Scripts/Game/ERR/ERR_ItemStationTeardown.c": "102e61216e86e47ad243cc19f6f66da1fcd8cc77f8799354ceaeb4a6dc74622e",
|
||||
"addons/core/Scripts/Game/ERR/ERR_LoadCasualtyAction.c": "5d868fb065a1a0fea7d5abfc81d1d8be96823815535f2ffd0dc4ff60d8eae794",
|
||||
"addons/core/Scripts/Game/ERR/ERR_LocalAdmission.c": "b0e824c6880cbf9b1f368dc7ada9907bde78b22b948d419b6c861323f32258be",
|
||||
"addons/core/Scripts/Game/ERR/ERR_Location.c": "d2035f56ded35743312ec6ca91a67f8623a466c91255e1893c570edf0954ff24",
|
||||
"addons/core/Scripts/Game/ERR/ERR_MissionRules.c": "f7abc38ad8fdc375ce7fa63a65f0bade027d22b37e09de5e3f94ba8c3c63cc41",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeAdminSettingsProbe.c": "6a4df33a2b0e6b6aeed944c6895961e658d4f0db9aed9b8433a43a451fa35cae",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeBaseSmoke.c": "4c58290868fe4b669ff0f05835e9e7626766c13eab9633a721e0ebe5c551428e",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeCasualtyGroundProbe.c": "41f40db3735f8aa535c0061458d9892dcfe972222b8a17238e67b65633c46bc3",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeCasualtyLoadProbe.c": "94c533deea654ec546df3f1ebb7cd213e6cd79b1125d99bab5303ef8e87b018b",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeCasualtyRemoveProbe.c": "4cfc06f6d43b709b63caae714155ffc562502e884f49a9825623c6f46972ad36",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeCasualtyServiceProbe.c": "02a9d70d1f4ba20326df199ba41489cfbae12b7bb0aab441361421cee38ad9ea",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeContractsProbe.c": "d1556df885171cf967e96eef5d8981a2e9ee4e3938b7d32fdd0426f647bbabc0",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeDefinitionProbe.c": "c07048341396443ddf5307e340e2087254d65f0bddf8f7d93e6b6bd7f03ca360",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeDiscoveryMapProbe.c": "9ed32d391d08d937ff4f9f33bd11c3a204ce9a2230c2d72b6761e0320b31f24f",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeEnvironmentProbe.c": "8b1d110717aa5f07bfb8fe633847eab519109e3a8a4100970a487be05c053418",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeEnvironmentRestartProbe.c": "7a836a206381b8e8de24c92baf141beb30575723675dd6948d738169ed26b507",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeExtractionProbe.c": "4b09385f73206de004f4f3c38a6192fd658a0d2012482dc5a84dff1599ceb972",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeFactionCatalogProbe.c": "ee11486cf77da9070506ccc0cfd6e94240c5387c8be455bf864934a8fef65f78",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeFlightFootprint.c": "a8862a04ac784426f431e2e9cd3f1f19f820b38a081674d29549b8b00277c72d",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeFlightInputDiagnostics.c": "e231dcbdaefe81beb3bd587cb73be8fb3d3184bc86d18dbaa30a43ca4ba5dcc4",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeGuardCancelProbe.c": "bfa1c65cb039e19c3d1891e4a3fd33469e974c97cc7f42128efc31a8f587d514",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeItemStationTeardownProbe.c": "028588a49fa9207087cd27ec9396979cd932daa071e2490bd0262cd71dae30d9",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeLifecycleProbe.c": "b613a5dbe81c2032fd02d31070ae5631166d14d0b02cdbec3b63b782f0bbc115",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeLocalAdmissionProbe.c": "29c860ca3e51cdaa463f3c23a98ff45ee0499ce9bbc322046e0efbed587f1e09",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeMedicalProbe.c": "43fa51ffbcf9dd0cd8e905a1135bcf0756651da3af34677f3c92e7d264ed70b6",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeObjectiveLossProbe.c": "6b80fd317a8c6e8583bd4337870838a86251e3b8a681d0aa07e8b52e22abfc48",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeObjectiveProbe.c": "71fbd2479a70048f83d8b262ad1452f427be2c0afc8b071ef13bff477b9dc29b",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativePatrolProbe.c": "02bd9fdf5454db7dd4c82240efe0d947bf8d6488ace42809c3aef6505cb6266d",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativePoweredFlightProbe.c": "7ae5d6f85bbbebec214c66bfdf6f53b93c802f109fcd5bcfbad82c5d61d6cb9f",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativePrisonBreachProbe.c": "22d0923cd69ba591e783011d424c97c86520272b70da9aa6a6d15558464c264c",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativePrisonerProbe.c": "e2d1ffe5d269add55124601ff0d1dc032a398c7d51010736e6f0e4cbeb38ae86",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeProgressionIdentityProbe.c": "1d3fe20bce761a1082964bca9bad9ce79d1fa8b04db9b5053bae98da380ae560",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeRadioDefenceProbe.c": "cad54f0b43cefd72df74697dfc7e93725ff0acac6d311e963ce4f817428c272c",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeRecoveryProbe.c": "4de6868cd3e76afb80cf0ae5dac1e35d2c417838f640c2f18aa3343217eea2f2",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeResultProbe.c": "b9a117eef2d5f65fbd792041a2b3e59e38e201bda8e0df8c503e1a1242cfc415",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeSiteSurvey.c": "ac0203753ade0cdff6d483132797a80e4520f14546c3f9dea288281cd4cd9c4f",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeStationTeardownDiagnostic.c": "172248540932174341882861bb78a9720a6cf89981fcdf9dffe63f7fd4023f2c",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeTimeProbe.c": "c93d049785971e51246fcc07cf24804270a752a655505f2c38e48c80df5255ae",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeTransportProbe.c": "63ec0c5ce698114daa6314b2a6e3c3e98e76094117c477f9d0d072260686d193",
|
||||
"addons/core/Scripts/Game/ERR/ERR_NativeTransportSurvey.c": "3e94493bf54d15be2bceb106ad18873e14e6755af09c05f07db7fb26fb7adff5",
|
||||
"addons/core/Scripts/Game/ERR/ERR_ObjectiveDetails.c": "628ebd166dcb5544c2d18dce489619984887ee18296cf2446fc6d894f89dd5bf",
|
||||
"addons/core/Scripts/Game/ERR/ERR_ObjectiveText.c": "d1740ddbe8a4bac04aeb0ffc68c2957bc807cbb0a197afd515f0fcb742aac04f",
|
||||
"addons/core/Scripts/Game/ERR/ERR_Objectives.c": "88d95a52fcba103483ea751571e1afeae0e6fc82115ed50d6fc195424726960f",
|
||||
"addons/core/Scripts/Game/ERR/ERR_Observer.c": "f417f503dcb66b4d5224d49046ab47c08fa14255d5c4af02a85047096069da33",
|
||||
"addons/core/Scripts/Game/ERR/ERR_OrdinaryScenarioLaunch.c": "11541755401af8da68dc004d9a8e2a49578d617d4dec63b1d6e5a75b00b9c3ff",
|
||||
"addons/core/Scripts/Game/ERR/ERR_PlayerAdminCommands.c": "a084d7dc097dfcde3d6cd9c2e326b9e2f8f2a519f77b30c4f56ab7a1429aa721",
|
||||
"addons/core/Scripts/Game/ERR/ERR_PlayerEscapeResult.c": "3124ab4d2d6d471409a47aba0af528790734df53243c03fe0cdac3e407047c14",
|
||||
"addons/core/Scripts/Game/ERR/ERR_PlayerGuide.c": "2c9c365b88651f8b12618443638bf52c04e497221592bd483c33447e30468f36",
|
||||
"addons/core/Scripts/Game/ERR/ERR_PlayerRoster.c": "c038dac0f749bd29ebc35a4e952904b41a34417ba738904d5efcd1833a263e0b",
|
||||
"addons/core/Scripts/Game/ERR/ERR_PrisonEscapeService.c": "ed6286342ed12cc9a3df618474f5647c7ac36bc65336be7a5bec908dee04421d",
|
||||
"addons/core/Scripts/Game/ERR/ERR_RadioDefenceService.c": "5d347cf1a3b021074d560aad8db774828b797e685b63ddff3d28f55781e33ea8",
|
||||
"addons/core/Scripts/Game/ERR/ERR_RadioFeedback.c": "545262e65e9b3645d9e514a0521e442fe08f0c2e4e7c83ba638c5105d09818db",
|
||||
"addons/core/Scripts/Game/ERR/ERR_RecoveryService.c": "ba9ffb93679012c20a665636ef6b201cbe7d8d14fdf53e8cd3eb12ac7248943f",
|
||||
"addons/core/Scripts/Game/ERR/ERR_Resources.c": "d0169aac6fd0c38928c47a9002ded0fd8ba558ea3314a600e792d307ac1ca3ee",
|
||||
"addons/core/Scripts/Game/ERR/ERR_ScenarioComponent.c": "abba03af68323d5715ba1e2497fc66f3c706e6a6a983c3a386228abdfddeec89",
|
||||
"addons/core/Scripts/Game/ERR/ERR_Settings.c": "b9390548936b386e0211d487c1b76b65410fdd2e339cd8ecd9585ef4f4ffed31",
|
||||
"addons/core/Scripts/Game/ERR/ERR_SiteDefinition.c": "b422feeaca90084ea2bee5cde94dc9c6fcae542749b55d95f7226f922a6a68ac",
|
||||
"addons/core/Scripts/Game/ERR/ERR_SiteDiscoveryRules.c": "50f7b3ff4f32bde37ce5ae2be7f598fc4dd8cc08f45914f6e3260d31c84baef2",
|
||||
"addons/core/Scripts/Game/ERR/ERR_SiteService.c": "6bc9a0b5aed326822807f05e17459d0fc2da10b709e8168972b4e40fd09f87cb",
|
||||
"addons/core/Scripts/Game/ERR/ERR_SpawnFinalization.c": "2e295b0f225208c5414728ba83333e5e0ab6eef3a708ce39eb1dfce5400c94c9",
|
||||
"addons/core/Scripts/Game/ERR/ERR_SpawnLogic.c": "ed88279cbeb3e5e6daf8471763df87a251881f550632eddadd08153594bbd1a1",
|
||||
"addons/core/Scripts/Game/ERR/ERR_SpawnPlacement.c": "55323dcb330c23d49dc8fa3fde86a3ae3958c0b6563d6c3cd0960da88333f93d",
|
||||
"addons/core/Scripts/Game/ERR/ERR_Supplies.c": "752e8a56b0b8cde33cf532032cb0a203553017b686faac2e19bee06f7a9c0713",
|
||||
"addons/core/Scripts/Game/ERR/ERR_TimeService.c": "d9f481b7da01e08d2c982a948c18ba6bde76889d7cc9d26d510708f4ba479453",
|
||||
"addons/core/Scripts/GameLib/ERR/ERR_DiagnosticNoSplash.c": "0ea1b402f8927b6d68bf6f7e5caa2967f7e3a39147634faab147796b791933b5",
|
||||
"addons/core/Scripts/WorkbenchGame/ERR/ERR_ValidateBaseWorldPlugin.c": "7f44aa64ea7b4f8b3640a37a4b781c43bf6adb80e4a3a702d9491131698056f6",
|
||||
"addons/core/Worlds/ERR_Everon.ent": "22324bc7eb0d80dec8a1f3bd61a9df9201b72f749f4e751766b0c9d0b7d46b87",
|
||||
"addons/core/Worlds/ERR_Everon.ent.meta": "19d1903ca72baa326b4724a6791d0da1104a1e53f3f31dc1ca41a856efd68e83",
|
||||
"addons/core/Worlds/ERR_Everon_Layers/default.layer": "17105538007199bfc419aa04a64debaf2da0a7b77a1dc3ba983e92bf747c862e",
|
||||
"addons/core/addon.gproj": "231274e6b62302f32c65f9ff31de2ce8a9e51e1c8d12c87deeec82ae1f40de46",
|
||||
"addons/intel/Scripts/Game/ERR/ERR_IntelService.c": "5e167630a76437fd731fbb7a62a41381ea71509f3adb8a6b9c77f6816384861a",
|
||||
"addons/intel/addon.gproj": "d0ced242cefb220a5cccd6779d61fda9bc55581701b9f78bcf13c09d92ddbcbe",
|
||||
"addons/map/Materials/ERRM_MapRenderTarget.emat": "9877c5b175c5f30dd80bea36c711b21ab38219aa7075adf9ae79932e1d4c76bc",
|
||||
"addons/map/Materials/ERRM_MapRenderTarget.emat.meta": "5ee1a705f8011a44020ca2691f5ccc17d44bf596fef9a36e98d1f267eb58fab0",
|
||||
"addons/map/Prefabs/Items/Equipment/ERRM_SharedFieldMap.et": "30dee8e9eab459ea32a7d835a9fb89d43df47708057cab829f067b25a24cd581",
|
||||
"addons/map/Prefabs/Items/Equipment/ERRM_SharedFieldMap.et.meta": "59093e9072c50d99c86da7609c87541016da5bcf92b98d99db4c524e1c8e0e8d",
|
||||
"addons/map/Scripts/Game/ERR/ERR_ScenarioMap.c": "75fa1d742129ef7f33c005cd48f56db7f49ee7bde3c5428532b0b501b032b0a3",
|
||||
"addons/map/Scripts/Game/ERRMap/ERRM_NativeMapOwnershipProbe.c": "32418602f085e3edba7343f42a6a691630b533be262002f29248e8ae87a38254",
|
||||
"addons/map/Scripts/Game/ERRMap/ERRM_SharedMapComponent.c": "3a10f6ea25bf88a400bc3e6c339823433845c5db4f2f423835ccf99a1b1a843f",
|
||||
"addons/map/UI/layouts/ERRMap/SharedMapRenderTarget.layout": "49a2da4181b94d42174f58c7f9b6bf2207116c99427d61ff1c212092ac7db4d2",
|
||||
"addons/map/UI/layouts/ERRMap/SharedMapRenderTarget.layout.meta": "5c846445cc089e2e08899ba703059d4adb805ff30ab5a16409a3cd59718cbd54",
|
||||
"addons/map/addon.gproj": "1b371e5e01ea9fa46e76570fecb8aaa3a9298e34cba7ccca8a6bdb5ca86eb46a"
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
SCR_AIGroup : "{8DE0C0830FE0C33D}Prefabs/Groups/OPFOR/Group_USSR_Base.et" {
|
||||
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"
|
||||
"{86E565387A634103}Prefabs/Characters/Campaign/Final/OPFOR/USSR_Army/Regular/Campaign_USSR_Player_MortarCrew.et"
|
||||
"{612F43A4D5AE765F}Prefabs/Characters/Factions/OPFOR/USSR_Army/Character_USSR_RTO.et"
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
MetaFileClass {
|
||||
Name "{B84C05941C4C81B8}Prefabs/ERR/MortarCrew.et"
|
||||
Configurations {
|
||||
EntityTemplateResourceClass PC {}
|
||||
EntityTemplateResourceClass HEADLESS {}
|
||||
}
|
||||
}
|
||||
+436
@@ -0,0 +1,436 @@
|
||||
// Prepared control mechanism only. No global hooks, force allocation or HQ selection.
|
||||
enum ERR_AircraftFlightState
|
||||
{
|
||||
IDLE, HOOKS, STARTING, POSITIVE, NEGATIVE, CLIMB, LANDING, SHUTDOWN,
|
||||
COMPLETE, CANCELLED, FAILED, RELINQUISHED, TRANSLATING
|
||||
}
|
||||
class ERR_AircraftFlightRules
|
||||
{
|
||||
static bool Terminal(int state) { return state >= ERR_AircraftFlightState.COMPLETE && state <= ERR_AircraftFlightState.RELINQUISHED; }
|
||||
static bool Deadline(float now, float deadline) { return now >= 0 && deadline > now && deadline - now <= 240000; }
|
||||
static bool Envelope(float height, float drift, float upright)
|
||||
{
|
||||
return height >= -3 && height <= 18 && drift >= 0 && drift < 8 && upright >= 0.85;
|
||||
}
|
||||
static bool Hover(float height, float speed, float vertical, float upright)
|
||||
{
|
||||
return height >= 7 && height <= 9 && speed >= 0 && speed < 1.5 && Math.AbsFloat(vertical) < 0.5 && upright > 0.98;
|
||||
}
|
||||
static bool Landed(bool contact, float height, float speed, float angular, float upright)
|
||||
{
|
||||
return contact && height > -1 && height < 1 && speed >= 0 && speed < 0.5 && angular >= 0 && angular < 0.1 && upright > 0.98;
|
||||
}
|
||||
static float Vertical(float height, bool landing)
|
||||
{
|
||||
float target = 8; if (landing) target = 0;
|
||||
return Math.Clamp((target - height) * 0.6, -0.8, 1.5);
|
||||
}
|
||||
// Ground aborts may stop the engine; airborne cancellation retains the flight lease.
|
||||
static int CancelAction(bool ownershipValid, bool landed, bool engineOn)
|
||||
{
|
||||
if (!ownershipValid) return 0;
|
||||
if (!landed) return 1;
|
||||
if (engineOn) return 2;
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Bounded migration of the separately proven native-autohover translation.
|
||||
class ERR_AircraftTranslationRules
|
||||
{
|
||||
static bool Target(vector home, vector target)
|
||||
{
|
||||
float distance = vector.DistanceXZ(home, target);
|
||||
return distance >= 0 && distance <= 5;
|
||||
}
|
||||
static bool Reachable(float distance) { return distance >= 0 && distance <= 4.05; }
|
||||
static vector Demand(vector target, vector position, vector velocity)
|
||||
{
|
||||
vector error = target - position; error[1] = 0;
|
||||
vector desired = error * 0.25;
|
||||
float speed = desired.Length();
|
||||
if (speed > 0.5) desired = desired * (0.5 / speed);
|
||||
velocity[1] = 0;
|
||||
vector demand = (desired - velocity) * 0.05;
|
||||
float magnitude = demand.Length();
|
||||
if (magnitude > 0.03) demand = demand * (0.03 / magnitude);
|
||||
return demand;
|
||||
}
|
||||
static bool Held(bool contact, float height, float speed, float vertical, float upright, float horizontalSpeed, float targetError, float displacement, float distance)
|
||||
{
|
||||
if (contact || !ERR_AircraftFlightRules.Hover(height, speed, vertical, upright)) return false;
|
||||
if (!(horizontalSpeed >= 0 && horizontalSpeed < 0.25 && targetError >= 0 && targetError <= 0.75)) return false;
|
||||
return Reachable(distance) && displacement >= Math.Max(0, distance - 0.75);
|
||||
}
|
||||
}
|
||||
|
||||
class ERR_AircraftFlight
|
||||
{
|
||||
protected static ref map<Vehicle, ERR_AircraftFlight> s_Owners = new map<Vehicle, ERR_AircraftFlight>();
|
||||
protected ERR_GameMode m_Mode;
|
||||
protected Vehicle m_Aircraft;
|
||||
protected SCR_AIGroup m_Group;
|
||||
protected IEntity m_Pilot;
|
||||
protected PilotCompartmentSlot m_Seat;
|
||||
protected HelicopterControllerComponent m_Controller;
|
||||
protected VehicleHelicopterSimulation m_Simulation;
|
||||
protected CharacterControllerComponent m_Character;
|
||||
protected CharacterInputContext m_Input;
|
||||
protected AIAgent m_Agent;
|
||||
protected ActionManager m_Actions;
|
||||
protected FuelManagerComponent m_Fuel;
|
||||
protected ref ERR_AircraftFlightEnvelope m_Qualification;
|
||||
protected ERR_AircraftFlightState m_State;
|
||||
protected int m_Token;
|
||||
protected vector m_Home;
|
||||
protected float m_Heading;
|
||||
protected float m_Deadline;
|
||||
protected float m_StageDeadline;
|
||||
protected float m_Stable = -1;
|
||||
protected float m_LastTick = -1;
|
||||
protected float m_LastControl = -1;
|
||||
protected float m_InitialFuel;
|
||||
protected float m_Collective = -0.8;
|
||||
protected int m_ActionUpdates;
|
||||
protected int m_HelicopterUpdates;
|
||||
protected int m_GateUpdates;
|
||||
protected int m_OriginalLOD;
|
||||
protected CollectiveMode m_OriginalMode;
|
||||
protected bool m_OriginalAutohover;
|
||||
protected bool m_SettingsOwned;
|
||||
protected bool m_Readback = true;
|
||||
protected bool m_Cancelled;
|
||||
protected bool m_StartAttempted;
|
||||
protected string m_Reason;
|
||||
protected bool m_TranslationRequested;
|
||||
protected bool m_TranslationStarted;
|
||||
protected bool m_TranslationProven;
|
||||
protected vector m_TranslationTarget;
|
||||
protected vector m_TranslationOrigin;
|
||||
protected float m_TranslationDistance;
|
||||
protected int m_TranslationUpdates;
|
||||
|
||||
ERR_AircraftFlightState State() { return m_State; }
|
||||
string Reason() { return m_Reason; }
|
||||
int OrderToken() { return m_Token; }
|
||||
bool Finished() { return ERR_AircraftFlightRules.Terminal(m_State); }
|
||||
Vehicle Aircraft() { return m_Aircraft; }
|
||||
bool TranslationStarted() { return m_TranslationStarted; }
|
||||
bool TranslationProven() { return m_TranslationProven; }
|
||||
vector TranslationTarget() { return m_TranslationTarget; }
|
||||
vector TranslationOrigin() { return m_TranslationOrigin; }
|
||||
int TranslationUpdates() { return m_TranslationUpdates; }
|
||||
// Absolute horizontal target fixed before takeoff. Its altitude is the existing
|
||||
// eight-metre hover, not a caller-supplied route or a new qualification home.
|
||||
bool StartTranslation(ERR_GameMode mode, Vehicle aircraft, SCR_AIGroup group, IEntity pilot, PilotCompartmentSlot seat, ERR_AircraftFlightEnvelope qualification, int token, float deadline, vector target)
|
||||
{
|
||||
if (m_StartAttempted || m_TranslationRequested || !qualification || !ERR_AircraftTranslationRules.Target(qualification.Home(), target)) return false;
|
||||
m_TranslationRequested = true; m_TranslationTarget = target;
|
||||
vector home = qualification.Home(); m_TranslationTarget[1] = home[1] + 8;
|
||||
return Start(mode, aircraft, group, pilot, seat, qualification, token, deadline);
|
||||
}
|
||||
protected float Now()
|
||||
{
|
||||
if (!GetGame() || !GetGame().GetWorld()) return -1;
|
||||
return GetGame().GetWorld().GetWorldTime();
|
||||
}
|
||||
protected bool NoPlayers()
|
||||
{
|
||||
if (!GetGame() || !GetGame().GetPlayerManager()) return false;
|
||||
if (!m_Group || m_Group.GetPlayerCount(true) > 0 || ERR_Actors.Player(m_Pilot)) return false;
|
||||
array<int> players = {}; GetGame().GetPlayerManager().GetPlayers(players);
|
||||
foreach (int id : players)
|
||||
{
|
||||
IEntity actor = GetGame().GetPlayerManager().GetPlayerControlledEntity(id);
|
||||
if (actor && (actor == m_Pilot || CompartmentAccessComponent.GetVehicleIn(actor) == m_Aircraft)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
protected bool Owns()
|
||||
{
|
||||
if (!GetGame() || !GetGame().GetWorld() || !GetGame().GetPlayerManager()) return false;
|
||||
if (!Replication.IsServer() || !m_Mode || m_Mode != ERR_GameMode.Get() || !m_Mode.Active() || !m_Aircraft || !m_Group || !m_Pilot) return false;
|
||||
if (!m_Controller || m_Controller.GetOwner() != m_Aircraft || !m_Seat || m_Controller.GetPilotCompartmentSlot() != m_Seat || m_Seat.GetOccupant() != m_Pilot) return false;
|
||||
if (CompartmentAccessComponent.GetVehicleIn(m_Pilot) != m_Aircraft || ERR_Actors.Group(m_Pilot) != m_Group || m_Group.GetFaction() != m_Mode.EnemyFaction()) return false;
|
||||
if (SCR_FactionManager.SGetFaction(m_Aircraft) != m_Mode.EnemyFaction() || !NoPlayers()) return false;
|
||||
return m_Agent && m_Agent.GetControlledEntity() == m_Pilot && m_Agent.GetActionManager() == m_Actions && m_Actions && m_Actions != GetGame().GetInputManager();
|
||||
}
|
||||
protected bool Healthy()
|
||||
{
|
||||
if (!ERR_Actors.Alive(m_Aircraft) || !ERR_Actors.CanFight(m_Pilot) || !m_Simulation || !m_Simulation.IsValid() || !m_Fuel || !m_Fuel.HasFuel()) return false;
|
||||
SCR_VehicleDamageManagerComponent damage = SCR_VehicleDamageManagerComponent.Cast(m_Aircraft.FindComponent(SCR_VehicleDamageManagerComponent));
|
||||
if (!damage || !damage.GetEngineFunctional() || damage.GetMovementDamage() >= 1 || damage.IsOnFire() || m_Simulation.RotorCount() != 2) return false;
|
||||
for (int rotor = 0; rotor < 2; rotor++) if (m_Simulation.RotorGetState(rotor) == RotorState.DESTROYED) return false;
|
||||
return true;
|
||||
}
|
||||
protected bool Grounded()
|
||||
{
|
||||
if (!GetGame() || !GetGame().GetWorld() || !m_Aircraft || !m_Simulation) return false;
|
||||
vector transform[4]; m_Aircraft.GetWorldTransform(transform);
|
||||
return ERR_AircraftFlightRules.Landed(m_Simulation.HasAnyGroundContact(), transform[3][1] - m_Home[1], m_Simulation.GetVelocity().Length(), m_Simulation.GetAngularVelocity().Length(), transform[1][1]);
|
||||
}
|
||||
// Single use. A future HQ owner supplies its immutable unique order token and
|
||||
// retains this instance, calling Tick and the two scoped callbacks until terminal.
|
||||
bool Start(ERR_GameMode mode, Vehicle aircraft, SCR_AIGroup group, IEntity pilot, PilotCompartmentSlot seat, ERR_AircraftFlightEnvelope qualification, int token, float deadline)
|
||||
{
|
||||
if (m_StartAttempted || m_State != ERR_AircraftFlightState.IDLE || m_Token != 0 || !Replication.IsServer() || !GetGame() || !qualification || !aircraft || !pilot || !group || !seat) return false;
|
||||
m_StartAttempted = true;
|
||||
float now = Now();
|
||||
if (!ERR_AircraftFlightRules.Deadline(now, deadline) || token <= 0 || s_Owners.Get(aircraft)) return false;
|
||||
m_Mode = mode; m_Aircraft = aircraft; m_Group = group; m_Pilot = pilot; m_Seat = seat;
|
||||
m_Controller = HelicopterControllerComponent.Cast(aircraft.FindComponent(HelicopterControllerComponent));
|
||||
if (m_Controller) m_Simulation = m_Controller.GetSimulation();
|
||||
m_Character = CharacterControllerComponent.Cast(pilot.FindComponent(CharacterControllerComponent));
|
||||
if (m_Character) m_Input = m_Character.GetInputContext();
|
||||
AIControlComponent control = AIControlComponent.Cast(pilot.FindComponent(AIControlComponent));
|
||||
if (control) m_Agent = control.GetControlAIAgent();
|
||||
if (m_Agent) m_Actions = m_Agent.GetActionManager();
|
||||
m_Fuel = FuelManagerComponent.Cast(aircraft.FindComponent(FuelManagerComponent));
|
||||
if (!Owns() || !Healthy() || !m_Input || m_Controller.IsEngineOn() || !ERR_CasualtyLoadingRules.AircraftReady(aircraft)) return false;
|
||||
Physics physics = aircraft.GetPhysics();
|
||||
if (!physics || !physics.IsDynamic() || !physics.IsActive() || !m_Simulation.HasAnyGroundContact()) return false;
|
||||
SignalsManagerComponent signals = m_Controller.GetSignalsManager();
|
||||
if (!signals || signals.FindSignal("MainRotorCollective") < 0) return false;
|
||||
if (!qualification.Claim(mode, aircraft, pilot, token)) return false;
|
||||
m_Qualification = qualification; m_Home = qualification.Home(); m_Token = token; m_Deadline = deadline;
|
||||
m_Heading = aircraft.GetYawPitchRoll()[0]; m_InitialFuel = m_Fuel.GetTotalFuel();
|
||||
m_OriginalMode = m_Controller.GetCollectiveMode(); m_OriginalAutohover = m_Controller.GetAutohoverEnabled(); m_OriginalLOD = m_Agent.GetPermanentLOD();
|
||||
m_SettingsOwned = true; s_Owners.Set(aircraft, this);
|
||||
m_Agent.SetPermanentLOD(0); m_Agent.ActivateAI();
|
||||
m_Controller.SetAutohoverEnabled(false);
|
||||
if (m_Controller.GetAutohoverEnabled()) { Fail("native autohover disable refused"); return false; }
|
||||
m_State = ERR_AircraftFlightState.HOOKS; m_StageDeadline = Math.Min(deadline, now + 5000);
|
||||
PrintFormat("ERR AIRCRAFT CONTROL: token=%1 bound qualified finite aircraft; no HQ dispatch installed", token);
|
||||
return true;
|
||||
}
|
||||
protected bool Lease()
|
||||
{
|
||||
return m_Token > 0 && !Finished() && m_State != ERR_AircraftFlightState.IDLE && s_Owners.Get(m_Aircraft) == this && m_Qualification && m_Qualification.ClaimedBy(m_Aircraft, m_Pilot, m_Token);
|
||||
}
|
||||
// Every input entry checks the immutable deadlines even if owner Tick stalls.
|
||||
protected bool ControlLease()
|
||||
{
|
||||
if (!Lease()) return false;
|
||||
if (!Owns()) { Relinquish("ownership or world unavailable at control callback"); return false; }
|
||||
float now = Now();
|
||||
if (now < 0 || now >= m_Deadline || now >= m_StageDeadline) { Fail("control callback reached bounded flight deadline"); return false; }
|
||||
if (!Healthy() || !m_Readback) { Fail("flight capability or input readback lost"); return false; }
|
||||
vector transform[4]; m_Aircraft.GetWorldTransform(transform);
|
||||
if (!ERR_AircraftFlightRules.Envelope(transform[3][1] - m_Home[1], vector.DistanceXZ(transform[3], m_Home), transform[1][1])) { Fail("control callback exceeded qualified flight envelope"); return false; }
|
||||
return true;
|
||||
}
|
||||
// Must be called after the native helicopter's OnPrepareControls.
|
||||
void PrepareHelicopter(HelicopterControllerComponent controller)
|
||||
{
|
||||
if (!Lease() || controller != m_Controller) return;
|
||||
if (!ControlLease()) return;
|
||||
controller.SetCollectiveMode(CollectiveMode.Direct); m_HelicopterUpdates++;
|
||||
}
|
||||
protected bool Pair(string positive, string negative, float demand)
|
||||
{
|
||||
float a = Math.Clamp(demand, 0, 1), b = Math.Clamp(-demand, 0, 1);
|
||||
m_Actions.SetActionValue(positive, a); m_Actions.SetActionValue(negative, b);
|
||||
return Math.AbsFloat(m_Actions.GetActionValue(positive) - a) < 0.001 && Math.AbsFloat(m_Actions.GetActionValue(negative) - b) < 0.001;
|
||||
}
|
||||
// Must be called on the exact AI actor manager before native OnPrepareControls.
|
||||
void PrepareActions(CharacterControllerComponent controller, IEntity owner, ActionManager actions, float dt)
|
||||
{
|
||||
if (!Lease() || controller != m_Character || owner != m_Pilot || actions != m_Actions) return;
|
||||
if (!ControlLease()) return;
|
||||
if (!m_Readback || dt <= 0 || dt > 0.2) return;
|
||||
if (!actions.IsContextActive("HelicopterContext")) actions.ActivateContext("HelicopterContext", 1);
|
||||
if (!actions.IsContextActive("HelicopterContext")) { m_Readback = false; return; }
|
||||
float collective, torque, forward, aside;
|
||||
if (m_State == ERR_AircraftFlightState.POSITIVE) collective = 0.05;
|
||||
else if (m_State == ERR_AircraftFlightState.NEGATIVE) collective = -0.05;
|
||||
else if (m_State == ERR_AircraftFlightState.SHUTDOWN) collective = -1;
|
||||
else if (m_State == ERR_AircraftFlightState.CLIMB || m_State == ERR_AircraftFlightState.LANDING || m_State == ERR_AircraftFlightState.TRANSLATING)
|
||||
{
|
||||
if (!m_Controller.GetAutohoverEnabled()) { Fail("native autohover lost while flying"); return; }
|
||||
float height = m_Aircraft.GetOrigin()[1] - m_Home[1];
|
||||
float error = ERR_AircraftFlightRules.Vertical(height, m_State == ERR_AircraftFlightState.LANDING) - m_Simulation.GetVelocity()[1];
|
||||
m_Collective = Math.Clamp(m_Collective + error * dt * 0.12, -0.95, 0.8);
|
||||
collective = Math.Clamp(m_Collective + error * 0.12, -1, 0.9);
|
||||
float headingError = m_Heading - m_Aircraft.GetYawPitchRoll()[0];
|
||||
while (headingError > 180) headingError -= 360;
|
||||
while (headingError < -180) headingError += 360;
|
||||
torque = Math.Clamp(headingError * 0.01 - m_Simulation.GetAngularVelocityLocal()[1] * 0.3, -0.3, 0.3);
|
||||
if (m_State == ERR_AircraftFlightState.TRANSLATING)
|
||||
{
|
||||
if (!m_TranslationStarted || !ERR_AircraftTranslationRules.Target(m_Home, m_TranslationTarget)) { Fail("immutable translation target unavailable"); return; }
|
||||
vector transform[4]; m_Aircraft.GetWorldTransform(transform);
|
||||
vector demand = ERR_AircraftTranslationRules.Demand(m_TranslationTarget, transform[3], m_Simulation.GetVelocity());
|
||||
forward = Math.Clamp(vector.Dot(demand, transform[2]), -0.03, 0.03);
|
||||
aside = Math.Clamp(vector.Dot(demand, transform[0]), -0.03, 0.03);
|
||||
m_TranslationUpdates++;
|
||||
}
|
||||
if (m_State == ERR_AircraftFlightState.LANDING && Grounded()) collective = -1;
|
||||
}
|
||||
bool a = Pair("HelicopterCollectiveIncrease", "HelicopterCollectiveDecrease", collective);
|
||||
bool b = Pair("HelicopterCyclicForward", "HelicopterCyclicBack", forward);
|
||||
bool c = Pair("HelicopterCyclicRight", "HelicopterCyclicLeft", aside);
|
||||
bool d = Pair("HelicopterAntiTorqueRight", "HelicopterAntiTorqueLeft", torque);
|
||||
m_Readback = a && b && c && d; m_ActionUpdates++; m_LastControl = Now();
|
||||
}
|
||||
protected void Release(bool restore)
|
||||
{
|
||||
// No writes after capture/group/seat change, including apparently harmless zeros.
|
||||
if (m_SettingsOwned && Owns())
|
||||
{
|
||||
Pair("HelicopterCollectiveIncrease", "HelicopterCollectiveDecrease", 0);
|
||||
Pair("HelicopterCyclicForward", "HelicopterCyclicBack", 0);
|
||||
Pair("HelicopterCyclicRight", "HelicopterCyclicLeft", 0);
|
||||
Pair("HelicopterAntiTorqueRight", "HelicopterAntiTorqueLeft", 0);
|
||||
// Restore native modes only after verified ground completion. Airborne
|
||||
// failure leaves native autohover enabled rather than disabling it midair.
|
||||
if (restore && Grounded())
|
||||
{
|
||||
m_Controller.SetAutohoverEnabled(m_OriginalAutohover);
|
||||
m_Controller.SetCollectiveMode(m_OriginalMode);
|
||||
m_Agent.SetPermanentLOD(m_OriginalLOD);
|
||||
}
|
||||
}
|
||||
if (m_Aircraft && s_Owners && s_Owners.Get(m_Aircraft) == this) s_Owners.Remove(m_Aircraft);
|
||||
m_SettingsOwned = false;
|
||||
}
|
||||
protected void RecordState()
|
||||
{
|
||||
string detail = string.Format("token=%1 state=%2 reason=%3; physical availability remains with the allocating owner", m_Token, m_State, m_Reason);
|
||||
Print("ERR AIRCRAFT CONTROL: " + detail);
|
||||
if (m_Mode && m_Mode == ERR_GameMode.Get()) ERR_HQ.Event("AIRCRAFT CONTROL", detail);
|
||||
}
|
||||
protected void Relinquish(string reason)
|
||||
{
|
||||
if (Finished()) return;
|
||||
m_Reason = reason; m_State = ERR_AircraftFlightState.RELINQUISHED; Release(false); RecordState();
|
||||
}
|
||||
protected void Fail(string reason)
|
||||
{
|
||||
if (Finished()) return;
|
||||
m_Reason = reason; m_State = ERR_AircraftFlightState.FAILED; Release(true); RecordState();
|
||||
}
|
||||
protected bool Landing(float now)
|
||||
{
|
||||
m_Controller.SetAutohoverEnabled(true);
|
||||
if (!m_Controller.GetAutohoverEnabled()) { Fail("native autohover enable refused"); return false; }
|
||||
m_State = ERR_AircraftFlightState.LANDING; m_Stable = -1;
|
||||
m_StageDeadline = Math.Min(m_Deadline, now + 65000);
|
||||
return true;
|
||||
}
|
||||
protected void Shutdown(float now)
|
||||
{
|
||||
m_Controller.StopEngine(false);
|
||||
m_State = ERR_AircraftFlightState.SHUTDOWN; m_StageDeadline = Math.Min(m_Deadline, now + 15000); m_Stable = -1;
|
||||
}
|
||||
void Cancel(string reason)
|
||||
{
|
||||
if (!Lease()) return;
|
||||
if (m_Cancelled) return; // repeated invalid reports cannot extend a landing deadline
|
||||
m_Cancelled = true; m_Reason = reason; RecordState();
|
||||
// After flight begins, even a current contact sample must pass the normal
|
||||
// two-second landing observation; cancellation cannot shortcut touchdown.
|
||||
bool preflightGround = m_State < ERR_AircraftFlightState.CLIMB && Grounded();
|
||||
int action = ERR_AircraftFlightRules.CancelAction(Owns(), preflightGround, m_Controller && m_Controller.IsEngineOn());
|
||||
if (action == 0) { Relinquish(reason); return; }
|
||||
if (!Healthy()) { Fail(reason + "; flight capability lost"); return; }
|
||||
if (action == 3) { m_State = ERR_AircraftFlightState.CANCELLED; Release(true); RecordState(); return; }
|
||||
float now = Now();
|
||||
if (action == 2) { Shutdown(now); return; }
|
||||
if (m_State != ERR_AircraftFlightState.LANDING && m_State != ERR_AircraftFlightState.SHUTDOWN) Landing(now);
|
||||
}
|
||||
protected bool BeginTranslation(float now, vector position)
|
||||
{
|
||||
if (!m_TranslationRequested || m_TranslationStarted || m_State != ERR_AircraftFlightState.CLIMB) { Cancel("translation stage ownership refused"); return false; }
|
||||
float distance = vector.DistanceXZ(position, m_TranslationTarget);
|
||||
if (!ERR_AircraftTranslationRules.Target(m_Home, m_TranslationTarget) || !ERR_AircraftTranslationRules.Reachable(distance)) { Cancel("fixed target beyond qualified four-metre translation after hover drift"); return false; }
|
||||
SignalsManagerComponent signals = m_Controller.GetSignalsManager();
|
||||
if (!signals || signals.FindSignal("MainRotorCyclicForward") < 0 || signals.FindSignal("MainRotorCyclicAside") < 0 || !m_Controller.GetAutohoverEnabled()) { Cancel("native autohover cyclic signals unavailable"); return false; }
|
||||
m_TranslationOrigin = position; m_TranslationDistance = distance;
|
||||
m_TranslationStarted = true; m_State = ERR_AircraftFlightState.TRANSLATING;
|
||||
m_Stable = -1; m_StageDeadline = Math.Min(m_Deadline, now + 45000);
|
||||
PrintFormat("ERR AIRCRAFT TRANSLATION: token=%1 origin=%2 immutableTarget=%3 home=%4 distance=%5 deadline=%6", m_Token, position, m_TranslationTarget, m_Home, distance, m_StageDeadline);
|
||||
return true;
|
||||
}
|
||||
protected void CheckTranslation(float now, vector position, float height, float speed, float upright)
|
||||
{
|
||||
if (!m_TranslationStarted || !m_Controller.GetAutohoverEnabled() || !ERR_AircraftTranslationRules.Target(m_Home, m_TranslationTarget)) { Cancel("translation ownership or autohover lost"); return; }
|
||||
vector velocity = m_Simulation.GetVelocity(); vector horizontal = velocity; horizontal[1] = 0;
|
||||
bool held = m_TranslationUpdates >= 3 && ERR_AircraftTranslationRules.Held(m_Simulation.HasAnyGroundContact(), height, speed, velocity[1], upright, horizontal.Length(), vector.DistanceXZ(position, m_TranslationTarget), vector.DistanceXZ(position, m_TranslationOrigin), m_TranslationDistance);
|
||||
if (!held) { m_Stable = -1; return; }
|
||||
if (m_Stable < 0) m_Stable = now;
|
||||
if (now - m_Stable < 5000) return;
|
||||
m_TranslationProven = true;
|
||||
PrintFormat("ERR AIRCRAFT TRANSLATION HELD: token=%1 targetError=%2 displacement=%3 updates=%4", m_Token, vector.DistanceXZ(position, m_TranslationTarget), vector.DistanceXZ(position, m_TranslationOrigin), m_TranslationUpdates);
|
||||
Landing(now);
|
||||
}
|
||||
// The owner continues ticking after Cancel until Finished, including landing.
|
||||
void Tick()
|
||||
{
|
||||
if (!Lease()) return;
|
||||
float now = Now();
|
||||
if (!Owns()) { Relinquish("ownership or mission changed"); return; }
|
||||
if (!Healthy()) { Fail("flight capability lost"); return; }
|
||||
if (!m_Readback) { Fail("native action readback failed"); return; }
|
||||
if (now >= m_Deadline || now >= m_StageDeadline || now < 0) { Fail("bounded flight stage expired"); return; }
|
||||
if (m_LastTick >= 0 && (now < m_LastTick || now - m_LastTick > 1500)) m_Stable = -1;
|
||||
m_LastTick = now;
|
||||
vector transform[4]; m_Aircraft.GetWorldTransform(transform);
|
||||
float height = transform[3][1] - m_Home[1], speed = m_Simulation.GetVelocity().Length();
|
||||
if (!ERR_AircraftFlightRules.Envelope(height, vector.DistanceXZ(transform[3], m_Home), transform[1][1])) { Fail("qualified flight envelope exceeded"); return; }
|
||||
if (m_State == ERR_AircraftFlightState.HOOKS)
|
||||
{
|
||||
if (m_ActionUpdates < 3 || m_HelicopterUpdates < 3) return;
|
||||
if (m_Controller.GetCollectiveMode() != CollectiveMode.Direct || m_Input.GetVehicleCollective() != 0 || m_Controller.IsEngineOn()) { Fail("native initial control state refused"); return; }
|
||||
m_Controller.StartEngine(); m_State = ERR_AircraftFlightState.STARTING; m_StageDeadline = Math.Min(m_Deadline, now + 65000); return;
|
||||
}
|
||||
if (m_LastControl < 0 || now - m_LastControl > 1000) { Fail("pilot callback stopped"); return; }
|
||||
if (m_State == ERR_AircraftFlightState.STARTING)
|
||||
{
|
||||
float rpm = m_Simulation.RotorGetRPMTarget(0);
|
||||
if (!m_Controller.IsEngineOn() || rpm <= 0 || m_Simulation.RotorGetRPM(0) < rpm * 0.9 || m_Fuel.GetTotalFuel() >= m_InitialFuel) return;
|
||||
m_State = ERR_AircraftFlightState.POSITIVE; m_StageDeadline = Math.Min(m_Deadline, now + 3000); m_GateUpdates = m_ActionUpdates; m_Stable = -1; return;
|
||||
}
|
||||
if (m_State != ERR_AircraftFlightState.SHUTDOWN && !m_Controller.IsEngineOn()) { Fail("running engine lost"); return; }
|
||||
if (m_State == ERR_AircraftFlightState.POSITIVE || m_State == ERR_AircraftFlightState.NEGATIVE)
|
||||
{
|
||||
SignalsManagerComponent signals = m_Controller.GetSignalsManager();
|
||||
int index = -1; if (signals) index = signals.FindSignal("MainRotorCollective");
|
||||
if (index < 0 || m_Controller.GetCollectiveMode() != CollectiveMode.Direct || m_Controller.GetAutohoverEnabled()) { Fail("native collective proof unavailable"); return; }
|
||||
float expected = 0.05; if (m_State == ERR_AircraftFlightState.NEGATIVE) expected = -0.05;
|
||||
bool matched = m_ActionUpdates >= m_GateUpdates + 3 && Math.AbsFloat(signals.GetSignalValue(index) - expected) <= 0.01 && Math.AbsFloat(m_Input.GetVehicleCollective() - expected) <= 0.01;
|
||||
if (!matched) { m_Stable = -1; return; }
|
||||
if (m_Stable < 0) m_Stable = now;
|
||||
if (now - m_Stable < 500) return;
|
||||
m_Stable = -1;
|
||||
if (m_State == ERR_AircraftFlightState.POSITIVE) { m_State = ERR_AircraftFlightState.NEGATIVE; m_StageDeadline = Math.Min(m_Deadline, now + 3000); m_GateUpdates = m_ActionUpdates; return; }
|
||||
m_Controller.SetAutohoverEnabled(true);
|
||||
if (!m_Controller.GetAutohoverEnabled()) { Fail("native autohover enable refused"); return; }
|
||||
m_State = ERR_AircraftFlightState.CLIMB; m_StageDeadline = Math.Min(m_Deadline, now + 65000); return;
|
||||
}
|
||||
if (m_State == ERR_AircraftFlightState.TRANSLATING) { CheckTranslation(now, transform[3], height, speed, transform[1][1]); return; }
|
||||
if (m_State == ERR_AircraftFlightState.CLIMB || m_State == ERR_AircraftFlightState.LANDING)
|
||||
{
|
||||
if (!m_Controller.GetAutohoverEnabled()) { Fail("native autohover lost"); return; }
|
||||
bool stable = Grounded(); float duration = 2000;
|
||||
if (m_State == ERR_AircraftFlightState.CLIMB) { stable = !m_Simulation.HasAnyGroundContact() && ERR_AircraftFlightRules.Hover(height, speed, m_Simulation.GetVelocity()[1], transform[1][1]); duration = 5000; }
|
||||
if (!stable) m_Stable = -1;
|
||||
if (stable && m_Stable < 0) m_Stable = now;
|
||||
if (m_Stable < 0 || now - m_Stable < duration) return;
|
||||
if (m_State == ERR_AircraftFlightState.CLIMB)
|
||||
{
|
||||
if (m_TranslationRequested) BeginTranslation(now, transform[3]);
|
||||
else Landing(now);
|
||||
return;
|
||||
}
|
||||
Shutdown(now); return;
|
||||
}
|
||||
if (m_State == ERR_AircraftFlightState.SHUTDOWN && !m_Controller.IsEngineOn())
|
||||
{
|
||||
if (!Grounded()) { Fail("engine-off physical ground check failed"); return; }
|
||||
if (m_Cancelled) m_State = ERR_AircraftFlightState.CANCELLED;
|
||||
else m_State = ERR_AircraftFlightState.COMPLETE;
|
||||
Release(true); RecordState();
|
||||
}
|
||||
}
|
||||
void ~ERR_AircraftFlight() { Release(true); }
|
||||
}
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
// Successor-owned measured envelope, independent of aircraft yaw/pitch/roll.
|
||||
// Adapted from the separately qualified native vertical fixture; no native hook.
|
||||
// All distances are around the native physics origin, not the rotor centre.
|
||||
class ERR_AircraftFlightFootprintRules
|
||||
{
|
||||
static float CylinderSphere(float originDistance, float radius, float thickness)
|
||||
{
|
||||
// Triangle inequality deliberately overbounds the one-sided rotor cylinder.
|
||||
return originDistance + radius + thickness;
|
||||
}
|
||||
static bool Fits(float radius, float initialDrift, float initialHeight)
|
||||
{
|
||||
if (!(radius > 0 && radius < 32 && initialDrift >= 0 && initialDrift < 32)) return false;
|
||||
if (!(initialHeight >= -3 && initialHeight <= 5)) return false;
|
||||
return radius + initialDrift + 8 + 1 <= 40 && radius + initialHeight + 18 + 1 <= 60;
|
||||
}
|
||||
}
|
||||
|
||||
class ERR_AircraftFlightFootprint
|
||||
{
|
||||
static const int MAX_ENTITIES = 256;
|
||||
static const int MAX_DEPTH = 16;
|
||||
static const int MAX_HITZONES = 512;
|
||||
protected static bool Refuse(string reason)
|
||||
{
|
||||
Print("ERR AIRCRAFT FOOTPRINT: refused " + reason);
|
||||
return false;
|
||||
}
|
||||
protected static bool PointRadius(vector point, vector origin, inout float radius)
|
||||
{
|
||||
float distance = vector.Distance(point, origin);
|
||||
if (!(distance >= 0 && distance < 1000)) return false;
|
||||
radius = Math.Max(radius, distance);
|
||||
return true;
|
||||
}
|
||||
static bool Measure(Vehicle aircraft, VehicleHelicopterSimulation simulation, vector pad, out vector origin, out float radius)
|
||||
{
|
||||
radius = 0;
|
||||
if (!aircraft || !simulation || !simulation.IsValid() || !aircraft.GetPrefabData()) return Refuse("missing initialized native airframe");
|
||||
if (aircraft.GetPrefabData().GetPrefabName() != "{DF5CCB7C0FF049F4}Prefabs/Vehicles/Helicopters/Mi8MT/Mi8MT_unarmed_transport.et") return Refuse("different airframe resource");
|
||||
// Model bounds and damage pivots are world-space. The simulation getter
|
||||
// exposes internal physics space (identity at the airport in 100422), so
|
||||
// use this same physical aircraft's actual entity world transform.
|
||||
vector worldTransform[4]; aircraft.GetWorldTransform(worldTransform);
|
||||
origin = worldTransform[3];
|
||||
for (int axis = 0; axis < 3; axis++)
|
||||
if (!(Math.AbsFloat(worldTransform[axis].Length() - 1) < 0.001)) return Refuse("non-unit native entity world basis");
|
||||
|
||||
array<IEntity> entities = {aircraft};
|
||||
array<int> depths = {0};
|
||||
array<HitZone> seenZones = {};
|
||||
int meshes, mainSweeps, tailSweeps;
|
||||
float modelRadius, damageRadius, simulationRadius;
|
||||
for (int index = 0; index < entities.Count(); index++)
|
||||
{
|
||||
IEntity entity = entities[index];
|
||||
if (!entity) return Refuse("hierarchy changed during measurement");
|
||||
if (entity.GetVObject())
|
||||
{
|
||||
vector mins, maxs; entity.GetWorldBounds(mins, maxs);
|
||||
for (int dimension = 0; dimension < 3; dimension++)
|
||||
if (!(mins[dimension] <= maxs[dimension])) return Refuse("invalid model bounds");
|
||||
// A sphere around every corner encloses the complete world AABB. Its radius
|
||||
// remains conservative under any rigid orientation of the whole aircraft.
|
||||
for (int x = 0; x < 2; x++)
|
||||
for (int y = 0; y < 2; y++)
|
||||
for (int z = 0; z < 2; z++)
|
||||
{
|
||||
vector corner = mins;
|
||||
if (x == 1) corner[0] = maxs[0];
|
||||
if (y == 1) corner[1] = maxs[1];
|
||||
if (z == 1) corner[2] = maxs[2];
|
||||
if (!PointRadius(corner, origin, modelRadius)) return Refuse("non-finite or excessive model bounds");
|
||||
}
|
||||
meshes++;
|
||||
}
|
||||
HitZoneContainerComponent container = HitZoneContainerComponent.Cast(entity.FindComponent(HitZoneContainerComponent));
|
||||
if (container)
|
||||
{
|
||||
int count = container.CountAllHitZones();
|
||||
if (count < 0 || count + seenZones.Count() > MAX_HITZONES) return Refuse("hitzone traversal bound");
|
||||
array<HitZone> zones = {};
|
||||
if (container.GetAllHitZones(zones) != count || zones.Count() != count) return Refuse("hitzones changed during measurement");
|
||||
foreach (HitZone zone : zones)
|
||||
{
|
||||
if (!zone || seenZones.Contains(zone)) continue;
|
||||
seenZones.Insert(zone);
|
||||
SCR_RotorHitZone rotorZone = SCR_RotorHitZone.Cast(zone);
|
||||
if (!rotorZone || rotorZone.GetRotorRadius() <= 0) continue; // transmission zones have no sweep
|
||||
int rotorIndex = rotorZone.GetRotorIndex();
|
||||
float expectedRadius, thickness;
|
||||
if (rotorIndex == 0) { expectedRadius = 12; thickness = 1.5; mainSweeps++; }
|
||||
else if (rotorIndex == 1) { expectedRadius = 2.3; thickness = 0.75; tailSweeps++; }
|
||||
else return Refuse("unknown rotor sweep index");
|
||||
// Stock thickness is protected with no getter. Scope to the exact prefab
|
||||
// and verify its public sweep radius against the audited stock constants.
|
||||
if (!(Math.AbsFloat(rotorZone.GetRotorRadius() - expectedRadius) < 0.001)) return Refuse("stock damage sweep radius changed");
|
||||
PointInfo point = rotorZone.GetPointInfo();
|
||||
if (!point) return Refuse("missing actual rotor sweep pivot");
|
||||
vector rotorWorld[4]; point.GetWorldTransform(rotorWorld);
|
||||
// Frobenius norm bounds the transform's maximum stretch even with scale
|
||||
// or shear; do not assume the PointInfo basis is orthonormal.
|
||||
float scaleSquared = 0;
|
||||
for (int basis = 0; basis < 3; basis++) scaleSquared += vector.Dot(rotorWorld[basis], rotorWorld[basis]);
|
||||
float scale = Math.Sqrt(scaleSquared);
|
||||
if (!(scale > 0 && scale < 4)) return Refuse("invalid damage sweep basis");
|
||||
float distance = vector.Distance(origin, rotorWorld[3]);
|
||||
float sweepBound = ERR_AircraftFlightFootprintRules.CylinderSphere(distance, expectedRadius * scale, thickness * scale);
|
||||
if (!(sweepBound > 0 && sweepBound < 1000)) return Refuse("invalid damage sweep extent");
|
||||
damageRadius = Math.Max(damageRadius, sweepBound);
|
||||
PrintFormat("ERR AIRCRAFT FOOTPRINT ROTOR: index=%1 pivot=%2 damageRadius=%3 thickness=%4 scale=%5 sphereBound=%6", rotorIndex, rotorWorld[3], expectedRadius, thickness, scale, sweepBound);
|
||||
}
|
||||
}
|
||||
IEntity child = entity.GetChildren();
|
||||
while (child)
|
||||
{
|
||||
if (entities.Count() >= MAX_ENTITIES || depths[index] >= MAX_DEPTH || entities.Contains(child)) return Refuse("entity/depth/cycle traversal bound");
|
||||
entities.Insert(child); depths.Insert(depths[index] + 1);
|
||||
child = child.GetSibling();
|
||||
}
|
||||
}
|
||||
if (meshes == 0 || mainSweeps != 1 || tailSweeps != 1) return Refuse("complete model and both actual damage sweeps required");
|
||||
if (simulation.RotorCount() != 2) return Refuse("stock simulation rotor count changed");
|
||||
for (int rotor = 0; rotor < 2; rotor++)
|
||||
{
|
||||
vector localRotor[4]; simulation.RotorGetTransform(rotor, localRotor);
|
||||
float physicalRadius = simulation.RotorGetRadius(rotor);
|
||||
float physicalBound = localRotor[3].Length() + physicalRadius;
|
||||
if (!(physicalRadius > 0 && physicalBound > 0 && physicalBound < 1000)) return Refuse("invalid native simulation rotor extent");
|
||||
simulationRadius = Math.Max(simulationRadius, physicalBound);
|
||||
PrintFormat("ERR AIRCRAFT FOOTPRINT PHYSICS ROTOR: index=%1 localPivot=%2 radius=%3 sphereBound=%4", rotor, localRotor[3], physicalRadius, physicalBound);
|
||||
}
|
||||
radius = Math.Max(modelRadius, Math.Max(damageRadius, simulationRadius));
|
||||
float initialDrift = vector.DistanceXZ(origin, pad);
|
||||
float initialHeight = origin[1] - pad[1];
|
||||
PrintFormat("ERR AIRCRAFT FOOTPRINT: entities=%1 meshes=%2 zones=%3 modelSphere=%4 damageSphere=%5 simulationSphere=%6 completeSphere=%7 initialDrift=%8 initialHeight=%9", entities.Count(), meshes, seenZones.Count(), modelRadius, damageRadius, simulationRadius, radius, initialDrift, initialHeight);
|
||||
if (!ERR_AircraftFlightFootprintRules.Fits(radius, initialDrift, initialHeight)) return Refuse("measured footprint plus initial placement,8m drift and1m margin exceeds40m corridor or60m ceiling");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Issued only by physical qualification; fields have no public mutators.
|
||||
// One claim binds this short-lived record to one original aircraft/pilot/order.
|
||||
class ERR_AircraftFlightEnvelope
|
||||
{
|
||||
protected ERR_GameMode m_Mode;
|
||||
protected Vehicle m_Aircraft;
|
||||
protected IEntity m_Pilot;
|
||||
protected vector m_Home;
|
||||
protected float m_Radius;
|
||||
protected float m_QualifiedAt;
|
||||
protected int m_Claim;
|
||||
vector Home() { return m_Home; }
|
||||
float Radius() { return m_Radius; }
|
||||
bool ClaimedBy(Vehicle aircraft, IEntity pilot, int token)
|
||||
{
|
||||
return m_Claim > 0 && m_Claim == token && m_Aircraft == aircraft && m_Pilot == pilot;
|
||||
}
|
||||
bool Claim(ERR_GameMode mode, Vehicle aircraft, IEntity pilot, int token)
|
||||
{
|
||||
if (!GetGame() || !GetGame().GetWorld()) return false;
|
||||
float now = GetGame().GetWorld().GetWorldTime();
|
||||
if (!Replication.IsServer() || !mode || mode != m_Mode || mode != ERR_GameMode.Get() || !mode.Active() || m_Claim != 0 || token <= 0) return false;
|
||||
if (aircraft != m_Aircraft || pilot != m_Pilot || !aircraft || !pilot || now < m_QualifiedAt || now - m_QualifiedAt > 2000) return false;
|
||||
if (vector.Distance(aircraft.GetOrigin(), m_Home) > 0.1) return false;
|
||||
m_Claim = token;
|
||||
return true;
|
||||
}
|
||||
protected static bool Hierarchy(IEntity root, array<IEntity> excluded)
|
||||
{
|
||||
if (!root) return false;
|
||||
array<IEntity> pending = {root};
|
||||
array<int> depths = {0};
|
||||
for (int i = 0; i < pending.Count(); i++)
|
||||
{
|
||||
IEntity entity = pending[i];
|
||||
if (!entity || excluded.Count() >= 512) return false;
|
||||
if (!excluded.Contains(entity)) excluded.Insert(entity);
|
||||
IEntity child = entity.GetChildren();
|
||||
while (child)
|
||||
{
|
||||
if (pending.Count() >= 256 || depths[i] >= 16 || pending.Contains(child)) return false;
|
||||
pending.Insert(child); depths.Insert(depths[i] + 1); child = child.GetSibling();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
protected static bool ClearColumn(Vehicle aircraft, IEntity pilot, vector pad)
|
||||
{
|
||||
array<IEntity> excluded = {};
|
||||
if (!Hierarchy(aircraft, excluded) || !Hierarchy(pilot, excluded)) return false;
|
||||
for (int edge = 0; edge < 4; edge++)
|
||||
{
|
||||
float side = edge * Math.PI / 2;
|
||||
vector point = pad + Vector(Math.Cos(side) * 14, 0, Math.Sin(side) * 14);
|
||||
if (!(Math.AbsFloat(SCR_TerrainHelper.GetTerrainY(point) - pad[1]) <= 0.8)) return false;
|
||||
}
|
||||
// Same four finite diagonal cylinder traces as the native fixture, with
|
||||
// exclusions restricted to this already-existing aircraft and seated pilot.
|
||||
array<vector> diagonals = {"40 2 0", "-40 2 0", "0 2 40", "0 2 -40"};
|
||||
foreach (vector diagonal : diagonals)
|
||||
{
|
||||
TraceParam trace = new TraceParam();
|
||||
trace.Start = pad + "0 2.5 0" + diagonal;
|
||||
trace.End = pad + "0 2.5 0" - diagonal;
|
||||
trace.Flags = TraceFlags.ENTS; trace.ExcludeArray = excluded;
|
||||
if (!(aircraft.GetWorld().TraceMove(trace, null) >= 1)) return false;
|
||||
}
|
||||
TraceBox clearance = new TraceBox();
|
||||
clearance.Start = pad + "0 3 0"; clearance.End = pad + "0 60 0";
|
||||
clearance.Mins = "-40 -1 -40"; clearance.Maxs = "40 1 40";
|
||||
clearance.Flags = TraceFlags.ENTS | TraceFlags.WORLD; clearance.ExcludeArray = excluded;
|
||||
return aircraft.GetWorld().TraceMove(clearance, null) >= 1;
|
||||
}
|
||||
static ERR_AircraftFlightEnvelope Qualify(ERR_GameMode mode, Vehicle aircraft, IEntity pilot)
|
||||
{
|
||||
if (!GetGame() || !GetGame().GetWorld() || !GetGame().GetPlayerManager()) return null;
|
||||
if (!Replication.IsServer() || !mode || mode != ERR_GameMode.Get() || !mode.Active() || !aircraft || !pilot || ERR_Actors.Player(pilot)) return null;
|
||||
if (CompartmentAccessComponent.GetVehicleIn(pilot) != aircraft || !ERR_Actors.Alive(aircraft) || !ERR_Actors.CanFight(pilot)) return null;
|
||||
HelicopterControllerComponent controller = HelicopterControllerComponent.Cast(aircraft.FindComponent(HelicopterControllerComponent));
|
||||
if (!controller || controller.IsEngineOn() || !controller.GetSimulation() || !controller.GetSimulation().IsValid()) return null;
|
||||
PilotCompartmentSlot seat = controller.GetPilotCompartmentSlot();
|
||||
if (!seat || seat.GetOccupant() != pilot) return null;
|
||||
if (!ERR_CasualtyLoadingRules.AircraftReady(aircraft)) return null;
|
||||
vector pad = ERR_Actors.Ground(aircraft.GetOrigin());
|
||||
if (!(pad[1] >= 2) || !ClearColumn(aircraft, pilot, pad)) return null;
|
||||
vector origin; float radius;
|
||||
if (!ERR_AircraftFlightFootprint.Measure(aircraft, controller.GetSimulation(), pad, origin, radius)) return null;
|
||||
ERR_AircraftFlightEnvelope record = new ERR_AircraftFlightEnvelope();
|
||||
record.m_Mode = mode; record.m_Aircraft = aircraft; record.m_Pilot = pilot;
|
||||
record.m_Home = origin; record.m_Radius = radius;
|
||||
record.m_QualifiedAt = GetGame().GetWorld().GetWorldTime();
|
||||
return record;
|
||||
}
|
||||
}
|
||||
+391
@@ -0,0 +1,391 @@
|
||||
class ERR_HQUnit
|
||||
{
|
||||
int Id;
|
||||
SCR_AIGroup Group;
|
||||
SCR_AIGroupUtilityComponent Utility;
|
||||
IEntity Radio;
|
||||
float NextReport;
|
||||
ref ERR_HQRoute Uplink;
|
||||
ref ERR_HQReport PendingReport;
|
||||
ref ERR_HQRoute Downlink;
|
||||
ref ERR_HQReport PendingOrder;
|
||||
ref ERR_HQReport Assignment;
|
||||
AIWaypoint Waypoint;
|
||||
bool ReservedForSupport;
|
||||
int LogisticsReservation;
|
||||
ref ERR_MortarSupplyStatus PendingSupplyStatus;
|
||||
|
||||
bool Eligible(ERR_GameMode mode)
|
||||
{
|
||||
if (ReservedForSupport || LogisticsReservation > 0 || !Group || !Utility || Group.GetPlayerCount(true) > 0 || Group.IsSlave() || Group.GetFaction() != mode.EnemyFaction()) return false;
|
||||
array<AIAgent> agents = {};
|
||||
Group.GetAgents(agents);
|
||||
bool survivor;
|
||||
foreach (AIAgent agent : agents)
|
||||
{
|
||||
if (!agent || !ERR_Actors.CanFight(agent.GetControlledEntity())) continue;
|
||||
survivor = true;
|
||||
if (CompartmentAccessComponent.GetVehicleIn(agent.GetControlledEntity())) return false;
|
||||
}
|
||||
return survivor;
|
||||
}
|
||||
|
||||
void Release()
|
||||
{
|
||||
if (Waypoint)
|
||||
{
|
||||
if (Group) Group.RemoveWaypoint(Waypoint);
|
||||
SCR_EntityHelper.DeleteEntityAndChildren(Waypoint);
|
||||
}
|
||||
Waypoint = null;
|
||||
Assignment = null;
|
||||
PendingOrder = null;
|
||||
Downlink = null;
|
||||
}
|
||||
}
|
||||
|
||||
class ERR_HQ : ERR_CommandService
|
||||
{
|
||||
protected ERR_GameMode m_Mode;
|
||||
protected ref ERR_MortarSystem m_Mortars;
|
||||
protected ref ERR_MortarLogistics m_Logistics;
|
||||
protected ref array<ref ERR_HQUnit> m_Units = {};
|
||||
protected ref array<ref ERR_HQReport> m_Contacts = {};
|
||||
protected ref ERR_HQNetwork m_Network = new ERR_HQNetwork();
|
||||
protected ref ERR_HQJournal m_Journal = new ERR_HQJournal();
|
||||
protected int m_NextId;
|
||||
protected float m_NextNetwork;
|
||||
protected float m_NextAssignment;
|
||||
|
||||
void ERR_HQ(ERR_GameMode mode) { m_Mode = mode; m_Mortars = new ERR_MortarSystem(this); m_Logistics = new ERR_MortarLogistics(this, mode); }
|
||||
static ERR_HQ Get()
|
||||
{
|
||||
ERR_GameMode mode = ERR_GameMode.Get();
|
||||
if (!mode) return null;
|
||||
return ERR_HQ.Cast(mode.Command());
|
||||
}
|
||||
static bool MissionActive()
|
||||
{
|
||||
ERR_GameMode mode = ERR_GameMode.Get();
|
||||
return mode && mode.Active();
|
||||
}
|
||||
static void Event(string kind, string message, int messageId = 0)
|
||||
{
|
||||
ERR_HQ hq = Get();
|
||||
if (hq) hq.Journal().Write(kind, message, messageId);
|
||||
}
|
||||
ERR_MortarSystem Mortars() { return m_Mortars; }
|
||||
bool PrepareSupport() { return m_Mortars.Prepare() && m_Logistics.Prepare(); }
|
||||
ERR_MortarLogistics Logistics() { return m_Logistics; }
|
||||
void ReserveGroup(SCR_AIGroup group)
|
||||
{
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
{
|
||||
if (unit.Group != group) continue;
|
||||
unit.Release();
|
||||
unit.ReservedForSupport = true;
|
||||
}
|
||||
}
|
||||
// Temporary reservations never alter the permanent support reservation.
|
||||
int LogisticsToken(SCR_AIGroup group)
|
||||
{
|
||||
foreach (ERR_HQUnit unit : m_Units) if (unit.Group == group) return unit.LogisticsReservation;
|
||||
return 0;
|
||||
}
|
||||
bool ReserveLogistics(SCR_AIGroup group, int token)
|
||||
{
|
||||
if (!Replication.IsServer() || token <= 0 || !MissionActive()) return false;
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
{
|
||||
if (unit.Group != group || !unit.Eligible(m_Mode) || !unit.Radio || unit.Assignment || unit.PendingOrder || unit.Downlink || unit.Waypoint) continue;
|
||||
if (unit.Utility.m_Perception && !unit.Utility.m_Perception.m_aTargets.IsEmpty()) continue;
|
||||
unit.LogisticsReservation = token;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void ReleaseLogistics(SCR_AIGroup group, int token)
|
||||
{
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
if (unit.Group == group && unit.LogisticsReservation == token) unit.LogisticsReservation = 0;
|
||||
}
|
||||
bool QueueSupplyStatus(ERR_MortarBattery battery, float now)
|
||||
{
|
||||
if (!Replication.IsServer() || !MissionActive() || !battery || !battery.StartupActivated || !battery.Operational() || !m_Logistics.CanSample(battery) || !ERR_MortarLogisticsRules.Need(battery.CountShells())) return false;
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
{
|
||||
if (unit.Group != battery.Group || unit.Uplink || !unit.Radio || unit.Radio != battery.RadioOperator) continue;
|
||||
ERR_HQRoute route = m_Network.Route(unit.Radio);
|
||||
if (!route) return false;
|
||||
ERR_MortarSupplyStatus report = new ERR_MortarSupplyStatus();
|
||||
report.Group = battery.Group; report.Gun = battery.Gun; report.Position = battery.Gun.GetOrigin();
|
||||
report.Shells = battery.CountShells(); report.ObservedAt = now; report.MessageId = m_Journal.Message();
|
||||
route.MessageId = report.MessageId;
|
||||
route.Payload = string.Format("mortar supply status; usable HE=%1; position=%2; sampled=%3", report.Shells, report.Position, report.ObservedAt);
|
||||
unit.PendingSupplyStatus = report; unit.Uplink = route;
|
||||
m_Journal.Write("SUPPLY REPORT", route.Payload, route.MessageId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void PhysicalForces(out array<IEntity> entities)
|
||||
{
|
||||
entities.Clear();
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
{
|
||||
if (!unit.Group) continue;
|
||||
array<AIAgent> agents = {};
|
||||
unit.Group.GetAgents(agents);
|
||||
foreach (AIAgent agent : agents)
|
||||
{
|
||||
if (!agent || !agent.GetControlledEntity()) continue;
|
||||
IEntity actor = agent.GetControlledEntity();
|
||||
if (!entities.Contains(actor)) entities.Insert(actor);
|
||||
IEntity vehicle = CompartmentAccessComponent.GetVehicleIn(actor);
|
||||
if (vehicle && !entities.Contains(vehicle)) entities.Insert(vehicle);
|
||||
}
|
||||
}
|
||||
m_Mortars.AppendGuns(entities);
|
||||
}
|
||||
bool MortarConnected(SCR_AIGroup group)
|
||||
{
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
if (unit.Group == group && unit.Radio) return m_Network.Route(unit.Radio) != null;
|
||||
return false;
|
||||
}
|
||||
ERR_HQRoute Downlink(SCR_AIGroup group, string kind, string detail, int sourceId)
|
||||
{
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
{
|
||||
if (unit.Group != group || !unit.Radio) continue;
|
||||
ERR_HQRoute route = m_Network.Route(unit.Radio, true);
|
||||
if (!route) return null;
|
||||
route.MessageId = m_Journal.Message();
|
||||
route.Payload = string.Format("%1 from report M%2; %3", kind, sourceId, detail);
|
||||
m_Journal.Write("ORDER QUEUED", route.Payload, route.MessageId);
|
||||
return route;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
void SupportContacts(out array<ref ERR_HQReport> contacts, float now)
|
||||
{
|
||||
contacts.Clear();
|
||||
foreach (ERR_HQReport contact : m_Contacts)
|
||||
if (ERR_MortarPrediction.Fresh(contact, now)) contacts.Insert(contact.Snapshot());
|
||||
}
|
||||
|
||||
ERR_HQJournal Journal() { return m_Journal; }
|
||||
string Status()
|
||||
{
|
||||
return string.Format("HQ: %1 groups, %2 delivered contacts, %3 assignments", m_Units.Count(), m_Contacts.Count(), Assigned()) + "; " + m_Mortars.Status() + "; " + m_Logistics.Status();
|
||||
}
|
||||
|
||||
override void RegisterGroup(SCR_AIGroup group)
|
||||
{
|
||||
if (!Replication.IsServer() || !group || m_Units.Count() >= 128) return;
|
||||
foreach (ERR_HQUnit known : m_Units) if (known.Group == group) return;
|
||||
SCR_AIGroupUtilityComponent utility = SCR_AIGroupUtilityComponent.Cast(group.FindComponent(SCR_AIGroupUtilityComponent));
|
||||
if (!utility || group.GetFaction() != m_Mode.EnemyFaction()) return;
|
||||
ERR_HQUnit unit = new ERR_HQUnit();
|
||||
unit.Id = ++m_NextId;
|
||||
unit.Group = group;
|
||||
unit.Utility = utility;
|
||||
int phase = unit.Id % 5;
|
||||
unit.NextReport = GetGame().GetWorld().GetWorldTime() + phase * 1000;
|
||||
m_Units.Insert(unit);
|
||||
m_Journal.Write("REGISTER", string.Format("group %1", unit.Id));
|
||||
}
|
||||
|
||||
override void UnregisterGroup(SCR_AIGroup group)
|
||||
{
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
{
|
||||
if (unit.Group != group) continue;
|
||||
unit.Release();
|
||||
unit.Group = null;
|
||||
unit.Utility = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected int Assigned()
|
||||
{
|
||||
int total;
|
||||
foreach (ERR_HQUnit unit : m_Units) if (unit.Assignment || unit.PendingOrder) total++;
|
||||
return total;
|
||||
}
|
||||
|
||||
protected void IndexRadios()
|
||||
{
|
||||
m_Network.Clear();
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
{
|
||||
unit.Radio = null;
|
||||
if (!unit.Group || unit.Group.GetPlayerCount(true) > 0 || unit.Group.GetFaction() != m_Mode.EnemyFaction()) continue;
|
||||
array<AIAgent> agents = {};
|
||||
unit.Group.GetAgents(agents);
|
||||
int best;
|
||||
foreach (AIAgent agent : agents)
|
||||
{
|
||||
if (!agent) continue;
|
||||
IEntity actor = agent.GetControlledEntity();
|
||||
int capability = ERR_HQRadio.Capability(actor);
|
||||
if (capability <= 0) continue;
|
||||
m_Network.Add(actor, unit.Group, unit.Id, capability);
|
||||
if (capability > best) { unit.Radio = actor; best = capability; }
|
||||
}
|
||||
}
|
||||
m_Network.Build();
|
||||
}
|
||||
|
||||
protected void Observe(ERR_HQUnit unit, float now)
|
||||
{
|
||||
if (!unit.Utility || !unit.Radio || unit.Uplink || now < unit.NextReport) return;
|
||||
unit.NextReport = now + m_Mode.Settings().Get("reportIntervalSeconds") * 1000;
|
||||
array<ref ERR_HQVisualContact> samples = {};
|
||||
ERR_HQObservation.Gather(unit.Utility, now, samples);
|
||||
if (samples.IsEmpty()) return;
|
||||
ERR_HQVisualContact seen = samples[0];
|
||||
ERR_HQRoute route = m_Network.Route(unit.Radio);
|
||||
if (!route) return;
|
||||
ERR_HQReport report = new ERR_HQReport();
|
||||
report.MessageId = m_Journal.Message();
|
||||
report.Target = seen.Entity;
|
||||
report.Source = unit.Group;
|
||||
report.Position = seen.Position;
|
||||
report.ObservedAt = seen.ObservedAt;
|
||||
ERR_HQObservation.Describe(report, samples, now);
|
||||
route.MessageId = report.MessageId;
|
||||
route.Payload = report.Description(now);
|
||||
unit.PendingReport = report;
|
||||
unit.Uplink = route;
|
||||
m_Journal.Write("REPORT", route.Payload, report.MessageId);
|
||||
}
|
||||
|
||||
protected void Accept(ERR_HQReport report, float now)
|
||||
{
|
||||
if (!report || !ERR_MissionRules.ContactFresh(report.ObservedAt, now)) return;
|
||||
for (int i = 0; i < m_Contacts.Count(); i++)
|
||||
{
|
||||
ERR_HQReport previous = m_Contacts[i];
|
||||
if (previous.Target != report.Target) continue;
|
||||
if (previous.ObservedAt >= report.ObservedAt) return;
|
||||
float seconds = (report.ObservedAt - previous.ObservedAt) / 1000;
|
||||
if (seconds > 0 && seconds < 30 && vector.DistanceXZ(previous.Position, report.Position) < 500)
|
||||
{
|
||||
report.Velocity = (report.Position - previous.Position) / seconds;
|
||||
report.HasVelocity = report.Velocity.Length() <= 100;
|
||||
}
|
||||
m_Contacts[i] = report.Snapshot();
|
||||
return;
|
||||
}
|
||||
if (m_Contacts.Count() >= 64) m_Contacts.Remove(0);
|
||||
m_Contacts.Insert(report.Snapshot());
|
||||
}
|
||||
|
||||
protected void DeliverOrder(ERR_HQUnit unit, float now)
|
||||
{
|
||||
ERR_HQReport order = unit.PendingOrder;
|
||||
if (!order || !ERR_MissionRules.ContactFresh(order.ObservedAt, now) || !unit.Eligible(m_Mode)) { unit.Release(); return; }
|
||||
SCR_AIGroupPerception perception = unit.Utility.m_Perception;
|
||||
if (perception && !perception.m_aTargets.IsEmpty()) { unit.Release(); return; }
|
||||
int deliveryId;
|
||||
if (unit.Downlink) deliveryId = unit.Downlink.MessageId;
|
||||
unit.Release();
|
||||
vector position = order.Position;
|
||||
if (order.HasVelocity) position = ERR_HQReport.Predict(position, order.Velocity, (now - order.ObservedAt) / 1000);
|
||||
AIWaypoint waypoint = AIWaypoint.Cast(ERR_Actors.Spawn(ERR_Resources.SearchWaypoint(), ERR_Actors.Ground(position)));
|
||||
if (!waypoint) { m_Journal.Write("ORDER FAILED", "waypoint spawn failed", order.MessageId); return; }
|
||||
unit.Waypoint = waypoint;
|
||||
unit.Assignment = order;
|
||||
unit.Group.AddWaypointAt(waypoint, 0);
|
||||
m_Journal.Write("ORDER DELIVERED", string.Format("group %1 from report M%2: %3", unit.Id, order.MessageId, order.Description(now)), deliveryId);
|
||||
}
|
||||
|
||||
protected void Assign(float now)
|
||||
{
|
||||
int limit = m_Mode.Settings().Get("supportSquadLimit");
|
||||
int radius = m_Mode.Settings().Get("responseRadiusMetres");
|
||||
int assigned = Assigned();
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
{
|
||||
if (unit.Assignment && (!ERR_MissionRules.ContactFresh(unit.Assignment.ObservedAt, now) || !unit.Eligible(m_Mode) || assigned > limit))
|
||||
{
|
||||
unit.Release(); assigned--;
|
||||
}
|
||||
if (unit.PendingOrder && (!ERR_MissionRules.ContactFresh(unit.PendingOrder.ObservedAt, now) || !unit.Eligible(m_Mode) || assigned > limit))
|
||||
{
|
||||
unit.Release(); assigned--;
|
||||
}
|
||||
}
|
||||
foreach (ERR_HQReport report : m_Contacts)
|
||||
{
|
||||
if (!ERR_MissionRules.ContactFresh(report.ObservedAt, now)) continue;
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
{
|
||||
if (assigned >= limit) return;
|
||||
if (unit.Assignment || unit.PendingOrder || !unit.Eligible(m_Mode) || !unit.Radio) continue;
|
||||
if (vector.DistanceXZ(unit.Group.GetOrigin(), report.Position) > radius) continue;
|
||||
SCR_AIGroupPerception perception = unit.Utility.m_Perception;
|
||||
if (perception && !perception.m_aTargets.IsEmpty()) continue;
|
||||
ERR_HQRoute route = m_Network.Route(unit.Radio, true);
|
||||
if (!route) continue;
|
||||
unit.PendingOrder = report.Snapshot();
|
||||
unit.Downlink = route;
|
||||
route.MessageId = m_Journal.Message();
|
||||
route.Payload = "order from report M" + report.MessageId.ToString();
|
||||
assigned++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override void Tick(float now)
|
||||
{
|
||||
if (!Replication.IsServer()) return;
|
||||
if (now >= m_NextNetwork) { IndexRadios(); m_NextNetwork = now + 5000; }
|
||||
for (int i = m_Contacts.Count() - 1; i >= 0; i--)
|
||||
if (!ERR_MissionRules.ContactFresh(m_Contacts[i].ObservedAt, now)) m_Contacts.Remove(i);
|
||||
for (int i = m_Units.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
ERR_HQUnit unit = m_Units[i];
|
||||
if (unit.Uplink)
|
||||
{
|
||||
int delivered = unit.Uplink.Advance(now, m_Journal);
|
||||
if (delivered == 1)
|
||||
{
|
||||
if (unit.PendingSupplyStatus) m_Logistics.Receive(unit.PendingSupplyStatus, now);
|
||||
else Accept(unit.PendingReport, now);
|
||||
}
|
||||
if (delivered != 0) { unit.Uplink = null; unit.PendingReport = null; unit.PendingSupplyStatus = null; }
|
||||
}
|
||||
if (unit.Downlink)
|
||||
{
|
||||
int delivered = unit.Downlink.Advance(now, m_Journal);
|
||||
if (delivered == 1) DeliverOrder(unit, now);
|
||||
else if (delivered < 0) unit.Release();
|
||||
}
|
||||
if (!unit.Group && !unit.Uplink) { unit.Release(); m_Units.Remove(i); continue; }
|
||||
Observe(unit, now);
|
||||
}
|
||||
if (now >= m_NextAssignment) { Assign(now); m_NextAssignment = now + 5000; }
|
||||
m_Logistics.Tick(now);
|
||||
m_Mortars.Tick(now);
|
||||
m_Journal.Flush();
|
||||
}
|
||||
|
||||
override void Stop()
|
||||
{
|
||||
m_Logistics.Stop();
|
||||
m_Mortars.Stop();
|
||||
foreach (ERR_HQUnit unit : m_Units) unit.Release();
|
||||
m_Units.Clear();
|
||||
m_Contacts.Clear();
|
||||
m_Network.Clear();
|
||||
m_Journal.Write("STOP", "HQ stopped; tactical orders released");
|
||||
m_Journal.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
modded class ERR_GameMode
|
||||
{
|
||||
protected override ERR_CommandService CreateCommandService() { return new ERR_HQ(this); }
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
// Server-owned audit stream. The file contains every event; chat reads a bounded
|
||||
// recent-history window with explicit gap notices for clients that fall behind.
|
||||
class ERR_HQJournal
|
||||
{
|
||||
protected ref array<string> m_Recent = {};
|
||||
protected ref array<string> m_Pending = {};
|
||||
protected int m_Sequence;
|
||||
protected int m_Message;
|
||||
protected static int s_Part;
|
||||
protected static int s_Session;
|
||||
protected string m_Session;
|
||||
|
||||
void ERR_HQJournal()
|
||||
{
|
||||
s_Session++;
|
||||
m_Session = string.Format("%1-%2", System.GetUnixTime(), s_Session);
|
||||
}
|
||||
protected float m_RetryAt;
|
||||
protected int m_Lost;
|
||||
|
||||
string Path()
|
||||
{
|
||||
if (s_Part == 0)
|
||||
return "$logs:rereintroduced-hq.log";
|
||||
return "$logs:rereintroduced-hq-" + s_Part.ToString() + ".log";
|
||||
}
|
||||
|
||||
int Message()
|
||||
{
|
||||
return ++m_Message;
|
||||
}
|
||||
|
||||
int Sequence()
|
||||
{
|
||||
return m_Sequence;
|
||||
}
|
||||
|
||||
void Write(string kind, string message, int messageId = 0)
|
||||
{
|
||||
if (!Replication.IsServer())
|
||||
return;
|
||||
float seconds = GetGame().GetWorld().GetWorldTime() / 1000;
|
||||
string line = string.Format("#%1 t=%2s M%3 %4 %5", ++m_Sequence, Math.Round(seconds * 10) / 10.0, messageId, kind, message) + " [session=" + m_Session + "]";
|
||||
if (m_Recent.Count() >= 300)
|
||||
m_Recent.Remove(0);
|
||||
m_Recent.Insert(line);
|
||||
if (m_Pending.Count() < 4096)
|
||||
m_Pending.Insert(line);
|
||||
else
|
||||
m_Lost++;
|
||||
}
|
||||
|
||||
void Read(int after, out array<string> lines, out int cursor, int limit = 8)
|
||||
{
|
||||
lines.Clear();
|
||||
cursor = Math.Clamp(after, 0, m_Sequence);
|
||||
int first = m_Sequence - m_Recent.Count();
|
||||
if (cursor < first)
|
||||
{
|
||||
lines.Insert(string.Format("HQ log: %1 older entries are outside chat history; see %2", first - cursor, Path()));
|
||||
cursor = first;
|
||||
}
|
||||
int characters;
|
||||
for (int i = cursor - first; i < m_Recent.Count() && lines.Count() < Math.Clamp(limit, 1, 40); i++)
|
||||
{
|
||||
// Bound each menu RPC without skipping unread records. Chat still
|
||||
// requests eight; the live menu can drain a busy journal faster.
|
||||
if (characters + m_Recent[i].Length() > 16000 && !lines.IsEmpty())
|
||||
break;
|
||||
lines.Insert(m_Recent[i]);
|
||||
characters += m_Recent[i].Length();
|
||||
cursor++;
|
||||
}
|
||||
}
|
||||
|
||||
void Flush()
|
||||
{
|
||||
if (m_Pending.IsEmpty() || !Replication.IsServer())
|
||||
return;
|
||||
float now = GetGame().GetWorld().GetWorldTime();
|
||||
if (now < m_RetryAt)
|
||||
return;
|
||||
FileHandle file = FileIO.OpenFile(Path(), FileMode.APPEND);
|
||||
if (!file)
|
||||
{
|
||||
m_RetryAt = now + 30000;
|
||||
PrintFormat("Escapists AI [HQ log]: cannot open %1; retry in 30s, chat history remains available", Path(), level: LogLevel.ERROR);
|
||||
return;
|
||||
}
|
||||
foreach (string line : m_Pending)
|
||||
file.WriteLine(line);
|
||||
if (m_Lost > 0)
|
||||
file.WriteLine(string.Format("HQ log: %1 entries lost while file was unavailable and buffer full", m_Lost));
|
||||
bool rotate = file.GetLength() >= 5242880;
|
||||
file.Close();
|
||||
m_Pending.Clear();
|
||||
m_Lost = 0;
|
||||
if (rotate)
|
||||
s_Part++;
|
||||
}
|
||||
}
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
// Radio propagation is simulated using actual living carriers and inventory.
|
||||
// A manpack is the HQ uplink; short-range hops are limited to 500 m and three
|
||||
// retransmissions. No virtual relay is fabricated when the network is broken.
|
||||
class ERR_HQRadioNode
|
||||
{
|
||||
IEntity Carrier;
|
||||
SCR_AIGroup Group;
|
||||
int UnitId;
|
||||
int Capability;
|
||||
protected float m_CheckedAt = -1;
|
||||
protected int m_LiveCapability;
|
||||
|
||||
bool Valid(bool manpack = false)
|
||||
{
|
||||
ERR_GameMode factions = ERR_GameMode.Get();
|
||||
if (!Carrier || !Group || Group.GetPlayerCount(true) > 0 || !factions || Group.GetFaction() != factions.EnemyFaction() || ERR_Actors.Group(Carrier) != Group)
|
||||
return false;
|
||||
int required = 1;
|
||||
if (manpack)
|
||||
required = 2;
|
||||
float now = GetGame().GetWorld().GetWorldTime();
|
||||
if (m_CheckedAt < 0 || now < m_CheckedAt || now - m_CheckedAt >= 1000)
|
||||
{
|
||||
m_CheckedAt = now;
|
||||
m_LiveCapability = ERR_HQRadio.Capability(Carrier);
|
||||
}
|
||||
return m_LiveCapability >= required;
|
||||
}
|
||||
|
||||
string Label()
|
||||
{
|
||||
return "unit " + UnitId.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
class ERR_HQRoute
|
||||
{
|
||||
ref array<ref ERR_HQRadioNode> Nodes = {};
|
||||
bool Downlink;
|
||||
int Cursor;
|
||||
bool Sending;
|
||||
float Due;
|
||||
vector SentFrom;
|
||||
float StartedAt = -1;
|
||||
int MessageId;
|
||||
string Payload;
|
||||
bool Failed;
|
||||
|
||||
// -1 failed, 0 travelling, 1 delivered. Each hop really gates delivery.
|
||||
int Advance(float now, ERR_HQJournal journal)
|
||||
{
|
||||
if (Failed)
|
||||
return -1;
|
||||
if (StartedAt < 0)
|
||||
{
|
||||
StartedAt = now;
|
||||
if (Downlink)
|
||||
Cursor = -1; // first leg is HQ to the manpack
|
||||
}
|
||||
if (now - StartedAt > 30000)
|
||||
return Fail(journal, "route timed out");
|
||||
int target = Cursor + 1;
|
||||
if (Sending)
|
||||
{
|
||||
if (now < Due)
|
||||
return 0;
|
||||
if (target < Nodes.Count())
|
||||
{
|
||||
if (!Nodes[target].Valid(Downlink && target == 0))
|
||||
return Fail(journal, "receiving carrier lost radio, died or changed ownership");
|
||||
if (Cursor >= 0 && vector.DistanceXZ(SentFrom, Nodes[target].Carrier.GetOrigin()) > 500)
|
||||
return Fail(journal, "receiver moved beyond the 500m link");
|
||||
journal.Write("RECEIVE", Nodes[target].Label() + " received from " + SenderLabel(), MessageId);
|
||||
}
|
||||
else
|
||||
journal.Write("RECEIVE", "HQ received from " + SenderLabel(), MessageId);
|
||||
Cursor++;
|
||||
Sending = false;
|
||||
if ((!Downlink && Cursor >= Nodes.Count()) || (Downlink && Cursor == Nodes.Count() - 1))
|
||||
return 1;
|
||||
target = Cursor + 1;
|
||||
}
|
||||
if (Nodes.IsEmpty())
|
||||
return Fail(journal, "no physical radio route");
|
||||
if (Cursor >= 0 && !Nodes[Cursor].Valid(!Downlink && Cursor == Nodes.Count() - 1))
|
||||
return Fail(journal, "transmitting carrier lost radio, died or changed ownership");
|
||||
string recipient = "HQ";
|
||||
string link = "long-range";
|
||||
if (target < Nodes.Count())
|
||||
{
|
||||
if (!Nodes[target].Valid(Downlink && target == 0))
|
||||
return Fail(journal, "next relay unavailable");
|
||||
recipient = Nodes[target].Label();
|
||||
if (Cursor >= 0)
|
||||
{
|
||||
SentFrom = Nodes[Cursor].Carrier.GetOrigin();
|
||||
float distance = vector.DistanceXZ(SentFrom, Nodes[target].Carrier.GetOrigin());
|
||||
if (distance > 500)
|
||||
return Fail(journal, "next relay beyond 500m");
|
||||
link = Math.Round(distance).ToString() + "m short-range";
|
||||
}
|
||||
}
|
||||
journal.Write("TRANSMIT", SenderLabel() + " -> " + recipient + " (" + link + ")", MessageId);
|
||||
Sending = true;
|
||||
Due = now + 2000;
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected string SenderLabel()
|
||||
{
|
||||
if (Cursor < 0)
|
||||
return "HQ";
|
||||
return Nodes[Cursor].Label();
|
||||
}
|
||||
|
||||
protected int Fail(ERR_HQJournal journal, string reason)
|
||||
{
|
||||
Failed = true;
|
||||
journal.Write("LOST", reason + "; " + Payload, MessageId);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
class ERR_HQNetwork
|
||||
{
|
||||
protected ref array<ref ERR_HQRadioNode> m_Nodes = {};
|
||||
protected ref array<int> m_Parents = {};
|
||||
protected ref array<int> m_Depths = {};
|
||||
protected ref map<IEntity, int> m_Indices = new map<IEntity, int>();
|
||||
protected ref map<string, ref array<int>> m_Cells = new map<string, ref array<int>>();
|
||||
bool Overflow;
|
||||
|
||||
void Clear()
|
||||
{
|
||||
m_Nodes.Clear();
|
||||
m_Parents.Clear();
|
||||
m_Depths.Clear();
|
||||
m_Indices.Clear();
|
||||
m_Cells.Clear();
|
||||
Overflow = false;
|
||||
}
|
||||
|
||||
protected string Cell(int x, int z)
|
||||
{
|
||||
return string.Format("%1:%2", x, z);
|
||||
}
|
||||
|
||||
void Add(IEntity carrier, SCR_AIGroup group, int unitId, int capability)
|
||||
{
|
||||
if (m_Indices.Contains(carrier))
|
||||
return;
|
||||
if (m_Nodes.Count() >= 2048)
|
||||
{
|
||||
Overflow = true;
|
||||
return;
|
||||
}
|
||||
ERR_HQRadioNode node = new ERR_HQRadioNode();
|
||||
node.Carrier = carrier;
|
||||
node.Group = group;
|
||||
node.UnitId = unitId;
|
||||
node.Capability = capability;
|
||||
int index = m_Nodes.Count();
|
||||
m_Nodes.Insert(node);
|
||||
m_Indices.Insert(carrier, index);
|
||||
m_Parents.Insert(-2);
|
||||
m_Depths.Insert(99);
|
||||
vector position = carrier.GetOrigin();
|
||||
string key = Cell(Math.Floor(position[0] / 500), Math.Floor(position[2] / 500));
|
||||
array<int> cell = m_Cells.Get(key);
|
||||
if (!cell)
|
||||
{
|
||||
cell = {};
|
||||
m_Cells.Insert(key, cell);
|
||||
}
|
||||
cell.Insert(index);
|
||||
}
|
||||
|
||||
void Build()
|
||||
{
|
||||
array<int> queue = {};
|
||||
int checks;
|
||||
for (int root = 0; root < m_Nodes.Count(); root++)
|
||||
{
|
||||
if (m_Nodes[root].Capability == 2)
|
||||
{
|
||||
m_Parents[root] = -1;
|
||||
m_Depths[root] = 0;
|
||||
queue.Insert(root);
|
||||
}
|
||||
}
|
||||
for (int head = 0; head < queue.Count(); head++)
|
||||
{
|
||||
int from = queue[head];
|
||||
if (m_Depths[from] >= 3)
|
||||
continue;
|
||||
vector position = m_Nodes[from].Carrier.GetOrigin();
|
||||
int x = Math.Floor(position[0] / 500);
|
||||
int z = Math.Floor(position[2] / 500);
|
||||
for (int dx = -1; dx <= 1; dx++)
|
||||
{
|
||||
for (int dz = -1; dz <= 1; dz++)
|
||||
{
|
||||
array<int> neighbors = m_Cells.Get(Cell(x + dx, z + dz));
|
||||
if (!neighbors)
|
||||
continue;
|
||||
foreach (int candidate : neighbors)
|
||||
{
|
||||
if (++checks > 100000)
|
||||
{
|
||||
Overflow = true;
|
||||
return;
|
||||
}
|
||||
if (m_Parents[candidate] != -2 || vector.DistanceXZ(position, m_Nodes[candidate].Carrier.GetOrigin()) > 500)
|
||||
continue;
|
||||
m_Parents[candidate] = from;
|
||||
m_Depths[candidate] = m_Depths[from] + 1;
|
||||
queue.Insert(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ERR_HQRoute Route(IEntity caller, bool downlink = false)
|
||||
{
|
||||
int index;
|
||||
if (!m_Indices.Find(caller, index) || m_Parents[index] == -2)
|
||||
return null;
|
||||
ERR_HQRoute route = new ERR_HQRoute();
|
||||
route.Downlink = downlink;
|
||||
while (index >= 0)
|
||||
{
|
||||
if (downlink)
|
||||
route.Nodes.InsertAt(m_Nodes[index], 0);
|
||||
else
|
||||
route.Nodes.Insert(m_Nodes[index]);
|
||||
index = m_Parents[index];
|
||||
}
|
||||
return route;
|
||||
}
|
||||
}
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
// These samples exist only at intake. HQ never revisits their entity loadouts.
|
||||
class ERR_HQVisualContact
|
||||
{
|
||||
IEntity Entity;
|
||||
vector Position;
|
||||
float ObservedAt;
|
||||
bool Escapist;
|
||||
string Travel = "unknown";
|
||||
string Weapon = "unknown";
|
||||
}
|
||||
|
||||
class ERR_HQObservation
|
||||
{
|
||||
// Require current native line of sight before reading visible equipment.
|
||||
static bool IsClearSighting(float age, float traceFraction, float exposure)
|
||||
{
|
||||
return age >= 0 && age <= 0.5 && traceFraction >= 1 && exposure > 0;
|
||||
}
|
||||
|
||||
static bool CanIdentifyWeapon(float distance, float exposure)
|
||||
{
|
||||
return distance >= 0 && distance <= 150 && exposure >= 0.5;
|
||||
}
|
||||
|
||||
static string WeaponCategory(EWeaponType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case EWeaponType.WT_HANDGUN: return "pistol";
|
||||
case EWeaponType.WT_RIFLE: return "rifle";
|
||||
case EWeaponType.WT_SNIPERRIFLE: return "sniper rifle";
|
||||
case EWeaponType.WT_MACHINEGUN: return "machine gun";
|
||||
case EWeaponType.WT_ROCKETLAUNCHER: return "rocket launcher";
|
||||
case EWeaponType.WT_GRENADELAUNCHER: return "grenade launcher";
|
||||
case EWeaponType.WT_FRAGGRENADE: return "grenade";
|
||||
case EWeaponType.WT_SMOKEGRENADE: return "smoke grenade";
|
||||
}
|
||||
return "unidentified weapon";
|
||||
}
|
||||
|
||||
static ERR_HQVisualContact Capture(BaseTarget target, float now)
|
||||
{
|
||||
if (!target || target.GetTargetCategory() != ETargetCategory.ENEMY || !IsClearSighting(target.GetTimeSinceSeen(), target.GetTraceFraction(), target.GetExposure()))
|
||||
return null;
|
||||
DamageManagerComponent damage = target.GetDamageManagerComponent();
|
||||
if (damage && damage.IsDestroyed())
|
||||
return null;
|
||||
IEntity entity = target.GetTargetEntity();
|
||||
if (!entity)
|
||||
return null;
|
||||
ERR_HQVisualContact seen = new ERR_HQVisualContact();
|
||||
seen.Entity = entity;
|
||||
seen.Position = target.GetLastSeenPosition();
|
||||
seen.ObservedAt = now - target.GetTimeSinceSeen() * 1000;
|
||||
SCR_ChimeraCharacter character = SCR_ChimeraCharacter.Cast(entity);
|
||||
if (character)
|
||||
{
|
||||
if (GetGame().GetPlayerManager())
|
||||
seen.Escapist = GetGame().GetPlayerManager().GetPlayerIdFromControlledEntity(entity) > 0;
|
||||
seen.Travel = "on foot";
|
||||
if (CompartmentAccessComponent.GetVehicleIn(entity))
|
||||
seen.Travel = "in vehicle";
|
||||
// Read only the drawn weapon; never enumerate weapons or inventory.
|
||||
if (CanIdentifyWeapon(target.GetDistance(), target.GetExposure()))
|
||||
{
|
||||
CharacterControllerComponent controller = CharacterControllerComponent.Cast(entity.FindComponent(CharacterControllerComponent));
|
||||
if (controller)
|
||||
{
|
||||
IEntity heldItem = controller.GetCurrentItemInHands();
|
||||
BaseWeaponComponent weapon;
|
||||
if (heldItem)
|
||||
weapon = BaseWeaponComponent.Cast(heldItem.FindComponent(BaseWeaponComponent));
|
||||
seen.Weapon = "none drawn";
|
||||
if (weapon)
|
||||
seen.Weapon = WeaponCategory(weapon.GetWeaponType());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Vehicle.Cast(entity))
|
||||
{
|
||||
seen.Travel = "vehicle";
|
||||
if (target.GetUnitType() == EAIUnitType.UnitType_Aircraft)
|
||||
seen.Travel = "aircraft";
|
||||
}
|
||||
return seen;
|
||||
}
|
||||
|
||||
static void Gather(SCR_AIGroupUtilityComponent utility, float now, out array<ref ERR_HQVisualContact> samples)
|
||||
{
|
||||
samples.Clear();
|
||||
if (!utility)
|
||||
return;
|
||||
array<BaseTarget> visible = {};
|
||||
utility.ERR_HQGetVisualTargets(visible);
|
||||
foreach (BaseTarget target : visible)
|
||||
{
|
||||
ERR_HQVisualContact seen = Capture(target, now);
|
||||
if (!seen)
|
||||
continue;
|
||||
ERR_HQVisualContact existing;
|
||||
foreach (ERR_HQVisualContact sample : samples)
|
||||
{
|
||||
if (sample.Entity == seen.Entity)
|
||||
{
|
||||
existing = sample;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!existing)
|
||||
{
|
||||
if (samples.Count() < 64)
|
||||
samples.Insert(seen);
|
||||
}
|
||||
else if (seen.ObservedAt > existing.ObservedAt || (seen.ObservedAt == existing.ObservedAt && existing.Weapon == "unknown" && seen.Weapon != "unknown"))
|
||||
{
|
||||
existing.Position = seen.Position;
|
||||
existing.ObservedAt = seen.ObservedAt;
|
||||
existing.Travel = seen.Travel;
|
||||
existing.Weapon = seen.Weapon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Describe(ERR_HQReport report, array<ref ERR_HQVisualContact> samples, float now)
|
||||
{
|
||||
// New observations must not silently inherit old equipment/travel details.
|
||||
report.SeenEscapists = -1;
|
||||
report.TravelMode = "unknown";
|
||||
report.SeenWeapons = "unknown";
|
||||
report.DetailsObservedAt = -1;
|
||||
if (report.Distress)
|
||||
return;
|
||||
ERR_HQVisualContact anchor;
|
||||
foreach (ERR_HQVisualContact sample : samples)
|
||||
{
|
||||
if (sample.Entity == report.Target && now - sample.ObservedAt >= 0 && now - sample.ObservedAt <= 500)
|
||||
{
|
||||
anchor = sample;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!anchor)
|
||||
return;
|
||||
report.DetailsObservedAt = anchor.ObservedAt;
|
||||
report.TravelMode = anchor.Travel;
|
||||
report.SeenWeapons = anchor.Weapon;
|
||||
array<IEntity> counted = {};
|
||||
array<string> weapons = {};
|
||||
string travel;
|
||||
foreach (ERR_HQVisualContact sighting : samples)
|
||||
{
|
||||
if (!sighting.Escapist || !sighting.Entity || counted.Contains(sighting.Entity) || now - sighting.ObservedAt < 0 || now - sighting.ObservedAt > 500 || vector.DistanceXZ(anchor.Position, sighting.Position) > 100)
|
||||
continue;
|
||||
counted.Insert(sighting.Entity);
|
||||
if (travel.IsEmpty())
|
||||
travel = sighting.Travel;
|
||||
else if (travel != sighting.Travel)
|
||||
travel = "mixed travel";
|
||||
if (sighting.Weapon != "unknown" && !weapons.Contains(sighting.Weapon) && weapons.Count() < 4)
|
||||
weapons.Insert(sighting.Weapon);
|
||||
}
|
||||
if (counted.IsEmpty())
|
||||
return; // A visible vehicle does not reveal its hidden occupants.
|
||||
report.SeenEscapists = counted.Count();
|
||||
// A vehicle anchor remains a vehicle report, even with people near it.
|
||||
if (anchor.Escapist)
|
||||
report.TravelMode = travel;
|
||||
report.SeenWeapons = "unknown";
|
||||
if (!weapons.IsEmpty())
|
||||
{
|
||||
report.SeenWeapons = weapons[0];
|
||||
for (int i = 1; i < weapons.Count(); i++)
|
||||
report.SeenWeapons += ", " + weapons[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
modded class SCR_AIGroupUtilityComponent
|
||||
{
|
||||
void ERR_HQGetVisualTargets(out array<BaseTarget> targets)
|
||||
{
|
||||
targets.Clear();
|
||||
foreach (SCR_AIInfoComponent info : m_aInfoComponents)
|
||||
{
|
||||
if (!info || !info.m_Perception)
|
||||
continue;
|
||||
AIAgent observer = AIAgent.Cast(info.GetOwner());
|
||||
if (!observer || !ERR_Actors.CanFight(observer.GetControlledEntity()))
|
||||
continue;
|
||||
array<BaseTarget> own = {};
|
||||
info.m_Perception.GetTargetsList(own, ETargetCategory.ENEMY);
|
||||
foreach (BaseTarget target : own)
|
||||
{
|
||||
if (target && ERR_HQObservation.IsClearSighting(target.GetTimeSinceSeen(), target.GetTraceFraction(), target.GetExposure()))
|
||||
targets.Insert(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
// A radio's category is protected in the stock gadget component.
|
||||
modded class SCR_RadioComponent
|
||||
{
|
||||
bool ERR_IsHQManpack()
|
||||
{
|
||||
return m_iRadioCategory == ERadioCategory.MANPACK;
|
||||
}
|
||||
}
|
||||
|
||||
class ERR_HQRadio
|
||||
{
|
||||
// 0: no radio, 1: basic radio, 2: manpack. Inventory is checked live.
|
||||
static int Capability(IEntity character)
|
||||
{
|
||||
if (!ERR_Actors.CanFight(character))
|
||||
return 0;
|
||||
InventoryStorageManagerComponent inventory = InventoryStorageManagerComponent.Cast(character.FindComponent(InventoryStorageManagerComponent));
|
||||
if (!inventory)
|
||||
return 0;
|
||||
array<IEntity> items = {};
|
||||
inventory.GetItems(items);
|
||||
int result;
|
||||
foreach (IEntity item : items)
|
||||
{
|
||||
if (!item || !item.FindComponent(BaseRadioComponent))
|
||||
continue;
|
||||
result = 1;
|
||||
SCR_RadioComponent gadget = SCR_RadioComponent.Cast(item.FindComponent(SCR_RadioComponent));
|
||||
if (gadget && gadget.ERR_IsHQManpack())
|
||||
return 2;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// HQ uses entity identity only for matching observations, never for its origin.
|
||||
// Visible details are captured at intake and copied as scalar snapshots.
|
||||
// Reports and orders contain immutable observed coordinates and timestamps.
|
||||
class ERR_HQReport
|
||||
{
|
||||
int MessageId;
|
||||
IEntity Target;
|
||||
SCR_AIGroup Source;
|
||||
vector Position;
|
||||
vector Velocity;
|
||||
float ObservedAt = -1;
|
||||
float LastSent = -60000;
|
||||
float PerceptionStamp = -1;
|
||||
bool HasVelocity;
|
||||
bool Engaged;
|
||||
bool Distress;
|
||||
int SeenEscapists = -1;
|
||||
string TravelMode = "unknown";
|
||||
string SeenWeapons = "unknown";
|
||||
float DetailsObservedAt = -1;
|
||||
|
||||
ERR_HQReport Snapshot()
|
||||
{
|
||||
ERR_HQReport copy = new ERR_HQReport();
|
||||
copy.MessageId = MessageId;
|
||||
copy.Target = Target;
|
||||
copy.Source = Source;
|
||||
copy.Position = Position;
|
||||
copy.Velocity = Velocity;
|
||||
copy.ObservedAt = ObservedAt;
|
||||
copy.Engaged = Engaged;
|
||||
copy.Distress = Distress;
|
||||
copy.HasVelocity = HasVelocity;
|
||||
copy.SeenEscapists = SeenEscapists;
|
||||
copy.TravelMode = TravelMode;
|
||||
copy.SeenWeapons = SeenWeapons;
|
||||
copy.DetailsObservedAt = DetailsObservedAt;
|
||||
return copy;
|
||||
}
|
||||
|
||||
string TravelDirection()
|
||||
{
|
||||
if (!HasVelocity)
|
||||
return "unknown";
|
||||
if (Velocity.Length() < 0.5)
|
||||
return "stationary";
|
||||
float x = Math.AbsFloat(Velocity[0]);
|
||||
float z = Math.AbsFloat(Velocity[2]);
|
||||
string northSouth = "N";
|
||||
string eastWest = "E";
|
||||
if (Velocity[2] < 0)
|
||||
northSouth = "S";
|
||||
if (Velocity[0] < 0)
|
||||
eastWest = "W";
|
||||
if (x < z * 0.414214)
|
||||
return northSouth;
|
||||
if (z < x * 0.414214)
|
||||
return eastWest;
|
||||
return northSouth + eastWest;
|
||||
}
|
||||
|
||||
string Description(float now, bool brief = false)
|
||||
{
|
||||
if (Distress)
|
||||
return string.Format("Distress at friendly X=%1 Z=%2; enemy details unknown (%3s ago)", Math.Round(Position[0]), Math.Round(Position[2]), Math.Round(Math.Max(0, now - ObservedAt) / 1000));
|
||||
string count = "count unknown";
|
||||
if (SeenEscapists >= 0)
|
||||
count = "at least " + SeenEscapists.ToString() + " spotted nearby";
|
||||
string motion = "speed/direction unknown";
|
||||
if (HasVelocity)
|
||||
{
|
||||
float speed = Math.Round(Velocity.Length() * 10) / 10.0;
|
||||
float azimuth = Math.Atan2(Velocity[0], Velocity[2]) * Math.RAD2DEG;
|
||||
if (azimuth < 0)
|
||||
azimuth += 360;
|
||||
int heading = Math.Round(azimuth);
|
||||
motion = string.Format("%1 km/h, azimuth %2 degrees (%3)", Math.Round(speed * 3.6), heading % 360, TravelDirection());
|
||||
}
|
||||
string details = count + "; " + TravelMode + "; " + motion + "; seen weapons: " + SeenWeapons;
|
||||
if (brief)
|
||||
return details;
|
||||
string velocity = "unknown";
|
||||
if (HasVelocity)
|
||||
velocity = string.Format("[%1,%2] m/s", Math.Round(Velocity[0] * 10) / 10.0, Math.Round(Velocity[2] * 10) / 10.0);
|
||||
return string.Format("X=%1 Z=%2: %3; vector X/Z=%4 (%5s ago)", Math.Round(Position[0]), Math.Round(Position[2]), details, velocity, Math.Round(Math.Max(0, now - ObservedAt) / 1000));
|
||||
}
|
||||
|
||||
static vector Predict(vector position, vector velocity, float seconds)
|
||||
{
|
||||
vector offset = velocity * Math.Clamp(seconds, 0, 20);
|
||||
offset[1] = 0;
|
||||
if (offset.Length() > 300)
|
||||
offset = offset.Normalized() * 300;
|
||||
return position + offset;
|
||||
}
|
||||
|
||||
int Priority(float now)
|
||||
{
|
||||
if (Distress)
|
||||
return 0;
|
||||
if (now - ObservedAt <= 15000)
|
||||
{
|
||||
if (Engaged)
|
||||
return 3;
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// An idle rotor or zero speed alone is normal during takeoff, landing and reload.
|
||||
// Confirm prolonged grounding, but recognize actual loss of flight capability.
|
||||
class ERR_HelicopterAvailability
|
||||
{
|
||||
protected float m_fGroundedSince = -1;
|
||||
|
||||
string Check(float now, float spawnedAt, bool destroyed, bool disabled, bool livingPilot, bool playerControlled, bool servicing, float height, float speed)
|
||||
{
|
||||
if (destroyed) return "destroyed";
|
||||
if (now - spawnedAt < 20000)
|
||||
{
|
||||
m_fGroundedSince = -1;
|
||||
return "";
|
||||
}
|
||||
if (disabled) return "engine, rotor or airframe disabled";
|
||||
if (playerControlled)
|
||||
{
|
||||
m_fGroundedSince = -1;
|
||||
return "";
|
||||
}
|
||||
if (!livingPilot) return "no living pilot";
|
||||
if (servicing || height > 5 || speed > 2)
|
||||
{
|
||||
m_fGroundedSince = -1;
|
||||
return "";
|
||||
}
|
||||
if (m_fGroundedSince < 0) m_fGroundedSince = now;
|
||||
if (now - m_fGroundedSince >= 60000) return "grounded for 60 seconds";
|
||||
return "";
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
// The stored geometry is the command actually emitted to the native aiming
|
||||
// tree, never a chain of refreshed approximations. Entity/report/node links
|
||||
// are weak; cancellation and physical projectile observation clear ownership.
|
||||
class ERR_MortarAimSolution
|
||||
{
|
||||
IEntity Gun;
|
||||
// Last delivered observation proved equivalent to this original geometry.
|
||||
ERR_HQReport Report;
|
||||
int OrderId;
|
||||
int Shot;
|
||||
vector GunOrigin;
|
||||
vector Impact;
|
||||
vector AimTarget;
|
||||
vector SalvoOffset;
|
||||
int Charge;
|
||||
float FlightTime;
|
||||
|
||||
bool Matches(ERR_MortarBattery battery, ERR_HQReport report)
|
||||
{
|
||||
return battery && Gun && Gun == battery.Gun && Report && Report == report && OrderId == battery.OrderId && Shot == battery.OrderShots;
|
||||
}
|
||||
}
|
||||
|
||||
class ERR_MortarAimRules
|
||||
{
|
||||
// Three-dimensional tolerances include terrain/aim-height changes. These are
|
||||
// equivalence limits, not permission to move the gun or redirect its barrel.
|
||||
static bool Equivalent(int commandedCharge, int updatedCharge, vector commandedOrigin, vector updatedOrigin, vector commandedImpact, vector updatedImpact, vector commandedAim, vector updatedAim, float commandedFlight, float updatedFlight)
|
||||
{
|
||||
return commandedCharge >= 0 && commandedCharge == updatedCharge && commandedFlight > 0 && updatedFlight > 0 && vector.Distance(commandedOrigin, updatedOrigin) <= 0.05 && vector.Distance(commandedImpact, updatedImpact) <= 0.1 && vector.Distance(commandedAim, updatedAim) <= 0.1 && Math.AbsFloat(commandedFlight - updatedFlight) <= 0.05;
|
||||
}
|
||||
}
|
||||
+557
@@ -0,0 +1,557 @@
|
||||
// Migrated from the independently authored AI extension; successor-owned physical groups.
|
||||
// Each site/phase has its own repeat limiter. Changed reasons and lifecycle
|
||||
// events are immediate; only identical polling results are summarized.
|
||||
class ERR_MortarTraceState
|
||||
{
|
||||
string LastReason;
|
||||
float NextLog;
|
||||
int Suppressed;
|
||||
|
||||
bool Due(string reason, float now)
|
||||
{
|
||||
if (reason != LastReason || now >= NextLog)
|
||||
{
|
||||
LastReason = reason;
|
||||
NextLog = now + 30000;
|
||||
return true;
|
||||
}
|
||||
Suppressed++;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class ERR_MortarBattery
|
||||
{
|
||||
static const ResourceName GUN = "{D1FFE458E8AC4BDB}Prefabs/Weapons/Mortars/2B14/Mortar_2B14.et";
|
||||
static const ResourceName SHELL = "{98EC9C526AFBA282}Prefabs/Weapons/Ammo/Ammo_Shell_82mm_HE_O832DU.et";
|
||||
static const ResourceName CREW = "{86E565387A634103}Prefabs/Characters/Campaign/Final/OPFOR/USSR_Army/Regular/Campaign_USSR_Player_MortarCrew.et";
|
||||
static const ResourceName RADIO_OPERATOR = "{612F43A4D5AE765F}Prefabs/Characters/Factions/OPFOR/USSR_Army/Character_USSR_RTO.et";
|
||||
IEntity RadioOperator;
|
||||
IEntity Gun;
|
||||
SCR_AIGroup Group;
|
||||
ref SCR_AIStaticArtilleryActivity Activity;
|
||||
ref ERR_HQReport Report;
|
||||
ref ERR_HQRoute RadioOrder;
|
||||
ref ERR_HQReport PendingRadioReport;
|
||||
ref ERR_HQReport PendingSupplyReport;
|
||||
ref ERR_MortarRefresh Refresh;
|
||||
bool RefreshRejected; // latched through an already committed projectile
|
||||
protected ref ERR_MortarReadiness m_Readiness;
|
||||
protected ref ERR_MortarSupplyTransfer m_Transfer;
|
||||
protected ref array<IEntity> m_AttemptedShells = {};
|
||||
protected bool m_Stopped;
|
||||
protected SCR_AIStaticArtilleryActivity m_ObservedActivity;
|
||||
protected int m_ObservedActivityOrder;
|
||||
protected EAIActionState m_ActivityTerminal = EAIActionState.EVALUATED;
|
||||
protected ref ERR_MortarReportLedger m_ReportLedger = new ERR_MortarReportLedger();
|
||||
vector Position;
|
||||
vector Impact;
|
||||
vector AimOffset;
|
||||
int AimedShot = -1;
|
||||
float FlightTime;
|
||||
float AimedAt = -1;
|
||||
float AimValidatedAt = -1;
|
||||
SCR_AIGetArtilleryAimDistanceCompensation AimSolver;
|
||||
ref ERR_MortarAimSolution AimSolution;
|
||||
float AirspaceUntil;
|
||||
vector AirspaceImpact;
|
||||
float AirspaceRadius = 600;
|
||||
float OrderedAt;
|
||||
float NextOrder;
|
||||
float SpawnedAt;
|
||||
bool StartupActivated;
|
||||
bool Released;
|
||||
bool Rejected;
|
||||
bool Claimed; // keeps the world slot occupied after the battery is destroyed
|
||||
int SiteId;
|
||||
int OrderId;
|
||||
int RadioId;
|
||||
int SourceId;
|
||||
int OrderShots;
|
||||
int TotalShots;
|
||||
bool CommandOpen;
|
||||
string LastFailure;
|
||||
string SpawnFailure;
|
||||
vector LastCandidate;
|
||||
ref map<string, int> SearchCounts = new map<string, int>();
|
||||
ref map<string, ref ERR_MortarTraceState> Traces = new map<string, ref ERR_MortarTraceState>();
|
||||
|
||||
void Audit(string kind, string detail)
|
||||
{
|
||||
ERR_HQ.Event("MORTAR " + kind, string.Format("site=%1 order=%2 radio=M%3 source=M%4 position=%5; %6", SiteId, OrderId, RadioId, SourceId, Position, detail), RadioId);
|
||||
}
|
||||
|
||||
void Trace(string phase, string reason, string detail)
|
||||
{
|
||||
ERR_MortarTraceState state;
|
||||
if (!Traces.Find(phase, state))
|
||||
{
|
||||
state = new ERR_MortarTraceState();
|
||||
Traces.Insert(phase, state);
|
||||
}
|
||||
string previous = state.LastReason;
|
||||
if (!state.Due(reason, GetGame().GetWorld().GetWorldTime()))
|
||||
return;
|
||||
Audit(phase, string.Format("reason=%1; %2; previous=%3 repeated polls omitted=%4", reason, detail, previous, state.Suppressed));
|
||||
state.Suppressed = 0;
|
||||
}
|
||||
|
||||
void FlushTraces()
|
||||
{
|
||||
foreach (string phase, ERR_MortarTraceState state : Traces)
|
||||
{
|
||||
if (state.Suppressed > 0)
|
||||
Audit("REPEATS", string.Format("phase=%1 reason=%2 repeated polls omitted=%3", phase, state.LastReason, state.Suppressed));
|
||||
}
|
||||
Traces.Clear();
|
||||
}
|
||||
|
||||
string OperationalReason()
|
||||
{
|
||||
if (m_Stopped) return "battery stopped";
|
||||
if (!Gun) return "gun missing";
|
||||
if (!SpawnFailure.IsEmpty()) return SpawnFailure;
|
||||
if (!Group || !Group.GetGroupUtilityComponent()) return "crew group missing";
|
||||
if (Group.GetFaction() != ERR_GameMode.Get().EnemyFaction()) return "crew changed faction";
|
||||
if (Group.GetPlayerCount(true) > 0) return "player in crew group";
|
||||
if (Group.GetAgentsCount() == 0) return "crew group empty";
|
||||
DamageManagerComponent damage = DamageManagerComponent.Cast(Gun.FindComponent(DamageManagerComponent));
|
||||
if (damage && damage.IsDestroyed()) return "gun destroyed";
|
||||
return "";
|
||||
}
|
||||
|
||||
bool CanRequest(ERR_HQReport report, float now)
|
||||
{
|
||||
return !m_Stopped && ERR_MortarPrediction.Fresh(report, now) && m_ReportLedger.Available(report.MessageId, report.ObservedAt, now);
|
||||
}
|
||||
bool BeginRequest(ERR_HQReport report, float now)
|
||||
{
|
||||
if (!CanRequest(report, now) || !m_ReportLedger.Claim(report.MessageId, report.ObservedAt, now)) return false;
|
||||
Cancel("superseded by new HQ order");
|
||||
FlushTraces();
|
||||
OrderId++;
|
||||
RadioId = 0;
|
||||
SourceId = report.MessageId;
|
||||
OrderShots = 0;
|
||||
Released = false;
|
||||
CommandOpen = true;
|
||||
OrderedAt = now;
|
||||
LastFailure = "";
|
||||
Audit("COMMAND", string.Format("requested rounds=%1 HE radius=%2m; %3", ERR_MortarSalvo.ROUNDS, ERR_MortarSalvo.RADIUS_METRES, report.Description(now)));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CanRefresh(ERR_HQReport report, float now)
|
||||
{
|
||||
if (m_Stopped || !CommandOpen || !Activity || Refresh || RefreshRejected || Rejected || !Operational()) return false;
|
||||
return ERR_MortarRefreshRules.Newer(Report, report, now) && m_ReportLedger.Available(report.MessageId, report.ObservedAt, now);
|
||||
}
|
||||
bool BeginRefresh(ERR_HQReport report, ERR_HQRoute route, float now)
|
||||
{
|
||||
if (!route || !CanRefresh(report, now) || !m_ReportLedger.Claim(report.MessageId, report.ObservedAt, now)) return false;
|
||||
Refresh = new ERR_MortarRefresh(this, report, route);
|
||||
Audit("UPDATE QUEUED", string.Format("same order; update radio=M%1 newer observation=M%2 observed=%3", route.MessageId, report.MessageId, report.ObservedAt));
|
||||
return true;
|
||||
}
|
||||
void RejectRefresh(string reason)
|
||||
{
|
||||
// Never clear the commit latch or steal the physical projectile callback.
|
||||
// AllowShot refuses further loads now; HQ cancels after it is observed.
|
||||
RefreshRejected = true;
|
||||
Refresh = null;
|
||||
LastFailure = reason;
|
||||
Audit("UPDATE REJECTED", reason);
|
||||
}
|
||||
|
||||
void ObserveProjectile(IEntity projectileEntity, IEntity firingCharacter, bool playerGunner)
|
||||
{
|
||||
if (!projectileEntity)
|
||||
{
|
||||
Audit("FIRE CALLBACK", "native OnWeaponFired had no projectile; no confirmed shot counted");
|
||||
return;
|
||||
}
|
||||
TotalShots++;
|
||||
if (CommandOpen && Released && !playerGunner)
|
||||
{
|
||||
OrderShots++;
|
||||
// Only a physical projectile unlocks the next load. It must be aimed again.
|
||||
Released = false;
|
||||
AimedAt = -1;
|
||||
AimValidatedAt = -1;
|
||||
AimSolver = null;
|
||||
AimSolution = null;
|
||||
Audit("FIRED", string.Format("physical projectile=%1 origin=%2 order shots=%3 total shots=%4 predicted impact=%5 predicted flight=%6s gunner=%7 (impact not observed)", projectileEntity, projectileEntity.GetOrigin(), OrderShots, TotalShots, Impact, FlightTime, firingCharacter));
|
||||
}
|
||||
else
|
||||
Audit("FIRED UNASSIGNED", string.Format("physical projectile=%1 origin=%2 player gunner=%3 order open=%4 committed=%5 total shots=%6; no active HQ order attribution", projectileEntity, projectileEntity.GetOrigin(), playerGunner, CommandOpen, Released, TotalShots));
|
||||
}
|
||||
|
||||
void ReserveShellAirspace(float now)
|
||||
{
|
||||
// Enclose every still-airborne shell, including an earlier, longer flight.
|
||||
// A later round must not move/shrink the existing exclusion or deadline.
|
||||
if (now >= AirspaceUntil)
|
||||
{
|
||||
AirspaceImpact = Impact;
|
||||
AirspaceRadius = 600;
|
||||
}
|
||||
AirspaceRadius = Math.Max(AirspaceRadius, vector.DistanceXZ(AirspaceImpact, Impact) + 600);
|
||||
AirspaceUntil = Math.Max(AirspaceUntil, now + (FlightTime + 10) * 1000);
|
||||
}
|
||||
|
||||
bool SpawnFailed(string reason)
|
||||
{
|
||||
SpawnFailure = reason;
|
||||
Audit("DEPLOY FAILED", string.Format("reason=%1 claimed=%2 gun=%3 group=%4", reason, Claimed, Gun, Group));
|
||||
return false;
|
||||
}
|
||||
|
||||
void SearchResult(vector candidate, string reason)
|
||||
{
|
||||
LastCandidate = candidate;
|
||||
int count;
|
||||
SearchCounts.Find(reason, count);
|
||||
SearchCounts.Set(reason, count + 1);
|
||||
}
|
||||
|
||||
void SiteStatus(float now)
|
||||
{
|
||||
if (!SearchCounts.IsEmpty())
|
||||
{
|
||||
string counts;
|
||||
foreach (string reason, int count : SearchCounts) counts += string.Format("%1=%2; ", reason, count);
|
||||
Audit("SITE SEARCH", string.Format("candidate counts since last summary: %1 last candidate=%2", counts, LastCandidate));
|
||||
SearchCounts.Clear();
|
||||
}
|
||||
int agents, living;
|
||||
int actionState = -1;
|
||||
if (Group)
|
||||
{
|
||||
array<AIAgent> crew = {};
|
||||
Group.GetAgents(crew);
|
||||
agents = crew.Count();
|
||||
foreach (AIAgent agent : crew)
|
||||
{
|
||||
if (agent && ERR_Actors.CanFight(agent.GetControlledEntity())) living++;
|
||||
}
|
||||
}
|
||||
if (Activity) actionState = Activity.GetActionState();
|
||||
bool connected;
|
||||
ERR_HQ hq = ERR_HQ.Get();
|
||||
if (hq && Group) connected = hq.MortarConnected(Group);
|
||||
Audit("SITE STATUS", string.Format("claimed=%1 startup=%2 operational=%3 crew agents=%4 fighting=%5 inventory HE=%6 radio connected=%7 cooldown remaining=%8s", Claimed, StartupActivated, Operational(), agents, living, CountShells(), connected, Math.Max(0, NextOrder - now) / 1000));
|
||||
Audit("ORDER STATUS", string.Format("open=%1 route pending=%2 native action=%3 shell committed=%4 order shots=%5 total shots=%6 age=%7s last failure=%8", CommandOpen, RadioOrder != null, actionState, Released, OrderShots, TotalShots, Math.Max(0, now - OrderedAt) / 1000, LastFailure));
|
||||
if (!SpawnFailure.IsEmpty()) Audit("PARTIAL DEPLOYMENT", SpawnFailure);
|
||||
if (!Operational()) Trace("SITE BLOCK", OperationalReason(), "spawn failure=" + SpawnFailure);
|
||||
}
|
||||
|
||||
bool Operational()
|
||||
{
|
||||
return OperationalReason().IsEmpty();
|
||||
}
|
||||
|
||||
int CountShells()
|
||||
{
|
||||
if (!Group)
|
||||
return 0;
|
||||
int count;
|
||||
array<IEntity> items = {};
|
||||
array<AIAgent> agents = {};
|
||||
Group.GetAgents(agents);
|
||||
foreach (AIAgent agent : agents)
|
||||
{
|
||||
if (!agent || !ERR_Actors.CanFight(agent.GetControlledEntity()))
|
||||
continue;
|
||||
InventoryStorageManagerComponent inventory = InventoryStorageManagerComponent.Cast(agent.GetControlledEntity().FindComponent(InventoryStorageManagerComponent));
|
||||
if (!inventory)
|
||||
continue;
|
||||
ERR_Inventory.AppendUnique(inventory, items);
|
||||
}
|
||||
foreach (IEntity item : items)
|
||||
if (item && item.GetPrefabData() && item.GetPrefabData().GetPrefabName() == SHELL) count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
void Bind(int siteId, SCR_AIGroup group, IEntity gun, float now)
|
||||
{
|
||||
SiteId = siteId;
|
||||
Group = group;
|
||||
Gun = gun;
|
||||
Position = gun.GetOrigin();
|
||||
SpawnedAt = now;
|
||||
Claimed = true;
|
||||
m_Readiness = new ERR_MortarReadiness(this);
|
||||
}
|
||||
|
||||
|
||||
int InventoryShells(InventoryStorageManagerComponent inventory)
|
||||
{
|
||||
if (!inventory)
|
||||
return 0;
|
||||
int count;
|
||||
array<IEntity> items = {};
|
||||
ERR_Inventory.Items(inventory, items);
|
||||
foreach (IEntity item : items)
|
||||
{
|
||||
if (item && item.GetPrefabData() && item.GetPrefabData().GetPrefabName() == SHELL)
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
bool Stopped() { return m_Stopped; }
|
||||
bool SupplyPending() { return m_Transfer && m_Transfer.Unresolved(); }
|
||||
bool SupplyAuthorized(float now)
|
||||
{
|
||||
if (!Replication.IsServer() || m_Stopped || !StartupActivated || !Operational() || !CommandOpen || !ERR_HQ.MissionActive()) return false;
|
||||
ERR_MortarSystem system = ERR_MortarSystem.Get();
|
||||
if (!system || system.FindGun(Gun) != this) return false;
|
||||
ERR_HQReport suppliedReport = Report;
|
||||
if (!suppliedReport) suppliedReport = PendingSupplyReport;
|
||||
return ERR_MortarPrediction.Fresh(suppliedReport, now);
|
||||
}
|
||||
void Stop()
|
||||
{
|
||||
if (m_Stopped) return;
|
||||
m_Stopped = true;
|
||||
Cancel("HQ stopped");
|
||||
if (m_Readiness) m_Readiness.Stop();
|
||||
}
|
||||
|
||||
ERR_MortarSupplyState SupplyState(float now)
|
||||
{
|
||||
// Observe an existing native operation even after its order was canceled.
|
||||
// No second request may race an unresolved transfer on this battery.
|
||||
if (m_Transfer)
|
||||
{
|
||||
ERR_MortarSupplyState observed = m_Transfer.Poll(now);
|
||||
if (m_Transfer.CapturedOrder() == OrderId)
|
||||
{
|
||||
if (observed == ERR_MortarSupplyState.READY) m_Transfer = null;
|
||||
return observed;
|
||||
}
|
||||
if (m_Transfer.Unresolved()) return ERR_MortarSupplyState.FAILED;
|
||||
m_Transfer = null;
|
||||
}
|
||||
if (!SupplyAuthorized(now)) return SupplyStateFailed("current HQ order, crew or report is unavailable");
|
||||
SCR_AIVehicleUsageComponent usage = SCR_AIVehicleUsageComponent.Cast(Gun.FindComponent(SCR_AIVehicleUsageComponent));
|
||||
IEntity gunner;
|
||||
if (usage && usage.GetTurretCompartmentSlot())
|
||||
gunner = usage.GetTurretCompartmentSlot().GetOccupant();
|
||||
array<AIAgent> agents = {};
|
||||
Group.GetAgents(agents);
|
||||
float nearest = float.MAX;
|
||||
if (!gunner)
|
||||
{
|
||||
foreach (AIAgent candidate : agents)
|
||||
{
|
||||
if (!candidate || candidate.GetControlledEntity() == RadioOperator || !ERR_Actors.CanFight(candidate.GetControlledEntity()))
|
||||
continue;
|
||||
float distance = vector.DistanceXZ(Gun.GetOrigin(), candidate.GetControlledEntity().GetOrigin());
|
||||
if (distance < nearest)
|
||||
{
|
||||
nearest = distance;
|
||||
gunner = candidate.GetControlledEntity();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!gunner) return SupplyStateFailed("no fighting gunner found");
|
||||
if (ERR_Actors.Group(gunner) != Group) return SupplyStateFailed("turret occupant belongs to another group");
|
||||
if (!ERR_Actors.CanFight(gunner)) return SupplyStateFailed("gunner cannot fight");
|
||||
InventoryStorageManagerComponent destination = InventoryStorageManagerComponent.Cast(gunner.FindComponent(InventoryStorageManagerComponent));
|
||||
if (!destination)
|
||||
return SupplyStateFailed("gunner inventory missing");
|
||||
if (InventoryShells(destination) > 0)
|
||||
return ERR_MortarSupplyState.READY;
|
||||
int nearbyDonors, donorShells, storageRejected;
|
||||
array<IEntity> consideredShells = {};
|
||||
foreach (AIAgent bearer : agents)
|
||||
{
|
||||
if (!bearer || !ERR_Actors.CanFight(bearer.GetControlledEntity()))
|
||||
continue;
|
||||
IEntity donor = bearer.GetControlledEntity();
|
||||
if (donor == gunner || vector.DistanceXZ(donor.GetOrigin(), gunner.GetOrigin()) > 12)
|
||||
continue;
|
||||
InventoryStorageManagerComponent inventory = InventoryStorageManagerComponent.Cast(donor.FindComponent(InventoryStorageManagerComponent));
|
||||
if (!inventory)
|
||||
continue;
|
||||
nearbyDonors++;
|
||||
array<IEntity> items = {};
|
||||
ERR_Inventory.Items(inventory, items);
|
||||
foreach (IEntity item : items)
|
||||
{
|
||||
if (!item || !item.GetPrefabData() || item.GetPrefabData().GetPrefabName() != SHELL)
|
||||
continue;
|
||||
if (consideredShells.Contains(item) || m_AttemptedShells.Contains(item)) continue;
|
||||
consideredShells.Insert(item);
|
||||
donorShells++;
|
||||
BaseInventoryStorageComponent storage = destination.FindStorageForItem(item, EStoragePurpose.PURPOSE_DEPOSIT);
|
||||
if (!storage) { storageRejected++; continue; }
|
||||
m_AttemptedShells.Insert(item); // even rejected/ambiguous requests are never retried
|
||||
m_Transfer = new ERR_MortarSupplyTransfer(this, donor, gunner, item, storage, now);
|
||||
if (!m_Transfer.Begin(now)) return SupplyStateFailed("exact shell transfer request rejected; no retry");
|
||||
Audit("AMMO TRANSFER", string.Format("one exact-item move pending donor=%1 gunner=%2 shell=%3 order=%4; awaiting callback AND physical destination", donor, gunner, item, OrderId));
|
||||
return ERR_MortarSupplyState.PENDING;
|
||||
}
|
||||
}
|
||||
return SupplyStateFailed(string.Format("gunner empty; fighting donors within 12m=%1 unattempted HE=%2 destination storage rejected=%3", nearbyDonors, donorShells, storageRejected));
|
||||
}
|
||||
|
||||
// Compatibility for existing native callers; request acceptance is NOT ready.
|
||||
bool SupplyGunner() { return SupplyState(GetGame().GetWorld().GetWorldTime()) == ERR_MortarSupplyState.READY; }
|
||||
protected ERR_MortarSupplyState SupplyStateFailed(string reason)
|
||||
{
|
||||
SupplyFailed(reason);
|
||||
return ERR_MortarSupplyState.FAILED;
|
||||
}
|
||||
|
||||
bool SupplyFailed(string reason)
|
||||
{
|
||||
LastFailure = reason;
|
||||
Audit("AMMO FAILED", reason);
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void WatchActivity()
|
||||
{
|
||||
DetachActivity();
|
||||
if (!Activity || !CommandOpen || m_Stopped) return;
|
||||
m_ObservedActivity = Activity;
|
||||
m_ObservedActivityOrder = OrderId;
|
||||
Activity.m_OnActionCompleted.Insert(OnActivityCompleted);
|
||||
Activity.m_OnActionFailed.Insert(OnActivityFailed);
|
||||
}
|
||||
protected void DetachActivity()
|
||||
{
|
||||
if (m_ObservedActivity)
|
||||
{
|
||||
m_ObservedActivity.m_OnActionCompleted.Remove(OnActivityCompleted);
|
||||
m_ObservedActivity.m_OnActionFailed.Remove(OnActivityFailed);
|
||||
}
|
||||
m_ObservedActivity = null;
|
||||
m_ObservedActivityOrder = 0;
|
||||
m_ActivityTerminal = EAIActionState.EVALUATED;
|
||||
}
|
||||
protected void OnActivityCompleted(SCR_AIActionBase action)
|
||||
{
|
||||
ObserveActivityTerminal(action, EAIActionState.COMPLETED);
|
||||
}
|
||||
protected void OnActivityFailed(SCR_AIActionBase action)
|
||||
{
|
||||
ObserveActivityTerminal(action, EAIActionState.FAILED);
|
||||
}
|
||||
protected void ObserveActivityTerminal(SCR_AIActionBase action, EAIActionState terminal)
|
||||
{
|
||||
if (m_Stopped || !CommandOpen || !Activity || action != Activity || action != m_ObservedActivity || OrderId != m_ObservedActivityOrder) return;
|
||||
if (m_ActivityTerminal != EAIActionState.EVALUATED) return;
|
||||
if (terminal != EAIActionState.COMPLETED && terminal != EAIActionState.FAILED) return;
|
||||
// Vanilla deselection can overwrite COMPLETED/FAILED with EVALUATED
|
||||
// before the next HQ tick. Record its actual event, never infer completion
|
||||
// from projectile totals, and never cancel from inside the native callback.
|
||||
m_ActivityTerminal = terminal;
|
||||
Audit("ACTIVITY EVENT", string.Format("native terminal event=%1 current state=%2 captured order=%3 physical order shots=%4 total shots=%5 committed=%6", terminal, Activity.GetActionState(), m_ObservedActivityOrder, OrderShots, TotalShots, Released));
|
||||
}
|
||||
protected EAIActionState ActivityTerminal()
|
||||
{
|
||||
if (Activity == m_ObservedActivity && OrderId == m_ObservedActivityOrder)
|
||||
if (m_ActivityTerminal == EAIActionState.COMPLETED || m_ActivityTerminal == EAIActionState.FAILED) return m_ActivityTerminal;
|
||||
return Activity.GetActionState();
|
||||
}
|
||||
|
||||
void Cancel(string reason = "cancel requested")
|
||||
{
|
||||
if (CommandOpen)
|
||||
{
|
||||
FlushTraces();
|
||||
Audit("ORDER END", string.Format("reason=%1 observed shots=%2 shell committed=%3 elapsed=%4s last failure=%5", reason, OrderShots, Released, (GetGame().GetWorld().GetWorldTime() - OrderedAt) / 1000, LastFailure));
|
||||
}
|
||||
CommandOpen = false;
|
||||
// Remove before our own cleanup calls Complete; that is not a native
|
||||
// successful fire mission and must not manufacture an observed event.
|
||||
DetachActivity();
|
||||
RadioOrder = null;
|
||||
PendingRadioReport = null;
|
||||
PendingSupplyReport = null;
|
||||
Refresh = null;
|
||||
RefreshRejected = false;
|
||||
if (m_Transfer) m_Transfer.Invalidate();
|
||||
Report = null;
|
||||
AimedAt = -1;
|
||||
AimValidatedAt = -1;
|
||||
AimSolver = null;
|
||||
AimSolution = null;
|
||||
if (Activity)
|
||||
Activity.Complete();
|
||||
Activity = null;
|
||||
}
|
||||
|
||||
// Idle distant batteries still report through HQ's scheduler. Their native
|
||||
// AI needs full detail while firing or when players can interact with them.
|
||||
protected void UpdateCrewDetail(float now, bool firing = false)
|
||||
{
|
||||
if (!Group || Group.GetPlayerCount(true) > 0)
|
||||
return;
|
||||
bool active = firing || Activity != null || ERR_MortarSystem.NearPlayer(Position);
|
||||
int lod = -1;
|
||||
if (active)
|
||||
lod = 0;
|
||||
if (Group.GetPermanentLOD() != lod)
|
||||
Group.SetPermanentLOD(lod);
|
||||
if (active && !Group.IsAIActivated())
|
||||
Group.ActivateAI();
|
||||
array<AIAgent> agents = {};
|
||||
Group.GetAgents(agents);
|
||||
foreach (AIAgent agent : agents)
|
||||
{
|
||||
if (!agent || EntityUtils.IsPlayer(agent.GetControlledEntity()))
|
||||
continue;
|
||||
if (agent.GetPermanentLOD() != lod)
|
||||
agent.SetPermanentLOD(lod);
|
||||
if (active && !agent.IsAIActivated() && ERR_Actors.CanFight(agent.GetControlledEntity()))
|
||||
agent.ActivateAI();
|
||||
}
|
||||
}
|
||||
|
||||
void Update(float now)
|
||||
{
|
||||
if (m_Stopped) return;
|
||||
if (!StartupActivated)
|
||||
{
|
||||
if (!SpawnFailure.IsEmpty()) return;
|
||||
if (!m_Readiness) { SpawnFailed("initial support readiness owner missing"); return; }
|
||||
ERR_MortarSupplyState startup = m_Readiness.Poll(now);
|
||||
if (startup != ERR_MortarSupplyState.READY) return;
|
||||
StartupActivated = true;
|
||||
}
|
||||
if (m_Transfer) m_Transfer.Poll(now);
|
||||
if (!Operational())
|
||||
{
|
||||
Cancel(OperationalReason());
|
||||
return;
|
||||
}
|
||||
if (Activity)
|
||||
{
|
||||
Trace("ACTIVITY", string.Format("native state=%1", Activity.GetActionState()), string.Format("age=%1s aim age=%2s committed=%3 shots=%4", (now - OrderedAt) / 1000, (now - AimedAt) / 1000, Released, OrderShots));
|
||||
EAIActionState terminal = ActivityTerminal();
|
||||
if (terminal == EAIActionState.FAILED) Cancel("native artillery activity FAILED");
|
||||
else if (terminal == EAIActionState.COMPLETED) Cancel("native artillery activity COMPLETED (shot count is separate)");
|
||||
else if (now - OrderedAt > ERR_MortarSalvo.TIMEOUT_MS) Cancel("120s artillery salvo timeout");
|
||||
}
|
||||
if (StartupActivated)
|
||||
UpdateCrewDetail(now);
|
||||
}
|
||||
|
||||
void Order(ERR_HQReport report, float now)
|
||||
{
|
||||
if (m_Stopped || !StartupActivated || !Operational() || !CommandOpen || Activity || !ERR_MortarPrediction.Fresh(report, now)) return;
|
||||
Report = report.Snapshot();
|
||||
OrderedAt = now;
|
||||
NextOrder = now + 30000;
|
||||
Released = false;
|
||||
UpdateCrewDetail(now, true);
|
||||
Activity = new SCR_AIStaticArtilleryActivity(Group.GetGroupUtilityComponent(), null, report.Position, SCR_EAIArtilleryAmmoType.HIGH_EXPLOSIVE, ERR_MortarSalvo.ROUNDS);
|
||||
WatchActivity();
|
||||
Group.GetGroupUtilityComponent().AddAction(Activity);
|
||||
Audit("AIM", string.Format("native activity created; latest source=M%1 observed=%2", report.MessageId, report.Description(now)));
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
class ERR_MortarCourierCallback : ScriptedInventoryOperationCallback
|
||||
{
|
||||
ERR_MortarCourierTransfer Transfer;
|
||||
protected override void OnComplete() { if (Transfer) Transfer.Acknowledge(true); }
|
||||
protected override void OnFailed() { if (Transfer) Transfer.Acknowledge(false); }
|
||||
}
|
||||
// One exact native move belonging to one courier trip. Never retries ambiguous work.
|
||||
class ERR_MortarCourierTransfer
|
||||
{
|
||||
protected ERR_MortarLogistics m_Service;
|
||||
protected IEntity m_Shell;
|
||||
protected IEntity m_From;
|
||||
protected IEntity m_To;
|
||||
protected InventoryStorageManagerComponent m_Source;
|
||||
protected InventoryStorageManagerComponent m_Destination;
|
||||
protected BaseInventoryStorageComponent m_Storage;
|
||||
protected ref ERR_MortarCourierCallback m_Callback;
|
||||
protected int m_Trip;
|
||||
protected int m_Acknowledgement;
|
||||
protected float m_Deadline;
|
||||
protected bool m_Accepted;
|
||||
protected bool m_Invalidated;
|
||||
protected bool m_Resolved;
|
||||
void ERR_MortarCourierTransfer(ERR_MortarLogistics service, int trip, IEntity shell, IEntity from, IEntity to, BaseInventoryStorageComponent storage, float now)
|
||||
{
|
||||
m_Service = service; m_Trip = trip; m_Shell = shell; m_From = from; m_To = to; m_Storage = storage; m_Deadline = now + 8000;
|
||||
m_Source = InventoryStorageManagerComponent.Cast(from.FindComponent(InventoryStorageManagerComponent));
|
||||
m_Destination = InventoryStorageManagerComponent.Cast(to.FindComponent(InventoryStorageManagerComponent));
|
||||
}
|
||||
static bool Proven(bool complete, bool source, bool destination, bool exact) { return complete && !source && destination && exact; }
|
||||
protected bool Contains(InventoryStorageManagerComponent inventory)
|
||||
{
|
||||
if (!inventory || !m_Shell) return false;
|
||||
array<IEntity> items = {}; ERR_Inventory.Items(inventory, items); return items.Contains(m_Shell);
|
||||
}
|
||||
void Acknowledge(bool success)
|
||||
{
|
||||
if (m_Acknowledgement != 0) return;
|
||||
if (success) m_Acknowledgement = 1;
|
||||
else m_Acknowledgement = -1;
|
||||
}
|
||||
bool Begin(float now)
|
||||
{
|
||||
if (m_Callback || !Replication.IsServer() || !m_Service.TransferAllowed(m_Trip, m_Shell, m_From, m_To, now) || !Contains(m_Source) || Contains(m_Destination)) return false;
|
||||
m_Callback = new ERR_MortarCourierCallback(); m_Callback.Transfer = this;
|
||||
m_Accepted = m_Source.TryMoveItemToStorage(m_Shell, m_Storage, -1, m_Callback);
|
||||
if (!m_Accepted) { Acknowledge(false); m_Resolved = true; }
|
||||
return m_Accepted;
|
||||
}
|
||||
void Invalidate() { m_Invalidated = true; }
|
||||
bool Unresolved() { return m_Accepted && !m_Resolved; }
|
||||
IEntity Shell() { return m_Shell; }
|
||||
int Poll(float now)
|
||||
{
|
||||
// A deleted exact entity cannot complete later; close as a permanent loss.
|
||||
if (!m_Shell) { m_Resolved = true; m_Invalidated = true; return -1; }
|
||||
bool exact;
|
||||
InventoryItemComponent item;
|
||||
if (m_Shell) item = InventoryItemComponent.Cast(m_Shell.FindComponent(InventoryItemComponent));
|
||||
if (item && item.GetParentSlot()) exact = item.GetParentSlot().GetStorage() == m_Storage;
|
||||
bool proven = Proven(m_Acknowledgement == 1, Contains(m_Source), Contains(m_Destination), exact);
|
||||
if (proven || m_Acknowledgement < 0) m_Resolved = true;
|
||||
if (m_Invalidated || now >= m_Deadline || !m_Service.TransferAllowed(m_Trip, m_Shell, m_From, m_To, now)) { m_Invalidated = true; return -1; }
|
||||
if (proven) return 1;
|
||||
if (m_Acknowledgement < 0 || !m_Accepted) return -1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+308
@@ -0,0 +1,308 @@
|
||||
// The dedicated manpack operator has no mortar-shell backpack. Preserve the
|
||||
// native chooser and its caller's original index for every eligible gunner.
|
||||
modded class SCR_AIStaticArtilleryActivity
|
||||
{
|
||||
protected override AIAgent FindBestAgentForArtillery(notnull array<AIAgent> agents, notnull IEntity artilleryEntity, out int outIndex)
|
||||
{
|
||||
ERR_MortarSystem system = ERR_MortarSystem.Get();
|
||||
ERR_MortarBattery battery;
|
||||
if (system) battery = system.FindGun(artilleryEntity);
|
||||
if (!battery) return super.FindBestAgentForArtillery(agents, artilleryEntity, outIndex);
|
||||
array<AIAgent> gunners = {};
|
||||
foreach (AIAgent agent : agents)
|
||||
{
|
||||
if (agent && agent.GetControlledEntity() != battery.RadioOperator && ERR_Actors.CanFight(agent.GetControlledEntity()))
|
||||
gunners.Insert(agent);
|
||||
}
|
||||
int filteredIndex;
|
||||
AIAgent selected = super.FindBestAgentForArtillery(gunners, artilleryEntity, filteredIndex);
|
||||
outIndex = -1;
|
||||
if (selected) outIndex = agents.Find(selected);
|
||||
return selected;
|
||||
}
|
||||
}
|
||||
|
||||
// Leave all other artillery (including player-operated captured guns) native.
|
||||
modded class SCR_AIGetArtilleryAimDistanceCompensation
|
||||
{
|
||||
override ENodeResult EOnTaskSimulate(AIAgent owner, float dt)
|
||||
{
|
||||
IEntity gun;
|
||||
GetVariableIn(PORT_ARTILLERY_ENTITY, gun);
|
||||
ERR_MortarSystem system = ERR_MortarSystem.Get();
|
||||
ERR_MortarBattery battery;
|
||||
if (system)
|
||||
battery = system.FindGun(gun);
|
||||
if (!battery)
|
||||
return super.EOnTaskSimulate(owner, dt);
|
||||
battery.AimedAt = -1;
|
||||
battery.AimValidatedAt = -1;
|
||||
battery.AimSolver = null;
|
||||
battery.AimSolution = null;
|
||||
float now = GetGame().GetWorld().GetWorldTime();
|
||||
ERR_HQReport report = system.Latest(battery, now);
|
||||
if (!ERR_HQ.MissionActive()) { system.Withhold(battery, "aim: mission inactive"); return ENodeResult.FAIL; }
|
||||
if (!battery.CommandOpen || battery.OrderShots >= ERR_MortarSalvo.ROUNDS) { system.Withhold(battery, "aim: salvo complete or order closed"); return ENodeResult.FAIL; }
|
||||
if (battery.Released) { system.Withhold(battery, "aim: waiting for committed projectile"); return ENodeResult.FAIL; }
|
||||
if (!report) { system.Withhold(battery, "aim: " + system.ContactFailure(battery)); return ENodeResult.FAIL; }
|
||||
battery.Trace("AIM START", "solving moving intercept", string.Format("latest source=M%1; %2", report.MessageId, report.Description(now)));
|
||||
ERR_MortarAimSolution solution = ERR_Solve(battery, system, report, now);
|
||||
if (!solution) return ENodeResult.FAIL;
|
||||
battery.Impact = solution.Impact;
|
||||
battery.AimOffset = solution.SalvoOffset;
|
||||
battery.AimedShot = battery.OrderShots;
|
||||
battery.FlightTime = solution.FlightTime;
|
||||
battery.AimSolution = solution;
|
||||
battery.AimSolver = this; // weak: do not retain a destroyed native behavior tree
|
||||
battery.AimedAt = now;
|
||||
battery.AimValidatedAt = now;
|
||||
battery.LastFailure = "";
|
||||
battery.Trace("AIM SOLUTION", "ready", string.Format("predicted impact=%1 flight=%2s charge index=%3 aim target=%4 latest source=M%5", solution.Impact, solution.FlightTime, solution.Charge, solution.AimTarget, report.MessageId));
|
||||
SetVariableOut(PORT_AIM_POS, solution.AimTarget);
|
||||
SetVariableOut(PORT_INIT_SPEED_ID, solution.Charge);
|
||||
return ENodeResult.SUCCESS;
|
||||
}
|
||||
|
||||
// Reuse this exact native node's coefficient/elevation APIs. A slow native
|
||||
// barrel can keep its command only if a NEW solve still matches that command.
|
||||
bool ERR_RevalidateAim(ERR_MortarBattery battery, ERR_MortarSystem system, ERR_HQReport report, float now)
|
||||
{
|
||||
IEntity gun;
|
||||
ResourceName ammo;
|
||||
GetVariableIn(PORT_ARTILLERY_ENTITY, gun);
|
||||
GetVariableIn(PORT_AMMO_PREFAB, ammo);
|
||||
ERR_MortarAimSolution commanded = battery.AimSolution;
|
||||
if (battery.AimSolver != this || !commanded || !commanded.Matches(battery, report) || gun != battery.Gun || ammo != ERR_MortarBattery.SHELL)
|
||||
return system.Withhold(battery, "original aim solver or command no longer available");
|
||||
if (!ERR_MortarPrediction.Fresh(report, now)) return system.Withhold(battery, system.ContactFailure(battery));
|
||||
ERR_MortarAimSolution updated = ERR_Solve(battery, system, report, now);
|
||||
if (!updated) return false;
|
||||
if (!ERR_MortarAimRules.Equivalent(commanded.Charge, updated.Charge, commanded.GunOrigin, updated.GunOrigin, commanded.Impact, updated.Impact, commanded.AimTarget, updated.AimTarget, commanded.FlightTime, updated.FlightTime))
|
||||
return system.Withhold(battery, "new ballistic solution requires native re-aim", string.Format("gun drift=%1m impact drift=%2m aim drift=%3m charge=%4/%5 flight=%6/%7s", vector.Distance(commanded.GunOrigin, updated.GunOrigin), vector.Distance(commanded.Impact, updated.Impact), vector.Distance(commanded.AimTarget, updated.AimTarget), commanded.Charge, updated.Charge, commanded.FlightTime, updated.FlightTime));
|
||||
// Keep the ORIGINAL command/impact/flight. Comparing successive refreshed
|
||||
// solutions would allow a moving target to drift away in small increments.
|
||||
battery.AimValidatedAt = now;
|
||||
battery.Trace("AIM REVALIDATED", "same native command", string.Format("fresh ballistic solve matched original; source=M%1 observed age=%2s", report.MessageId, (now - report.ObservedAt) / 1000));
|
||||
return true;
|
||||
}
|
||||
|
||||
// The report may change only after a real HQ downlink. Re-solve against the
|
||||
// ORIGINAL geometry; never replace the commanded solution with an
|
||||
// approximately equivalent successor (which would permit tolerance creep).
|
||||
bool ERR_AdoptReport(ERR_MortarBattery battery, ERR_MortarSystem system, ERR_HQReport report, float now)
|
||||
{
|
||||
IEntity gun;
|
||||
ResourceName ammo;
|
||||
GetVariableIn(PORT_ARTILLERY_ENTITY, gun);
|
||||
GetVariableIn(PORT_AMMO_PREFAB, ammo);
|
||||
ERR_MortarAimSolution commanded = battery.AimSolution;
|
||||
if (battery.Released || battery.RefreshRejected || !battery.CommandOpen || !battery.Refresh || !battery.Refresh.DeliveredFor(battery, report))
|
||||
return system.Withhold(battery, "target update lacks completed delivery for this uncommitted order");
|
||||
if (battery.AimSolver != this || !commanded || !commanded.Matches(battery, battery.Report) || gun != battery.Gun || ammo != ERR_MortarBattery.SHELL)
|
||||
return system.Withhold(battery, "target update lost its original native aim owner");
|
||||
if (battery.AimedAt < 0 || now < battery.AimedAt) return system.Withhold(battery, "original native aim timestamp invalid");
|
||||
if (!ERR_MortarRefreshRules.Newer(battery.Report, report, now)) return system.Withhold(battery, "target update is not a newer fresh same-target observation");
|
||||
ERR_MortarAimSolution updated = ERR_Solve(battery, system, report, now);
|
||||
if (!updated) return false;
|
||||
if (!ERR_MortarAimRules.Equivalent(commanded.Charge, updated.Charge, commanded.GunOrigin, updated.GunOrigin, commanded.Impact, updated.Impact, commanded.AimTarget, updated.AimTarget, commanded.FlightTime, updated.FlightTime))
|
||||
return system.Withhold(battery, "target update requires a different native aim command");
|
||||
// Synchronous adoption changes accepted observation identity together.
|
||||
// Original command time/geometry, order deadline and shot count survive.
|
||||
commanded.Report = report;
|
||||
battery.Report = report;
|
||||
battery.AimValidatedAt = now;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected ERR_MortarAimSolution ERR_Solve(ERR_MortarBattery battery, ERR_MortarSystem system, ERR_HQReport report, float now)
|
||||
{
|
||||
IEntity gun = battery.Gun;
|
||||
vector gunOrigin = gun.GetOrigin();
|
||||
ResourceName ammo;
|
||||
GetVariableIn(PORT_AMMO_PREFAB, ammo);
|
||||
if (ammo != ERR_MortarBattery.SHELL) { system.Withhold(battery, "unsupported ammunition prefab", ammo); return null; }
|
||||
Resource resource = Resource.Load(ammo);
|
||||
if (!resource || !resource.IsValid() || !resource.GetResource()) { system.Withhold(battery, "shell resource load failed", ammo); return null; }
|
||||
IEntitySource source = resource.GetResource().ToEntitySource();
|
||||
if (!source) { system.Withhold(battery, "shell resource has no entity source", ammo); return null; }
|
||||
array<float> coefficients = {};
|
||||
GetAmmoInitialSpeedCoefficients(ammo, coefficients);
|
||||
if (coefficients.IsEmpty())
|
||||
coefficients.Insert(1.0);
|
||||
float minimum, maximum;
|
||||
GetVerticalAimLimits(gun, minimum, maximum);
|
||||
float flight = 30;
|
||||
float offset;
|
||||
int charge;
|
||||
vector impact;
|
||||
vector salvoOffset = ERR_MortarSalvo.Offset(battery.SiteId, battery.OrderId, battery.OrderShots);
|
||||
bool converged;
|
||||
// Iterate the moving intercept using the native shell's flight time.
|
||||
// Reject non-convergence rather than fire with an arbitrary lead time.
|
||||
for (int iteration = 0; iteration < 4; iteration++)
|
||||
{
|
||||
if (!ERR_MortarPrediction.Predict(report, now, flight + 2, impact)) { system.Withhold(battery, "aim prediction: " + ERR_MortarPrediction.FailureReason, string.Format("flight lead=%1s; %2", flight + 2, report.Description(now))); return null; }
|
||||
if (!system.OutsideSettlements(impact)) { system.Withhold(battery, "aim target inside settlement exclusion", system.SettlementReason); return null; }
|
||||
impact += salvoOffset;
|
||||
impact[1] = SCR_TerrainHelper.GetTerrainY(impact);
|
||||
float distance = vector.DistanceXZ(gunOrigin, impact);
|
||||
float altitude = impact[1] - gunOrigin[1];
|
||||
if (distance < 350 || distance > 4000) { system.Withhold(battery, "predicted impact out of range", string.Format("iteration=%1 distance=%2m impact=%3 allowed=350..4000m", iteration, distance, impact)); return null; }
|
||||
bool reachable;
|
||||
float calculated;
|
||||
for (int i = 0; i < coefficients.Count(); i++)
|
||||
{
|
||||
if (!BallisticTable.GetAimHeightOfProjectileAltitudeFromSource(distance, offset, calculated, source, altitude, coefficients[i]))
|
||||
continue;
|
||||
float tangent = (offset + altitude) / distance;
|
||||
if (calculated <= 0 || calculated > 75 || tangent < Math.Tan(minimum) || tangent > Math.Tan(maximum))
|
||||
continue;
|
||||
charge = i;
|
||||
reachable = true;
|
||||
break;
|
||||
}
|
||||
if (!reachable) { system.Withhold(battery, "no native ballistic solution within elevation/flight-time limits", string.Format("iteration=%1 distance=%2 altitude=%3 charges=%4 elevation radians=%5..%6 flight limit=75s", iteration, distance, altitude, coefficients.Count(), minimum, maximum)); return null; }
|
||||
converged = Math.AbsFloat(calculated - flight) < 1;
|
||||
flight = calculated;
|
||||
if (converged)
|
||||
break;
|
||||
}
|
||||
if (!converged) { system.Withhold(battery, "moving intercept did not converge in four iterations"); return null; }
|
||||
if (!system.Safe(battery, impact, flight + 2)) { system.Withhold(battery, "aim impact safety rejected", system.SafetyReason); return null; }
|
||||
ERR_MortarAimSolution solution = new ERR_MortarAimSolution();
|
||||
solution.Gun = gun;
|
||||
solution.Report = report;
|
||||
solution.OrderId = battery.OrderId;
|
||||
solution.Shot = battery.OrderShots;
|
||||
solution.GunOrigin = gunOrigin;
|
||||
solution.Impact = impact;
|
||||
solution.AimTarget = impact + Vector(0, offset, 0);
|
||||
solution.SalvoOffset = salvoOffset;
|
||||
solution.Charge = charge;
|
||||
solution.FlightTime = flight;
|
||||
return solution;
|
||||
}
|
||||
}
|
||||
|
||||
modded class SCR_AIShootStaticArtillery
|
||||
{
|
||||
override ENodeResult EOnTaskSimulate(AIAgent owner, float dt)
|
||||
{
|
||||
IEntity gun;
|
||||
GetVariableIn(PORT_ARTILLERY_ENTITY, gun);
|
||||
ERR_MortarSystem system = ERR_MortarSystem.Get();
|
||||
ERR_MortarBattery battery;
|
||||
if (system)
|
||||
battery = system.FindGun(gun);
|
||||
if (battery && !system.AllowShot(battery))
|
||||
{
|
||||
battery.Trace("SHOOT NODE", "authorization rejected", battery.LastFailure);
|
||||
return ENodeResult.FAIL;
|
||||
}
|
||||
ENodeResult result = super.EOnTaskSimulate(owner, dt);
|
||||
if (battery)
|
||||
battery.Trace("SHOOT NODE", string.Format("native result=%1", result), "authorization passed; node result does not confirm a physical shot");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
modded class SCR_MortarMuzzleComponent
|
||||
{
|
||||
protected override void TransferShellToMortar()
|
||||
{
|
||||
ERR_MortarSystem system = ERR_MortarSystem.Get();
|
||||
ERR_MortarBattery battery;
|
||||
if (system && m_CharController && !EntityUtils.IsPlayer(m_CharController.GetOwner()))
|
||||
battery = system.FindGun(GetOwner());
|
||||
if (battery)
|
||||
{
|
||||
// The loading animation takes time. Recheck at the actual transfer,
|
||||
// before native inventory insertion triggers the physical projectile.
|
||||
if (!system.AllowShot(battery))
|
||||
{
|
||||
if (m_ShellComponent)
|
||||
{
|
||||
m_ShellComponent.GetOnShellUsed().Remove(TransferShellToMortar);
|
||||
m_ShellComponent.SetLoadedState(false);
|
||||
}
|
||||
CharacterAnimationComponent animation = m_CharController.GetAnimationComponent();
|
||||
if (animation && animation.GetCommandHandler())
|
||||
animation.GetCommandHandler().FinishItemUse(false);
|
||||
battery.Audit("ABORT", "final shell transfer rejected; " + battery.LastFailure);
|
||||
return;
|
||||
}
|
||||
battery.Released = true;
|
||||
battery.ReserveShellAirspace(GetGame().GetWorld().GetWorldTime());
|
||||
battery.Audit("SHELL COMMIT", string.Format("final safety passed; gunner=%1 predicted impact=%2 flight=%3s; invoking native transfer, awaiting projectile event", m_CharController.GetOwner(), battery.Impact, battery.FlightTime));
|
||||
}
|
||||
super.TransferShellToMortar();
|
||||
}
|
||||
|
||||
override void OnWeaponFired(IEntity effectEntity, BaseMuzzleComponent muzzle, IEntity projectileEntity)
|
||||
{
|
||||
IEntity firingCharacter;
|
||||
if (m_CharController) firingCharacter = m_CharController.GetOwner();
|
||||
super.OnWeaponFired(effectEntity, muzzle, projectileEntity);
|
||||
ERR_MortarSystem system = ERR_MortarSystem.Get();
|
||||
ERR_MortarBattery battery;
|
||||
if (system)
|
||||
battery = system.FindGun(GetOwner());
|
||||
if (!battery)
|
||||
return;
|
||||
bool playerGunner;
|
||||
if (firingCharacter) playerGunner = EntityUtils.IsPlayer(firingCharacter);
|
||||
battery.ObserveProjectile(projectileEntity, firingCharacter, playerGunner);
|
||||
}
|
||||
}
|
||||
|
||||
// The native tree normally re-aims only every five shots (and skips the first
|
||||
// re-aim). Each salvo shell needs its own spread point and fresh authorization.
|
||||
modded class SCR_AIStaticArtilleryBehavior
|
||||
{
|
||||
ERR_MortarBattery ERR_GetMortarBattery()
|
||||
{
|
||||
ERR_MortarSystem system = ERR_MortarSystem.Get();
|
||||
if (!system) return null;
|
||||
return system.FindGun(m_ArtilleryEntity.m_Value);
|
||||
}
|
||||
}
|
||||
|
||||
modded class SCR_AIStaticArtilleryBehavior_ShootingLogic
|
||||
{
|
||||
override ENodeResult EOnTaskSimulate(AIAgent owner, float dt)
|
||||
{
|
||||
ERR_MortarBattery battery;
|
||||
SCR_AIStaticArtilleryBehavior behavior;
|
||||
if (m_Utility)
|
||||
{
|
||||
behavior = SCR_AIStaticArtilleryBehavior.Cast(m_Utility.GetExecutedAction());
|
||||
if (behavior) battery = behavior.ERR_GetMortarBattery();
|
||||
}
|
||||
if (battery)
|
||||
{
|
||||
int gate = ERR_MortarSupplyRules.CycleGate(battery.CommandOpen, battery.OrderShots, behavior.GetShotCount(), ERR_MortarSalvo.ROUNDS, ERR_MortarSupplyState.READY);
|
||||
if (gate < 0) return ENodeResult.FAIL;
|
||||
if (gate == 0) return ENodeResult.RUNNING;
|
||||
// Native super increments the behavior counter. Wait BEFORE calling it,
|
||||
// so repeated pending ticks cannot count the same projectile twice.
|
||||
if (battery.OrderShots < ERR_MortarSalvo.ROUNDS)
|
||||
{
|
||||
float now = GetGame().GetWorld().GetWorldTime();
|
||||
ERR_MortarSupplyState supply = battery.SupplyState(now);
|
||||
if (supply == ERR_MortarSupplyState.PENDING) return ENodeResult.RUNNING;
|
||||
if (supply == ERR_MortarSupplyState.FAILED)
|
||||
{
|
||||
ERR_MortarSystem system = ERR_MortarSystem.Get();
|
||||
if (system) system.Withhold(battery, system.SupplyFailure(battery, now));
|
||||
return ENodeResult.FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
ENodeResult result = super.EOnTaskSimulate(owner, dt);
|
||||
if (battery && result == ENodeResult.SUCCESS)
|
||||
{
|
||||
SetVariableOut(PORT_AIMING_NEEDED, true);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+347
@@ -0,0 +1,347 @@
|
||||
class ERR_MortarSupplyStatus
|
||||
{
|
||||
SCR_AIGroup Group;
|
||||
IEntity Gun;
|
||||
vector Position;
|
||||
float ObservedAt;
|
||||
int MessageId;
|
||||
int Shells;
|
||||
}
|
||||
class ERR_MortarLogisticsRules
|
||||
{
|
||||
static bool Fresh(float observedAt, float now) { return observedAt >= 0 && now >= observedAt && now - observedAt <= 180000; }
|
||||
static bool Need(int shells) { return shells >= 0 && shells <= 6; }
|
||||
static bool NewRequest(int message, int consumed, bool fresh) { return fresh && message > 0 && message > consumed; }
|
||||
static bool Near(float distance) { return distance >= 0 && distance <= 12; }
|
||||
static bool ActiveTrip(bool server, bool stopped, int trip, int current, bool fresh, bool ownershipValid) { return server && !stopped && trip > 0 && trip == current && fresh && ownershipValid; }
|
||||
}
|
||||
// One finite western-cache courier, owned and ticked exclusively by HQ.
|
||||
class ERR_MortarLogistics
|
||||
{
|
||||
protected ERR_HQ m_HQ;
|
||||
protected ERR_GameMode m_Mode;
|
||||
protected ERR_SiteRuntime m_Site;
|
||||
protected IEntity m_Cache;
|
||||
protected SCR_AIGroup m_Group;
|
||||
protected ERR_MortarBattery m_Battery;
|
||||
protected IEntity m_OriginalGun;
|
||||
protected SCR_AIGroup m_OriginalBatteryGroup;
|
||||
protected ref array<IEntity> m_BatteryMembers = {};
|
||||
protected ref array<IEntity> m_OriginalMembers = {};
|
||||
protected ref array<IEntity> m_Stock = {};
|
||||
protected ref array<IEntity> m_Cargo = {};
|
||||
protected ref array<IEntity> m_AttemptedPickup = {};
|
||||
protected ref array<IEntity> m_AttemptedUnload = {};
|
||||
protected ref array<ref ERR_MortarInitialItem> m_Initial = {};
|
||||
protected ref ERR_MortarSupplyStatus m_Report;
|
||||
protected ref ERR_HQRoute m_Order;
|
||||
protected ref ERR_MortarCourierTransfer m_Move;
|
||||
protected IEntity m_Courier;
|
||||
protected AIWaypoint m_Waypoint;
|
||||
protected AIWaypoint m_HoldWaypoint;
|
||||
protected float m_InitialDeadline;
|
||||
protected float m_NextReport;
|
||||
protected float m_LastDeliveryAt = -1;
|
||||
protected int m_ConsumedMessage;
|
||||
protected int m_Trip;
|
||||
protected int m_State; // 0 initial, 1 idle, 2 downlink, 3 pickup, 4 travel, 5 unload
|
||||
protected int m_Delivered;
|
||||
protected int m_CompletedTrips;
|
||||
protected bool m_Started;
|
||||
protected bool m_Stopped;
|
||||
protected string m_Reason;
|
||||
static const int INITIAL_SHELLS = 12;
|
||||
static const int TRIP_SHELLS = 6;
|
||||
void ERR_MortarLogistics(ERR_HQ hq, ERR_GameMode mode) { m_HQ = hq; m_Mode = mode; }
|
||||
int State() { return m_State; }
|
||||
int Trip() { return m_Trip; }
|
||||
int Delivered() { return m_Delivered; }
|
||||
int CompletedTrips() { return m_CompletedTrips; }
|
||||
IEntity Cache() { return m_Cache; }
|
||||
SCR_AIGroup Group() { return m_Group; }
|
||||
IEntity Courier() { return m_Courier; }
|
||||
ERR_MortarBattery Battery() { return m_Battery; }
|
||||
ERR_MortarSupplyStatus Report() { return m_Report; }
|
||||
bool Stopped() { return m_Stopped; }
|
||||
void InitialStock(out array<IEntity> items) { items.Copy(m_Stock); }
|
||||
protected void Audit(string kind, string reason)
|
||||
{
|
||||
int message; if (m_Report) message = m_Report.MessageId;
|
||||
m_Reason = reason;
|
||||
m_HQ.Journal().Write("COURIER " + kind, string.Format("trip=%1 state=%2 delivered=%3; %4", m_Trip, m_State, m_Delivered, reason), message);
|
||||
}
|
||||
protected InventoryStorageManagerComponent Inventory(IEntity entity)
|
||||
{
|
||||
if (!entity) return null;
|
||||
return InventoryStorageManagerComponent.Cast(entity.FindComponent(InventoryStorageManagerComponent));
|
||||
}
|
||||
protected bool Contains(IEntity owner, IEntity shell)
|
||||
{
|
||||
InventoryStorageManagerComponent inventory = Inventory(owner);
|
||||
if (!inventory || !shell) return false;
|
||||
array<IEntity> items = {}; ERR_Inventory.Items(inventory, items); return items.Contains(shell);
|
||||
}
|
||||
protected void Shells(IEntity owner, out array<IEntity> shells)
|
||||
{
|
||||
shells.Clear(); InventoryStorageManagerComponent inventory = Inventory(owner); if (!inventory) return;
|
||||
array<IEntity> items = {}; ERR_Inventory.Items(inventory, items);
|
||||
foreach (IEntity item : items)
|
||||
if (item && item.GetPrefabData() && item.GetPrefabData().GetPrefabName() == ERR_MortarBattery.SHELL) shells.Insert(item);
|
||||
}
|
||||
bool Prepare()
|
||||
{
|
||||
if (!Replication.IsServer() || m_Started || m_Stopped) return false;
|
||||
m_Started = true;
|
||||
ERR_ScenarioComponent scenario = ERR_ScenarioComponent.Cast(m_Mode.FindComponent(ERR_ScenarioComponent));
|
||||
if (!scenario || !scenario.Sites()) return false;
|
||||
m_Site = scenario.Sites().Find("everon-supply-2");
|
||||
if (!m_Site || !m_Site.Group || !m_Site.PatrolCycle || !scenario.Sites().Owns(m_Site.Container)) return false;
|
||||
m_Cache = m_Site.Container; m_Group = m_Site.Group;
|
||||
array<ref ERR_MortarBattery> batteries = {}; m_HQ.Mortars().Batteries(batteries);
|
||||
if (batteries.Count() != 1) return false;
|
||||
m_Battery = batteries[0]; m_OriginalGun = m_Battery.Gun; m_OriginalBatteryGroup = m_Battery.Group;
|
||||
InventoryStorageManagerComponent inventory = Inventory(m_Cache);
|
||||
if (!inventory || !inventory.CanInsertResource(ERR_MortarBattery.SHELL, EStoragePurpose.PURPOSE_DEPOSIT)) return false;
|
||||
array<IEntity> existing = {}; Shells(m_Cache, existing); if (!existing.IsEmpty()) return false;
|
||||
array<AIAgent> agents = {}; m_Group.GetAgents(agents);
|
||||
foreach (AIAgent agent : agents)
|
||||
{
|
||||
if (!agent || !ERR_Actors.CanFight(agent.GetControlledEntity())) return false;
|
||||
m_OriginalMembers.Insert(agent.GetControlledEntity());
|
||||
}
|
||||
if (m_OriginalMembers.Count() != m_Site.InitialGuardCount) return false;
|
||||
m_InitialDeadline = GetGame().GetWorld().GetWorldTime() + 15000;
|
||||
// SiteService owns this existing cache and its contents through rollback/world teardown.
|
||||
// This bounded startup allocation is never reopened, including partial failure/theft.
|
||||
for (int i = 0; i < INITIAL_SHELLS; i++)
|
||||
{
|
||||
IEntity shell = ERR_Actors.Spawn(ERR_MortarBattery.SHELL, m_Cache.GetOrigin());
|
||||
if (!shell) return false;
|
||||
// Keep the exact created identity; counts cannot prove an external item was
|
||||
// not substituted while an asynchronous insertion was pending.
|
||||
m_Mode.Own(shell); m_Stock.Insert(shell);
|
||||
ERR_MortarInitialItem operation = new ERR_MortarInitialItem(); m_Initial.Insert(operation);
|
||||
if (!inventory.TryInsertItem(shell, EStoragePurpose.PURPOSE_DEPOSIT, operation)) { operation.Rejected(); return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
protected bool OriginalGroup()
|
||||
{
|
||||
if (!m_Site || m_Site.Group != m_Group || m_Site.Container != m_Cache || !m_Group || !m_Cache || m_Group.GetPlayerCount(true) > 0 || m_Group.GetFaction() != m_Mode.EnemyFaction()) return false;
|
||||
array<AIAgent> agents = {}; m_Group.GetAgents(agents); bool living;
|
||||
foreach (AIAgent agent : agents)
|
||||
{
|
||||
if (!agent || !agent.GetControlledEntity()) continue;
|
||||
IEntity actor = agent.GetControlledEntity();
|
||||
if (!m_OriginalMembers.Contains(actor) || ERR_Actors.Player(actor) || ERR_Actors.Group(actor) != m_Group || SCR_FactionManager.SGetFaction(actor) != m_Mode.EnemyFaction()) return false;
|
||||
if (ERR_Actors.CanFight(actor)) living = true;
|
||||
}
|
||||
return living;
|
||||
}
|
||||
protected bool BindBatteryMembers()
|
||||
{
|
||||
if (!m_Battery || !m_Battery.StartupActivated || m_Battery.Group != m_OriginalBatteryGroup || m_Battery.Gun != m_OriginalGun) return false;
|
||||
if (!m_BatteryMembers.IsEmpty()) return true;
|
||||
array<AIAgent> agents = {}; m_Battery.Group.GetAgents(agents);
|
||||
foreach (AIAgent agent : agents)
|
||||
if (agent && agent.GetControlledEntity() && !m_BatteryMembers.Contains(agent.GetControlledEntity())) m_BatteryMembers.Insert(agent.GetControlledEntity());
|
||||
return !m_BatteryMembers.IsEmpty();
|
||||
}
|
||||
bool TargetAvailable()
|
||||
{
|
||||
if (!m_Battery || m_Battery.Gun != m_OriginalGun || m_Battery.Group != m_OriginalBatteryGroup || !m_OriginalGun || !m_Battery.StartupActivated || m_Battery.Stopped() || !m_Battery.Operational() || m_HQ.Mortars().FindGun(m_OriginalGun) != m_Battery || m_BatteryMembers.IsEmpty()) return false;
|
||||
if (SCR_FactionManager.SGetFaction(m_OriginalGun) != m_Mode.EnemyFaction()) return false;
|
||||
SCR_AIVehicleUsageComponent usage = SCR_AIVehicleUsageComponent.Cast(m_OriginalGun.FindComponent(SCR_AIVehicleUsageComponent));
|
||||
if (!usage || !usage.GetTurretCompartmentSlot()) return false;
|
||||
IEntity occupant = usage.GetTurretCompartmentSlot().GetOccupant();
|
||||
if (!occupant) return true;
|
||||
return m_BatteryMembers.Contains(occupant) && ERR_Actors.CanFight(occupant) && !ERR_Actors.Player(occupant) && ERR_Actors.Group(occupant) == m_OriginalBatteryGroup && SCR_FactionManager.SGetFaction(occupant) == m_Mode.EnemyFaction();
|
||||
}
|
||||
bool CanSample(ERR_MortarBattery battery) { return battery == m_Battery && TargetAvailable(); }
|
||||
protected bool Bearer(IEntity actor)
|
||||
{
|
||||
return actor && m_OriginalMembers.Contains(actor) && ERR_Actors.Group(actor) == m_Group && ERR_Actors.CanFight(actor) && !ERR_Actors.Player(actor) && !CompartmentAccessComponent.GetVehicleIn(actor) && SCR_FactionManager.SGetFaction(actor) == m_Mode.EnemyFaction() && ERR_HQRadio.Capability(actor) != 2;
|
||||
}
|
||||
bool AcceptsStatus(ERR_MortarSupplyStatus report, float now)
|
||||
{
|
||||
return !m_Stopped && m_State == 1 && report && m_Battery && report.Group == m_Battery.Group && report.Gun == m_Battery.Gun && ERR_MortarLogisticsRules.Need(report.Shells) && ERR_MortarLogisticsRules.NewRequest(report.MessageId, m_ConsumedMessage, ERR_MortarLogisticsRules.Fresh(report.ObservedAt, now));
|
||||
}
|
||||
void Receive(ERR_MortarSupplyStatus report, float now)
|
||||
{
|
||||
if (!AcceptsStatus(report, now)) return;
|
||||
m_Report = report;
|
||||
Audit("RECEIVED", "immutable low-ammunition status delivered by native physical radio route");
|
||||
}
|
||||
protected bool Authorized(float now)
|
||||
{
|
||||
if (!m_Mode || !m_Mode.Active() || !m_Report || !m_Battery) return false;
|
||||
return ERR_MortarLogisticsRules.ActiveTrip(Replication.IsServer(), m_Stopped, m_Trip, m_HQ.LogisticsToken(m_Group), ERR_MortarLogisticsRules.Fresh(m_Report.ObservedAt, now), OriginalGroup()) && TargetAvailable() && m_Report.Gun == m_Battery.Gun && m_Report.Group == m_Battery.Group && vector.Distance(m_Report.Position, m_Battery.Gun.GetOrigin()) <= 5;
|
||||
}
|
||||
bool TransferAllowed(int trip, IEntity shell, IEntity from, IEntity to, float now)
|
||||
{
|
||||
if (trip != m_Trip || !Authorized(now) || !shell || !m_Stock.Contains(shell) || !Bearer(m_Courier)) return false;
|
||||
if (m_State == 3) return from == m_Cache && to == m_Courier && ERR_MortarLogisticsRules.Near(vector.Distance(from.GetOrigin(), to.GetOrigin()));
|
||||
if (m_State != 5 || from != m_Courier || !m_Cargo.Contains(shell) || !ERR_Actors.CanFight(to) || ERR_Actors.Player(to) || to == m_Battery.RadioOperator || !m_BatteryMembers.Contains(to) || ERR_Actors.Group(to) != m_Battery.Group || SCR_FactionManager.SGetFaction(to) != m_Mode.EnemyFaction()) return false;
|
||||
return !CompartmentAccessComponent.GetVehicleIn(to) && ERR_MortarLogisticsRules.Near(vector.Distance(from.GetOrigin(), to.GetOrigin()));
|
||||
}
|
||||
protected void ClearWaypoint()
|
||||
{
|
||||
if (m_Waypoint) { if (m_Group) m_Group.RemoveWaypoint(m_Waypoint); ERR_Actors.DeleteSpawned(m_Waypoint); }
|
||||
m_Waypoint = null;
|
||||
if (m_HoldWaypoint) { if (m_Group) m_Group.RemoveWaypoint(m_HoldWaypoint); ERR_Actors.DeleteSpawned(m_HoldWaypoint); }
|
||||
m_HoldWaypoint = null;
|
||||
}
|
||||
protected bool MoveTo(vector position)
|
||||
{
|
||||
ClearWaypoint();
|
||||
m_Waypoint = AIWaypoint.Cast(ERR_Actors.Spawn(ERR_Resources.MoveWaypoint(), ERR_Actors.Ground(position)));
|
||||
if (!m_Waypoint) return false;
|
||||
// A completed Move alone would let the original patrol resume during the
|
||||
// asynchronous item operation. This owned, non-completing native Defend
|
||||
// keeps that queue below us until release; its original entries are untouched.
|
||||
m_HoldWaypoint = AIWaypoint.Cast(ERR_Actors.Spawn(ERR_Resources.DefendWaypoint(), ERR_Actors.Ground(position)));
|
||||
if (!m_HoldWaypoint) { ClearWaypoint(); return false; }
|
||||
m_HoldWaypoint.SetCompletionRadius(8);
|
||||
m_Group.AddWaypointAt(m_HoldWaypoint, 0); m_Group.AddWaypointAt(m_Waypoint, 0); m_Group.ActivateAI(); return true;
|
||||
}
|
||||
protected void Finish(string reason)
|
||||
{
|
||||
Audit("END", reason);
|
||||
if (m_Move) m_Move.Invalidate();
|
||||
ClearWaypoint(); m_HQ.ReleaseLogistics(m_Group, m_Trip);
|
||||
m_Order = null; m_Report = null; m_Courier = null; m_Cargo.Clear(); m_State = 1;
|
||||
// Native requests cannot be canceled. A pending move remains held until its
|
||||
// physical outcome resolves; callbacks cannot authorize a later trip.
|
||||
m_NextReport = GetGame().GetWorld().GetWorldTime() + 60000;
|
||||
}
|
||||
protected bool BeginMove(IEntity shell, IEntity from, IEntity to, float now)
|
||||
{
|
||||
InventoryStorageManagerComponent destination = Inventory(to);
|
||||
BaseInventoryStorageComponent storage; if (destination) storage = destination.FindStorageForItem(shell, EStoragePurpose.PURPOSE_DEPOSIT);
|
||||
if (!storage) return false;
|
||||
m_Move = new ERR_MortarCourierTransfer(this, m_Trip, shell, from, to, storage, now);
|
||||
return m_Move.Begin(now);
|
||||
}
|
||||
protected bool PollMove(float now)
|
||||
{
|
||||
if (!m_Move) return true;
|
||||
int result = m_Move.Poll(now);
|
||||
if (result == 0) return false;
|
||||
if (result < 0) { Finish("native item operation rejected, expired or lost authority; physical items retained"); return false; }
|
||||
IEntity shell = m_Move.Shell(); m_Move = null;
|
||||
if (m_State == 3) { m_Cargo.Insert(shell); Audit("PICKUP", "exact depot shell confirmed in original bearer inventory"); }
|
||||
else if (m_State == 5) { m_Cargo.RemoveItem(shell); m_Delivered++; m_LastDeliveryAt = now; Audit("DELIVERED", "exact original shell confirmed in living mortar crew inventory"); }
|
||||
return true;
|
||||
}
|
||||
protected void Pickup(float now)
|
||||
{
|
||||
if (!m_Courier)
|
||||
{
|
||||
foreach (IEntity candidate : m_OriginalMembers)
|
||||
if (Bearer(candidate) && Inventory(candidate) && Inventory(candidate).CanInsertResource(ERR_MortarBattery.SHELL, EStoragePurpose.PURPOSE_DEPOSIT) && ERR_MortarLogisticsRules.Near(vector.Distance(candidate.GetOrigin(), m_Cache.GetOrigin()))) { m_Courier = candidate; break; }
|
||||
if (!m_Courier) return;
|
||||
}
|
||||
if (!Bearer(m_Courier)) { Finish("original bearer lost or captured"); return; }
|
||||
if (!PollMove(now)) return;
|
||||
if (m_Cargo.Count() < TRIP_SHELLS)
|
||||
{
|
||||
foreach (IEntity shell : m_Stock)
|
||||
{
|
||||
if (!shell || !Contains(m_Cache, shell) || m_AttemptedPickup.Contains(shell)) continue;
|
||||
InventoryStorageManagerComponent destination = Inventory(m_Courier);
|
||||
if (!destination || !destination.FindStorageForItem(shell, EStoragePurpose.PURPOSE_DEPOSIT)) break;
|
||||
m_AttemptedPickup.Insert(shell);
|
||||
if (!BeginMove(shell, m_Cache, m_Courier, now)) Finish("native pickup rejected; no second request for that shell");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (m_Cargo.IsEmpty()) { Finish("finite cache empty or bearer native storage full"); return; }
|
||||
m_State = 4;
|
||||
if (!MoveTo(m_Report.Position)) { Finish("native delivery waypoint unavailable"); return; }
|
||||
Audit("OUTBOUND", "original patrol walking with verified finite cargo to reported battery position");
|
||||
}
|
||||
protected void Unload(float now)
|
||||
{
|
||||
if (!PollMove(now)) return;
|
||||
if (m_Cargo.IsEmpty()) { m_CompletedTrips++; Finish("finite delivery complete; original patrol restored"); return; }
|
||||
array<AIAgent> agents = {}; m_Battery.Group.GetAgents(agents);
|
||||
foreach (IEntity shell : m_Cargo)
|
||||
{
|
||||
if (!shell || !Contains(m_Courier, shell)) { Finish("carried shell lost or stolen; no replacement"); return; }
|
||||
if (m_AttemptedUnload.Contains(shell)) { Finish("previous unload unresolved or failed; no retry"); return; }
|
||||
foreach (AIAgent agent : agents)
|
||||
{
|
||||
if (!agent) continue;
|
||||
IEntity recipient = agent.GetControlledEntity();
|
||||
if (!TransferAllowed(m_Trip, shell, m_Courier, recipient, now)) continue;
|
||||
InventoryStorageManagerComponent destination = Inventory(recipient);
|
||||
if (!destination || !destination.FindStorageForItem(shell, EStoragePurpose.PURPOSE_DEPOSIT)) continue;
|
||||
m_AttemptedUnload.Insert(shell);
|
||||
if (!BeginMove(shell, m_Courier, recipient, now)) Finish("native recipient move rejected; cargo remains physical");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Tick(float now)
|
||||
{
|
||||
if (m_Stopped || !m_Started || !Replication.IsServer() || !m_Mode.Active()) return;
|
||||
if (m_State == 0)
|
||||
{
|
||||
bool failed; foreach (ERR_MortarInitialItem operation : m_Initial) { if (!operation.Finished) { if (now < m_InitialDeadline) return; failed = true; } else if (!operation.Succeeded) failed = true; }
|
||||
if (!OriginalGroup()) failed = true;
|
||||
array<IEntity> shells = {}; Shells(m_Cache, shells);
|
||||
if (shells.Count() != INITIAL_SHELLS) { if (now < m_InitialDeadline && !failed) return; failed = true; }
|
||||
foreach (IEntity originalShell : m_Stock) if (!originalShell || !shells.Contains(originalShell)) failed = true;
|
||||
if (m_Stock.Count() != INITIAL_SHELLS) failed = true;
|
||||
if (failed) { Audit("STARTUP FAILED", "one-time native stock allocation incomplete; no refill"); Stop(); return; }
|
||||
m_State = 1; Audit("READY", "12 distinct original HE shells physically confirmed in existing western cache");
|
||||
}
|
||||
if (m_State == 1)
|
||||
{
|
||||
if (m_Move) { m_Move.Poll(now); if (m_Move.Unresolved()) return; m_Move = null; }
|
||||
if (!OriginalGroup() || !BindBatteryMembers() || !TargetAvailable()) return;
|
||||
bool available; foreach (IEntity stocked : m_Stock) if (Contains(m_Cache, stocked) && !m_AttemptedPickup.Contains(stocked)) available = true;
|
||||
if (!available) return;
|
||||
if (now >= m_NextReport && ERR_MortarLogisticsRules.Need(m_Battery.CountShells())) { if (m_HQ.QueueSupplyStatus(m_Battery, now)) m_NextReport = now + 60000; }
|
||||
if (!m_Report || !ERR_MortarLogisticsRules.NewRequest(m_Report.MessageId, m_ConsumedMessage, ERR_MortarLogisticsRules.Fresh(m_Report.ObservedAt, now))) return;
|
||||
int nextTrip = m_Trip + 1;
|
||||
if (!m_HQ.ReserveLogistics(m_Group, nextTrip)) return;
|
||||
m_Trip = nextTrip; m_ConsumedMessage = m_Report.MessageId;
|
||||
m_Order = m_HQ.Downlink(m_Group, "MORTAR SUPPLY", "collect finite western-cache HE; walk to reported battery", m_Report.MessageId);
|
||||
m_State = 2;
|
||||
if (!m_Order) { Finish("no native courier downlink"); return; }
|
||||
Audit("QUEUED", "original patrol reserved pending physical radio command delivery");
|
||||
}
|
||||
if (!Authorized(now)) { Finish("supply report expired or original ownership/gun/group changed"); return; }
|
||||
if (m_Courier && !Bearer(m_Courier)) { Finish("original bearer unavailable; surviving cargo retained"); return; }
|
||||
if (m_State == 2)
|
||||
{
|
||||
int delivered = m_Order.Advance(now, m_HQ.Journal());
|
||||
if (delivered == 0) return;
|
||||
if (delivered < 0) { Finish("courier command interrupted"); return; }
|
||||
m_Order = null; m_State = 3;
|
||||
if (!MoveTo(m_Cache.GetOrigin())) { Finish("native pickup waypoint unavailable"); return; }
|
||||
Audit("DISPATCH", "same original patrol receives delivered order and walks to finite cache");
|
||||
}
|
||||
if (m_State == 3) { Pickup(now); return; }
|
||||
if (m_State == 4)
|
||||
{
|
||||
if (!ERR_MortarLogisticsRules.Near(vector.Distance(m_Courier.GetOrigin(), m_Report.Position))) return;
|
||||
m_State = 5; Audit("ARRIVED", "physical bearer reached reported gun area; exact recipient checks still required");
|
||||
}
|
||||
if (m_State == 5) Unload(now);
|
||||
}
|
||||
void Stop()
|
||||
{
|
||||
if (m_Stopped) return;
|
||||
m_Stopped = true;
|
||||
if (m_Move) m_Move.Invalidate();
|
||||
ClearWaypoint(); m_HQ.ReleaseLogistics(m_Group, m_Trip); m_Order = null; m_Report = null;
|
||||
}
|
||||
#ifdef WORKBENCH
|
||||
ERR_HQRoute ProbeOrder() { return m_Order; }
|
||||
int ProbeSourceMessage() { return m_ConsumedMessage; }
|
||||
float ProbeLastDelivery() { return m_LastDeliveryAt; }
|
||||
#endif
|
||||
string Status() { return string.Format("courier state=%1 finite initial=%2 delivered=%3 stopped=%4 (%5)", m_State, m_Stock.Count(), m_Delivered, m_Stopped, m_Reason); }
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
// All target coordinates come from delivered HQ observations, never a player's
|
||||
// current transform. Road projection stops at an unknown junction/road end.
|
||||
class ERR_MortarPrediction
|
||||
{
|
||||
// Read immediately after a failed synchronous prediction; no extra world scan.
|
||||
static string FailureReason;
|
||||
// Includes observation uplink, HQ downlink and native aiming. All stages
|
||||
// retain the original observation time; radio delivery never renews it.
|
||||
static const float MAX_REPORT_AGE_MS = 60000;
|
||||
|
||||
static bool Fresh(ERR_HQReport report, float now)
|
||||
{
|
||||
return report && report.Target && !report.Distress && now >= report.ObservedAt && now - report.ObservedAt <= MAX_REPORT_AGE_MS && report.TravelMode != "aircraft";
|
||||
}
|
||||
|
||||
static bool Reject(string reason)
|
||||
{
|
||||
FailureReason = reason;
|
||||
return false;
|
||||
}
|
||||
|
||||
static vector ClosestOnSegment(vector point, vector start, vector end)
|
||||
{
|
||||
vector segment = end - start;
|
||||
segment[1] = 0;
|
||||
vector offset = point - start;
|
||||
offset[1] = 0;
|
||||
float lengthSq = segment.LengthSq();
|
||||
if (lengthSq < 0.01)
|
||||
return start;
|
||||
return vector.Lerp(start, end, Math.Clamp(vector.Dot(offset, segment) / lengthSq, 0, 1));
|
||||
}
|
||||
|
||||
static bool FollowRoad(array<vector> points, vector observed, vector velocity, float seconds, out vector impact)
|
||||
{
|
||||
if (!points || points.Count() < 2)
|
||||
return Reject("road polyline has fewer than two points");
|
||||
int segment = -1;
|
||||
float closest = 20;
|
||||
vector projected;
|
||||
for (int i = 0; i < points.Count() - 1; i++)
|
||||
{
|
||||
vector candidate = ClosestOnSegment(observed, points[i], points[i + 1]);
|
||||
float distance = vector.DistanceXZ(candidate, observed);
|
||||
if (distance < closest)
|
||||
{
|
||||
closest = distance;
|
||||
segment = i;
|
||||
projected = candidate;
|
||||
}
|
||||
}
|
||||
if (segment < 0)
|
||||
return Reject("observed vehicle is not within 20m of a road segment");
|
||||
vector forward = points[segment + 1] - points[segment];
|
||||
forward[1] = 0;
|
||||
forward.Normalize();
|
||||
vector travel = velocity;
|
||||
travel[1] = 0;
|
||||
float speed = travel.Length();
|
||||
travel.Normalize();
|
||||
float alignment = vector.Dot(forward, travel);
|
||||
if (Math.AbsFloat(alignment) < 0.7)
|
||||
return Reject("vehicle heading crosses road; alignment below 0.7");
|
||||
int step = 1;
|
||||
int next = segment + 1;
|
||||
if (alignment < 0)
|
||||
{
|
||||
step = -1;
|
||||
next = segment;
|
||||
}
|
||||
float remaining = speed * seconds;
|
||||
vector position = projected;
|
||||
while (next >= 0 && next < points.Count())
|
||||
{
|
||||
float length = vector.DistanceXZ(position, points[next]);
|
||||
if (length >= remaining && length > 0.01)
|
||||
{
|
||||
impact = vector.Lerp(position, points[next], remaining / length);
|
||||
return true;
|
||||
}
|
||||
remaining -= length;
|
||||
position = points[next];
|
||||
next += step;
|
||||
}
|
||||
return Reject("prediction reaches unknown road end/junction");
|
||||
}
|
||||
|
||||
static bool Predict(ERR_HQReport report, float now, float flightSeconds, out vector impact)
|
||||
{
|
||||
FailureReason = "";
|
||||
if (!report) return Reject("target report missing");
|
||||
if (report.Distress) return Reject("distress report cannot authorize fire");
|
||||
if (now < report.ObservedAt) return Reject("observation timestamp is in the future");
|
||||
if (now - report.ObservedAt > MAX_REPORT_AGE_MS) return Reject("observation older than 60s");
|
||||
float seconds = (now - report.ObservedAt) / 1000 + flightSeconds;
|
||||
if (seconds < 0 || seconds > 90)
|
||||
return Reject("observation age plus flight outside 0..90s");
|
||||
if (report.TravelMode == "aircraft") return Reject("aircraft target unsupported");
|
||||
if (report.TravelMode == "mixed travel") return Reject("mixed travel cannot be predicted");
|
||||
impact = report.Position;
|
||||
if (!report.HasVelocity || report.Velocity.Length() < 0.5)
|
||||
return true;
|
||||
if (report.Velocity.Length() * seconds > 1800)
|
||||
return Reject("predicted travel exceeds 1800m");
|
||||
bool vehicle = report.TravelMode == "vehicle" || report.TravelMode == "in vehicle";
|
||||
if (vehicle)
|
||||
{
|
||||
SCR_AIWorld world = SCR_AIWorld.Cast(GetGame().GetAIWorld());
|
||||
if (!world || !world.GetRoadNetworkManager())
|
||||
return Reject("road network unavailable");
|
||||
BaseRoad road;
|
||||
float roadDistance;
|
||||
world.GetRoadNetworkManager().GetClosestRoad(report.Position, road, roadDistance, skipNavlinks: true);
|
||||
if (!road) return Reject("no road near reported vehicle position");
|
||||
if (roadDistance > 20) return Reject("reported vehicle farther than 20m from road");
|
||||
array<vector> points = {};
|
||||
road.GetPoints(points);
|
||||
return FollowRoad(points, report.Position, report.Velocity, seconds, impact);
|
||||
}
|
||||
impact += report.Velocity * seconds;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ThreatensFriendly(vector impact, vector position, vector velocity, float flightSeconds)
|
||||
{
|
||||
velocity[1] = 0;
|
||||
vector future = position + velocity * Math.Clamp(flightSeconds, 0, 90);
|
||||
// Protect the entire predicted path, not only where a soldier is now.
|
||||
return vector.DistanceXZ(impact, ClosestOnSegment(impact, position, future)) <= 100;
|
||||
}
|
||||
}
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
enum ERR_MortarSupplyState { FAILED = -1, PENDING, READY }
|
||||
|
||||
class ERR_MortarSupplyRules
|
||||
{
|
||||
static int CycleGate(bool commandOpen, int confirmedShots, int behaviorShots, int limit, int supplyState)
|
||||
{
|
||||
if (!commandOpen) return -1;
|
||||
if (confirmedShots <= behaviorShots) return 0;
|
||||
if (confirmedShots < limit) return supplyState;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
class ERR_MortarInitialItem : ScriptedInventoryOperationCallback
|
||||
{
|
||||
bool Finished;
|
||||
bool Succeeded;
|
||||
protected void Finish(bool success) { if (!Finished) { Finished = true; Succeeded = success; } }
|
||||
protected override void OnComplete() { Finish(true); }
|
||||
protected override void OnFailed() { Finish(false); }
|
||||
protected override void OnAllComplete() { Finish(true); }
|
||||
protected override void OnAllFailed() { Finish(false); }
|
||||
void Rejected() { Finish(false); }
|
||||
}
|
||||
|
||||
// One finite allocation, after the ENTIRE original support crew exists. Failed
|
||||
// or stopped setup retains physical assets and retires only its pending spawn intent.
|
||||
class ERR_MortarReadiness
|
||||
{
|
||||
protected ERR_MortarBattery m_Battery;
|
||||
protected ref array<IEntity> m_Members = {};
|
||||
protected ref array<ref ERR_MortarInitialItem> m_Operations = {};
|
||||
protected bool m_Allocated;
|
||||
protected bool m_Ready;
|
||||
protected bool m_Stopped;
|
||||
protected bool m_Retired;
|
||||
protected int m_RequestedShells;
|
||||
void ERR_MortarReadiness(ERR_MortarBattery battery) { m_Battery = battery; }
|
||||
static ERR_MortarSupplyState Population(int physical, int gunners, int operators, bool complete, bool expired)
|
||||
{
|
||||
if (physical > 4 || gunners > 3 || operators > 1) return ERR_MortarSupplyState.FAILED;
|
||||
if (physical == 4 && gunners == 3 && operators == 1 && complete && !expired) return ERR_MortarSupplyState.READY;
|
||||
if (expired) return ERR_MortarSupplyState.FAILED;
|
||||
return ERR_MortarSupplyState.PENDING;
|
||||
}
|
||||
static bool AllocationValid(int shells) { return shells >= 0 && shells <= 9; }
|
||||
int RequestedShells() { return m_RequestedShells; }
|
||||
bool Ready() { return m_Ready && !m_Stopped; }
|
||||
void Stop()
|
||||
{
|
||||
if (m_Stopped) return;
|
||||
m_Stopped = true;
|
||||
if (m_Ready || m_Retired || !m_Battery || !m_Battery.Group || !Replication.IsServer()) return;
|
||||
m_Retired = true;
|
||||
// As in Core initial guard cleanup: stopping the current request alone does
|
||||
// not retire the native group's still-authored future expansion intent.
|
||||
RetireGroup(m_Battery.Group);
|
||||
}
|
||||
static void RetireGroup(SCR_AIGroup group)
|
||||
{
|
||||
if (!group || !Replication.IsServer()) return;
|
||||
group.SetSpawnImmediately(false);
|
||||
group.m_aUnitPrefabSlots = new array<ResourceName>();
|
||||
if (!GetGame()) return;
|
||||
ChimeraAIWorld world = ChimeraAIWorld.Cast(GetGame().GetAIWorld());
|
||||
if (world) world.PurgeSpawnRequestsForGroup(group);
|
||||
}
|
||||
|
||||
protected ERR_MortarSupplyState Fail(string reason)
|
||||
{
|
||||
m_Battery.SpawnFailed(reason);
|
||||
Stop();
|
||||
return ERR_MortarSupplyState.FAILED;
|
||||
}
|
||||
protected bool Allocate()
|
||||
{
|
||||
m_Allocated = true; // before native requests; never refill or retry a failure
|
||||
foreach (IEntity actor : m_Members)
|
||||
{
|
||||
if (actor == m_Battery.RadioOperator) continue;
|
||||
InventoryStorageManagerComponent inventory = InventoryStorageManagerComponent.Cast(actor.FindComponent(InventoryStorageManagerComponent));
|
||||
int existing = m_Battery.InventoryShells(inventory);
|
||||
if (!AllocationValid(existing)) return false;
|
||||
if (ERR_HQRadio.Capability(actor) == 0 && !inventory.TrySpawnPrefabToStorage("{E1A5D4B878AA8980}Prefabs/Items/Equipment/Radios/Radio_R148.et"))
|
||||
m_Battery.Audit("RADIO EQUIP", "optional crew handset did not fit; dedicated manpack remains required");
|
||||
for (int i = existing; i < 9; i++)
|
||||
{
|
||||
ERR_MortarInitialItem operation = new ERR_MortarInitialItem();
|
||||
m_Operations.Insert(operation);
|
||||
m_RequestedShells++;
|
||||
if (!inventory.TrySpawnPrefabToStorage(ERR_MortarBattery.SHELL, purpose: EStoragePurpose.PURPOSE_DEPOSIT, cb: operation))
|
||||
{
|
||||
operation.Rejected();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
ERR_MortarSupplyState Poll(float now)
|
||||
{
|
||||
if (m_Stopped || !Replication.IsServer() || !m_Battery || m_Battery.Stopped()) return ERR_MortarSupplyState.FAILED;
|
||||
if (m_Ready) return ERR_MortarSupplyState.READY;
|
||||
SCR_AIGroup group = m_Battery.Group;
|
||||
ERR_GameMode mode = ERR_GameMode.Get();
|
||||
if (!mode || !group || !m_Battery.Gun || group.GetFaction() != mode.EnemyFaction() || group.GetPlayerCount(true) > 0) return Fail("initial support group/gun/faction changed");
|
||||
DamageManagerComponent damage = DamageManagerComponent.Cast(m_Battery.Gun.FindComponent(DamageManagerComponent));
|
||||
if (!damage || damage.IsDestroyed()) return Fail("initial support gun missing damage manager or destroyed");
|
||||
bool expired = now < m_Battery.SpawnedAt || now - m_Battery.SpawnedAt >= 90000;
|
||||
array<AIAgent> agents = {}; group.GetAgents(agents);
|
||||
array<IEntity> physical = {};
|
||||
int gunners, operators;
|
||||
IEntity operatorActor;
|
||||
foreach (AIAgent agent : agents)
|
||||
{
|
||||
if (!agent || !agent.GetControlledEntity()) continue;
|
||||
IEntity actor = agent.GetControlledEntity();
|
||||
if (physical.Contains(actor)) return Fail("initial support group reports duplicate physical actor");
|
||||
physical.Insert(actor);
|
||||
if (!ERR_Actors.CanFight(actor) || SCR_FactionManager.SGetFaction(actor) != mode.EnemyFaction() || !actor.GetPrefabData()) return Fail("initial support actor unavailable or has wrong faction/resource");
|
||||
ResourceName prefab = actor.GetPrefabData().GetPrefabName();
|
||||
if (prefab == ERR_MortarBattery.CREW) gunners++;
|
||||
else if (prefab == ERR_MortarBattery.RADIO_OPERATOR) { operators++; operatorActor = actor; }
|
||||
else return Fail("initial support actor does not belong to authored crew catalog");
|
||||
}
|
||||
ERR_MortarSupplyState population = Population(physical.Count(), gunners, operators, group.IsExpandComplete(), expired);
|
||||
if (population == ERR_MortarSupplyState.FAILED) return Fail("initial four-person support population incomplete/invalid within 90 seconds");
|
||||
if (population == ERR_MortarSupplyState.PENDING) return population;
|
||||
if (m_Allocated)
|
||||
{
|
||||
foreach (IEntity original : m_Members)
|
||||
if (!physical.Contains(original)) return Fail("initial support member replaced after finite allocation");
|
||||
}
|
||||
foreach (IEntity member : physical)
|
||||
{
|
||||
InventoryStorageManagerComponent inventory = InventoryStorageManagerComponent.Cast(member.FindComponent(InventoryStorageManagerComponent));
|
||||
if (!inventory) return ERR_MortarSupplyState.PENDING;
|
||||
if (!m_Allocated && member != operatorActor && m_Battery.InventoryShells(inventory) < 9 && !inventory.CanInsertResource(ERR_MortarBattery.SHELL, EStoragePurpose.PURPOSE_DEPOSIT)) return ERR_MortarSupplyState.PENDING;
|
||||
}
|
||||
if (ERR_HQRadio.Capability(operatorActor) != 2) return ERR_MortarSupplyState.PENDING;
|
||||
// Preserve the existing earliest activation delay, but it is not readiness.
|
||||
if (now - m_Battery.SpawnedAt < 10000) return ERR_MortarSupplyState.PENDING;
|
||||
if (!m_Allocated)
|
||||
{
|
||||
if (!group.GetGroupUtilityComponent() || !group.GetGroupUtilityComponent().m_VehicleMgr) return ERR_MortarSupplyState.PENDING;
|
||||
SCR_AIVehicleUsageComponent usage = SCR_AIVehicleUsageComponent.Cast(m_Battery.Gun.FindComponent(SCR_AIVehicleUsageComponent));
|
||||
if (!usage || !group.GetGroupUtilityComponent().m_VehicleMgr.TryAddVehicle(usage)) return Fail("native support gun registration failed");
|
||||
m_Members.Copy(physical); m_Battery.RadioOperator = operatorActor;
|
||||
if (!Allocate()) return Fail("finite initial support shell allocation rejected; no retry");
|
||||
return ERR_MortarSupplyState.PENDING; // never accept mutation-frame readiness
|
||||
}
|
||||
foreach (ERR_MortarInitialItem operation : m_Operations)
|
||||
{
|
||||
if (!operation.Finished) return ERR_MortarSupplyState.PENDING;
|
||||
if (!operation.Succeeded) return Fail("finite initial support shell operation failed; no retry");
|
||||
}
|
||||
foreach (IEntity equipped : m_Members)
|
||||
{
|
||||
InventoryStorageManagerComponent inventory = InventoryStorageManagerComponent.Cast(equipped.FindComponent(InventoryStorageManagerComponent));
|
||||
int shells = m_Battery.InventoryShells(inventory);
|
||||
if (equipped == operatorActor)
|
||||
{
|
||||
if (shells != 0) return Fail("dedicated initial manpack operator unexpectedly carries mortar shells");
|
||||
}
|
||||
else if (shells != 9) return ERR_MortarSupplyState.PENDING;
|
||||
}
|
||||
if (m_Battery.CountShells() != 27) return ERR_MortarSupplyState.PENDING;
|
||||
m_Ready = true;
|
||||
// One physical-ready handoff: HQ already owns/reserves this group, while
|
||||
// Intel must see its actual late-spawned members after finite stock settles.
|
||||
mode.RegisterGroup(group);
|
||||
group.ActivateAI();
|
||||
foreach (AIAgent readyAgent : agents)
|
||||
{
|
||||
if (readyAgent) { readyAgent.SetPermanentLOD(0); readyAgent.ActivateAI(); }
|
||||
}
|
||||
m_Battery.Audit("CREW READY", string.Format("four original physical members, one manpack, 27 confirmed HE; new shell requests=%1; allocation permanently closed", m_RequestedShells));
|
||||
return ERR_MortarSupplyState.READY;
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
// One newer observation in transit for one existing physical firing order.
|
||||
// The report's observed time survives both radio legs unchanged.
|
||||
class ERR_MortarRefresh
|
||||
{
|
||||
IEntity Gun;
|
||||
SCR_AIGroup Group;
|
||||
int OrderId;
|
||||
int RadioId;
|
||||
ref ERR_HQReport Report;
|
||||
ref ERR_HQRoute Route;
|
||||
|
||||
void ERR_MortarRefresh(ERR_MortarBattery battery, ERR_HQReport report, ERR_HQRoute route)
|
||||
{
|
||||
Gun = battery.Gun;
|
||||
Group = battery.Group;
|
||||
OrderId = battery.OrderId;
|
||||
RadioId = route.MessageId;
|
||||
Report = report.Snapshot();
|
||||
Route = route;
|
||||
}
|
||||
bool Owns(ERR_MortarBattery battery)
|
||||
{
|
||||
return battery && Gun && Group && Gun == battery.Gun && Group == battery.Group && OrderId == battery.OrderId;
|
||||
}
|
||||
bool DeliveredFor(ERR_MortarBattery battery, ERR_HQReport report)
|
||||
{
|
||||
return !Route && Report && Report == report && Owns(battery);
|
||||
}
|
||||
}
|
||||
|
||||
class ERR_MortarRefreshRules
|
||||
{
|
||||
static bool Newer(ERR_HQReport current, ERR_HQReport incoming, float now)
|
||||
{
|
||||
return current && incoming && current.Target && incoming.Target == current.Target && incoming.MessageId > 0 && incoming.MessageId != current.MessageId && incoming.ObservedAt > current.ObservedAt && ERR_MortarPrediction.Fresh(incoming, now);
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// One finite response per delivered observation at this battery. Cancellation
|
||||
// does not turn the same radio message into new ammunition authorization.
|
||||
class ERR_MortarReportLedger
|
||||
{
|
||||
protected ref map<int, float> m_Attempts = new map<int, float>();
|
||||
|
||||
bool Available(int messageId, float observedAt, float now)
|
||||
{
|
||||
if (messageId <= 0 || observedAt < 0 || now < observedAt || now - observedAt > ERR_MortarPrediction.MAX_REPORT_AGE_MS) return false;
|
||||
for (int i = m_Attempts.Count() - 1; i >= 0; i--)
|
||||
if (now - m_Attempts.GetElement(i) > ERR_MortarPrediction.MAX_REPORT_AGE_MS) m_Attempts.Remove(m_Attempts.GetKey(i));
|
||||
return !m_Attempts.Contains(messageId) && m_Attempts.Count() < 128;
|
||||
}
|
||||
bool Claim(int messageId, float observedAt, float now)
|
||||
{
|
||||
if (!Available(messageId, observedAt, now)) return false;
|
||||
m_Attempts.Insert(messageId, observedAt);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// One delivered HQ order drives a continuous native loading/firing cycle.
|
||||
class ERR_MortarSalvo
|
||||
{
|
||||
static const int ROUNDS = 8;
|
||||
static const float RADIUS_METRES = 40;
|
||||
static const float TIMEOUT_MS = 120000;
|
||||
|
||||
static vector Offset(int site, int order, int shot)
|
||||
{
|
||||
// Stable throughout one ballistic solve; successive shells cover the disk
|
||||
// instead of clustering on the same point or changing every iteration.
|
||||
float radius = RADIUS_METRES * Math.Sqrt((shot + 1.0) / ROUNDS);
|
||||
float angle = (site * 53 + order * 97 + shot * 137.5) * Math.DEG2RAD;
|
||||
return Vector(Math.Sin(angle) * radius, 0, Math.Cos(angle) * radius);
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
class ERR_MortarSupplyCallback : ScriptedInventoryOperationCallback
|
||||
{
|
||||
ERR_MortarSupplyTransfer Transfer;
|
||||
protected override void OnComplete() { if (Transfer) Transfer.Acknowledge(true); }
|
||||
protected override void OnFailed() { if (Transfer) Transfer.Acknowledge(false); }
|
||||
}
|
||||
|
||||
// Exact-item native transaction. Callbacks only record outcomes; the HQ tick
|
||||
// authorizes progress after checking actual inventory ownership and the order.
|
||||
class ERR_MortarSupplyTransfer
|
||||
{
|
||||
protected ERR_MortarBattery m_Battery;
|
||||
protected IEntity m_Gun;
|
||||
protected SCR_AIGroup m_Group;
|
||||
protected IEntity m_Donor;
|
||||
protected IEntity m_Gunner;
|
||||
protected IEntity m_Shell;
|
||||
protected InventoryStorageManagerComponent m_Source;
|
||||
protected InventoryStorageManagerComponent m_Destination;
|
||||
protected BaseInventoryStorageComponent m_Storage;
|
||||
protected ref ERR_MortarSupplyCallback m_Callback;
|
||||
protected int m_Order;
|
||||
protected int m_Acknowledgement;
|
||||
protected float m_Deadline;
|
||||
protected bool m_Accepted;
|
||||
protected bool m_Invalidated;
|
||||
protected bool m_Resolved;
|
||||
protected bool m_Ready;
|
||||
void ERR_MortarSupplyTransfer(ERR_MortarBattery battery, IEntity donor, IEntity gunner, IEntity shell, BaseInventoryStorageComponent storage, float now)
|
||||
{
|
||||
m_Battery = battery; m_Gun = battery.Gun; m_Group = battery.Group;
|
||||
m_Donor = donor; m_Gunner = gunner; m_Shell = shell; m_Storage = storage;
|
||||
m_Order = battery.OrderId; m_Deadline = now + 8000;
|
||||
m_Source = InventoryStorageManagerComponent.Cast(donor.FindComponent(InventoryStorageManagerComponent));
|
||||
m_Destination = InventoryStorageManagerComponent.Cast(gunner.FindComponent(InventoryStorageManagerComponent));
|
||||
}
|
||||
static bool Authority(bool valid, bool invalidated, int capturedOrder, int currentOrder)
|
||||
{
|
||||
return valid && !invalidated && capturedOrder == currentOrder;
|
||||
}
|
||||
static bool Proven(bool callbackComplete, bool sourceContains, bool destinationContains, bool exactSlot)
|
||||
{
|
||||
return callbackComplete && !sourceContains && destinationContains && exactSlot;
|
||||
}
|
||||
protected bool Contains(InventoryStorageManagerComponent inventory)
|
||||
{
|
||||
if (!inventory || !m_Shell) return false;
|
||||
array<IEntity> items = {}; ERR_Inventory.Items(inventory, items);
|
||||
return items.Contains(m_Shell);
|
||||
}
|
||||
protected bool Valid(float now)
|
||||
{
|
||||
if (!m_Battery || !Authority(m_Battery.SupplyAuthorized(now), m_Invalidated, m_Order, m_Battery.OrderId)) return false;
|
||||
if (m_Battery.Gun != m_Gun || m_Battery.Group != m_Group || !m_Donor || !m_Gunner || m_Donor == m_Gunner || !m_Shell || !m_Source || !m_Destination || !m_Storage) return false;
|
||||
if (!ERR_Actors.CanFight(m_Donor) || !ERR_Actors.CanFight(m_Gunner) || ERR_Actors.Group(m_Donor) != m_Group || ERR_Actors.Group(m_Gunner) != m_Group) return false;
|
||||
Faction faction = m_Group.GetFaction();
|
||||
if (SCR_FactionManager.SGetFaction(m_Donor) != faction || SCR_FactionManager.SGetFaction(m_Gunner) != faction || vector.Distance(m_Donor.GetOrigin(), m_Gunner.GetOrigin()) > 12) return false;
|
||||
SCR_AIVehicleUsageComponent usage = SCR_AIVehicleUsageComponent.Cast(m_Gun.FindComponent(SCR_AIVehicleUsageComponent));
|
||||
if (!usage || !usage.GetTurretCompartmentSlot()) return false;
|
||||
IEntity occupant = usage.GetTurretCompartmentSlot().GetOccupant();
|
||||
return !occupant || occupant == m_Gunner;
|
||||
}
|
||||
void Acknowledge(bool success)
|
||||
{
|
||||
if (m_Acknowledgement != 0) return;
|
||||
if (success) m_Acknowledgement = 1;
|
||||
else m_Acknowledgement = -1;
|
||||
}
|
||||
bool Begin(float now)
|
||||
{
|
||||
if (m_Callback || !Replication.IsServer() || !Valid(now) || !Contains(m_Source) || Contains(m_Destination)) return false;
|
||||
m_Callback = new ERR_MortarSupplyCallback(); m_Callback.Transfer = this;
|
||||
// Do not use callback.GetItem(): the native contract excludes Move operations.
|
||||
m_Accepted = m_Source.TryMoveItemToStorage(m_Shell, m_Storage, -1, m_Callback);
|
||||
if (!m_Accepted) { Acknowledge(false); m_Resolved = true; }
|
||||
return m_Accepted;
|
||||
}
|
||||
void Invalidate() { m_Invalidated = true; m_Ready = false; }
|
||||
bool Unresolved() { return m_Accepted && !m_Resolved; }
|
||||
int CapturedOrder() { return m_Order; }
|
||||
ERR_MortarSupplyState Poll(float now)
|
||||
{
|
||||
if (!Replication.IsServer()) return ERR_MortarSupplyState.FAILED;
|
||||
bool valid = Valid(now);
|
||||
bool sourceContains = Contains(m_Source);
|
||||
bool destinationContains = Contains(m_Destination);
|
||||
bool exactSlot;
|
||||
InventoryItemComponent item;
|
||||
if (m_Shell) item = InventoryItemComponent.Cast(m_Shell.FindComponent(InventoryItemComponent));
|
||||
if (item && item.GetParentSlot()) exactSlot = item.GetParentSlot().GetStorage() == m_Storage;
|
||||
bool proven = Proven(m_Acknowledgement == 1, sourceContains, destinationContains, exactSlot);
|
||||
if (proven || m_Acknowledgement < 0) m_Resolved = true;
|
||||
// A successful callback without physical destination proof is ambiguous.
|
||||
// Keep it reserved through timeout/cancellation; never start a second move.
|
||||
if (!valid || now >= m_Deadline)
|
||||
{
|
||||
m_Invalidated = true; m_Ready = false;
|
||||
return ERR_MortarSupplyState.FAILED;
|
||||
}
|
||||
if (proven)
|
||||
{
|
||||
m_Ready = true;
|
||||
return ERR_MortarSupplyState.READY;
|
||||
}
|
||||
if (m_Acknowledgement < 0 || !m_Accepted) return ERR_MortarSupplyState.FAILED;
|
||||
return ERR_MortarSupplyState.PENDING;
|
||||
}
|
||||
}
|
||||
+343
@@ -0,0 +1,343 @@
|
||||
// Owned and ticked by HQ. Initial assets are authored once; losses are never replaced.
|
||||
class ERR_MortarSystem
|
||||
{
|
||||
protected ERR_HQ m_HQ;
|
||||
protected ref array<ref ERR_MortarBattery> m_Batteries = {};
|
||||
protected ref ERR_SettlementSafety m_Settlements = new ERR_SettlementSafety();
|
||||
protected bool m_Prepared;
|
||||
protected bool m_Stopped;
|
||||
protected float m_NextDecision;
|
||||
protected float m_NextRefreshDecision;
|
||||
string SafetyReason;
|
||||
string SettlementReason;
|
||||
|
||||
void ERR_MortarSystem(ERR_HQ hq) { m_HQ = hq; }
|
||||
static ERR_MortarSystem Get()
|
||||
{
|
||||
ERR_HQ hq = ERR_HQ.Get();
|
||||
if (!hq || !Replication.IsServer()) return null;
|
||||
return hq.Mortars();
|
||||
}
|
||||
static bool NearPlayer(vector position)
|
||||
{
|
||||
array<IEntity> players = {};
|
||||
ERR_Actors.Players(players);
|
||||
foreach (IEntity player : players)
|
||||
if (vector.DistanceXZ(player.GetOrigin(), position) <= 1200) return true;
|
||||
return false;
|
||||
}
|
||||
bool Prepare()
|
||||
{
|
||||
if (!Replication.IsServer() || m_Prepared || m_Stopped) return false;
|
||||
m_Prepared = true;
|
||||
vector position = ERR_Actors.Ground("6000 0 7000");
|
||||
if (position[1] < 1) return false;
|
||||
IEntity gun = ERR_Actors.Spawn(ERR_MortarBattery.GUN, position);
|
||||
SCR_AIGroup group = SCR_AIGroup.Cast(ERR_Actors.Spawn(MortarGroup(), ERR_Actors.Ground(position + "0 0 4")));
|
||||
if (!gun || !group)
|
||||
{
|
||||
ERR_Actors.DeleteSpawned(gun);
|
||||
ERR_MortarReadiness.RetireGroup(group);
|
||||
ERR_Actors.DeleteSpawned(group);
|
||||
return false;
|
||||
}
|
||||
if (group.GetFaction() != ERR_GameMode.Get().EnemyFaction())
|
||||
{
|
||||
ERR_Actors.DeleteSpawned(gun);
|
||||
ERR_MortarReadiness.RetireGroup(group);
|
||||
ERR_Actors.DeleteSpawned(group);
|
||||
return false;
|
||||
}
|
||||
FactionAffiliationComponent affiliation = FactionAffiliationComponent.Cast(gun.FindComponent(FactionAffiliationComponent));
|
||||
if (affiliation) affiliation.SetAffiliatedFaction(ERR_GameMode.Get().EnemyFaction());
|
||||
ERR_MortarBattery battery = new ERR_MortarBattery();
|
||||
battery.Bind(1, group, gun, GetGame().GetWorld().GetWorldTime());
|
||||
m_Batteries.Insert(battery);
|
||||
ERR_GameMode mode = ERR_GameMode.Get();
|
||||
mode.Own(gun);
|
||||
// HQ owns/reserves the finite asset immediately. Publish its physical
|
||||
// membership to companion consumers only after readiness has completed.
|
||||
mode.Own(group);
|
||||
m_HQ.RegisterGroup(group);
|
||||
m_HQ.ReserveGroup(group);
|
||||
battery.Audit("DEPLOY", "authored gun and four-person crew; finite ammunition initialization pending");
|
||||
return true;
|
||||
}
|
||||
static ResourceName MortarGroup() { return "{B84C05941C4C81B8}Prefabs/ERR/MortarCrew.et"; }
|
||||
void Batteries(out array<ref ERR_MortarBattery> batteries)
|
||||
{
|
||||
batteries.Clear();
|
||||
foreach (ERR_MortarBattery battery : m_Batteries) batteries.Insert(battery);
|
||||
}
|
||||
ERR_MortarBattery FindGun(IEntity gun)
|
||||
{
|
||||
foreach (ERR_MortarBattery battery : m_Batteries)
|
||||
if (gun && battery.Gun == gun) return battery;
|
||||
return null;
|
||||
}
|
||||
ERR_MortarBattery FindGroup(SCR_AIGroup group)
|
||||
{
|
||||
foreach (ERR_MortarBattery battery : m_Batteries)
|
||||
if (group && battery.Group == group) return battery;
|
||||
return null;
|
||||
}
|
||||
ERR_HQReport Latest(ERR_MortarBattery battery, float now)
|
||||
{
|
||||
// Only the coordinates actually delivered to this battery are available.
|
||||
// A newer HQ contact cannot update a gun's knowledge without another downlink.
|
||||
if (!battery || !ERR_MortarPrediction.Fresh(battery.Report, now)) return null;
|
||||
return battery.Report;
|
||||
}
|
||||
string ContactFailure(ERR_MortarBattery battery)
|
||||
{
|
||||
if (!battery.Report) return "no delivered target report";
|
||||
return "delivered target report expired or invalid";
|
||||
}
|
||||
// Classify only after SupplyState has observed any accepted inventory move.
|
||||
// Report expiry must not be mislabeled as physical ammunition depletion.
|
||||
string SupplyFailure(ERR_MortarBattery battery, float now)
|
||||
{
|
||||
if (!Latest(battery, now)) return ContactFailure(battery);
|
||||
return "next native load lacks confirmed physical gunner ammunition";
|
||||
}
|
||||
bool OutsideSettlements(vector impact)
|
||||
{
|
||||
bool clear = m_Settlements.OutsideSettlements(impact);
|
||||
SettlementReason = m_Settlements.SettlementReason;
|
||||
return clear;
|
||||
}
|
||||
bool Safe(ERR_MortarBattery battery, vector impact, float flight)
|
||||
{
|
||||
SafetyReason = "clear";
|
||||
if (!battery.Operational()) { SafetyReason = battery.OperationalReason(); return false; }
|
||||
if (!OutsideSettlements(impact)) { SafetyReason = SettlementReason; return false; }
|
||||
if (SCR_TerrainHelper.GetTerrainY(impact) < 0) { SafetyReason = "impact below Everon sea level"; return false; }
|
||||
Faction faction = battery.Group.GetFaction();
|
||||
if (!faction) { SafetyReason = "battery faction missing"; return false; }
|
||||
array<IEntity> friendlies = {};
|
||||
m_HQ.PhysicalForces(friendlies);
|
||||
foreach (IEntity entity : friendlies)
|
||||
{
|
||||
Faction friendly = SCR_FactionManager.SGetFaction(entity);
|
||||
if (!friendly || faction.IsFactionEnemy(friendly) || !ERR_Actors.Alive(entity)) continue;
|
||||
vector velocity;
|
||||
IEntity moving = CompartmentAccessComponent.GetVehicleIn(entity);
|
||||
if (!moving) moving = entity;
|
||||
if (moving.GetPhysics()) velocity = moving.GetPhysics().GetVelocity();
|
||||
if (ERR_MortarPrediction.ThreatensFriendly(impact, entity.GetOrigin(), velocity, flight))
|
||||
{
|
||||
SafetyReason = "friendly physical force can enter the impact area";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool Withhold(ERR_MortarBattery battery, string reason, string detail = "")
|
||||
{
|
||||
battery.LastFailure = reason + "; " + detail;
|
||||
battery.Trace("WITHHELD", reason, detail);
|
||||
// Finish the activity from the next HQ tick, never from inside its own node.
|
||||
battery.Rejected = true;
|
||||
return false;
|
||||
}
|
||||
bool AllowShot(ERR_MortarBattery battery)
|
||||
{
|
||||
float now = GetGame().GetWorld().GetWorldTime();
|
||||
if (m_Stopped || !ERR_HQ.MissionActive()) return Withhold(battery, "mission inactive or HQ stopped");
|
||||
if (!battery.Operational()) return Withhold(battery, battery.OperationalReason());
|
||||
if (battery.RefreshRejected) return Withhold(battery, "target update failed; order awaiting cancellation");
|
||||
if (!battery.CommandOpen || battery.OrderShots >= ERR_MortarSalvo.ROUNDS) return Withhold(battery, "salvo complete or order closed");
|
||||
if (battery.Released) return Withhold(battery, "waiting for committed projectile");
|
||||
if (battery.AimedShot != battery.OrderShots || battery.AimedAt < 0 || now < battery.AimedAt) return Withhold(battery, "fresh aim solution required");
|
||||
ERR_HQReport report = Latest(battery, now);
|
||||
if (!report) return Withhold(battery, ContactFailure(battery));
|
||||
if (!battery.AimSolution || !battery.AimSolution.Matches(battery, report)) return Withhold(battery, "aim command does not match delivered order");
|
||||
if (battery.AimValidatedAt < battery.AimedAt || now < battery.AimValidatedAt) return Withhold(battery, "aim validation timestamp invalid");
|
||||
if (now - battery.AimValidatedAt > 10000)
|
||||
{
|
||||
if (!battery.AimSolver) return Withhold(battery, "original native aim solver no longer available");
|
||||
if (!battery.AimSolver.ERR_RevalidateAim(battery, this, report, now)) return false;
|
||||
}
|
||||
vector updated;
|
||||
if (!ERR_MortarPrediction.Predict(report, now, battery.FlightTime, updated)) return Withhold(battery, ERR_MortarPrediction.FailureReason);
|
||||
updated += battery.AimOffset;
|
||||
if (vector.DistanceXZ(updated, battery.Impact) > 60) return Withhold(battery, "predicted target left aimed impact area");
|
||||
if (!OutsideSettlements(updated)) return Withhold(battery, SettlementReason);
|
||||
if (!Safe(battery, battery.Impact, battery.FlightTime)) return Withhold(battery, SafetyReason);
|
||||
return true;
|
||||
}
|
||||
protected void AdvanceOrder(ERR_MortarBattery battery, float now)
|
||||
{
|
||||
if (battery.RadioOrder)
|
||||
{
|
||||
int deliveredState = battery.RadioOrder.Advance(now, m_HQ.Journal());
|
||||
if (deliveredState == 0) return;
|
||||
ERR_HQReport delivered = battery.PendingRadioReport;
|
||||
battery.RadioOrder = null;
|
||||
battery.PendingRadioReport = null;
|
||||
if (deliveredState < 0 || !ERR_MortarPrediction.Fresh(delivered, now) || !battery.Operational() || battery.Activity || !battery.CommandOpen)
|
||||
{
|
||||
battery.Cancel("radio delivery, report freshness or battery availability failed");
|
||||
battery.NextOrder = now + 10000;
|
||||
return;
|
||||
}
|
||||
battery.PendingSupplyReport = delivered;
|
||||
}
|
||||
if (!battery.PendingSupplyReport) return;
|
||||
if (!battery.CommandOpen || !ERR_MortarPrediction.Fresh(battery.PendingSupplyReport, now) || !battery.Operational() || battery.Activity)
|
||||
{
|
||||
battery.Cancel("delivered order expired or became unavailable while awaiting physical ammunition");
|
||||
battery.NextOrder = now + 10000;
|
||||
return;
|
||||
}
|
||||
ERR_MortarSupplyState supply = battery.SupplyState(now);
|
||||
if (supply == ERR_MortarSupplyState.PENDING) return;
|
||||
if (supply == ERR_MortarSupplyState.FAILED)
|
||||
{
|
||||
battery.Cancel("physical gunner ammunition unavailable or transfer failed");
|
||||
battery.NextOrder = now + 10000;
|
||||
return;
|
||||
}
|
||||
ERR_HQReport ready = battery.PendingSupplyReport;
|
||||
battery.PendingSupplyReport = null;
|
||||
battery.Order(ready, now);
|
||||
}
|
||||
|
||||
protected void AdvanceRefresh(ERR_MortarBattery battery, float now)
|
||||
{
|
||||
ERR_MortarRefresh update = battery.Refresh;
|
||||
if (!update) return;
|
||||
// A stale transaction must not cancel or modify a later order.
|
||||
if (update.OrderId != battery.OrderId || !battery.CommandOpen)
|
||||
{
|
||||
battery.Refresh = null;
|
||||
return;
|
||||
}
|
||||
if (!update.Owns(battery) || !battery.Operational() || !battery.Activity || !ERR_MortarRefreshRules.Newer(battery.Report, update.Report, now))
|
||||
{
|
||||
battery.RejectRefresh("target update ownership, observation freshness or activity changed");
|
||||
return;
|
||||
}
|
||||
if (update.Route)
|
||||
{
|
||||
int state = update.Route.Advance(now, m_HQ.Journal());
|
||||
if (state == 0) return;
|
||||
if (state < 0)
|
||||
{
|
||||
battery.RejectRefresh("new target observation failed physical HQ radio delivery");
|
||||
return;
|
||||
}
|
||||
update.Route = null;
|
||||
}
|
||||
// The original committed shell owns its report, geometry and airspace until
|
||||
// native OnWeaponFired confirms it. A delivered update cannot relabel it.
|
||||
if (battery.Released) return;
|
||||
if (battery.AimSolution)
|
||||
{
|
||||
if (!battery.AimSolver || !battery.AimSolver.ERR_AdoptReport(battery, this, update.Report, now))
|
||||
{
|
||||
battery.RejectRefresh("delivered update cannot preserve the original native aim command");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No barrel command exists for the next shell yet. Its normal native aim
|
||||
// node must solve this report; receiving it is not an aim authorization.
|
||||
battery.Report = update.Report;
|
||||
}
|
||||
battery.Audit("UPDATE RECEIVED", string.Format("same order; update radio=M%1 accepted observation=M%2 observed=%3; shots=%4 original aim time=%5", update.RadioId, update.Report.MessageId, update.Report.ObservedAt, battery.OrderShots, battery.AimedAt));
|
||||
battery.Refresh = null;
|
||||
}
|
||||
|
||||
protected void QueueRefreshes(float now)
|
||||
{
|
||||
if (now < m_NextRefreshDecision) return;
|
||||
m_NextRefreshDecision = now + 5000;
|
||||
array<ref ERR_HQReport> reports = {};
|
||||
m_HQ.SupportContacts(reports, now);
|
||||
foreach (ERR_MortarBattery battery : m_Batteries)
|
||||
{
|
||||
ERR_HQReport newest;
|
||||
foreach (ERR_HQReport report : reports)
|
||||
{
|
||||
if (!battery.CanRefresh(report, now)) continue;
|
||||
if (!newest || report.ObservedAt > newest.ObservedAt) newest = report;
|
||||
}
|
||||
if (!newest) continue;
|
||||
ERR_HQRoute route = m_HQ.Downlink(battery.Group, "MORTAR UPDATE", newest.Description(now), newest.MessageId);
|
||||
if (!route)
|
||||
{
|
||||
battery.RejectRefresh("HQ has a newer observation but no physical downlink to the active battery");
|
||||
continue;
|
||||
}
|
||||
battery.BeginRefresh(newest, route, now);
|
||||
}
|
||||
}
|
||||
|
||||
void Tick(float now)
|
||||
{
|
||||
if (m_Stopped || !Replication.IsServer()) return;
|
||||
foreach (ERR_MortarBattery battery : m_Batteries)
|
||||
{
|
||||
if ((battery.Rejected || battery.RefreshRejected) && !battery.Released)
|
||||
{
|
||||
battery.Cancel("native aim or firing authorization rejected");
|
||||
battery.NextOrder = now + 10000;
|
||||
}
|
||||
battery.Rejected = false;
|
||||
battery.Update(now);
|
||||
AdvanceOrder(battery, now);
|
||||
AdvanceRefresh(battery, now);
|
||||
}
|
||||
QueueRefreshes(now);
|
||||
if (now < m_NextDecision) return;
|
||||
m_NextDecision = now + 10000;
|
||||
array<ref ERR_HQReport> reports = {};
|
||||
m_HQ.SupportContacts(reports, now);
|
||||
ERR_MortarBattery selected;
|
||||
ERR_HQReport target;
|
||||
float distance = float.MAX;
|
||||
foreach (ERR_MortarBattery battery : m_Batteries)
|
||||
{
|
||||
if (!battery.StartupActivated || !battery.Operational() || battery.Activity || battery.RadioOrder || battery.PendingSupplyReport || battery.SupplyPending() || now < battery.NextOrder || battery.CountShells() == 0) continue;
|
||||
foreach (ERR_HQReport report : reports)
|
||||
{
|
||||
if (!battery.CanRequest(report, now)) continue;
|
||||
float candidate = vector.DistanceXZ(battery.Position, report.Position);
|
||||
if (candidate < 350 || candidate > 4000 || candidate >= distance || !OutsideSettlements(report.Position)) continue;
|
||||
selected = battery;
|
||||
target = report;
|
||||
distance = candidate;
|
||||
}
|
||||
}
|
||||
if (!selected || !target) return;
|
||||
ERR_HQRoute route = m_HQ.Downlink(selected.Group, "MORTAR ORDER", target.Description(now), target.MessageId);
|
||||
if (!route) return;
|
||||
if (!selected.BeginRequest(target, now)) return;
|
||||
selected.RadioId = route.MessageId;
|
||||
selected.RadioOrder = route;
|
||||
selected.PendingRadioReport = target.Snapshot();
|
||||
m_NextDecision = now + 20000;
|
||||
}
|
||||
void AppendGuns(array<IEntity> entities)
|
||||
{
|
||||
foreach (ERR_MortarBattery battery : m_Batteries)
|
||||
if (battery.Gun && !entities.Contains(battery.Gun)) entities.Insert(battery.Gun);
|
||||
}
|
||||
string Status()
|
||||
{
|
||||
int ready, shells;
|
||||
foreach (ERR_MortarBattery battery : m_Batteries)
|
||||
{
|
||||
if (battery.StartupActivated && battery.Operational()) ready++;
|
||||
shells += battery.CountShells();
|
||||
}
|
||||
return string.Format("mortars %1/%2; HE shells %3", ready, m_Batteries.Count(), shells);
|
||||
}
|
||||
void Stop()
|
||||
{
|
||||
m_Stopped = true;
|
||||
foreach (ERR_MortarBattery battery : m_Batteries) battery.Stop();
|
||||
}
|
||||
}
|
||||
+366
@@ -0,0 +1,366 @@
|
||||
#ifdef WORKBENCH
|
||||
// Separate Chat fixture for the ACTUAL dormant AI controller. Core's original
|
||||
// powered-flight probe is disabled for this outcome and retains its own evidence.
|
||||
modded class SCR_CharacterControllerComponent
|
||||
{
|
||||
override void OnPrepareControls(IEntity owner, ActionManager am, float dt, bool player)
|
||||
{
|
||||
ERR_NativeAircraftControlProbe probe = ERR_NativeAircraftControlProbe.Current();
|
||||
if (probe && !player) probe.PrepareActions(this, owner, am, dt);
|
||||
super.OnPrepareControls(owner, am, dt, player);
|
||||
}
|
||||
}
|
||||
modded class SCR_HelicopterControllerComponent
|
||||
{
|
||||
protected override event void OnPrepareControls()
|
||||
{
|
||||
super.OnPrepareControls();
|
||||
ERR_NativeAircraftControlProbe probe = ERR_NativeAircraftControlProbe.Current();
|
||||
if (probe) probe.PrepareHelicopter(this);
|
||||
}
|
||||
}
|
||||
// Read-only native fixture observation; no production method or state override.
|
||||
modded class ERR_AircraftFlight
|
||||
{
|
||||
float ERR_NativeControlDeadline() { return m_Deadline; }
|
||||
}
|
||||
class ERR_NativeAircraftControlRules
|
||||
{
|
||||
static bool Terminal(bool cancellation, int state, int cancellationCount)
|
||||
{
|
||||
if (cancellation) return state == ERR_AircraftFlightState.CANCELLED && cancellationCount == 1;
|
||||
return state == ERR_AircraftFlightState.COMPLETE && cancellationCount == 0;
|
||||
}
|
||||
static bool CanCancel(int state, bool contact, float height)
|
||||
{
|
||||
return state == ERR_AircraftFlightState.CLIMB && !contact && height >= 5 && height <= 18;
|
||||
}
|
||||
static bool CancelProof(bool airborneHooks, bool landingHeld, bool sameDeadline, bool sameToken)
|
||||
{
|
||||
return airborneHooks && landingHeld && sameDeadline && sameToken;
|
||||
}
|
||||
static bool TranslateProof(bool proven, bool held, float requestedDistance, float displacement, float targetError)
|
||||
{
|
||||
return proven && held && requestedDistance >= 1.75 && requestedDistance <= 4.05 && displacement >= 1 && targetError >= 0 && targetError <= 0.75;
|
||||
}
|
||||
static bool PhysicalFinish(bool originalOccupancy, bool landed, bool engineOff, float fuelUsed, int wakes, int starts, int controllerStarts, int pilotHooks, int helicopterHooks)
|
||||
{
|
||||
if (!originalOccupancy || !landed || !engineOff) return false;
|
||||
return fuelUsed > 0.05 && wakes == 1 && starts == 1 && controllerStarts == 1 && pilotHooks > 100 && helicopterHooks > 100;
|
||||
}
|
||||
static bool CanDispose(bool context, bool noPlayers, bool sameFaction, bool groupUnchanged, bool pilotUnchanged)
|
||||
{
|
||||
return context && noPlayers && sameFaction && groupUnchanged && pilotUnchanged;
|
||||
}
|
||||
}
|
||||
class ERR_NativeAircraftControlProbe
|
||||
{
|
||||
protected static ERR_NativeAircraftControlProbe s_Current;
|
||||
protected ERR_GameMode m_Mode;
|
||||
protected Vehicle m_Aircraft;
|
||||
protected SCR_AIGroup m_Group;
|
||||
protected IEntity m_Pilot;
|
||||
protected PilotCompartmentSlot m_Seat;
|
||||
protected HelicopterControllerComponent m_Controller;
|
||||
protected VehicleHelicopterSimulation m_Simulation;
|
||||
protected FuelManagerComponent m_Fuel;
|
||||
protected ref ERR_AircraftFlightEnvelope m_Envelope;
|
||||
protected ref ERR_AircraftFlight m_Flight;
|
||||
protected float m_Deadline;
|
||||
protected float m_StageDeadline;
|
||||
protected float m_Stable = -1;
|
||||
protected float m_LastTick = -1;
|
||||
protected float m_NextLog;
|
||||
protected float m_InitialFuel;
|
||||
protected float m_ControlDeadline;
|
||||
protected float m_CancelAt = -1;
|
||||
protected float m_LandingStable = -1;
|
||||
protected int m_CancelCount;
|
||||
protected int m_CancelPilotHooks;
|
||||
protected int m_CancelHelicopterHooks;
|
||||
protected bool m_CancelFixture;
|
||||
protected bool m_TranslationFixture;
|
||||
protected bool m_TranslationObserved;
|
||||
protected bool m_TranslationHeld;
|
||||
protected vector m_TranslationOrigin;
|
||||
protected vector m_TranslationTarget;
|
||||
protected float m_TranslationStable = -1;
|
||||
protected bool m_ContinuedAirborneControl;
|
||||
protected bool m_LandingHeld;
|
||||
protected vector m_Pad;
|
||||
protected int m_Step;
|
||||
protected int m_Wakes;
|
||||
protected int m_ControllerStarts;
|
||||
protected int m_EngineStarts;
|
||||
protected int m_PilotHooks;
|
||||
protected int m_HelicopterHooks;
|
||||
protected bool m_LastEngine;
|
||||
protected bool m_AllocationRequested;
|
||||
protected bool m_PilotRequested;
|
||||
protected bool m_Stopped;
|
||||
protected bool m_Complete;
|
||||
static const int ORDER_TOKEN = 771001;
|
||||
static const ResourceName AIRCRAFT = "{DF5CCB7C0FF049F4}Prefabs/Vehicles/Helicopters/Mi8MT/Mi8MT_unarmed_transport.et";
|
||||
static const ResourceName GROUP = "{000CD338713F2B5A}Prefabs/AI/Groups/Group_Base.et";
|
||||
static const ResourceName PILOT = "{86E565387A634103}Prefabs/Characters/Campaign/Final/OPFOR/USSR_Army/Regular/Campaign_USSR_Player_MortarCrew.et";
|
||||
static bool Enabled()
|
||||
{
|
||||
string enabled, outcome;
|
||||
return Replication.IsServer() && System.GetCLIParam("errSmoke", enabled) && enabled == "1" && System.GetCLIParam("errSmokeOutcome", outcome) && (outcome == "aircraft-control" || outcome == "aircraft-cancel" || outcome == "aircraft-translate");
|
||||
}
|
||||
static ERR_NativeAircraftControlProbe Current() { return s_Current; }
|
||||
void ERR_NativeAircraftControlProbe(ERR_GameMode mode)
|
||||
{
|
||||
m_Mode = mode;
|
||||
if (!GetGame() || !GetGame().GetWorld() || !Enabled() || s_Current) { m_Stopped = true; return; }
|
||||
string outcome; System.GetCLIParam("errSmokeOutcome", outcome); m_CancelFixture = outcome == "aircraft-cancel"; m_TranslationFixture = outcome == "aircraft-translate";
|
||||
m_Deadline = GetGame().GetWorld().GetWorldTime() + 300000;
|
||||
s_Current = this;
|
||||
}
|
||||
protected bool Check(bool passed, string detail)
|
||||
{
|
||||
string outcome = "aircraft-control"; if (m_CancelFixture) outcome = "aircraft-cancel"; else if (m_TranslationFixture) outcome = "aircraft-translate";
|
||||
if (passed) { Print("ERR SMOKE PASS: " + outcome + " " + detail); return true; }
|
||||
Print("ERR SMOKE FAIL: " + outcome + " " + detail, LogLevel.ERROR); Stop(); return false;
|
||||
}
|
||||
protected bool NoPlayers()
|
||||
{
|
||||
if (!GetGame() || !GetGame().GetPlayerManager()) return false;
|
||||
if (m_Group && m_Group.GetPlayerCount(true) > 0) return false;
|
||||
array<int> players = {}; GetGame().GetPlayerManager().GetPlayers(players);
|
||||
foreach (int id : players)
|
||||
{
|
||||
IEntity actor = GetGame().GetPlayerManager().GetPlayerControlledEntity(id);
|
||||
if (actor && (actor == m_Pilot || actor == m_Aircraft || (m_Aircraft && CompartmentAccessComponent.GetVehicleIn(actor) == m_Aircraft))) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
protected bool OriginalOccupancy()
|
||||
{
|
||||
if (!m_Pilot || !m_Group || !m_Aircraft || !m_Controller || !m_Seat || !NoPlayers()) return false;
|
||||
if (m_Group.GetAgentsCount() != 1 || ERR_Actors.Group(m_Pilot) != m_Group || !ERR_Actors.CanFight(m_Pilot)) return false;
|
||||
return m_Seat.GetOccupant() == m_Pilot && m_Controller.GetPilotCompartmentSlot() == m_Seat && CompartmentAccessComponent.GetVehicleIn(m_Pilot) == m_Aircraft;
|
||||
}
|
||||
void PrepareActions(CharacterControllerComponent controller, IEntity owner, ActionManager am, float dt)
|
||||
{
|
||||
if (m_Stopped || !m_Flight || owner != m_Pilot) return;
|
||||
m_Flight.PrepareActions(controller, owner, am, dt); m_PilotHooks++;
|
||||
}
|
||||
void PrepareHelicopter(HelicopterControllerComponent controller)
|
||||
{
|
||||
if (m_Stopped || !m_Flight || controller != m_Controller) return;
|
||||
m_Flight.PrepareHelicopter(controller); m_HelicopterHooks++;
|
||||
}
|
||||
protected bool FindPad(out vector result)
|
||||
{
|
||||
vector anchor = m_Mode.ExtractionVehicle().GetOrigin();
|
||||
float bestSlope = float.MAX; vector bestCandidate, refinementAnchor;
|
||||
int localCandidates = ERR_PoweredFlightRules.PAD_CANDIDATES + ERR_PoweredFlightRules.PAD_REFINEMENTS;
|
||||
for (int index = 0; index < localCandidates + ERR_PoweredFlightRules.AIRPORT_CANDIDATES; index++)
|
||||
{
|
||||
if (index == ERR_PoweredFlightRules.PAD_CANDIDATES)
|
||||
{
|
||||
refinementAnchor = anchor; if (bestSlope != float.MAX) refinementAnchor = bestCandidate;
|
||||
}
|
||||
vector candidate;
|
||||
if (index < ERR_PoweredFlightRules.PAD_CANDIDATES) candidate = ERR_Actors.Ground(anchor + ERR_PoweredFlightRules.PadOffset(index));
|
||||
else if (index < localCandidates) candidate = ERR_Actors.Ground(refinementAnchor + ERR_PoweredFlightRules.RefineOffset(index - ERR_PoweredFlightRules.PAD_CANDIDATES));
|
||||
else candidate = ERR_Actors.Ground("4900.953 0 11787.412" + ERR_PoweredFlightRules.AirportOffset(index - localCandidates));
|
||||
if (candidate[1] < 2) continue;
|
||||
float delta;
|
||||
for (int edge = 0; edge < 4; edge++)
|
||||
{
|
||||
float side = edge * Math.PI / 2;
|
||||
vector point = candidate + Vector(Math.Cos(side) * 14, 0, Math.Sin(side) * 14);
|
||||
delta = Math.Max(delta, Math.AbsFloat(SCR_TerrainHelper.GetTerrainY(point) - candidate[1]));
|
||||
}
|
||||
if (delta < bestSlope) { bestSlope = delta; bestCandidate = candidate; }
|
||||
if (delta > 0.8) continue;
|
||||
if (!SCR_WorldTools.TraceCilinderUtil(candidate + "0 2.5 0", 40, 4, TraceFlags.ENTS)) continue;
|
||||
TraceBox clearance = new TraceBox(); clearance.Start = candidate + "0 3 0"; clearance.End = candidate + "0 60 0";
|
||||
clearance.Mins = "-40 -1 -40"; clearance.Maxs = "40 1 40"; clearance.Flags = TraceFlags.ENTS | TraceFlags.WORLD;
|
||||
if (!(GetGame().GetWorld().TraceMove(clearance, null) >= 1)) continue;
|
||||
PrintFormat("ERR AIRCRAFT FIXTURE PAD: candidate=%1 tested=%2 terrainDelta=%3; no fallback", candidate, index + 1, delta);
|
||||
result = candidate; return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
protected bool ReadyComponents()
|
||||
{
|
||||
m_Controller = HelicopterControllerComponent.Cast(m_Aircraft.FindComponent(HelicopterControllerComponent));
|
||||
if (!m_Controller) return false;
|
||||
m_Simulation = m_Controller.GetSimulation();
|
||||
m_Fuel = FuelManagerComponent.Cast(m_Aircraft.FindComponent(FuelManagerComponent));
|
||||
if (!m_Simulation || !m_Simulation.IsValid() || !m_Fuel || !m_Fuel.HasFuel()) return false;
|
||||
BaseCompartmentManagerComponent manager = BaseCompartmentManagerComponent.Cast(m_Aircraft.FindComponent(BaseCompartmentManagerComponent));
|
||||
if (!manager) return false;
|
||||
array<BaseCompartmentSlot> slots = {}; manager.GetCompartments(slots);
|
||||
if (slots.Count() > 64) return false;
|
||||
foreach (BaseCompartmentSlot slot : slots)
|
||||
{
|
||||
PilotCompartmentSlot pilotSlot = PilotCompartmentSlot.Cast(slot);
|
||||
if (!pilotSlot || pilotSlot.GetOwner() != m_Aircraft || pilotSlot.GetVehicle() != m_Aircraft || pilotSlot.GetCompartmentName() != "PilotCompartment") continue;
|
||||
if (pilotSlot.GetType() != ECompartmentType.PILOT) continue;
|
||||
if (pilotSlot.GetOccupant() || pilotSlot.IsReserved() || pilotSlot.IsGetInLocked() || !pilotSlot.IsCompartmentAccessible()) continue;
|
||||
m_Seat = pilotSlot; return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
protected void Diagnostic(float now)
|
||||
{
|
||||
if (!m_Aircraft || !m_Controller || !m_Simulation || now < m_NextLog) return;
|
||||
m_NextLog = now + 2000;
|
||||
vector transform[4]; m_Aircraft.GetWorldTransform(transform);
|
||||
vector home = m_Pad; if (m_Envelope) home = m_Envelope.Home();
|
||||
int state = -1; string reason; if (m_Flight) { state = m_Flight.State(); reason = m_Flight.Reason(); }
|
||||
PrintFormat("ERR AIRCRAFT FIXTURE: step=%1 state=%2 height=%3 drift=%4 speed=%5 contact=%6 engine=%7 fuelUsed=%8 reason=%9", m_Step, state, transform[3][1] - home[1], vector.DistanceXZ(transform[3], home), m_Simulation.GetVelocity().Length(), m_Simulation.HasAnyGroundContact(), m_Controller.IsEngineOn(), m_InitialFuel - m_Fuel.GetTotalFuel(), reason);
|
||||
}
|
||||
int Tick(float now)
|
||||
{
|
||||
if (m_Complete) return 1;
|
||||
if (m_Stopped) return -1;
|
||||
if (!Enabled() || !m_Mode || m_Mode != ERR_GameMode.Get() || !m_Mode.Active() || now >= m_Deadline) { Check(false, "active exact mission and bounded300-second fixture"); return -1; }
|
||||
if (!NoPlayers()) { Check(false, "fixture remains unclaimed by any human"); return -1; }
|
||||
if (m_LastTick >= 0 && (now < m_LastTick || now - m_LastTick > 1500)) { m_Stable = -1; m_LandingStable = -1; m_TranslationStable = -1; }
|
||||
m_LastTick = now;
|
||||
if (m_Step == 0)
|
||||
{
|
||||
if (!m_Mode.ExtractionVehicle()) return 0;
|
||||
if (!Check(!m_AllocationRequested && FindPad(m_Pad), "independent qualified candidate found before one finite allocation")) return -1;
|
||||
m_AllocationRequested = true;
|
||||
m_Aircraft = Vehicle.Cast(ERR_Actors.Spawn(AIRCRAFT, m_Pad));
|
||||
m_Group = SCR_AIGroup.Cast(ERR_Actors.Spawn(GROUP, m_Pad + "20 0 0"));
|
||||
if (!Check(m_Aircraft && m_Group && m_Group.InitFactionKey("USSR") && m_Group.GetFaction() == m_Mode.EnemyFaction(), "one finite native Mi8 and memberless enemy group created")) return -1;
|
||||
m_Group.SetPermanentLOD(0); m_Group.ActivateAI();
|
||||
m_Step = 1; m_StageDeadline = now + 30000; return 0;
|
||||
}
|
||||
if (!m_Aircraft || !m_Group || !ERR_Actors.Alive(m_Aircraft)) { Check(false, "original airframe and group survive without replacement"); return -1; }
|
||||
if (m_Step == 1)
|
||||
{
|
||||
if (now >= m_StageDeadline) { Check(false, "real components and original pilot initialize within30seconds"); return -1; }
|
||||
if (!ReadyComponents()) return 0;
|
||||
if (!Check(!m_PilotRequested && m_Group.GetAgentsCount() == 0, "one original native pilot request only")) return -1;
|
||||
m_PilotRequested = true; AIGroup originalGroup = m_Group;
|
||||
m_Pilot = m_Seat.SpawnCharacterInCompartment(PILOT, originalGroup, GROUP);
|
||||
AIControlComponent control; if (m_Pilot) control = AIControlComponent.Cast(m_Pilot.FindComponent(AIControlComponent));
|
||||
AIAgent agent; if (control) agent = control.GetControlAIAgent();
|
||||
if (!Check(m_Pilot && originalGroup == m_Group && agent, "pilot seated by native mechanism in original group")) return -1;
|
||||
agent.SetPermanentLOD(0); agent.ActivateAI(); m_Step = 2; return 0;
|
||||
}
|
||||
Diagnostic(now);
|
||||
if (m_Step == 2)
|
||||
{
|
||||
if (now >= m_StageDeadline) { Check(false, "actual initial seating and native ground settling within30seconds"); return -1; }
|
||||
if (!OriginalOccupancy()) { m_Stable = -1; return 0; }
|
||||
Physics physics = m_Aircraft.GetPhysics();
|
||||
if (!physics || !physics.IsDynamic()) return 0;
|
||||
if (m_Wakes == 0) { m_Wakes = 1; physics.SetActive(ActiveState.ACTIVE); m_Stable = -1; return 0; }
|
||||
if (!m_Simulation.HasAnyGroundContact() || !ERR_CasualtyLoadingRules.AircraftReady(m_Aircraft)) { m_Stable = -1; return 0; }
|
||||
if (m_Stable < 0) m_Stable = now;
|
||||
if (now - m_Stable < 1000) return 0;
|
||||
if (!Check(!m_Controller.IsEngineOn(), "post-spawn qualification precedes native engine start")) return -1;
|
||||
m_Envelope = ERR_AircraftFlightEnvelope.Qualify(m_Mode, m_Aircraft, m_Pilot);
|
||||
if (!Check(m_Envelope != null, "actual post-spawn column and complete rotor/model footprint issue exact qualification")) return -1;
|
||||
m_InitialFuel = m_Fuel.GetTotalFuel();
|
||||
m_Flight = new ERR_AircraftFlight(); m_ControllerStarts++;
|
||||
m_ControlDeadline = Math.Min(m_Deadline, now + 240000);
|
||||
bool started;
|
||||
if (m_TranslationFixture) started = m_Flight.StartTranslation(m_Mode, m_Aircraft, m_Group, m_Pilot, m_Seat, m_Envelope, ORDER_TOKEN, m_ControlDeadline, m_Envelope.Home());
|
||||
else started = m_Flight.Start(m_Mode, m_Aircraft, m_Group, m_Pilot, m_Seat, m_Envelope, ORDER_TOKEN, m_ControlDeadline);
|
||||
if (!Check(started, "actual controller accepts one qualified original pilot/aircraft order")) return -1;
|
||||
m_Step = 3; return 0;
|
||||
}
|
||||
if (m_Step == 3)
|
||||
{
|
||||
if (!OriginalOccupancy()) { Check(false, "original pilot remains conscious and in its exact seat/group"); return -1; }
|
||||
bool engine = m_Controller.IsEngineOn(); if (engine && !m_LastEngine) m_EngineStarts++; m_LastEngine = engine;
|
||||
vector transform[4]; m_Aircraft.GetWorldTransform(transform);
|
||||
vector home = m_Envelope.Home(); float height = transform[3][1] - home[1];
|
||||
bool contact = m_Simulation.HasAnyGroundContact();
|
||||
bool landed = ERR_AircraftFlightRules.Landed(contact, height, m_Simulation.GetVelocity().Length(), m_Simulation.GetAngularVelocity().Length(), transform[1][1]);
|
||||
if (m_CancelFixture && m_CancelCount == 0 && ERR_NativeAircraftControlRules.CanCancel(m_Flight.State(), contact, height))
|
||||
{
|
||||
m_CancelCount = 1; m_CancelAt = now;
|
||||
m_CancelPilotHooks = m_PilotHooks; m_CancelHelicopterHooks = m_HelicopterHooks;
|
||||
m_Flight.Cancel("native fixture deliberate airborne cancellation");
|
||||
PrintFormat("ERR AIRCRAFT CANCEL: height=%1 contact=%2 state=%3 token=%4 deadline=%5 pilotHooks=%6 helicopterHooks=%7", height, contact, m_Flight.State(), m_Flight.OrderToken(), m_Flight.ERR_NativeControlDeadline(), m_PilotHooks, m_HelicopterHooks);
|
||||
if (!Check(m_Flight.State() == ERR_AircraftFlightState.LANDING && !m_Flight.Finished(), "one actual airborne Cancel enters retained LANDING control")) return -1;
|
||||
}
|
||||
if (m_CancelCount == 1)
|
||||
{
|
||||
if (m_Flight.ERR_NativeControlDeadline() != m_ControlDeadline || m_Flight.OrderToken() != ORDER_TOKEN) { Check(false, "cancel retains original overall deadline and exact order token"); return -1; }
|
||||
int state = m_Flight.State();
|
||||
if (state != ERR_AircraftFlightState.LANDING && state != ERR_AircraftFlightState.SHUTDOWN && state != ERR_AircraftFlightState.CANCELLED) { Check(false, "cancel retains landing or grounded shutdown states only"); return -1; }
|
||||
if (state == ERR_AircraftFlightState.LANDING && !contact && height > 1 && m_PilotHooks > m_CancelPilotHooks + 10 && m_HelicopterHooks > m_CancelHelicopterHooks + 10) m_ContinuedAirborneControl = true;
|
||||
if (!landed) m_LandingStable = -1;
|
||||
else if (m_LandingStable < 0) m_LandingStable = now;
|
||||
}
|
||||
if (m_TranslationFixture && m_Flight.State() == ERR_AircraftFlightState.TRANSLATING)
|
||||
{
|
||||
if (!m_TranslationObserved)
|
||||
{
|
||||
m_TranslationObserved = true; m_TranslationOrigin = m_Flight.TranslationOrigin(); m_TranslationTarget = m_Flight.TranslationTarget();
|
||||
if (!Check(vector.DistanceXZ(m_TranslationTarget, home) < 0.001, "pre-takeoff translation target remains the original qualified home")) return -1;
|
||||
}
|
||||
if (m_Flight.TranslationTarget() != m_TranslationTarget || m_Flight.TranslationOrigin() != m_TranslationOrigin || m_Flight.ERR_NativeControlDeadline() != m_ControlDeadline) { Check(false, "translation never retargets or renews its original deadline"); return -1; }
|
||||
vector horizontal = m_Simulation.GetVelocity(); horizontal[1] = 0;
|
||||
float requested = vector.DistanceXZ(m_TranslationOrigin, m_TranslationTarget);
|
||||
bool held = ERR_AircraftTranslationRules.Held(contact, height, m_Simulation.GetVelocity().Length(), m_Simulation.GetVelocity()[1], transform[1][1], horizontal.Length(), vector.DistanceXZ(transform[3], m_TranslationTarget), vector.DistanceXZ(transform[3], m_TranslationOrigin), requested);
|
||||
if (!held) m_TranslationStable = -1;
|
||||
else if (m_TranslationStable < 0) m_TranslationStable = now;
|
||||
if (m_TranslationStable >= 0 && now - m_TranslationStable >= 5000) m_TranslationHeld = true;
|
||||
}
|
||||
m_Flight.Tick();
|
||||
if (m_CancelCount == 1 && m_Flight.State() == ERR_AircraftFlightState.SHUTDOWN)
|
||||
{
|
||||
if (!Check(landed && m_LandingStable >= 0 && now - m_LandingStable >= 2000, "cancel reaches shutdown only after two observed stable grounded seconds")) return -1;
|
||||
m_LandingHeld = true;
|
||||
}
|
||||
if (!m_Flight.Finished()) return 0;
|
||||
PrintFormat("ERR AIRCRAFT FIXTURE RESULT: state=%1 token=%2 engineStarts=%3 controllerStarts=%4 wakes=%5 pilotHooks=%6 helicopterHooks=%7 reason=%8", m_Flight.State(), m_Flight.OrderToken(), m_EngineStarts, m_ControllerStarts, m_Wakes, m_PilotHooks, m_HelicopterHooks, m_Flight.Reason());
|
||||
bool complete = ERR_NativeAircraftControlRules.Terminal(m_CancelFixture, m_Flight.State(), m_CancelCount);
|
||||
complete = complete && ERR_NativeAircraftControlRules.PhysicalFinish(OriginalOccupancy(), landed, !m_Controller.IsEngineOn(), m_InitialFuel - m_Fuel.GetTotalFuel(), m_Wakes, m_EngineStarts, m_ControllerStarts, m_PilotHooks, m_HelicopterHooks);
|
||||
if (m_CancelFixture)
|
||||
{
|
||||
bool proof = ERR_NativeAircraftControlRules.CancelProof(m_ContinuedAirborneControl, m_LandingHeld, m_Flight.ERR_NativeControlDeadline() == m_ControlDeadline, m_Flight.OrderToken() == ORDER_TOKEN);
|
||||
PrintFormat("ERR AIRCRAFT CANCEL RESULT: count=%1 elapsed=%2 airborneHooks=%3 landingHeld=%4 deadline=%5 originalDeadline=%6", m_CancelCount, now - m_CancelAt, m_ContinuedAirborneControl, m_LandingHeld, m_Flight.ERR_NativeControlDeadline(), m_ControlDeadline);
|
||||
complete = complete && proof;
|
||||
}
|
||||
if (m_TranslationFixture)
|
||||
{
|
||||
float requestedDistance = vector.DistanceXZ(m_TranslationOrigin, m_TranslationTarget);
|
||||
float displacement = vector.DistanceXZ(transform[3], m_TranslationOrigin);
|
||||
float targetError = vector.DistanceXZ(transform[3], m_TranslationTarget);
|
||||
bool translated = ERR_NativeAircraftControlRules.TranslateProof(m_Flight.TranslationProven(), m_TranslationObserved && m_TranslationHeld, requestedDistance, displacement, targetError);
|
||||
PrintFormat("ERR AIRCRAFT TRANSLATION RESULT: proven=%1 held=%2 requested=%3 displacement=%4 targetError=%5 updates=%6", m_Flight.TranslationProven(), m_TranslationHeld, requestedDistance, displacement, targetError, m_Flight.TranslationUpdates());
|
||||
complete = complete && translated;
|
||||
}
|
||||
if (!Check(complete && m_Flight.OrderToken() == ORDER_TOKEN, "actual control order completes on original physical grounded aircraft with engine off, finite fuel use and exact pilot")) return -1;
|
||||
m_Complete = true; Stop(); return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void Stop()
|
||||
{
|
||||
if (m_Stopped) return;
|
||||
m_Stopped = true;
|
||||
if (s_Current == this) s_Current = null;
|
||||
if (m_Flight && !m_Flight.Finished() && m_CancelCount == 0) m_Flight.Cancel("native fixture stopped");
|
||||
// End-of-test disposal is not successful flight or a production cancellation.
|
||||
// These assets are fixture-owned only, never registered with mission cleanup.
|
||||
// Release the controller before deleting disposable unclaimed physical assets.
|
||||
m_Flight = null;
|
||||
bool context = GetGame() && GetGame().GetWorld() && GetGame().GetPlayerManager() && m_Mode && m_Mode == ERR_GameMode.Get();
|
||||
bool faction = context && (!m_Group || (!m_PilotRequested && m_Group.GetAgentsCount() == 0 && !m_Group.GetFaction()) || m_Group.GetFaction() == m_Mode.EnemyFaction()) && (!m_Aircraft || SCR_FactionManager.SGetFaction(m_Aircraft) == m_Mode.EnemyFaction());
|
||||
bool groupUnchanged = context && (!m_Group || m_Group.GetAgentsCount() == 0 || (m_Group.GetAgentsCount() == 1 && m_Pilot && ERR_Actors.Group(m_Pilot) == m_Group));
|
||||
bool pilotUnchanged = context && (!m_Pilot || (m_Group && ERR_Actors.Group(m_Pilot) == m_Group && !ERR_Actors.Player(m_Pilot)));
|
||||
if (ERR_NativeAircraftControlRules.CanDispose(context, NoPlayers(), faction, groupUnchanged, pilotUnchanged))
|
||||
{
|
||||
ERR_Actors.DeleteSpawned(m_Group); ERR_Actors.DeleteSpawned(m_Pilot); ERR_Actors.DeleteSpawned(m_Aircraft);
|
||||
}
|
||||
else Print("ERR AIRCRAFT FIXTURE CLEANUP: ownership changed; captured entities preserved");
|
||||
m_Envelope = null;
|
||||
}
|
||||
void ~ERR_NativeAircraftControlProbe() { Stop(); }
|
||||
}
|
||||
#endif
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
#ifdef WORKBENCH
|
||||
class ERR_NativeCourierMoveCallback : ScriptedInventoryOperationCallback
|
||||
{
|
||||
int Result;
|
||||
protected override void OnComplete() { if (Result == 0) Result = 1; }
|
||||
protected override void OnFailed() { if (Result == 0) Result = -1; }
|
||||
}
|
||||
modded class ERR_HQ
|
||||
{
|
||||
override bool QueueSupplyStatus(ERR_MortarBattery battery, float now)
|
||||
{
|
||||
bool queued = super.QueueSupplyStatus(battery, now);
|
||||
ERR_NativeMortarCourierProbe probe = ERR_NativeMortarCourierProbe.Current();
|
||||
if (queued && probe)
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
if (unit.Group == battery.Group && unit.PendingSupplyStatus) probe.Uplink(unit.Uplink, unit.PendingSupplyStatus);
|
||||
return queued;
|
||||
}
|
||||
}
|
||||
class ERR_NativeMortarCourierProbe
|
||||
{
|
||||
protected static ERR_NativeMortarCourierProbe s_Current;
|
||||
protected ERR_GameMode m_Mode;
|
||||
protected ERR_MortarLogistics m_Service;
|
||||
protected ERR_MortarBattery m_Battery;
|
||||
protected ref array<IEntity> m_Original = {};
|
||||
protected ref array<IEntity> m_Crew = {};
|
||||
protected ref array<IEntity> m_Depot = {};
|
||||
protected ref array<IEntity> m_Forces = {};
|
||||
protected ref array<IEntity> m_Boxes = {};
|
||||
protected ref array<BaseInventoryStorageComponent> m_Stores = {};
|
||||
protected ref ERR_NativeCourierMoveCallback m_Callback;
|
||||
protected IEntity m_Moving;
|
||||
protected IEntity m_Source;
|
||||
protected BaseInventoryStorageComponent m_Destination;
|
||||
protected ref ERR_HQRoute m_Uplink;
|
||||
protected ref ERR_HQRoute m_Downlink;
|
||||
protected ref ERR_MortarSupplyStatus m_Status;
|
||||
protected float m_ObservedAt;
|
||||
protected int m_Message;
|
||||
protected int m_Index;
|
||||
protected int m_Stage;
|
||||
protected float m_Deadline;
|
||||
protected float m_MoveDeadline;
|
||||
protected bool m_Complete;
|
||||
protected bool m_Stopped;
|
||||
protected vector m_Departure;
|
||||
protected IEntity m_Bearer;
|
||||
protected AIWaypointCycle m_OriginalPatrolCycle;
|
||||
protected ref array<AIWaypoint> m_OriginalPatrolMoves = {};
|
||||
static const int STORAGE_CANDIDATES = 64;
|
||||
static const int STORAGE_COUNT = 5;
|
||||
// The compact 8x8 grid keeps 3m between centers; blocked points are skipped.
|
||||
static vector StorageOffset(int index)
|
||||
{
|
||||
if (index < 0 || index >= STORAGE_CANDIDATES) return vector.Zero;
|
||||
int column = Math.Mod(index, 8);
|
||||
int row = index / 8;
|
||||
return Vector(20 + column * 3, 0, 15 + row * 3);
|
||||
}
|
||||
static const ResourceName BOX = "{EBBC859CCD94D96D}Prefabs/Props/EmptyStorages/USSR_AmmoBox_empty.et";
|
||||
static ERR_NativeMortarCourierProbe Current() { return s_Current; }
|
||||
static bool Enabled()
|
||||
{
|
||||
string smoke, outcome;
|
||||
return Replication.IsServer() && System.GetCLIParam("errSmoke", smoke) && smoke == "1" && System.GetCLIParam("errSmokeOutcome", outcome) && outcome == "mortar-courier";
|
||||
}
|
||||
void ERR_NativeMortarCourierProbe(ERR_GameMode mode) { m_Mode = mode; if (Enabled()) s_Current = this; }
|
||||
protected bool Check(bool condition, string detail)
|
||||
{
|
||||
if (condition) { Print("ERR SMOKE PASS: mortar courier " + detail); return true; }
|
||||
Print("ERR SMOKE FAIL: mortar courier " + detail, LogLevel.ERROR); Stop(); return false;
|
||||
}
|
||||
protected bool Has(IEntity owner, IEntity shell)
|
||||
{
|
||||
if (!owner || !shell) return false;
|
||||
InventoryStorageManagerComponent inventory = InventoryStorageManagerComponent.Cast(owner.FindComponent(InventoryStorageManagerComponent));
|
||||
if (!inventory) return false;
|
||||
array<IEntity> items = {}; ERR_Inventory.Items(inventory, items); return items.Contains(shell);
|
||||
}
|
||||
void Uplink(ERR_HQRoute route, ERR_MortarSupplyStatus report)
|
||||
{
|
||||
if (m_Stopped || !report || !m_Battery || report.Group != m_Battery.Group || m_Status) return;
|
||||
m_Uplink = route; m_Status = report; m_ObservedAt = report.ObservedAt; m_Message = report.MessageId;
|
||||
}
|
||||
protected bool OriginalStored()
|
||||
{
|
||||
foreach (IEntity shell : m_Original)
|
||||
{
|
||||
if (!shell) return false;
|
||||
int owners;
|
||||
foreach (BaseInventoryStorageComponent storage : m_Stores) if (storage && storage.Contains(shell)) owners++;
|
||||
foreach (IEntity actor : m_Crew) if (Has(actor, shell)) owners++;
|
||||
if (owners != 1) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
protected bool PreservedForces(ERR_HQ hq)
|
||||
{
|
||||
array<IEntity> forces = {}; hq.PhysicalForces(forces);
|
||||
if (forces.Count() != m_Forces.Count()) return false;
|
||||
foreach (IEntity original : m_Forces) if (!original || !forces.Contains(original)) return false;
|
||||
return true;
|
||||
}
|
||||
int Tick(float now)
|
||||
{
|
||||
if (m_Complete) return 1;
|
||||
if (m_Stopped || !Enabled()) return -1;
|
||||
if (m_Deadline == 0) m_Deadline = now + 360000;
|
||||
if (now > m_Deadline) { Check(false, "bounded native courier deadline"); return -1; }
|
||||
ERR_HQ hq = ERR_HQ.Get(); if (!hq) return 0;
|
||||
m_Service = hq.Logistics(); if (!m_Service) return 0;
|
||||
m_Battery = m_Service.Battery();
|
||||
if (m_Service.Stopped()) { Check(false, "production finite stock setup remains available"); return -1; }
|
||||
if (!m_Battery || !m_Battery.StartupActivated || m_Service.State() == 0) return 0;
|
||||
// No fire-controller hold, synthetic contact or routing bypass: this fixture
|
||||
// requires the actual local prisoner to remain inside the gun's minimum range.
|
||||
PlayerController controller = GetGame().GetPlayerController();
|
||||
IEntity player; if (controller) player = controller.GetControlledEntity();
|
||||
if (!player || !m_Mode.Players().CurrentActor(player) || vector.DistanceXZ(player.GetOrigin(), m_Battery.Gun.GetOrigin()) >= 300) { Check(false, "actual local prisoner remains below artillery minimum engagement range"); return -1; }
|
||||
if (m_Stage == 0)
|
||||
{
|
||||
m_Service.InitialStock(m_Depot); hq.PhysicalForces(m_Forces);
|
||||
ERR_ScenarioComponent scenario = ERR_ScenarioComponent.Cast(m_Mode.FindComponent(ERR_ScenarioComponent));
|
||||
ERR_SiteRuntime site = scenario.Sites().Find("everon-supply-2");
|
||||
if (!Check(site && site.Group == m_Service.Group() && site.PatrolCycle, "same original western cache patrol")) return -1;
|
||||
m_OriginalPatrolCycle = site.PatrolCycle; m_OriginalPatrolMoves.Copy(site.PatrolWaypoints);
|
||||
array<AIAgent> agents = {}; m_Battery.Group.GetAgents(agents);
|
||||
foreach (AIAgent agent : agents)
|
||||
{
|
||||
if (!agent || !agent.GetControlledEntity()) continue;
|
||||
IEntity actor = agent.GetControlledEntity(); m_Crew.Insert(actor);
|
||||
InventoryStorageManagerComponent inventory = InventoryStorageManagerComponent.Cast(actor.FindComponent(InventoryStorageManagerComponent));
|
||||
array<IEntity> items = {}; ERR_Inventory.Items(inventory, items);
|
||||
foreach (IEntity item : items) if (item && item.GetPrefabData() && item.GetPrefabData().GetPrefabName() == ERR_MortarBattery.SHELL && !m_Original.Contains(item)) m_Original.Insert(item);
|
||||
}
|
||||
if (!Check(m_Original.Count() == 27 && m_Depot.Count() == 12 && m_Battery.TotalShots == 0, "27 original crew rounds and 12 distinct finite depot rounds")) return -1;
|
||||
foreach (IEntity depotShell : m_Depot) if (!Check(!m_Original.Contains(depotShell) && Has(m_Service.Cache(), depotShell), "depot round is a distinct original cache item")) return -1;
|
||||
int tested, lowGround, obstructed;
|
||||
for (int i = 0; i < STORAGE_CANDIDATES && m_Stores.Count() < STORAGE_COUNT; i++)
|
||||
{
|
||||
tested++;
|
||||
vector position = ERR_Actors.Ground(m_Battery.Gun.GetOrigin() + StorageOffset(i));
|
||||
if (position[1] <= 1) { lowGround++; continue; }
|
||||
if (!SCR_WorldTools.TraceCilinderUtil(position + "0 1 0", 1.3, 2, TraceFlags.ENTS | TraceFlags.WORLD)) { obstructed++; continue; }
|
||||
IEntity box = ERR_Actors.Spawn(BOX, position);
|
||||
if (!Check(box != null, "fixture-only empty box created; no new ammunition")) return -1;
|
||||
m_Mode.Own(box); m_Boxes.Insert(box);
|
||||
BaseInventoryStorageComponent storage = BaseInventoryStorageComponent.Cast(box.FindComponent(BaseInventoryStorageComponent));
|
||||
if (!Check(storage != null, "fixture box has real native storage")) return -1;
|
||||
m_Stores.Insert(storage);
|
||||
}
|
||||
Print(string.Format("ERR COURIER STORAGE: tested=%1/64 ready=%2/5 lowGround=%3 obstructed=%4", tested, m_Stores.Count(), lowGround, obstructed));
|
||||
if (!Check(m_Boxes.Count() == STORAGE_COUNT && m_Stores.Count() == STORAGE_COUNT, "five owned empty native stores ready before moving any original shell")) return -1;
|
||||
Print("ERR COURIER FIXTURE: original 27 shells will be physically sequestered in five fixture boxes, not fired/destroyed; no report insertion, teleport, AI hold or replacement ammunition");
|
||||
m_Stage = 1;
|
||||
}
|
||||
if (m_Stage == 1)
|
||||
{
|
||||
if (m_Callback)
|
||||
{
|
||||
if (m_Callback.Result < 0 || now >= m_MoveDeadline || !m_Moving) { Check(false, "native depletion move neither failed nor timed out/item lost"); return -1; }
|
||||
InventoryItemComponent item = InventoryItemComponent.Cast(m_Moving.FindComponent(InventoryItemComponent));
|
||||
if (m_Callback.Result == 0 || !item || !item.GetParentSlot() || item.GetParentSlot().GetStorage() != m_Destination || Has(m_Source, m_Moving) || !m_Destination.Contains(m_Moving)) return 0;
|
||||
m_Callback = null; m_Moving = null; m_Source = null; m_Destination = null; m_Index++;
|
||||
}
|
||||
if (m_Index < m_Original.Count())
|
||||
{
|
||||
m_Moving = m_Original[m_Index];
|
||||
foreach (IEntity owner : m_Crew) if (Has(owner, m_Moving)) m_Source = owner;
|
||||
if (!Check(m_Source != null, "original shell still belongs to original crew before depletion")) return -1;
|
||||
foreach (BaseInventoryStorageComponent candidate : m_Stores) if (candidate.PerformVolumeValidation(m_Moving)) { m_Destination = candidate; break; }
|
||||
if (!Check(m_Destination != null, "five native boxes have capacity for original shells")) return -1;
|
||||
InventoryStorageManagerComponent source = InventoryStorageManagerComponent.Cast(m_Source.FindComponent(InventoryStorageManagerComponent));
|
||||
m_Callback = new ERR_NativeCourierMoveCallback(); m_MoveDeadline = now + 8000;
|
||||
if (!Check(source.TryMoveItemToStorage(m_Moving, m_Destination, -1, m_Callback), "native depletion move accepted, awaiting exact ownership proof")) return -1;
|
||||
return 0;
|
||||
}
|
||||
if (!Check(OriginalStored() && m_Battery.CountShells() == 0, "all 27 original shells remain physical while real battery inventory is exhausted")) return -1;
|
||||
m_Stage = 2;
|
||||
}
|
||||
if (!OriginalStored() || !PreservedForces(hq)) { Check(false, "all original shells and all original physical forces preserved"); return -1; }
|
||||
if (!m_Downlink && m_Service.ProbeOrder()) m_Downlink = m_Service.ProbeOrder();
|
||||
if (!m_Bearer && m_Service.Courier()) { m_Bearer = m_Service.Courier(); m_Departure = m_Bearer.GetOrigin(); }
|
||||
if (m_Service.Delivered() < 1 || m_Service.CompletedTrips() < 1 || m_Service.State() != 1) return 0;
|
||||
AIWaypoint current = m_Service.Group().GetCurrentWaypoint();
|
||||
if (!current || (current != m_OriginalPatrolCycle && !m_OriginalPatrolMoves.Contains(current))) return 0;
|
||||
if (!Check(m_Service.CompletedTrips() == 1 && hq.LogisticsToken(m_Service.Group()) == 0, "complete physical trip released HQ reservation and restored original native patrol")) return -1;
|
||||
if (!Check(m_Status && m_Service.ProbeSourceMessage() == m_Message && m_Status.MessageId == m_Message && m_Status.ObservedAt == m_ObservedAt && ERR_MortarLogisticsRules.Fresh(m_ObservedAt, m_Service.ProbeLastDelivery()), "same immutable originally delivered supply status authorizes delivery")) return -1;
|
||||
if (!Check(m_Uplink && !m_Uplink.Failed && !m_Uplink.Sending && m_Uplink.Cursor >= m_Uplink.Nodes.Count(), "actual low-ammo radio uplink reached HQ")) return -1;
|
||||
if (!Check(m_Downlink && !m_Downlink.Failed && !m_Downlink.Sending && m_Downlink.Cursor == m_Downlink.Nodes.Count() - 1, "actual courier downlink completed before item delivery")) return -1;
|
||||
if (!Check(m_Bearer && ERR_Actors.Group(m_Bearer) == m_Service.Group() && vector.DistanceXZ(m_Departure, m_Bearer.GetOrigin()) > 100, "same original bearer physically walked more than 100m")) return -1;
|
||||
bool found; foreach (IEntity deliveredShell : m_Depot) foreach (IEntity recipient : m_Crew) if (Has(recipient, deliveredShell)) found = true;
|
||||
if (!Check(found && m_Battery.CountShells() > 0 && m_Battery.TotalShots == 0, "previously empty battery now owns an exact original depot shell; no synthetic rounds or shots")) return -1;
|
||||
foreach (IEntity retained : m_Depot)
|
||||
{
|
||||
int owners;
|
||||
if (Has(m_Service.Cache(), retained)) owners++;
|
||||
if (Has(m_Bearer, retained)) owners++;
|
||||
foreach (IEntity crew : m_Crew) if (Has(crew, retained)) owners++;
|
||||
if (!Check(retained && owners == 1, "each of 12 initial depot shells has exactly one physical owner")) return -1;
|
||||
}
|
||||
m_Complete = true; Stop(); return 1;
|
||||
}
|
||||
void Stop()
|
||||
{
|
||||
if (m_Stopped) return;
|
||||
m_Stopped = true; if (s_Current == this) s_Current = null;
|
||||
// Boxes with borrowed originals remain owned by normal mission cleanup.
|
||||
// No manual restoration/deletion can race the production/native moves.
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+334
@@ -0,0 +1,334 @@
|
||||
#ifdef WORKBENCH
|
||||
// Explicit synthetic observation only. Delivery, HQ scheduling and native fire
|
||||
// are unchanged; this helper never writes directly into the HQ contact list.
|
||||
modded class ERR_HQ
|
||||
{
|
||||
int ERR_QueueMortarFixtureReport(SCR_AIGroup source, IEntity target, vector position, float now)
|
||||
{
|
||||
if (!ERR_NativeMortarFiringProbe.Enabled() || !target || !m_Contacts.IsEmpty()) return 0;
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
{
|
||||
if (unit.Group != source || !unit.ReservedForSupport || !unit.Radio || unit.Uplink || unit.PendingReport) continue;
|
||||
ERR_HQRoute route = m_Network.Route(unit.Radio);
|
||||
if (!route || route.Nodes.IsEmpty()) return 0;
|
||||
ERR_HQReport report = new ERR_HQReport();
|
||||
report.MessageId = m_Journal.Message(); report.Target = target; report.Source = source;
|
||||
report.Position = position; report.ObservedAt = now; report.DetailsObservedAt = now;
|
||||
report.HasVelocity = true; report.Velocity = vector.Zero;
|
||||
report.TravelMode = "on foot"; report.SeenEscapists = 1; report.SeenWeapons = "fixture observation";
|
||||
route.MessageId = report.MessageId; route.Payload = "EXPLICIT SYNTHETIC OBSERVATION: " + report.Description(now);
|
||||
unit.PendingReport = report; unit.Uplink = route;
|
||||
m_Journal.Write("FIXTURE REPORT", route.Payload, report.MessageId);
|
||||
return report.MessageId;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void ERR_RemoveMortarFixtureReport(IEntity target)
|
||||
{
|
||||
if (!target) return;
|
||||
for (int i = m_Contacts.Count() - 1; i >= 0; i--)
|
||||
if (m_Contacts[i].Target == target) m_Contacts.Remove(i);
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
{
|
||||
if (unit.PendingReport && unit.PendingReport.Target == target) { unit.PendingReport = null; unit.Uplink = null; }
|
||||
if ((unit.PendingOrder && unit.PendingOrder.Target == target) || (unit.Assignment && unit.Assignment.Target == target)) unit.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Production sets Released immediately before reserving shell airspace, then
|
||||
// invokes native inventory transfer. Observe that existing commit boundary so
|
||||
// even a synchronous native projectile has a prior recorded authorization.
|
||||
modded class ERR_MortarBattery
|
||||
{
|
||||
override void ReserveShellAirspace(float now)
|
||||
{
|
||||
super.ReserveShellAirspace(now);
|
||||
ERR_NativeMortarFiringProbe probe = ERR_NativeMortarFiringProbe.Active();
|
||||
if (probe) probe.NativeCommit(this, now);
|
||||
}
|
||||
override void Cancel(string reason = "cancel requested")
|
||||
{
|
||||
ERR_NativeMortarFiringProbe probe = ERR_NativeMortarFiringProbe.Active();
|
||||
if (probe) probe.NativeClosing(this, reason, LastFailure);
|
||||
super.Cancel(reason);
|
||||
}
|
||||
}
|
||||
|
||||
class ERR_NativeMortarEvidenceRules
|
||||
{
|
||||
static bool AimFresh(float commandedAt, float validatedAt, float now)
|
||||
{
|
||||
return commandedAt >= 0 && now >= commandedAt && validatedAt >= commandedAt && now >= validatedAt && now - validatedAt <= 10000;
|
||||
}
|
||||
static bool CommitFresh(float observed, float expectedObserved, float committed)
|
||||
{
|
||||
return observed == expectedObserved && observed >= 0 && committed >= observed && committed - observed <= ERR_MortarPrediction.MAX_REPORT_AGE_MS;
|
||||
}
|
||||
static bool EventBound(bool pending, int order, int currentOrder, int shot, int currentShot, float committed, float emitted)
|
||||
{
|
||||
// The fixture has its own overall deadline. A committed shell does not gain
|
||||
// a second authorization expiry while native physical transfer completes.
|
||||
return pending && order == currentOrder && shot == currentShot && emitted >= committed;
|
||||
}
|
||||
static bool ExpiredClosure(float observed, float closed, string failure)
|
||||
{
|
||||
return observed >= 0 && closed > observed + ERR_MortarPrediction.MAX_REPORT_AGE_MS && (failure.Contains("delivered target report expired") || failure.Contains("observation older than 60s"));
|
||||
}
|
||||
}
|
||||
|
||||
// Observe only the actual native event. Node SUCCESS, accepted inventory moves
|
||||
// and committed shell loading are never counted as physical projectiles.
|
||||
modded class SCR_MortarMuzzleComponent
|
||||
{
|
||||
override void OnWeaponFired(IEntity effectEntity, BaseMuzzleComponent muzzle, IEntity projectileEntity)
|
||||
{
|
||||
ERR_NativeMortarFiringProbe probe = ERR_NativeMortarFiringProbe.Active();
|
||||
if (probe) probe.NativeProjectile(GetOwner(), projectileEntity);
|
||||
super.OnWeaponFired(effectEntity, muzzle, projectileEntity);
|
||||
}
|
||||
}
|
||||
|
||||
class ERR_NativeMortarFiringProbe
|
||||
{
|
||||
protected static ERR_NativeMortarFiringProbe s_Active;
|
||||
protected ERR_GameMode m_Mode;
|
||||
protected ERR_MortarBattery m_Battery;
|
||||
protected IEntity m_Gun;
|
||||
protected SCR_AIGroup m_Group;
|
||||
protected IEntity m_Target;
|
||||
protected vector m_Impact;
|
||||
protected ref array<IEntity> m_Crew = {};
|
||||
protected ref array<IEntity> m_InitialShells = {};
|
||||
protected ref array<IEntity> m_Projectiles = {};
|
||||
protected int m_Source;
|
||||
protected int m_Order;
|
||||
protected int m_Step;
|
||||
protected int m_Events;
|
||||
protected int m_StoppedShells;
|
||||
protected float m_Deadline;
|
||||
protected float m_NextDiagnostic;
|
||||
protected float m_StopUntil;
|
||||
protected float m_ObservedAt;
|
||||
protected bool m_WaitForExpiry;
|
||||
protected int m_FinalEvents;
|
||||
protected bool m_CommitPending;
|
||||
protected float m_CommittedAt;
|
||||
protected int m_CommitOrder;
|
||||
protected int m_CommitShot;
|
||||
protected int m_Commits;
|
||||
protected bool m_ClosureSeen;
|
||||
protected bool m_ClosureExpired;
|
||||
protected bool m_ClosureComplete;
|
||||
protected bool m_Unsafe;
|
||||
protected bool m_BadProjectile;
|
||||
protected bool m_SawUplink;
|
||||
protected bool m_SawDownlink;
|
||||
protected bool m_SawActivity;
|
||||
protected bool m_Stopped;
|
||||
protected string m_BadReason;
|
||||
|
||||
static bool Enabled()
|
||||
{
|
||||
string enabled, outcome;
|
||||
return Replication.IsServer() && System.GetCLIParam("errSmoke", enabled) && enabled == "1" && System.GetCLIParam("errSmokeOutcome", outcome) && (outcome == "mortar-fire" || outcome == "mortar-expiry");
|
||||
}
|
||||
static ERR_NativeMortarFiringProbe Active() { return s_Active; }
|
||||
void ERR_NativeMortarFiringProbe(ERR_GameMode mode)
|
||||
{
|
||||
m_Mode = mode;
|
||||
string outcome; System.GetCLIParam("errSmokeOutcome", outcome); m_WaitForExpiry = outcome == "mortar-expiry";
|
||||
m_Deadline = GetGame().GetWorld().GetWorldTime() + 180000;
|
||||
if (Enabled() && !s_Active) s_Active = this;
|
||||
}
|
||||
protected bool Check(bool condition, string detail)
|
||||
{
|
||||
if (condition) { Print("ERR SMOKE PASS: mortar firing " + detail); return true; }
|
||||
Print("ERR SMOKE FAIL: mortar firing " + detail, LogLevel.ERROR); Stop(); return false;
|
||||
}
|
||||
protected void Shells(out array<IEntity> shells)
|
||||
{
|
||||
shells.Clear();
|
||||
foreach (IEntity actor : m_Crew)
|
||||
{
|
||||
if (!actor) continue;
|
||||
InventoryStorageManagerComponent inventory = InventoryStorageManagerComponent.Cast(actor.FindComponent(InventoryStorageManagerComponent));
|
||||
array<IEntity> items = {}; ERR_Inventory.Items(inventory, items);
|
||||
foreach (IEntity item : items)
|
||||
if (item && item.GetPrefabData() && item.GetPrefabData().GetPrefabName() == ERR_MortarBattery.SHELL && !shells.Contains(item)) shells.Insert(item);
|
||||
}
|
||||
}
|
||||
protected bool CheckNoOccupants(IEntity entity)
|
||||
{
|
||||
if (ChimeraCharacter.Cast(entity) || Vehicle.Cast(entity)) { m_Unsafe = true; return false; }
|
||||
return true;
|
||||
}
|
||||
protected bool EmptyImpact(vector position)
|
||||
{
|
||||
m_Unsafe = false;
|
||||
GetGame().GetWorld().QueryEntitiesByAABB(position - "200 5000 200", position + "200 5000 200", CheckNoOccupants, null, EQueryEntitiesFlags.ALL);
|
||||
return !m_Unsafe;
|
||||
}
|
||||
protected bool SelectImpact(ERR_MortarSystem system)
|
||||
{
|
||||
// Bounded candidate survey: no clearance assumption from coordinates alone.
|
||||
array<vector> offsets = {"-800 0 -600", "-600 0 -800", "0 0 -1000", "600 0 -800", "-1000 0 0", "800 0 -600", "-800 0 600", "0 0 1000"};
|
||||
foreach (vector offset : offsets)
|
||||
{
|
||||
vector candidate = ERR_Actors.Ground(m_Battery.Position + offset);
|
||||
if (candidate[1] < 1 || !EmptyImpact(candidate) || !system.Safe(m_Battery, candidate, 75)) continue;
|
||||
bool clear = true;
|
||||
for (int shot = 0; shot < ERR_MortarSalvo.ROUNDS; shot++)
|
||||
{
|
||||
vector spread = ERR_Actors.Ground(candidate + ERR_MortarSalvo.Offset(m_Battery.SiteId, m_Battery.OrderId + 1, shot));
|
||||
if (!system.Safe(m_Battery, spread, 75)) { clear = false; break; }
|
||||
}
|
||||
if (!clear) continue;
|
||||
m_Impact = candidate;
|
||||
PrintFormat("ERR MORTAR FIXTURE IMPACT: position=%1 batteryDistance=%2; no characters/vehicles in 400m square; production settlement/friendly exclusions passed", candidate, vector.DistanceXZ(candidate, m_Battery.Position));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
protected void Diagnose(float now)
|
||||
{
|
||||
if (now < m_NextDiagnostic) return;
|
||||
m_NextDiagnostic = now + 5000;
|
||||
if (!m_Battery) { Print("ERR MORTAR FIXTURE: waiting for one authored battery"); return; }
|
||||
PrintFormat("ERR MORTAR FIXTURE: step=%1 startup=%2 source=M%3 observedEvents=%4 ready=%5 failure=%6", m_Step, m_Battery.StartupActivated, m_Source, m_Events, m_Battery.Operational(), m_Battery.SpawnFailure);
|
||||
m_Battery.SiteStatus(now);
|
||||
}
|
||||
void NativeCommit(ERR_MortarBattery battery, float now)
|
||||
{
|
||||
if (m_Stopped || battery != m_Battery) return;
|
||||
if ((m_Step != 1 && m_Step != 3) || m_CommitPending || !battery.CommandOpen || !battery.Released || battery.SourceId != m_Source || battery.OrderId != m_Order || !battery.Report || battery.Report.MessageId != m_Source || !ERR_NativeMortarEvidenceRules.CommitFresh(battery.Report.ObservedAt, m_ObservedAt, now) || !ERR_NativeMortarEvidenceRules.AimFresh(battery.AimedAt, battery.AimValidatedAt, now) || battery.FlightTime <= 0)
|
||||
{ m_BadProjectile = true; m_BadReason = "physical commit lacks immutable fresh report/current order/native aim, or duplicates pending commit"; return; }
|
||||
m_CommitPending = true; m_CommittedAt = now; m_CommitOrder = battery.OrderId; m_CommitShot = battery.OrderShots; m_Commits++;
|
||||
PrintFormat("ERR MORTAR FIXTURE COMMIT: count=%1 order=%2 shot=%3 source=M%4 observedAge=%5; recorded before native inventory transfer", m_Commits, m_CommitOrder, m_CommitShot, m_Source, now - m_ObservedAt);
|
||||
}
|
||||
void NativeClosing(ERR_MortarBattery battery, string reason, string failure)
|
||||
{
|
||||
if (m_Stopped || !m_WaitForExpiry || battery != m_Battery || !battery.CommandOpen || battery.SourceId != m_Source || m_Source <= 0) return;
|
||||
float now = GetGame().GetWorld().GetWorldTime();
|
||||
m_ClosureSeen = true;
|
||||
m_ClosureComplete = battery.OrderShots == ERR_MortarSalvo.ROUNDS && m_Events == ERR_MortarSalvo.ROUNDS;
|
||||
m_ClosureExpired = ERR_NativeMortarEvidenceRules.ExpiredClosure(m_ObservedAt, now, failure);
|
||||
PrintFormat("ERR MORTAR FIXTURE CLOSURE: order=%1 observedAge=%2 reason=%3 lastFailure=%4 complete=%5 expiredWithheld=%6", battery.OrderId, now - m_ObservedAt, reason, failure, m_ClosureComplete, m_ClosureExpired);
|
||||
if (battery.Stopped() || battery.OrderId != m_Order || (!m_ClosureExpired && !m_ClosureComplete))
|
||||
{ m_BadProjectile = true; m_BadReason = "expiry evidence interrupted by Stop or unrelated/incomplete order closure"; }
|
||||
}
|
||||
void NativeProjectile(IEntity gun, IEntity projectile)
|
||||
{
|
||||
if (m_Stopped || !m_Battery || gun != m_Gun) return;
|
||||
if (!projectile) { m_BadProjectile = true; m_BadReason = "native fired event has no physical projectile"; return; }
|
||||
if (m_Projectiles.Contains(projectile)) { m_BadProjectile = true; m_BadReason = "same physical projectile observed twice"; return; }
|
||||
m_Projectiles.Insert(projectile); m_Events++;
|
||||
float now = GetGame().GetWorld().GetWorldTime();
|
||||
if ((m_Step != 1 && m_Step != 3) || !m_Battery.Released || m_Battery.SourceId != m_Source || !ERR_NativeMortarEvidenceRules.EventBound(m_CommitPending, m_CommitOrder, m_Battery.OrderId, m_CommitShot, m_Battery.OrderShots, m_CommittedAt, now))
|
||||
{ m_BadProjectile = true; m_BadReason = "projectile lacks the recorded fresh physical commit for its current order/shot"; }
|
||||
m_CommitPending = false;
|
||||
PrintFormat("ERR MORTAR FIXTURE PROJECTILE: event=%1 entity=%2 origin=%3 order=%4 source=M%5 aimAge=%6 flight=%7; impact is not measured", m_Events, projectile, projectile.GetOrigin(), m_Battery.OrderId, m_Battery.SourceId, now - m_Battery.AimedAt, m_Battery.FlightTime);
|
||||
}
|
||||
int Tick(float now)
|
||||
{
|
||||
if (m_Stopped) return -1;
|
||||
if (!Enabled() || s_Active != this || !m_Mode || !m_Mode.Active()) { Check(false, "explicit active fixture context retained"); return -1; }
|
||||
ERR_HQ hq = ERR_HQ.Get();
|
||||
ERR_MortarSystem system;
|
||||
if (hq) system = hq.Mortars();
|
||||
if (!system) { Check(false, "real HQ mortar service available"); return -1; }
|
||||
if (!m_Battery)
|
||||
{
|
||||
array<IEntity> guns = {}; system.AppendGuns(guns);
|
||||
if (guns.Count() == 1) { m_Gun = guns[0]; m_Battery = system.FindGun(m_Gun); }
|
||||
}
|
||||
Diagnose(now);
|
||||
if (now >= m_Deadline) { Check(false, "bounded deadline; inspect readiness/route/activity/aim diagnostics"); return -1; }
|
||||
if (m_BadProjectile) { Check(false, m_BadReason); return -1; }
|
||||
if (!m_Battery) return 0;
|
||||
if (!m_Battery.SpawnFailure.IsEmpty()) { Check(false, "battery startup failed: " + m_Battery.SpawnFailure); return -1; }
|
||||
if (m_Step == 0)
|
||||
{
|
||||
if (!m_Battery.StartupActivated || !m_Battery.Operational() || !hq.MortarConnected(m_Battery.Group)) return 0;
|
||||
m_Group = m_Battery.Group;
|
||||
if (!Check(m_Group && m_Group.IsExpandComplete() && m_Group.GetAgentsCount() == 4 && m_Battery.TotalShots == 0 && m_Battery.OrderId == 0 && !m_Battery.CommandOpen && !m_Battery.Activity && !m_Battery.RadioOrder, "untasked original gun and complete four-person crew start without shots")) return -1;
|
||||
array<AIAgent> agents = {}; m_Group.GetAgents(agents);
|
||||
foreach (AIAgent agent : agents)
|
||||
{
|
||||
IEntity actor; if (agent) actor = agent.GetControlledEntity();
|
||||
if (!Check(ERR_Actors.CanFight(actor) && !m_Crew.Contains(actor), "initial physical crew member is conscious and unique")) return -1;
|
||||
m_Crew.Insert(actor);
|
||||
}
|
||||
if (!Check(m_Battery.RadioOperator && ERR_HQRadio.Capability(m_Battery.RadioOperator) == 2, "dedicated actual manpack supplies the radio route")) return -1;
|
||||
Shells(m_InitialShells);
|
||||
if (!Check(m_InitialShells.Count() == 27 && m_Battery.CountShells() == 27, "exactly 27 distinct physical initial HE shells")) return -1;
|
||||
if (!Check(SelectImpact(system), "bounded candidate survey finds an unoccupied production-safe impact area")) return -1;
|
||||
m_Target = ERR_Actors.Spawn(ERR_Resources.IntelFolder(), m_Impact);
|
||||
if (!Check(m_Target != null, "fixture-owned inert report identity exists; no enemy perception is claimed")) return -1;
|
||||
m_Source = hq.ERR_QueueMortarFixtureReport(m_Group, m_Target, m_Impact, now);
|
||||
if (!Check(m_Source > 0, "synthetic observation enters actual existing radio uplink once")) return -1;
|
||||
m_ObservedAt = now; m_SawUplink = true; m_Deadline = now + 90000; m_Step = 1; return 0;
|
||||
}
|
||||
if (m_WaitForExpiry && m_Battery.Stopped()) { Check(false, "battery must remain running throughout natural expiry observation"); return -1; }
|
||||
if (m_Battery.Report && (m_Battery.Report.MessageId != m_Source || m_Battery.Report.ObservedAt != m_ObservedAt)) { Check(false, "delivered report identity and original observation timestamp remain immutable"); return -1; }
|
||||
if (m_Battery.Gun != m_Gun || m_Battery.Group != m_Group) { Check(false, "original gun and crew identity retained"); return -1; }
|
||||
if (!EmptyImpact(m_Impact)) { Check(false, "impact exclusion stays unoccupied before native firing/cancellation"); return -1; }
|
||||
array<IEntity> remaining = {}; Shells(remaining);
|
||||
foreach (IEntity item : remaining)
|
||||
if (!m_InitialShells.Contains(item)) { Check(false, "no replacement HE shell appears after initialization"); return -1; }
|
||||
if (m_Step == 1)
|
||||
{
|
||||
if (m_Battery.RadioOrder && m_Battery.SourceId == m_Source) { m_SawDownlink = true; m_Order = m_Battery.OrderId; }
|
||||
if (m_Battery.Activity && m_Battery.Report && m_Battery.Report.MessageId == m_Source) m_SawActivity = true;
|
||||
if (m_Battery.OrderId > 1 || (m_Battery.SourceId != 0 && m_Battery.SourceId != m_Source)) { Check(false, "one unchanged fixture source creates only one HQ order"); return -1; }
|
||||
if (m_Events == 0) return 0;
|
||||
if (!Check(m_Events == 1 && m_Battery.TotalShots == 1 && m_Battery.OrderShots == 1 && !m_Battery.Released && m_SawUplink && m_SawDownlink && m_SawActivity && m_Order == 1, "one physical projectile follows real uplink/downlink, native activity and fresh aim")) return -1;
|
||||
if (!Check(remaining.Count() == 26 && m_Battery.CountShells() == 26, "one fired projectile consumes one of 27 original inventory shells")) return -1;
|
||||
if (m_WaitForExpiry)
|
||||
{
|
||||
m_Step = 3; m_Deadline = m_ObservedAt + ERR_MortarPrediction.MAX_REPORT_AGE_MS + 50000;
|
||||
return 0;
|
||||
}
|
||||
m_FinalEvents = m_Events;
|
||||
// Cancel on this later fixture tick, never reentrantly inside the firing node.
|
||||
hq.ERR_RemoveMortarFixtureReport(m_Target);
|
||||
system.Stop(); system.Stop();
|
||||
if (!Check(!m_Battery.CommandOpen && !m_Battery.Activity && !m_Battery.RadioOrder, "explicit support Stop closes native order and route idempotently")) return -1;
|
||||
m_StoppedShells = remaining.Count(); m_StopUntil = now + 12000; m_Deadline = m_StopUntil + 2000; m_Step = 2; return 0;
|
||||
}
|
||||
if (m_Step == 3)
|
||||
{
|
||||
if (m_Battery.OrderId != m_Order || m_Battery.SourceId != m_Source) { Check(false, "unchanged delivered observation received a second order"); return -1; }
|
||||
if (m_Events > ERR_MortarSalvo.ROUNDS || m_Battery.TotalShots > ERR_MortarSalvo.ROUNDS) { Check(false, "single observation cannot exceed finite salvo"); return -1; }
|
||||
if (now < m_ObservedAt + ERR_MortarPrediction.MAX_REPORT_AGE_MS + 25000) return 0;
|
||||
if (!Check(m_ClosureSeen && (m_ClosureExpired || m_ClosureComplete) && !m_CommitPending && m_Commits == m_Events && m_Events >= 1 && m_Battery.TotalShots == m_Events && m_Battery.OrderShots == m_Events && remaining.Count() == 27 - m_Events && !m_Battery.CommandOpen && !m_Battery.Activity && !m_Battery.RadioOrder && !m_Battery.Report, "explicit expired-withheld closure or complete finite salvo, with only fresh commits and observed physical shells consumed")) return -1;
|
||||
m_FinalEvents = m_Events;
|
||||
// Keep HQ running and the expired contact present throughout this next wait.
|
||||
m_StoppedShells = remaining.Count(); m_StopUntil = now + 12000; m_Deadline = m_StopUntil + 2000; m_Step = 4;
|
||||
return 0;
|
||||
}
|
||||
if (now < m_StopUntil) return 0;
|
||||
if (!Check(m_Battery.TotalShots == m_FinalEvents && m_Events == m_FinalEvents && m_Battery.OrderId == m_Order && remaining.Count() == m_StoppedShells && !m_Battery.CommandOpen && !m_Battery.Activity && !m_Battery.RadioOrder, "closed order issues no duplicate order, projectile or replenishment over 12 seconds")) return -1;
|
||||
array<AIAgent> finalCrew = {}; m_Group.GetAgents(finalCrew);
|
||||
if (!Check(finalCrew.Count() == 4 && m_Battery.Gun == m_Gun, "original group and gun retain four crew")) return -1;
|
||||
foreach (AIAgent finalAgent : finalCrew)
|
||||
if (!Check(finalAgent && m_Crew.Contains(finalAgent.GetControlledEntity()) && ERR_Actors.CanFight(finalAgent.GetControlledEntity()), "final conscious crew entity belongs to original physical roster")) return -1;
|
||||
if (m_WaitForExpiry) { hq.ERR_RemoveMortarFixtureReport(m_Target); system.Stop(); system.Stop(); }
|
||||
Stop(); return 1;
|
||||
}
|
||||
void Stop()
|
||||
{
|
||||
if (m_Stopped) return;
|
||||
m_Stopped = true;
|
||||
ERR_HQ hq = ERR_HQ.Get();
|
||||
if (hq)
|
||||
{
|
||||
hq.ERR_RemoveMortarFixtureReport(m_Target);
|
||||
if (m_Battery && m_Battery.SourceId == m_Source && m_Source > 0) m_Battery.Cancel("mortar firing fixture cleanup");
|
||||
}
|
||||
ERR_Actors.DeleteSpawned(m_Target); m_Target = null;
|
||||
if (s_Active == this) s_Active = null;
|
||||
}
|
||||
void ~ERR_NativeMortarFiringProbe() { Stop(); }
|
||||
}
|
||||
#endif
|
||||
+476
@@ -0,0 +1,476 @@
|
||||
#ifdef WORKBENCH
|
||||
// Explicit synthetic observation only. Delivery, HQ scheduling and native fire
|
||||
// are unchanged; this helper never writes directly into the HQ contact list.
|
||||
modded class ERR_HQ
|
||||
{
|
||||
int ERR_QueueMortarRefreshReport(SCR_AIGroup source, IEntity radio, IEntity target, vector position, out float observedAt)
|
||||
{
|
||||
observedAt = -1;
|
||||
if (!ERR_NativeMortarSalvoProbe.RefreshEnabled() || !target || !radio || ERR_HQRadio.Capability(radio) != 2) return 0;
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
{
|
||||
if (unit.Group != source || !unit.ReservedForSupport || unit.Radio != radio || unit.Uplink || unit.PendingReport || unit.PendingSupplyStatus) continue;
|
||||
ERR_HQRoute route = m_Network.Route(radio);
|
||||
if (!route || route.Nodes.IsEmpty()) return 0;
|
||||
observedAt = GetGame().GetWorld().GetWorldTime();
|
||||
ERR_HQReport report = new ERR_HQReport();
|
||||
report.MessageId = m_Journal.Message(); report.Target = target; report.Source = source;
|
||||
report.Position = position; report.ObservedAt = observedAt; report.DetailsObservedAt = observedAt;
|
||||
report.HasVelocity = true; report.Velocity = vector.Zero;
|
||||
report.TravelMode = "on foot"; report.SeenEscapists = 1; report.SeenWeapons = "fixture observation";
|
||||
route.MessageId = report.MessageId; route.Payload = "NEW EXPLICIT SYNTHETIC OBSERVATION: " + report.Description(observedAt);
|
||||
unit.PendingReport = report; unit.Uplink = route;
|
||||
m_Journal.Write("FIXTURE REPORT", route.Payload, report.MessageId);
|
||||
return report.MessageId;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int ERR_QueueMortarSalvoReport(SCR_AIGroup source, IEntity target, vector position, float now)
|
||||
{
|
||||
if (!ERR_NativeMortarSalvoProbe.Enabled() || !target || !m_Contacts.IsEmpty()) return 0;
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
{
|
||||
if (unit.Group != source || !unit.ReservedForSupport || !unit.Radio || unit.Uplink || unit.PendingReport) continue;
|
||||
ERR_HQRoute route = m_Network.Route(unit.Radio);
|
||||
if (!route || route.Nodes.IsEmpty()) return 0;
|
||||
ERR_HQReport report = new ERR_HQReport();
|
||||
report.MessageId = m_Journal.Message(); report.Target = target; report.Source = source;
|
||||
report.Position = position; report.ObservedAt = now; report.DetailsObservedAt = now;
|
||||
report.HasVelocity = true; report.Velocity = vector.Zero;
|
||||
report.TravelMode = "on foot"; report.SeenEscapists = 1; report.SeenWeapons = "fixture observation";
|
||||
route.MessageId = report.MessageId; route.Payload = "EXPLICIT SYNTHETIC OBSERVATION: " + report.Description(now);
|
||||
unit.PendingReport = report; unit.Uplink = route;
|
||||
m_Journal.Write("FIXTURE REPORT", route.Payload, report.MessageId);
|
||||
return report.MessageId;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void ERR_RemoveMortarSalvoReport(IEntity target)
|
||||
{
|
||||
if (!target) return;
|
||||
for (int i = m_Contacts.Count() - 1; i >= 0; i--)
|
||||
if (m_Contacts[i].Target == target) m_Contacts.Remove(i);
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
{
|
||||
if (unit.PendingReport && unit.PendingReport.Target == target) { unit.PendingReport = null; unit.Uplink = null; }
|
||||
if ((unit.PendingOrder && unit.PendingOrder.Target == target) || (unit.Assignment && unit.Assignment.Target == target)) unit.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Production sets Released immediately before reserving shell airspace, then
|
||||
// invokes native inventory transfer. Observe that existing commit boundary so
|
||||
// even a synchronous native projectile has a prior recorded authorization.
|
||||
modded class ERR_MortarBattery
|
||||
{
|
||||
override void Audit(string kind, string detail)
|
||||
{
|
||||
super.Audit(kind, detail);
|
||||
ERR_NativeMortarSalvoProbe probe = ERR_NativeMortarSalvoProbe.Active();
|
||||
if (probe) probe.NativeAudit(this, kind);
|
||||
}
|
||||
override void ReserveShellAirspace(float now)
|
||||
{
|
||||
super.ReserveShellAirspace(now);
|
||||
ERR_NativeMortarSalvoProbe probe = ERR_NativeMortarSalvoProbe.Active();
|
||||
if (probe) probe.NativeCommit(this, now);
|
||||
}
|
||||
override void Cancel(string reason = "cancel requested")
|
||||
{
|
||||
ERR_NativeMortarSalvoProbe probe = ERR_NativeMortarSalvoProbe.Active();
|
||||
if (probe) probe.NativeClosing(this, reason, LastFailure);
|
||||
super.Cancel(reason);
|
||||
}
|
||||
}
|
||||
|
||||
// Exact completion is separate from native activity success or accepted loading.
|
||||
class ERR_NativeMortarSalvoRules
|
||||
{
|
||||
static bool Complete(int events, int commits, int total, int orderShots, int shells, bool pending)
|
||||
{
|
||||
return events == ERR_MortarSalvo.ROUNDS && commits == events && total == events && orderShots == events && shells == 27 - events && !pending;
|
||||
}
|
||||
static bool QueueUpdate(bool accepted, bool pending, int shots, float observedAt, float orderedAt, float now)
|
||||
{
|
||||
return accepted && !pending && shots < ERR_MortarSalvo.ROUNDS / 2 && now >= observedAt + 20000 && orderedAt >= 0 && now >= orderedAt && now - orderedAt < 90000;
|
||||
}
|
||||
}
|
||||
|
||||
class ERR_NativeMortarRefreshRecord
|
||||
{
|
||||
int MessageId;
|
||||
float ObservedAt;
|
||||
int DownlinkId;
|
||||
bool Accepted;
|
||||
ref ERR_HQReport Delivered;
|
||||
|
||||
bool Matches(ERR_HQReport report, IEntity target, SCR_AIGroup group, vector position)
|
||||
{
|
||||
if (!report || report.MessageId != MessageId || report.ObservedAt != ObservedAt || report.Target != target || report.Source != group) return false;
|
||||
return vector.Distance(report.Position, position) < 0.01 && report.HasVelocity && report.Velocity.Length() < 0.001 && !report.Distress;
|
||||
}
|
||||
}
|
||||
|
||||
// Observe only the actual native event. Node SUCCESS, accepted inventory moves
|
||||
// and committed shell loading are never counted as physical projectiles.
|
||||
modded class SCR_MortarMuzzleComponent
|
||||
{
|
||||
override void OnWeaponFired(IEntity effectEntity, BaseMuzzleComponent muzzle, IEntity projectileEntity)
|
||||
{
|
||||
ERR_NativeMortarSalvoProbe probe = ERR_NativeMortarSalvoProbe.Active();
|
||||
if (probe) probe.NativeProjectile(GetOwner(), projectileEntity);
|
||||
super.OnWeaponFired(effectEntity, muzzle, projectileEntity);
|
||||
}
|
||||
}
|
||||
|
||||
class ERR_NativeMortarSalvoProbe
|
||||
{
|
||||
protected static ERR_NativeMortarSalvoProbe s_Active;
|
||||
protected ERR_GameMode m_Mode;
|
||||
protected ERR_MortarBattery m_Battery;
|
||||
protected IEntity m_Gun;
|
||||
protected SCR_AIGroup m_Group;
|
||||
protected IEntity m_Target;
|
||||
protected vector m_Impact;
|
||||
protected ref array<IEntity> m_Crew = {};
|
||||
protected ref array<IEntity> m_InitialShells = {};
|
||||
protected ref array<IEntity> m_Projectiles = {};
|
||||
protected int m_Source;
|
||||
protected int m_Order;
|
||||
protected int m_Step;
|
||||
protected int m_Events;
|
||||
protected int m_StoppedShells;
|
||||
protected float m_Deadline;
|
||||
protected float m_NextDiagnostic;
|
||||
protected float m_StopUntil;
|
||||
protected float m_ObservedAt;
|
||||
protected ref ERR_HQReport m_Delivered;
|
||||
protected bool m_RefreshCase;
|
||||
protected IEntity m_OriginalRTO;
|
||||
protected SCR_AIStaticArtilleryActivity m_OriginalActivity;
|
||||
protected ref array<ref ERR_NativeMortarRefreshRecord> m_Observations = {};
|
||||
protected int m_UpdatesAccepted;
|
||||
protected int m_UpdatedCommits;
|
||||
protected int m_CommitSource;
|
||||
protected float m_CommitObservedAt;
|
||||
protected bool m_CommitPending;
|
||||
protected float m_CommittedAt;
|
||||
protected int m_CommitOrder;
|
||||
protected int m_CommitShot;
|
||||
protected int m_Commits;
|
||||
protected bool m_ClosureSeen;
|
||||
protected bool m_ClosureComplete;
|
||||
protected bool m_Unsafe;
|
||||
protected bool m_BadProjectile;
|
||||
protected bool m_SawUplink;
|
||||
protected bool m_SawDownlink;
|
||||
protected bool m_SawActivity;
|
||||
protected bool m_Stopped;
|
||||
protected string m_BadReason;
|
||||
|
||||
static bool Enabled()
|
||||
{
|
||||
string enabled, outcome;
|
||||
return Replication.IsServer() && System.GetCLIParam("errSmoke", enabled) && enabled == "1" && System.GetCLIParam("errSmokeOutcome", outcome) && (outcome == "mortar-salvo" || outcome == "mortar-refresh");
|
||||
}
|
||||
static bool RefreshEnabled()
|
||||
{
|
||||
string outcome;
|
||||
return Enabled() && System.GetCLIParam("errSmokeOutcome", outcome) && outcome == "mortar-refresh";
|
||||
}
|
||||
static ERR_NativeMortarSalvoProbe Active() { return s_Active; }
|
||||
void ERR_NativeMortarSalvoProbe(ERR_GameMode mode)
|
||||
{
|
||||
m_Mode = mode;
|
||||
m_RefreshCase = RefreshEnabled();
|
||||
m_Deadline = GetGame().GetWorld().GetWorldTime() + 180000;
|
||||
if (Enabled() && !s_Active) s_Active = this;
|
||||
}
|
||||
protected bool Check(bool condition, string detail)
|
||||
{
|
||||
if (condition) { Print("ERR SMOKE PASS: mortar salvo " + detail); return true; }
|
||||
Print("ERR SMOKE FAIL: mortar salvo " + detail, LogLevel.ERROR); Stop(); return false;
|
||||
}
|
||||
protected void Shells(out array<IEntity> shells)
|
||||
{
|
||||
shells.Clear();
|
||||
foreach (IEntity actor : m_Crew)
|
||||
{
|
||||
if (!actor) continue;
|
||||
InventoryStorageManagerComponent inventory = InventoryStorageManagerComponent.Cast(actor.FindComponent(InventoryStorageManagerComponent));
|
||||
array<IEntity> items = {}; ERR_Inventory.Items(inventory, items);
|
||||
foreach (IEntity item : items)
|
||||
if (item && item.GetPrefabData() && item.GetPrefabData().GetPrefabName() == ERR_MortarBattery.SHELL && !shells.Contains(item)) shells.Insert(item);
|
||||
}
|
||||
}
|
||||
protected bool CheckNoOccupants(IEntity entity)
|
||||
{
|
||||
if (ChimeraCharacter.Cast(entity) || Vehicle.Cast(entity)) { m_Unsafe = true; return false; }
|
||||
return true;
|
||||
}
|
||||
protected bool EmptyImpact(vector position)
|
||||
{
|
||||
m_Unsafe = false;
|
||||
GetGame().GetWorld().QueryEntitiesByAABB(position - "200 5000 200", position + "200 5000 200", CheckNoOccupants, null, EQueryEntitiesFlags.ALL);
|
||||
return !m_Unsafe;
|
||||
}
|
||||
protected bool SelectImpact(ERR_MortarSystem system)
|
||||
{
|
||||
// Bounded candidate survey: no clearance assumption from coordinates alone.
|
||||
array<vector> offsets = {"-800 0 -600", "-600 0 -800", "0 0 -1000", "600 0 -800", "-1000 0 0", "800 0 -600", "-800 0 600", "0 0 1000"};
|
||||
foreach (vector offset : offsets)
|
||||
{
|
||||
vector candidate = ERR_Actors.Ground(m_Battery.Position + offset);
|
||||
if (candidate[1] < 1 || !EmptyImpact(candidate) || !system.Safe(m_Battery, candidate, 75)) continue;
|
||||
bool clear = true;
|
||||
for (int shot = 0; shot < ERR_MortarSalvo.ROUNDS; shot++)
|
||||
{
|
||||
vector spread = ERR_Actors.Ground(candidate + ERR_MortarSalvo.Offset(m_Battery.SiteId, m_Battery.OrderId + 1, shot));
|
||||
if (!system.Safe(m_Battery, spread, 75)) { clear = false; break; }
|
||||
}
|
||||
if (!clear) continue;
|
||||
m_Impact = candidate;
|
||||
PrintFormat("ERR MORTAR SALVO IMPACT: position=%1 batteryDistance=%2; no characters/vehicles in 400m square; production settlement/friendly exclusions passed", candidate, vector.DistanceXZ(candidate, m_Battery.Position));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
protected void Diagnose(float now)
|
||||
{
|
||||
if (now < m_NextDiagnostic) return;
|
||||
m_NextDiagnostic = now + 5000;
|
||||
if (!m_Battery) { Print("ERR MORTAR SALVO: waiting for one authored battery"); return; }
|
||||
PrintFormat("ERR MORTAR SALVO: step=%1 startup=%2 source=M%3 observedEvents=%4 ready=%5 failure=%6", m_Step, m_Battery.StartupActivated, m_Source, m_Events, m_Battery.Operational(), m_Battery.SpawnFailure);
|
||||
m_Battery.SiteStatus(now);
|
||||
}
|
||||
protected ERR_NativeMortarRefreshRecord FindObservation(int messageId)
|
||||
{
|
||||
foreach (ERR_NativeMortarRefreshRecord observation : m_Observations)
|
||||
if (observation.MessageId == messageId) return observation;
|
||||
return null;
|
||||
}
|
||||
protected bool AddObservation(int messageId, float observedAt)
|
||||
{
|
||||
if (messageId <= 0 || observedAt < 0 || FindObservation(messageId) || m_Observations.Count() >= 8) return false;
|
||||
if (!m_Observations.IsEmpty() && observedAt <= m_Observations[m_Observations.Count() - 1].ObservedAt) return false;
|
||||
ERR_NativeMortarRefreshRecord record = new ERR_NativeMortarRefreshRecord();
|
||||
record.MessageId = messageId; record.ObservedAt = observedAt;
|
||||
m_Observations.Insert(record);
|
||||
PrintFormat("ERR MORTAR REFRESH OBSERVATION: source=M%1 actualObservedAt=%2 originalRTO=%3 synthetic stationary inert target; awaiting actual uplink and HQ downlink", messageId, observedAt, m_OriginalRTO);
|
||||
return true;
|
||||
}
|
||||
protected bool ReportKnown(ERR_HQReport report, bool accepted = true)
|
||||
{
|
||||
if (!report) return false;
|
||||
if (!m_RefreshCase) return report.MessageId == m_Source && report.ObservedAt == m_ObservedAt;
|
||||
ERR_NativeMortarRefreshRecord record = FindObservation(report.MessageId);
|
||||
return record && (!accepted || record.Accepted) && record.Matches(report, m_Target, m_Group, m_Impact);
|
||||
}
|
||||
protected bool AllAccepted()
|
||||
{
|
||||
if (!m_RefreshCase) return true;
|
||||
if (m_Observations.Count() < 2 || m_UpdatesAccepted != m_Observations.Count() - 1 || m_UpdatedCommits < 1) return false;
|
||||
foreach (ERR_NativeMortarRefreshRecord record : m_Observations)
|
||||
if (!record.Accepted || !record.Delivered || record.DownlinkId <= 0) return false;
|
||||
return true;
|
||||
}
|
||||
protected bool NoReportReplay(float now)
|
||||
{
|
||||
if (!m_RefreshCase) return m_Delivered && !m_Battery.CanRequest(m_Delivered, now);
|
||||
if (!AllAccepted()) return false;
|
||||
foreach (ERR_NativeMortarRefreshRecord record : m_Observations)
|
||||
if (m_Battery.CanRequest(record.Delivered, now)) return false;
|
||||
return true;
|
||||
}
|
||||
// Observational only: never stop/cancel inside production's current call.
|
||||
void NativeAudit(ERR_MortarBattery battery, string kind)
|
||||
{
|
||||
if (!m_RefreshCase || m_Stopped || battery != m_Battery || m_Source <= 0) return;
|
||||
if (kind == "AIM")
|
||||
{
|
||||
if (m_OriginalActivity || !battery.Activity || !m_SawDownlink || battery.SourceId != m_Source || battery.OrderId != m_Order || !ReportKnown(battery.Report, false))
|
||||
{ m_BadProjectile = true; m_BadReason = "initial native activity does not match recorded report/downlink or was recreated"; return; }
|
||||
m_OriginalActivity = battery.Activity; m_SawActivity = true;
|
||||
ERR_NativeMortarRefreshRecord initial = FindObservation(m_Source);
|
||||
initial.Accepted = true; initial.Delivered = battery.Report.Snapshot();
|
||||
m_Delivered = initial.Delivered;
|
||||
return;
|
||||
}
|
||||
if (kind != "UPDATE QUEUED" && kind != "UPDATE RECEIVED") return;
|
||||
ERR_MortarRefresh update = battery.Refresh;
|
||||
if (!update || !update.Owns(battery) || battery.OrderId != m_Order || battery.Activity != m_OriginalActivity || !ReportKnown(update.Report, false))
|
||||
{ m_BadProjectile = true; m_BadReason = "update lost recorded observation or original gun/group/order/activity"; return; }
|
||||
ERR_NativeMortarRefreshRecord record = FindObservation(update.Report.MessageId);
|
||||
if (kind == "UPDATE QUEUED")
|
||||
{
|
||||
if (record.DownlinkId != 0 || record.Accepted || !update.Route || !update.Route.Downlink || update.Route.Nodes.IsEmpty() || update.Route.MessageId != update.RadioId)
|
||||
{ m_BadProjectile = true; m_BadReason = "refresh downlink was missing, duplicated or already accepted"; return; }
|
||||
record.DownlinkId = update.RadioId;
|
||||
return;
|
||||
}
|
||||
if (record.Accepted || record.DownlinkId != update.RadioId || update.Route || battery.Released || battery.Report != update.Report || !ReportKnown(battery.Report, false))
|
||||
{ m_BadProjectile = true; m_BadReason = "refresh acceptance lacks its completed route or overwrites committed shell"; return; }
|
||||
record.Accepted = true; record.Delivered = battery.Report.Snapshot(); m_UpdatesAccepted++;
|
||||
m_Delivered = record.Delivered;
|
||||
PrintFormat("ERR MORTAR REFRESH ACCEPTED: source=M%1 observedAt=%2 downlink=M%3 sameOrder=%4 originalActivity=%5", record.MessageId, record.ObservedAt, record.DownlinkId, battery.OrderId, battery.Activity == m_OriginalActivity);
|
||||
}
|
||||
void NativeCommit(ERR_MortarBattery battery, float now)
|
||||
{
|
||||
if (m_Stopped || battery != m_Battery) return;
|
||||
if (m_RefreshCase && (!m_OriginalActivity || battery.Activity != m_OriginalActivity))
|
||||
{ m_BadProjectile = true; m_BadReason = "refreshed commit requires the original unchanged native activity"; return; }
|
||||
if (m_Commits >= ERR_MortarSalvo.ROUNDS || battery.OrderShots != m_Commits)
|
||||
{ m_BadProjectile = true; m_BadReason = "commit count or physical shot sequence exceeds configured finite salvo"; return; }
|
||||
if (m_Step != 1 || m_CommitPending || !battery.CommandOpen || !battery.Released || battery.SourceId != m_Source || battery.OrderId != m_Order || !ReportKnown(battery.Report) || !ERR_NativeMortarEvidenceRules.CommitFresh(battery.Report.ObservedAt, battery.Report.ObservedAt, now) || !ERR_NativeMortarEvidenceRules.AimFresh(battery.AimedAt, battery.AimValidatedAt, now) || battery.FlightTime <= 0)
|
||||
{ m_BadProjectile = true; m_BadReason = "physical commit lacks immutable fresh report/current order/native aim, or duplicates pending commit"; return; }
|
||||
m_CommitPending = true; m_CommittedAt = now; m_CommitOrder = battery.OrderId; m_CommitShot = battery.OrderShots; m_Commits++;
|
||||
m_CommitSource = battery.Report.MessageId; m_CommitObservedAt = battery.Report.ObservedAt;
|
||||
if (m_RefreshCase && m_CommitSource != m_Source) m_UpdatedCommits++;
|
||||
PrintFormat("ERR MORTAR SALVO COMMIT: count=%1 order=%2 shot=%3 acceptedSource=M%4 observedAge=%5 commandAge=%6 validationAge=%7; recorded before native inventory transfer", m_Commits, m_CommitOrder, m_CommitShot, m_CommitSource, now - m_CommitObservedAt, now - battery.AimedAt, now - battery.AimValidatedAt);
|
||||
}
|
||||
void NativeClosing(ERR_MortarBattery battery, string reason, string failure)
|
||||
{
|
||||
if (m_Stopped || battery != m_Battery || !battery.CommandOpen || battery.SourceId != m_Source || m_Source <= 0) return;
|
||||
m_ClosureSeen = true;
|
||||
m_ClosureComplete = battery.OrderShots == ERR_MortarSalvo.ROUNDS && m_Events == ERR_MortarSalvo.ROUNDS && !m_CommitPending && m_Commits == m_Events;
|
||||
PrintFormat("ERR MORTAR SALVO CLOSURE: order=%1 reason=%2 lastFailure=%3 events=%4 commits=%5 complete=%6", battery.OrderId, reason, failure, m_Events, m_Commits, m_ClosureComplete);
|
||||
if (battery.Stopped() || battery.OrderId != m_Order || !m_ClosureComplete || !AllAccepted() || reason != "native artillery activity COMPLETED (shot count is separate)")
|
||||
{ m_BadProjectile = true; m_BadReason = "full salvo requires natural native completion; partial expiry, rejection, timeout or Stop is not success"; }
|
||||
if (m_RefreshCase && (battery.Activity != m_OriginalActivity || battery.Refresh || battery.RefreshRejected))
|
||||
{ m_BadProjectile = true; m_BadReason = "natural completion cannot leave a refresh pending or replace original activity"; }
|
||||
}
|
||||
void NativeProjectile(IEntity gun, IEntity projectile)
|
||||
{
|
||||
if (m_Stopped || !m_Battery || gun != m_Gun) return;
|
||||
if (!projectile) { m_BadProjectile = true; m_BadReason = "native fired event has no physical projectile"; return; }
|
||||
if (m_Projectiles.Contains(projectile)) { m_BadProjectile = true; m_BadReason = "same physical projectile observed twice"; return; }
|
||||
m_Projectiles.Insert(projectile); m_Events++;
|
||||
float now = GetGame().GetWorld().GetWorldTime();
|
||||
if (m_Step != 1 || !m_Battery.Released || m_Battery.SourceId != m_Source || !ERR_NativeMortarEvidenceRules.EventBound(m_CommitPending, m_CommitOrder, m_Battery.OrderId, m_CommitShot, m_Battery.OrderShots, m_CommittedAt, now))
|
||||
{ m_BadProjectile = true; m_BadReason = "projectile lacks the recorded fresh physical commit for its current order/shot"; }
|
||||
if (!m_Battery.Report || m_Battery.Report.MessageId != m_CommitSource || m_Battery.Report.ObservedAt != m_CommitObservedAt)
|
||||
{ m_BadProjectile = true; m_BadReason = "committed shell was relabelled with another report before native emission"; }
|
||||
m_CommitPending = false;
|
||||
PrintFormat("ERR MORTAR SALVO PROJECTILE: event=%1 entity=%2 origin=%3 order=%4 acceptedSource=M%5 aimAge=%6 flight=%7; impact is not measured", m_Events, projectile, projectile.GetOrigin(), m_Battery.OrderId, m_CommitSource, now - m_Battery.AimedAt, m_Battery.FlightTime);
|
||||
}
|
||||
int Tick(float now)
|
||||
{
|
||||
if (m_Stopped) return -1;
|
||||
if (!Enabled() || s_Active != this || !m_Mode || !m_Mode.Active()) { Check(false, "explicit active fixture context retained"); return -1; }
|
||||
ERR_HQ hq = ERR_HQ.Get();
|
||||
ERR_MortarSystem system;
|
||||
if (hq) system = hq.Mortars();
|
||||
if (!system) { Check(false, "real HQ mortar service available"); return -1; }
|
||||
if (!m_Battery)
|
||||
{
|
||||
array<IEntity> guns = {}; system.AppendGuns(guns);
|
||||
if (guns.Count() == 1) { m_Gun = guns[0]; m_Battery = system.FindGun(m_Gun); if (m_Battery) m_Group = m_Battery.Group; }
|
||||
}
|
||||
Diagnose(now);
|
||||
if (now >= m_Deadline) { Check(false, "bounded deadline; inspect readiness/route/activity/aim diagnostics"); return -1; }
|
||||
if (m_BadProjectile) { Check(false, m_BadReason); return -1; }
|
||||
if (!m_Battery) return 0;
|
||||
if (!m_Battery.SpawnFailure.IsEmpty()) { Check(false, "battery startup failed: " + m_Battery.SpawnFailure); return -1; }
|
||||
if (m_Step == 0)
|
||||
{
|
||||
if (!m_Battery.StartupActivated || !m_Battery.Operational() || !hq.MortarConnected(m_Battery.Group)) return 0;
|
||||
m_Group = m_Battery.Group;
|
||||
if (!Check(m_Group && m_Group.IsExpandComplete() && m_Group.GetAgentsCount() == 4 && m_Battery.TotalShots == 0 && m_Battery.OrderId == 0 && !m_Battery.CommandOpen && !m_Battery.Activity && !m_Battery.RadioOrder, "untasked original gun and complete four-person crew start without shots")) return -1;
|
||||
array<AIAgent> agents = {}; m_Group.GetAgents(agents);
|
||||
foreach (AIAgent agent : agents)
|
||||
{
|
||||
IEntity actor; if (agent) actor = agent.GetControlledEntity();
|
||||
if (!Check(ERR_Actors.CanFight(actor) && !m_Crew.Contains(actor), "initial physical crew member is conscious and unique")) return -1;
|
||||
m_Crew.Insert(actor);
|
||||
}
|
||||
if (!Check(m_Battery.RadioOperator && ERR_HQRadio.Capability(m_Battery.RadioOperator) == 2, "dedicated actual manpack supplies the radio route")) return -1;
|
||||
m_OriginalRTO = m_Battery.RadioOperator;
|
||||
Shells(m_InitialShells);
|
||||
if (!Check(m_InitialShells.Count() == 27 && m_Battery.CountShells() == 27, "exactly 27 distinct physical initial HE shells")) return -1;
|
||||
if (!Check(SelectImpact(system), "bounded candidate survey finds an unoccupied production-safe impact area")) return -1;
|
||||
m_Target = ERR_Actors.Spawn(ERR_Resources.IntelFolder(), m_Impact);
|
||||
if (!Check(m_Target != null, "fixture-owned inert report identity exists; no enemy perception is claimed")) return -1;
|
||||
if (m_RefreshCase) m_Source = hq.ERR_QueueMortarRefreshReport(m_Group, m_OriginalRTO, m_Target, m_Impact, m_ObservedAt);
|
||||
else { m_Source = hq.ERR_QueueMortarSalvoReport(m_Group, m_Target, m_Impact, now); m_ObservedAt = now; }
|
||||
if (!Check(m_Source > 0, "synthetic observation enters actual existing radio uplink once")) return -1;
|
||||
if (m_RefreshCase && !Check(AddObservation(m_Source, m_ObservedAt), "first actual-clock observation recorded without replacement")) return -1;
|
||||
m_SawUplink = true; m_Deadline = now + ERR_MortarSalvo.TIMEOUT_MS + 35000; m_Step = 1; return 0;
|
||||
}
|
||||
if (m_Battery.Stopped()) { Check(false, "battery must remain running throughout full salvo and replay observation"); return -1; }
|
||||
if (m_Battery.Report && !ReportKnown(m_Battery.Report)) { Check(false, "delivered report identity and original observation timestamp remain immutable and accepted"); return -1; }
|
||||
if (m_Battery.Gun != m_Gun || m_Battery.Group != m_Group) { Check(false, "original gun and crew identity retained"); return -1; }
|
||||
if (m_RefreshCase && (m_Battery.RadioOperator != m_OriginalRTO || (m_OriginalActivity && m_Battery.Activity && m_Battery.Activity != m_OriginalActivity)))
|
||||
{ Check(false, "refreshes preserve original RTO and native activity"); return -1; }
|
||||
if (!EmptyImpact(m_Impact)) { Check(false, "impact exclusion stays unoccupied before native firing/cancellation"); return -1; }
|
||||
array<IEntity> remaining = {}; Shells(remaining);
|
||||
foreach (IEntity item : remaining)
|
||||
if (!m_InitialShells.Contains(item)) { Check(false, "no replacement HE shell appears after initialization"); return -1; }
|
||||
if (m_Battery.OrderId > 1 || (m_Battery.SourceId != 0 && m_Battery.SourceId != m_Source)) { Check(false, "one unchanged fixture source creates only one HQ order"); return -1; }
|
||||
if (m_Events > ERR_MortarSalvo.ROUNDS || m_Battery.TotalShots > ERR_MortarSalvo.ROUNDS) { Check(false, "physical event count never exceeds configured salvo"); return -1; }
|
||||
if (m_Step == 1)
|
||||
{
|
||||
if (m_Battery.RadioOrder && m_Battery.SourceId == m_Source)
|
||||
{
|
||||
m_SawDownlink = true; m_Order = m_Battery.OrderId;
|
||||
if (m_RefreshCase)
|
||||
{
|
||||
ERR_NativeMortarRefreshRecord first = FindObservation(m_Source);
|
||||
if (!first || first.DownlinkId == 0)
|
||||
{
|
||||
if (!Check(first && m_Battery.RadioOrder.Downlink && !m_Battery.RadioOrder.Nodes.IsEmpty(), "initial order traverses actual nonempty HQ downlink")) return -1;
|
||||
first.DownlinkId = m_Battery.RadioOrder.MessageId;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_Battery.Activity && m_Battery.Report && m_Battery.Report.MessageId == m_Source)
|
||||
{
|
||||
m_SawActivity = true;
|
||||
if (!m_Delivered) m_Delivered = m_Battery.Report.Snapshot();
|
||||
}
|
||||
if (m_RefreshCase && m_Battery.CommandOpen && m_Battery.Activity && !m_Observations.IsEmpty())
|
||||
{
|
||||
ERR_NativeMortarRefreshRecord latest = m_Observations[m_Observations.Count() - 1];
|
||||
if (ERR_NativeMortarSalvoRules.QueueUpdate(latest.Accepted, m_Battery.Refresh != null, m_Battery.OrderShots, latest.ObservedAt, m_Battery.OrderedAt, now))
|
||||
{
|
||||
float observed;
|
||||
int messageId = hq.ERR_QueueMortarRefreshReport(m_Group, m_OriginalRTO, m_Target, m_Impact, observed);
|
||||
if (!Check(messageId > 0 && AddObservation(messageId, observed), "next distinct actual-clock synthetic observation queued only after previous acceptance")) return -1;
|
||||
}
|
||||
}
|
||||
if (m_Battery.CommandOpen || m_Battery.Activity) return 0;
|
||||
if (!m_ClosureSeen) return 0;
|
||||
if (!Check(m_ClosureComplete && m_SawUplink && m_SawDownlink && m_SawActivity && m_Order == 1, "one real uplink/downlink and native activity naturally complete the entire configured salvo")) return -1;
|
||||
if (m_RefreshCase && !Check(AllAccepted(), "every queued update was accepted under the same order before closure and at least one updated report authorized a commit")) return -1;
|
||||
if (!Check(ERR_NativeMortarSalvoRules.Complete(m_Events, m_Commits, m_Battery.TotalShots, m_Battery.OrderShots, remaining.Count(), m_CommitPending) && m_Battery.CountShells() == remaining.Count(), "exact configured native projectiles equal fresh commits and missing original inventory shells")) return -1;
|
||||
if (!Check(NoReportReplay(now), "all delivered messages are ineligible for a second request after completion")) return -1;
|
||||
if (!Check(!m_Battery.RadioOrder && !m_Battery.Report && !m_Battery.PendingRadioReport && !m_Battery.PendingSupplyReport && !m_Battery.SupplyPending() && !m_Battery.Released && !m_Battery.Refresh, "natural completion clears command, routes, loading and projectile reservation")) return -1;
|
||||
// Keep the unchanged HQ contact, battery and scheduler alive. This exceeds
|
||||
// the normal 20-second post-dispatch decision interval without forging time.
|
||||
m_StoppedShells = remaining.Count(); m_StopUntil = now + 25000; m_Deadline = m_StopUntil + 2000; m_Step = 2;
|
||||
return 0;
|
||||
}
|
||||
if (m_Battery.CommandOpen || m_Battery.Activity || m_Battery.RadioOrder || m_Battery.Report || m_Battery.PendingRadioReport || m_Battery.PendingSupplyReport || m_Battery.SupplyPending() || m_Battery.Released)
|
||||
{ Check(false, "completed observation must not reopen command, activity, route or physical loading"); return -1; }
|
||||
if (!ERR_NativeMortarSalvoRules.Complete(m_Events, m_Commits, m_Battery.TotalShots, m_Battery.OrderShots, remaining.Count(), m_CommitPending) || m_Battery.OrderId != m_Order || remaining.Count() != m_StoppedShells)
|
||||
{ Check(false, "closed order cannot add shots, orders or replacement ammunition during replay window"); return -1; }
|
||||
if (now < m_StopUntil) return 0;
|
||||
if (!Check(NoReportReplay(now), "all claimed contacts produce no second request, projectile or replenishment over 25 seconds")) return -1;
|
||||
array<AIAgent> finalCrew = {}; m_Group.GetAgents(finalCrew);
|
||||
if (!Check(finalCrew.Count() == 4 && m_Battery.Gun == m_Gun, "original group and gun retain four crew")) return -1;
|
||||
foreach (AIAgent finalAgent : finalCrew)
|
||||
if (!Check(finalAgent && m_Crew.Contains(finalAgent.GetControlledEntity()) && ERR_Actors.CanFight(finalAgent.GetControlledEntity()), "final conscious crew entity belongs to original physical roster")) return -1;
|
||||
Stop(); return 1;
|
||||
}
|
||||
void Stop()
|
||||
{
|
||||
if (m_Stopped) return;
|
||||
m_Stopped = true;
|
||||
ERR_HQ hq = ERR_HQ.Get();
|
||||
if (hq) hq.ERR_RemoveMortarSalvoReport(m_Target);
|
||||
if (m_Battery && m_Battery.Gun == m_Gun && m_Battery.Group == m_Group) m_Battery.Stop();
|
||||
ERR_Actors.DeleteSpawned(m_Target); m_Target = null;
|
||||
if (s_Active == this) s_Active = null;
|
||||
}
|
||||
void ~ERR_NativeMortarSalvoProbe() { Stop(); }
|
||||
}
|
||||
#endif
|
||||
+383
@@ -0,0 +1,383 @@
|
||||
#ifdef WORKBENCH
|
||||
// Mechanism-only fixture: radio delivery and automatic artillery scheduling are
|
||||
// explicitly bypassed. All ammunition and inventory operations remain physical.
|
||||
modded class ERR_MortarSystem
|
||||
{
|
||||
protected ERR_NativeMortarTransferProbe m_ERRTransferFixture;
|
||||
bool ERR_HoldForTransfer(ERR_NativeMortarTransferProbe owner)
|
||||
{
|
||||
if (!ERR_NativeMortarTransferProbe.Enabled() || m_Stopped || m_ERRTransferFixture || !owner) return false;
|
||||
m_ERRTransferFixture = owner; return true;
|
||||
}
|
||||
void ERR_ReleaseTransferHold(ERR_NativeMortarTransferProbe owner)
|
||||
{
|
||||
if (m_ERRTransferFixture == owner) m_ERRTransferFixture = null;
|
||||
}
|
||||
override void Tick(float now)
|
||||
{
|
||||
if (m_ERRTransferFixture) return;
|
||||
super.Tick(now);
|
||||
}
|
||||
ERR_MortarBattery ERR_TransferBattery()
|
||||
{
|
||||
if (m_Batteries.Count() != 1) return null;
|
||||
return m_Batteries[0];
|
||||
}
|
||||
}
|
||||
modded class ERR_MortarBattery
|
||||
{
|
||||
ERR_MortarSupplyTransfer ERR_TransferForProbe() { return m_Transfer; }
|
||||
int ERR_TransferAttempts() { return m_AttemptedShells.Count(); }
|
||||
}
|
||||
modded class ERR_MortarSupplyTransfer
|
||||
{
|
||||
int ERR_CallbackState() { return m_Acknowledgement; }
|
||||
IEntity ERR_TransferredShell() { return m_Shell; }
|
||||
IEntity ERR_TransferRecipient() { return m_Gunner; }
|
||||
bool ERR_RequestAccepted() { return m_Accepted; }
|
||||
}
|
||||
class ERR_NativeMortarMoveCallback : ScriptedInventoryOperationCallback
|
||||
{
|
||||
int Result;
|
||||
protected override void OnComplete() { if (Result == 0) Result = 1; }
|
||||
protected override void OnFailed() { if (Result == 0) Result = -1; }
|
||||
}
|
||||
class ERR_NativeMortarShellOwner
|
||||
{
|
||||
IEntity Shell;
|
||||
IEntity OriginalOwner;
|
||||
}
|
||||
|
||||
class ERR_NativeMortarTransferProbe
|
||||
{
|
||||
protected ERR_GameMode m_Mode;
|
||||
protected ERR_MortarSystem m_System;
|
||||
protected ERR_MortarBattery m_Battery;
|
||||
protected IEntity m_Gunner;
|
||||
protected BaseCompartmentSlot m_Seat;
|
||||
protected ref array<IEntity> m_Crew = {};
|
||||
protected ref array<IEntity> m_Boxes = {};
|
||||
protected ref array<BaseInventoryStorageComponent> m_Stores = {};
|
||||
protected ref array<ref ERR_NativeMortarShellOwner> m_Original = {};
|
||||
protected ref ERR_NativeMortarMoveCallback m_MoveCallback;
|
||||
protected IEntity m_MoveShell;
|
||||
protected BaseInventoryStorageComponent m_MoveDestination;
|
||||
protected float m_MoveDeadline;
|
||||
protected ref ERR_MortarSupplyTransfer m_ObservedTransfer;
|
||||
protected IEntity m_FirstShell;
|
||||
protected IEntity m_SecondShell;
|
||||
protected int m_AttemptsAtStart;
|
||||
protected int m_ShotsAtStart;
|
||||
protected int m_CallbackAtCancel;
|
||||
protected int m_CancelOrder;
|
||||
protected int m_Step;
|
||||
protected float m_Deadline;
|
||||
protected float m_StepDeadline;
|
||||
protected float m_Wait;
|
||||
protected bool m_Held;
|
||||
protected bool m_Stopped;
|
||||
protected bool m_Complete;
|
||||
static const ResourceName STORE = "{EBBC859CCD94D96D}Prefabs/Props/EmptyStorages/USSR_AmmoBox_empty.et";
|
||||
static bool Enabled()
|
||||
{
|
||||
string enabled, outcome;
|
||||
return Replication.IsServer() && System.GetCLIParam("errSmoke", enabled) && enabled == "1" && System.GetCLIParam("errSmokeOutcome", outcome) && outcome == "mortar-transfer";
|
||||
}
|
||||
void ERR_NativeMortarTransferProbe(ERR_GameMode mode)
|
||||
{
|
||||
m_Mode = mode; m_Deadline = GetGame().GetWorld().GetWorldTime() + 180000;
|
||||
}
|
||||
protected bool Check(bool condition, string detail)
|
||||
{
|
||||
if (condition) { Print("ERR SMOKE PASS: mortar transfer " + detail); return true; }
|
||||
Print("ERR SMOKE FAIL: mortar transfer " + detail, LogLevel.ERROR); Stop(); return false;
|
||||
}
|
||||
protected bool IsShell(IEntity item)
|
||||
{
|
||||
return item && item.GetPrefabData() && item.GetPrefabData().GetPrefabName() == ERR_MortarBattery.SHELL;
|
||||
}
|
||||
protected InventoryStorageManagerComponent Inventory(IEntity actor)
|
||||
{
|
||||
if (!actor) return null;
|
||||
return InventoryStorageManagerComponent.Cast(actor.FindComponent(InventoryStorageManagerComponent));
|
||||
}
|
||||
protected bool Has(IEntity actor, IEntity item)
|
||||
{
|
||||
array<IEntity> items = {}; ERR_Inventory.Items(Inventory(actor), items);
|
||||
return item && items.Contains(item);
|
||||
}
|
||||
protected IEntity CrewOwner(IEntity item)
|
||||
{
|
||||
foreach (IEntity actor : m_Crew) if (Has(actor, item)) return actor;
|
||||
return null;
|
||||
}
|
||||
protected IEntity OriginalOwner(IEntity item)
|
||||
{
|
||||
foreach (ERR_NativeMortarShellOwner entry : m_Original) if (entry.Shell == item) return entry.OriginalOwner;
|
||||
return null;
|
||||
}
|
||||
protected bool InStore(IEntity item)
|
||||
{
|
||||
foreach (BaseInventoryStorageComponent storage : m_Stores) if (storage && storage.Contains(item)) return true;
|
||||
return false;
|
||||
}
|
||||
static bool Conservation(int originals, int actual, bool allOriginal, bool singleOwner)
|
||||
{
|
||||
return originals == 27 && actual == originals && allOriginal && singleOwner;
|
||||
}
|
||||
protected bool Conserved()
|
||||
{
|
||||
array<IEntity> actual = {};
|
||||
foreach (IEntity actor : m_Crew)
|
||||
{
|
||||
array<IEntity> items = {}; ERR_Inventory.Items(Inventory(actor), items);
|
||||
foreach (IEntity item : items) if (IsShell(item) && !actual.Contains(item)) actual.Insert(item);
|
||||
}
|
||||
foreach (BaseInventoryStorageComponent storage : m_Stores)
|
||||
{
|
||||
if (!storage) return false;
|
||||
array<IEntity> items = {}; storage.GetAll(items);
|
||||
foreach (IEntity item : items) if (IsShell(item) && !actual.Contains(item)) actual.Insert(item);
|
||||
}
|
||||
bool allOriginal = true;
|
||||
bool singleOwner = true;
|
||||
foreach (ERR_NativeMortarShellOwner entry : m_Original)
|
||||
{
|
||||
if (!entry.Shell || !actual.Contains(entry.Shell)) allOriginal = false;
|
||||
int owners;
|
||||
foreach (IEntity actor : m_Crew) if (Has(actor, entry.Shell)) owners++;
|
||||
foreach (BaseInventoryStorageComponent storage : m_Stores) if (storage && storage.Contains(entry.Shell)) owners++;
|
||||
if (owners != 1) singleOwner = false;
|
||||
}
|
||||
return Conservation(m_Original.Count(), actual.Count(), allOriginal, singleOwner);
|
||||
}
|
||||
protected IEntity FirstGunnerShell()
|
||||
{
|
||||
array<IEntity> items = {}; ERR_Inventory.Items(Inventory(m_Gunner), items);
|
||||
foreach (IEntity item : items) if (IsShell(item)) return item;
|
||||
return null;
|
||||
}
|
||||
protected bool BeginMove(IEntity shell, BaseInventoryStorageComponent destination, float now)
|
||||
{
|
||||
if (!shell || !destination || m_MoveCallback) return false;
|
||||
IEntity current = CrewOwner(shell);
|
||||
// A vanilla empty box has storage but no inventory manager. The original
|
||||
// recipient manager performs its explicit pickup from the external storage.
|
||||
if (!current) current = OriginalOwner(shell);
|
||||
InventoryStorageManagerComponent manager = Inventory(current);
|
||||
if (!manager) return false;
|
||||
m_MoveShell = shell; m_MoveDestination = destination;
|
||||
m_MoveCallback = new ERR_NativeMortarMoveCallback(); m_MoveDeadline = now + 8000;
|
||||
if (!manager.TryMoveItemToStorage(shell, destination, -1, m_MoveCallback)) return false;
|
||||
return true;
|
||||
}
|
||||
protected int MoveResult(float now)
|
||||
{
|
||||
if (!m_MoveCallback) return 1;
|
||||
if (m_MoveCallback.Result < 0 || now >= m_MoveDeadline) return -1;
|
||||
if (m_MoveCallback.Result == 0 || !m_MoveShell || !m_MoveDestination || !m_MoveDestination.Contains(m_MoveShell)) return 0;
|
||||
InventoryItemComponent item = InventoryItemComponent.Cast(m_MoveShell.FindComponent(InventoryItemComponent));
|
||||
if (!item || !item.GetParentSlot() || item.GetParentSlot().GetStorage() != m_MoveDestination) return 0;
|
||||
m_MoveCallback = null; m_MoveShell = null; m_MoveDestination = null;
|
||||
return 1;
|
||||
}
|
||||
protected bool Stash(IEntity shell, float now)
|
||||
{
|
||||
foreach (BaseInventoryStorageComponent storage : m_Stores)
|
||||
if (storage && storage.GetEstimatedCountFitForItem(shell) > 0) return BeginMove(shell, storage, now);
|
||||
return false;
|
||||
}
|
||||
protected void StageOrder(float now)
|
||||
{
|
||||
ERR_HQReport report = new ERR_HQReport();
|
||||
report.Target = GetGame().GetPlayerController().GetControlledEntity();
|
||||
report.Source = m_Battery.Group;
|
||||
report.Position = m_Battery.Gun.GetOrigin() + "700 0 0";
|
||||
report.ObservedAt = now; report.DetailsObservedAt = now;
|
||||
report.TravelMode = "on foot"; report.MessageId = 900001 + m_Battery.OrderId;
|
||||
m_Battery.BeginRequest(report, now);
|
||||
m_Battery.PendingSupplyReport = report.Snapshot();
|
||||
PrintFormat("ERR MORTAR TRANSFER FIXTURE: synthetic supply order=%1; radio bypassed; scheduler held; no artillery activity authorized", m_Battery.OrderId);
|
||||
}
|
||||
protected bool NoFire()
|
||||
{
|
||||
return m_Battery && m_Battery.TotalShots == m_ShotsAtStart && !m_Battery.Activity && !m_Battery.RadioOrder && !m_Battery.Released;
|
||||
}
|
||||
void Stop()
|
||||
{
|
||||
if (m_Stopped) return;
|
||||
m_Stopped = true;
|
||||
if (m_Battery) m_Battery.Cancel("native transfer fixture stopped");
|
||||
if (m_System)
|
||||
{
|
||||
m_System.Stop(); // do not resume automatic fire while test items may be stored
|
||||
if (m_Held) m_System.ERR_ReleaseTransferHold(this);
|
||||
}
|
||||
m_Held = false;
|
||||
// Never delete a box containing borrowed shells or an outstanding operation.
|
||||
// Failure preserves it for diagnosis/normal round-owned cleanup.
|
||||
if (!m_MoveCallback)
|
||||
{
|
||||
foreach (IEntity box : m_Boxes)
|
||||
{
|
||||
if (!box) continue;
|
||||
BaseInventoryStorageComponent storage = BaseInventoryStorageComponent.Cast(box.FindComponent(BaseInventoryStorageComponent));
|
||||
array<IEntity> items = {}; if (storage) storage.GetAll(items);
|
||||
if (storage && items.IsEmpty()) ERR_Actors.DeleteSpawned(box);
|
||||
}
|
||||
}
|
||||
}
|
||||
int Tick(float now)
|
||||
{
|
||||
if (m_Complete) return 1;
|
||||
if (m_Stopped) return -1;
|
||||
if (!Enabled() || now > m_Deadline) { Check(false, "enabled server fixture and bounded 180-second deadline"); return -1; }
|
||||
if (now < m_Wait) return 0;
|
||||
if (m_MoveCallback)
|
||||
{
|
||||
int moved = MoveResult(now);
|
||||
if (moved < 0) { Check(false, "fixture inventory move completed with exact destination before deadline; borrowed shells retained"); return -1; }
|
||||
if (moved == 0) return 0;
|
||||
if (!Check(Conserved(), "all original 27 shell identities conserved after fixture move")) return -1;
|
||||
}
|
||||
if (m_Step == 0)
|
||||
{
|
||||
m_System = ERR_MortarSystem.Get();
|
||||
if (!m_System) return 0;
|
||||
m_Battery = m_System.ERR_TransferBattery();
|
||||
if (!m_Battery || !m_Battery.StartupActivated) return 0;
|
||||
if (!Check(m_Battery.Operational() && m_Battery.CountShells() == 27 && !m_Battery.Activity && !m_Battery.RadioOrder && !m_Battery.CommandOpen, "actual initialized idle battery has finite 27 shells")) return -1;
|
||||
if (!Check(m_System.ERR_HoldForTransfer(this), "opt-in mechanism fixture holds automatic mortar scheduler")) return -1;
|
||||
m_Held = true; m_AttemptsAtStart = m_Battery.ERR_TransferAttempts(); m_ShotsAtStart = m_Battery.TotalShots;
|
||||
array<AIAgent> agents = {}; m_Battery.Group.GetAgents(agents);
|
||||
foreach (AIAgent agent : agents)
|
||||
{
|
||||
if (!agent || !ERR_Actors.CanFight(agent.GetControlledEntity())) continue;
|
||||
IEntity actor = agent.GetControlledEntity(); if (m_Crew.Contains(actor)) continue;
|
||||
m_Crew.Insert(actor);
|
||||
array<IEntity> items = {}; ERR_Inventory.Items(Inventory(actor), items);
|
||||
foreach (IEntity item : items)
|
||||
{
|
||||
if (!IsShell(item)) continue;
|
||||
ERR_NativeMortarShellOwner entry = new ERR_NativeMortarShellOwner(); entry.Shell = item; entry.OriginalOwner = actor; m_Original.Insert(entry);
|
||||
}
|
||||
if (!m_Gunner && actor != m_Battery.RadioOperator) m_Gunner = actor;
|
||||
}
|
||||
if (!Check(m_Crew.Count() == 4 && m_Original.Count() == 27 && m_Gunner != null, "four real crew and original shell-owner identities captured")) return -1;
|
||||
SCR_AIVehicleUsageComponent usage = SCR_AIVehicleUsageComponent.Cast(m_Battery.Gun.FindComponent(SCR_AIVehicleUsageComponent));
|
||||
if (usage) m_Seat = usage.GetTurretCompartmentSlot();
|
||||
if (m_Seat && m_Seat.GetOccupant())
|
||||
{
|
||||
IEntity occupant = m_Seat.GetOccupant();
|
||||
if (!Check(m_Crew.Contains(occupant) && occupant != m_Battery.RadioOperator && ERR_Actors.CanFight(occupant), "existing native seat occupant is original fighting gunner")) return -1;
|
||||
m_Gunner = occupant;
|
||||
}
|
||||
else
|
||||
{
|
||||
CompartmentAccessComponent access = CompartmentAccessComponent.Cast(m_Gunner.FindComponent(CompartmentAccessComponent));
|
||||
if (!Check(m_Seat && access && access.GetInVehicle(m_Battery.Gun, m_Seat, true, -1, ECloseDoorAfterActions.INVALID, false), "native seating request selects actual original crew gunner")) return -1;
|
||||
}
|
||||
if (!Check(m_Battery.InventoryShells(Inventory(m_Gunner)) == 9, "selected actual gunner owns its original nine physical shells")) return -1;
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
IEntity box = ERR_Actors.Spawn(STORE, ERR_Actors.Ground(m_Battery.Gun.GetOrigin() + Vector(3 + i * 2, 0, 3)));
|
||||
if (!Check(box != null, "fixture-owned empty vanilla storage created")) return -1;
|
||||
m_Boxes.Insert(box); m_Mode.Own(box);
|
||||
BaseInventoryStorageComponent storage = BaseInventoryStorageComponent.Cast(box.FindComponent(BaseInventoryStorageComponent));
|
||||
if (!Check(storage != null, "real vanilla storage component available without attaching it to gunner inventory")) return -1;
|
||||
m_Stores.Insert(storage);
|
||||
}
|
||||
m_Step = 1; m_StepDeadline = now + 8000; m_Wait = now + 500; return 0;
|
||||
}
|
||||
if (!NoFire()) { Check(false, "no artillery activity, committed load or physical projectile during inventory mechanism test"); return -1; }
|
||||
if (m_Step == 1)
|
||||
{
|
||||
if (m_Seat.GetOccupant() != m_Gunner)
|
||||
{
|
||||
if (now >= m_StepDeadline) { Check(false, "actual original gunner occupies native mortar seat"); return -1; }
|
||||
return 0;
|
||||
}
|
||||
if (!Check(Conserved(), "27 original shells have exactly one observed owner each")) return -1;
|
||||
m_Step = 2;
|
||||
}
|
||||
if (m_Step == 2 || m_Step == 5)
|
||||
{
|
||||
IEntity shell = FirstGunnerShell();
|
||||
if (shell)
|
||||
{
|
||||
if (!Check(Stash(shell, now), "one existing gunner shell move to vanilla store accepted; no shell minted")) return -1;
|
||||
return 0;
|
||||
}
|
||||
if (!Check(m_Battery.InventoryShells(Inventory(m_Gunner)) == 0 && Conserved(), "actual gunner empty while all 27 original shells remain in crew or fixture storage")) return -1;
|
||||
StageOrder(now);
|
||||
ERR_MortarSupplyState started = m_Battery.SupplyState(now);
|
||||
m_ObservedTransfer = m_Battery.ERR_TransferForProbe();
|
||||
if (!Check(started == ERR_MortarSupplyState.PENDING && m_ObservedTransfer && m_ObservedTransfer.ERR_RequestAccepted() && m_ObservedTransfer.ERR_TransferRecipient() == m_Gunner && m_ObservedTransfer.CapturedOrder() == m_Battery.OrderId, "production exact-shell request accepted once with captured current order and real gunner")) return -1;
|
||||
if (m_Step == 2)
|
||||
{
|
||||
m_FirstShell = m_ObservedTransfer.ERR_TransferredShell();
|
||||
m_Step = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_SecondShell = m_ObservedTransfer.ERR_TransferredShell(); m_CallbackAtCancel = m_ObservedTransfer.ERR_CallbackState(); m_CancelOrder = m_Battery.OrderId;
|
||||
m_Battery.Cancel("native fixture cancels authorization immediately after accepted inventory request");
|
||||
if (!Check(m_SecondShell && m_SecondShell != m_FirstShell && !m_Battery.CommandOpen && m_Battery.SupplyState(now) == ERR_MortarSupplyState.FAILED, "canceled current order cannot report supply READY or issue another request")) return -1;
|
||||
PrintFormat("ERR MORTAR TRANSFER CANCEL: callbackBeforeCancel=%1 order=%2; accepted native move is not retracted", m_CallbackAtCancel, m_CancelOrder);
|
||||
m_Step = 6;
|
||||
}
|
||||
m_StepDeadline = now + 8000; m_Wait = now + 250; return 0;
|
||||
}
|
||||
if (m_Step == 3)
|
||||
{
|
||||
ERR_MortarSupplyState state = m_Battery.SupplyState(now);
|
||||
if (state == ERR_MortarSupplyState.FAILED || now >= m_StepDeadline) { Check(false, "production transfer completes within its bounded deadline"); return -1; }
|
||||
if (state == ERR_MortarSupplyState.PENDING) return 0;
|
||||
if (!Check(m_ObservedTransfer.ERR_CallbackState() == 1 && Has(m_Gunner, m_FirstShell) && !Has(OriginalOwner(m_FirstShell), m_FirstShell) && Conserved() && m_Battery.ERR_TransferAttempts() == m_AttemptsAtStart + 1, "successful native callback and exact donor-to-gunner ownership, one attempt, original 27 conserved")) return -1;
|
||||
if (!Check(m_Battery.SupplyState(now) == ERR_MortarSupplyState.READY && m_Battery.ERR_TransferAttempts() == m_AttemptsAtStart + 1, "repeated supply check reuses actual inventory without another move")) return -1;
|
||||
m_Battery.Cancel("native success subcase finished; no artillery activity requested");
|
||||
m_Step = 5; return 0;
|
||||
}
|
||||
if (m_Step == 6)
|
||||
{
|
||||
if (m_Battery.SupplyState(now) != ERR_MortarSupplyState.FAILED || m_Battery.OrderId != m_CancelOrder || m_Battery.ERR_TransferAttempts() != m_AttemptsAtStart + 2) { Check(false, "canceled order/generation remains failed with exactly two total move attempts"); return -1; }
|
||||
if (m_ObservedTransfer.ERR_CallbackState() == 0 || m_ObservedTransfer.Unresolved())
|
||||
{
|
||||
if (now >= m_StepDeadline) { Check(false, "accepted canceled operation reached definite native outcome; no ambiguous retry"); return -1; }
|
||||
return 0;
|
||||
}
|
||||
if (!Check(m_ObservedTransfer.ERR_CallbackState() == 1 && Has(m_Gunner, m_SecondShell) && Conserved(), "canceled accepted native move has definite physical outcome with exactly two attempts and original 27 conserved")) return -1;
|
||||
if (m_CallbackAtCancel == 0) Print("ERR SMOKE PASS: mortar transfer actual native completion observed after authorization cancellation");
|
||||
else Print("ERR MORTAR TRANSFER LIMIT: native callback completed synchronously before cancellation; late callback timing not exercised");
|
||||
m_Step = 7; return 0;
|
||||
}
|
||||
if (m_Step == 7)
|
||||
{
|
||||
// Return foreign shells first, leaving room for each original nine-round kit.
|
||||
IEntity misplaced;
|
||||
foreach (ERR_NativeMortarShellOwner entry : m_Original)
|
||||
if (Has(m_Gunner, entry.Shell) && entry.OriginalOwner != m_Gunner) { misplaced = entry.Shell; break; }
|
||||
if (!misplaced)
|
||||
foreach (ERR_NativeMortarShellOwner entry : m_Original) if (InStore(entry.Shell)) { misplaced = entry.Shell; break; }
|
||||
if (misplaced)
|
||||
{
|
||||
InventoryStorageManagerComponent recipient = Inventory(OriginalOwner(misplaced));
|
||||
BaseInventoryStorageComponent destination;
|
||||
if (recipient) destination = recipient.FindStorageForItem(misplaced, EStoragePurpose.PURPOSE_DEPOSIT);
|
||||
if (!Check(destination && BeginMove(misplaced, destination, now), "one borrowed shell return to its original crew owner accepted")) return -1;
|
||||
return 0;
|
||||
}
|
||||
foreach (ERR_NativeMortarShellOwner entry : m_Original)
|
||||
if (!Check(Has(entry.OriginalOwner, entry.Shell), "original shell identity returned to original crew inventory")) return -1;
|
||||
if (!Check(Conserved() && m_Battery.CountShells() == 27 && NoFire(), "all original ammunition restored; no minted/deleted shell or shot")) return -1;
|
||||
foreach (BaseInventoryStorageComponent storage : m_Stores)
|
||||
{
|
||||
array<IEntity> items = {}; storage.GetAll(items);
|
||||
if (!Check(items.IsEmpty(), "borrowed-ammunition fixture storage empty before deletion")) return -1;
|
||||
}
|
||||
Stop(); m_Complete = true; return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
#ifdef WORKBENCH
|
||||
// Test access only: trigger the real delivered-order method on an existing
|
||||
// registered squad. Radio transit is intentionally not simulated by this test.
|
||||
modded class ERR_HQ
|
||||
{
|
||||
bool ERR_TestPatrolSearch(SCR_AIGroup group, out AIWaypoint search)
|
||||
{
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
{
|
||||
if (unit.Group != group) continue;
|
||||
if (unit.Assignment || unit.PendingOrder || unit.Waypoint || !unit.Eligible(m_Mode)) return false;
|
||||
ERR_HQReport order = new ERR_HQReport();
|
||||
order.Position = group.GetOrigin() + "40 0 0";
|
||||
order.ObservedAt = GetGame().GetWorld().GetWorldTime();
|
||||
order.MessageId = m_Journal.Message();
|
||||
unit.PendingOrder = order;
|
||||
DeliverOrder(unit, order.ObservedAt);
|
||||
search = unit.Waypoint;
|
||||
return search && unit.Assignment == order;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool ERR_TestPatrolRelease(SCR_AIGroup group, AIWaypoint search)
|
||||
{
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
{
|
||||
if (unit.Group != group || unit.Waypoint != search) continue;
|
||||
unit.Release();
|
||||
return !unit.Assignment && !unit.PendingOrder && !unit.Waypoint;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class ERR_NativePatrolHQProbe
|
||||
{
|
||||
protected ERR_GameMode m_Mode;
|
||||
protected ERR_SiteRuntime m_Site;
|
||||
protected AIWaypoint m_Search;
|
||||
protected ref array<AIWaypoint> m_Baseline = {};
|
||||
protected int m_Step;
|
||||
protected float m_Wait;
|
||||
void ERR_NativePatrolHQProbe(ERR_GameMode mode) { m_Mode = mode; }
|
||||
protected bool Check(bool condition, string detail)
|
||||
{
|
||||
if (condition) { Print("ERR SMOKE PASS: patrol HQ " + detail); return true; }
|
||||
Print("ERR SMOKE FAIL: patrol HQ " + detail, LogLevel.ERROR); return false;
|
||||
}
|
||||
int Tick(float now)
|
||||
{
|
||||
if (now < m_Wait) return 0;
|
||||
ERR_HQ hq = ERR_HQ.Get();
|
||||
if (!Check(hq != null, "real HQ available")) return -1;
|
||||
if (m_Step == 0)
|
||||
{
|
||||
m_Site = m_Mode.Sites().Find("everon-outpost-1");
|
||||
if (!Check(m_Site && m_Site.Group && m_Site.PatrolCycle, "existing outpost route available")) return -1;
|
||||
m_Site.Group.GetWaypoints(m_Baseline);
|
||||
if (!Check(!m_Baseline.IsEmpty() && hq.ERR_TestPatrolSearch(m_Site.Group, m_Search), "real delivered-order method assigns existing squad")) return -1;
|
||||
m_Step = 1; m_Wait = now + 750; return 0;
|
||||
}
|
||||
if (m_Step == 1)
|
||||
{
|
||||
array<AIWaypoint> queue = {};
|
||||
m_Site.Group.GetWaypoints(queue);
|
||||
if (!Check(m_Search && !queue.IsEmpty() && queue[0] == m_Search && m_Site.Group.GetCurrentWaypoint() == m_Search, "Search preempts native patrol")) return -1;
|
||||
foreach (AIWaypoint waypoint : m_Baseline)
|
||||
if (!Check(queue.Contains(waypoint), "preempted baseline route remains queued")) return -1;
|
||||
if (!Check(hq.ERR_TestPatrolRelease(m_Site.Group, m_Search), "real HQ release removes only its assignment")) return -1;
|
||||
m_Step = 2; m_Wait = now + 750; return 0;
|
||||
}
|
||||
if (m_Step == 2)
|
||||
{
|
||||
AIWaypoint current = m_Site.Group.GetCurrentWaypoint();
|
||||
if (!Check(current && (current == m_Site.PatrolCycle || m_Site.PatrolWaypoints.Contains(current)), "native patrol resumes after Search release")) return -1;
|
||||
m_Step = 3; return 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
void Stop()
|
||||
{
|
||||
ERR_HQ hq = ERR_HQ.Get();
|
||||
if (hq && m_Site && m_Search) hq.ERR_TestPatrolRelease(m_Site.Group, m_Search);
|
||||
}
|
||||
void ~ERR_NativePatrolHQProbe() { Stop(); }
|
||||
}
|
||||
#endif
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
// Vanilla named settlements supply exclusion areas; no original mission location system.
|
||||
// Shared town/village exclusion for mortar impacts, roadblocks and mine sites.
|
||||
// Fixed distance from mapped settlement centres, independent of mission
|
||||
// activation radii. Scattered buildings and props are not settlements.
|
||||
class ERR_SettlementSafety
|
||||
{
|
||||
static const float RADIUS = 200;
|
||||
string SettlementReason;
|
||||
protected vector m_SafeImpact;
|
||||
protected bool m_SettlementNear;
|
||||
protected vector m_LastSettlementImpact;
|
||||
protected float m_LastSettlementCheck = -1;
|
||||
protected bool m_LastSettlementClear;
|
||||
|
||||
bool OutsideSettlements(vector impact)
|
||||
{
|
||||
float now = GetGame().GetWorld().GetWorldTime();
|
||||
if (m_LastSettlementCheck >= 0 && now >= m_LastSettlementCheck && now - m_LastSettlementCheck < 1000 && vector.DistanceXZ(impact, m_LastSettlementImpact) == 0)
|
||||
return m_LastSettlementClear;
|
||||
m_LastSettlementCheck = now;
|
||||
m_LastSettlementImpact = impact;
|
||||
m_LastSettlementClear = false;
|
||||
m_SafeImpact = impact;
|
||||
m_SettlementNear = false;
|
||||
SettlementReason = "clear";
|
||||
// Also cover named native map settlements, independent of whether the
|
||||
// mission placed a garrison marker there. Generic building entities also
|
||||
// include deer stands, bridge pillars, rubble and electrical cabinets.
|
||||
// Broad vertical bounds keep the 200m distance horizontal on hills.
|
||||
GetGame().GetWorld().QueryEntitiesByAABB(impact - Vector(RADIUS, 5000, RADIUS), impact + Vector(RADIUS, 5000, RADIUS), CheckSettlement, FilterSettlement, EQueryEntitiesFlags.ALL);
|
||||
m_LastSettlementClear = !m_SettlementNear;
|
||||
return m_LastSettlementClear;
|
||||
}
|
||||
|
||||
protected bool FilterSettlement(IEntity entity)
|
||||
{
|
||||
return entity && entity.FindComponent(MapDescriptorComponent);
|
||||
}
|
||||
|
||||
protected bool CheckSettlement(IEntity entity)
|
||||
{
|
||||
float radius;
|
||||
MapDescriptorComponent descriptor = MapDescriptorComponent.Cast(entity.FindComponent(MapDescriptorComponent));
|
||||
if (descriptor)
|
||||
{
|
||||
int type = descriptor.GetBaseType();
|
||||
if (type == EMapDescriptorType.MDT_NAME_CITY)
|
||||
radius = RADIUS;
|
||||
else if (type == EMapDescriptorType.MDT_NAME_TOWN)
|
||||
radius = RADIUS;
|
||||
else if (type == EMapDescriptorType.MDT_NAME_VILLAGE || type == EMapDescriptorType.MDT_NAME_SETTLEMENT)
|
||||
radius = RADIUS;
|
||||
}
|
||||
if (radius > 0 && vector.DistanceXZ(m_SafeImpact, entity.GetOrigin()) <= radius)
|
||||
{
|
||||
m_SettlementNear = true;
|
||||
SettlementReason = string.Format("named map settlement entity=%1 position=%2 distance=%3m exclusion=%4m", entity, entity.GetOrigin(), vector.DistanceXZ(m_SafeImpact, entity.GetOrigin()), radius);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
modded class ERR_ScenarioComponent
|
||||
{
|
||||
protected override bool PrepareAdditionalForces()
|
||||
{
|
||||
if (!super.PrepareAdditionalForces()) return false;
|
||||
ERR_HQ hq = ERR_HQ.Get();
|
||||
return hq && hq.PrepareSupport();
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
GameProject {
|
||||
ID "ERR_Ai"
|
||||
GUID "5192138CB9D5B911"
|
||||
TITLE "Escapists Rereintroduced - Ai"
|
||||
Dependencies {
|
||||
"58D0FB3206B6F859"
|
||||
"2528466DE220D91C"
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
SCR_MissionHeader {
|
||||
World "{B6C516F86302C679}Worlds/ERR_Everon.ent"
|
||||
SystemsConfig "{C50579A2EC48E8EF}Configs/Systems/MissionSystems.conf"
|
||||
m_sName "Rereintroduced: Everon (development)"
|
||||
m_sAuthor "Rereintroduced contributors"
|
||||
m_sDescription "Independent escape prototype. Development scenario; runtime validation in progress."
|
||||
m_sGameMode "Escape"
|
||||
m_iPlayerCount 12
|
||||
m_eSaveTypes 0
|
||||
m_bShowInScenarioMenu 1
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
MetaFileClass {
|
||||
Name "{C1E20667DCE324ED}Missions/ERR_Everon.conf"
|
||||
Configurations {
|
||||
ResourceClass PC {
|
||||
}
|
||||
ResourceClass HEADLESS : PC {
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// Companion-specific commands extend Core's authenticated transport.
|
||||
modded class ERR_AdminService
|
||||
{
|
||||
protected override string Setting(string section, string option)
|
||||
{
|
||||
if (section == "hq")
|
||||
{
|
||||
if (option == "interval") return "reportIntervalSeconds";
|
||||
if (option == "squads") return "supportSquadLimit";
|
||||
if (option == "radius") return "responseRadiusMetres";
|
||||
}
|
||||
if (section == "intel")
|
||||
{
|
||||
if (option == "carrying") return "aiIntelChancePercent";
|
||||
}
|
||||
return super.Setting(section, option);
|
||||
}
|
||||
protected override bool Extension(ERR_GameMode mode, string arguments, array<string> replies)
|
||||
{
|
||||
if (arguments != "status" && arguments != "hq show" && arguments != "hq log") return super.Extension(mode, arguments, replies);
|
||||
ERR_HQ hq = ERR_HQ.Cast(mode.Command());
|
||||
if (!hq) { replies.Insert("HQ unavailable"); return true; }
|
||||
if (arguments == "hq show" || arguments == "status") replies.Insert(hq.Status());
|
||||
else
|
||||
{
|
||||
int cursor;
|
||||
hq.Journal().Read(Math.Max(0, hq.Journal().Sequence() - 8), replies, cursor);
|
||||
if (replies.IsEmpty()) replies.Insert("HQ journal is empty.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
protected override string Help()
|
||||
{
|
||||
return super.Help() + " | hq show|log | hq interval|squads|radius show|set N | intel carrying show|set N";
|
||||
}
|
||||
}
|
||||
+288
@@ -0,0 +1,288 @@
|
||||
#ifdef WORKBENCH
|
||||
// Read-only evidence hooks. Every production method delegates unchanged.
|
||||
modded class ERR_HQ
|
||||
{
|
||||
void ERR_ResponseUnits(out array<ERR_HQUnit> units)
|
||||
{
|
||||
units.Clear();
|
||||
if (!ERR_NativeHQResponseProof.Enabled()) return;
|
||||
foreach (ERR_HQUnit unit : m_Units) units.Insert(unit);
|
||||
}
|
||||
protected override void Observe(ERR_HQUnit unit, float now)
|
||||
{
|
||||
ERR_HQReport previous = unit.PendingReport;
|
||||
super.Observe(unit, now);
|
||||
ERR_NativeHQResponseProof proof = ERR_NativeHQResponseProof.Active();
|
||||
if (proof && unit.PendingReport && unit.PendingReport != previous) proof.Observed(this, unit, now);
|
||||
}
|
||||
protected override void Accept(ERR_HQReport report, float now)
|
||||
{
|
||||
super.Accept(report, now);
|
||||
ERR_NativeHQResponseProof proof = ERR_NativeHQResponseProof.Active();
|
||||
if (!proof || !report) return;
|
||||
foreach (ERR_HQReport accepted : m_Contacts)
|
||||
if (accepted.MessageId == report.MessageId) { proof.Accepted(this, accepted, now); break; }
|
||||
}
|
||||
protected override void DeliverOrder(ERR_HQUnit unit, float now)
|
||||
{
|
||||
ERR_HQReport order = unit.PendingOrder;
|
||||
ERR_HQRoute route = unit.Downlink;
|
||||
super.DeliverOrder(unit, now);
|
||||
ERR_NativeHQResponseProof proof = ERR_NativeHQResponseProof.Active();
|
||||
if (proof && order && route && unit.Assignment == order && unit.Waypoint) proof.Delivered(this, unit, route, now);
|
||||
}
|
||||
}
|
||||
modded class ERR_HQJournal
|
||||
{
|
||||
override void Write(string kind, string message, int messageId = 0)
|
||||
{
|
||||
super.Write(kind, message, messageId);
|
||||
ERR_NativeHQResponseProof proof = ERR_NativeHQResponseProof.Active();
|
||||
if (proof) proof.JournalEvent(this, kind, message, messageId);
|
||||
}
|
||||
}
|
||||
modded class SCR_AIGroupUtilityComponent
|
||||
{
|
||||
IEntity ERR_ResponseVisualObserver(ERR_HQReport report, float now)
|
||||
{
|
||||
foreach (SCR_AIInfoComponent info : m_aInfoComponents)
|
||||
{
|
||||
if (!info || !info.m_Perception) continue;
|
||||
AIAgent agent = AIAgent.Cast(info.GetOwner());
|
||||
if (!agent || !ERR_Actors.CanFight(agent.GetControlledEntity())) continue;
|
||||
array<BaseTarget> targets = {}; info.m_Perception.GetTargetsList(targets, ETargetCategory.ENEMY);
|
||||
foreach (BaseTarget target : targets)
|
||||
{
|
||||
ERR_HQVisualContact sample = ERR_HQObservation.Capture(target, now);
|
||||
if (sample && sample.Entity == report.Target && sample.Escapist && Math.AbsFloat(sample.ObservedAt - report.ObservedAt) < 1 && vector.Distance(sample.Position, report.Position) < 0.1) return agent.GetControlledEntity();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
class ERR_NativeResponseUnit
|
||||
{
|
||||
ERR_HQUnit Unit;
|
||||
SCR_AIGroup Group;
|
||||
IEntity Radio;
|
||||
ref array<IEntity> Actors = {};
|
||||
}
|
||||
class ERR_NativeResponseJournal
|
||||
{
|
||||
bool Report;
|
||||
bool Transmit;
|
||||
bool HQReceive;
|
||||
bool HQTransmit;
|
||||
bool UnitReceive;
|
||||
bool Delivered;
|
||||
}
|
||||
class ERR_NativeResponseLineage
|
||||
{
|
||||
ref ERR_HQReport Report;
|
||||
ERR_HQUnit Source;
|
||||
IEntity Observer;
|
||||
IEntity SourceRadio;
|
||||
ref ERR_HQRoute Uplink;
|
||||
bool Accepted;
|
||||
float AcceptedAt;
|
||||
ERR_HQUnit Recipient;
|
||||
IEntity RecipientRadio;
|
||||
int DeliveryId;
|
||||
AIWaypoint Waypoint;
|
||||
IEntity MovingActor;
|
||||
vector DeliveredOrigin;
|
||||
float DeliveredAt;
|
||||
}
|
||||
class ERR_NativeHQResponseProof
|
||||
{
|
||||
protected static ERR_NativeHQResponseProof s_Active;
|
||||
protected ERR_GameMode m_Mode;
|
||||
protected ERR_HQ m_HQ;
|
||||
protected IEntity m_Player;
|
||||
protected SCR_PlayerController m_Controller;
|
||||
protected ERR_PlayerLife m_Life;
|
||||
protected ERR_HQUnit m_Source;
|
||||
protected DamageManagerComponent m_Damage;
|
||||
protected bool m_DamageWasEnabled;
|
||||
protected bool m_Protected;
|
||||
protected bool m_Started;
|
||||
protected bool m_Stopped;
|
||||
protected bool m_Failed;
|
||||
protected string m_Failure;
|
||||
protected float m_Deadline;
|
||||
protected float m_NextDiagnostic;
|
||||
protected ref array<ref ERR_NativeResponseUnit> m_Units = {};
|
||||
protected ref array<IEntity> m_Forces = {};
|
||||
protected ref array<ref ERR_NativeResponseLineage> m_Reports = {};
|
||||
protected ref map<int, ref ERR_NativeResponseJournal> m_Journal = new map<int, ref ERR_NativeResponseJournal>();
|
||||
static bool Enabled()
|
||||
{
|
||||
string enabled, outcome;
|
||||
return Replication.IsServer() && System.GetCLIParam("errSmoke", enabled) && enabled == "1" && System.GetCLIParam("errSmokeOutcome", outcome) && outcome == "hq-response";
|
||||
}
|
||||
static ERR_NativeHQResponseProof Active() { return s_Active; }
|
||||
void ERR_NativeHQResponseProof(ERR_GameMode mode, IEntity player)
|
||||
{
|
||||
m_Mode = mode; m_Player = player; m_HQ = ERR_HQ.Get(); m_Deadline = GetGame().GetWorld().GetWorldTime() + 140000;
|
||||
if (Enabled() && !s_Active) s_Active = this;
|
||||
}
|
||||
protected bool Fail(string reason) { m_Failed = true; m_Failure = reason; return false; }
|
||||
protected bool Check(bool condition, string detail)
|
||||
{
|
||||
if (condition) { Print("ERR SMOKE PASS: HQ response " + detail); return true; }
|
||||
Print("ERR SMOKE FAIL: HQ response " + detail, LogLevel.ERROR); Stop(); return false;
|
||||
}
|
||||
protected ERR_NativeResponseUnit Original(ERR_HQUnit unit)
|
||||
{
|
||||
foreach (ERR_NativeResponseUnit original : m_Units) if (original.Unit == unit && original.Group == unit.Group) return original;
|
||||
return null;
|
||||
}
|
||||
protected ERR_NativeResponseLineage Report(int id)
|
||||
{
|
||||
foreach (ERR_NativeResponseLineage report : m_Reports) if (report.Report.MessageId == id) return report;
|
||||
return null;
|
||||
}
|
||||
protected bool Canonical()
|
||||
{
|
||||
return m_Player && m_Controller && m_Life && m_Mode.Players().CurrentActor(m_Player) == m_Life && m_Controller == GetGame().GetPlayerController() && m_Controller.GetControlledEntity() == m_Player && m_Life.Actor == m_Player && m_Life.Connected && m_Life.UsedLife;
|
||||
}
|
||||
protected bool UnchangedForces()
|
||||
{
|
||||
array<ERR_HQUnit> current = {}; m_HQ.ERR_ResponseUnits(current);
|
||||
if (current.Count() != m_Units.Count()) return false;
|
||||
foreach (ERR_HQUnit unit : current)
|
||||
{
|
||||
ERR_NativeResponseUnit original = Original(unit);
|
||||
if (!original) return false;
|
||||
array<AIAgent> agents = {}; unit.Group.GetAgents(agents);
|
||||
if (agents.Count() != original.Actors.Count()) return false;
|
||||
foreach (AIAgent agent : agents) if (!agent || !original.Actors.Contains(agent.GetControlledEntity())) return false;
|
||||
}
|
||||
array<IEntity> forces = {}; m_HQ.PhysicalForces(forces);
|
||||
if (forces.Count() != m_Forces.Count()) return false;
|
||||
foreach (IEntity entity : forces) if (!m_Forces.Contains(entity)) return false;
|
||||
return true;
|
||||
}
|
||||
protected bool Arrange()
|
||||
{
|
||||
array<ERR_HQUnit> units = {}; m_HQ.ERR_ResponseUnits(units);
|
||||
foreach (ERR_HQUnit unit : units)
|
||||
{
|
||||
if (!unit.Group || !unit.Utility || !unit.Radio || ERR_HQRadio.Capability(unit.Radio) != 2) return false;
|
||||
array<AIAgent> agents = {}; unit.Group.GetAgents(agents);
|
||||
if (agents.IsEmpty() || !unit.Group.IsExpandComplete()) return false;
|
||||
ERR_NativeResponseUnit original = new ERR_NativeResponseUnit(); original.Unit = unit; original.Group = unit.Group; original.Radio = unit.Radio;
|
||||
foreach (AIAgent agent : agents)
|
||||
{
|
||||
if (!agent || !ERR_Actors.CanFight(agent.GetControlledEntity())) return false;
|
||||
original.Actors.Insert(agent.GetControlledEntity());
|
||||
}
|
||||
m_Units.Insert(original);
|
||||
if (!m_Source && !unit.ReservedForSupport) m_Source = unit;
|
||||
}
|
||||
if (!m_Source || m_Units.Count() < 2) return false;
|
||||
m_HQ.PhysicalForces(m_Forces);
|
||||
m_Controller = SCR_PlayerController.Cast(GetGame().GetPlayerController()); m_Life = m_Mode.Players().CurrentActor(m_Player);
|
||||
if (!Canonical()) return false;
|
||||
m_Damage = DamageManagerComponent.Cast(m_Player.FindComponent(DamageManagerComponent));
|
||||
if (!m_Damage) return false;
|
||||
m_DamageWasEnabled = m_Damage.IsDamageHandlingEnabled(); m_Damage.EnableDamageHandling(false); m_Protected = true;
|
||||
// Preserve the established fixture arrangement: move only the real prisoner.
|
||||
// Native perception must discover him; neither targets nor reports are seeded.
|
||||
vector position = ERR_Actors.Ground(m_Source.Group.GetOrigin() + "0 0 20");
|
||||
vector transform[4]; m_Player.GetWorldTransform(transform); transform[3] = position;
|
||||
ChimeraCharacter.Cast(m_Player).Teleport(transform);
|
||||
m_Started = true;
|
||||
PrintFormat("ERR HQ RESPONSE ARRANGEMENT: canonical prisoner moved to=%1 near original source group=%2; temporary player damage protection=%3; no AI/report/radio cadence changes", position, m_Source.Id, m_Protected);
|
||||
return true;
|
||||
}
|
||||
void JournalEvent(ERR_HQJournal journal, string kind, string message, int messageId)
|
||||
{
|
||||
if (!m_Started || m_Stopped || !m_HQ || journal != m_HQ.Journal() || messageId <= 0) return;
|
||||
ERR_NativeResponseJournal entry = m_Journal.Get(messageId);
|
||||
if (!entry)
|
||||
{
|
||||
if (m_Journal.Count() >= 128) { Fail("bounded journal evidence map exhausted"); return; }
|
||||
entry = new ERR_NativeResponseJournal(); m_Journal.Insert(messageId, entry);
|
||||
}
|
||||
if (kind == "REPORT") entry.Report = true;
|
||||
if (kind == "TRANSMIT") { entry.Transmit = true; if (message.StartsWith("HQ -> ")) entry.HQTransmit = true; }
|
||||
if (kind == "RECEIVE") { if (message.StartsWith("HQ received from ")) entry.HQReceive = true; else if (message.StartsWith("unit ")) entry.UnitReceive = true; }
|
||||
if (kind == "ORDER DELIVERED") entry.Delivered = true;
|
||||
}
|
||||
void Observed(ERR_HQ hq, ERR_HQUnit unit, float now)
|
||||
{
|
||||
if (!m_Started || m_Stopped || hq != m_HQ || unit != m_Source || unit.PendingReport.Target != m_Player) return;
|
||||
ERR_HQReport report = unit.PendingReport;
|
||||
ERR_NativeResponseUnit original = Original(unit);
|
||||
IEntity observer = unit.Utility.ERR_ResponseVisualObserver(report, now);
|
||||
if (!Canonical() || !original || !original.Actors.Contains(observer) || !unit.Uplink || unit.Uplink.Nodes.IsEmpty() || unit.Uplink.Nodes[0].Carrier != unit.Radio || unit.Uplink.MessageId != report.MessageId || unit.Radio != original.Radio || !original.Actors.Contains(unit.Radio) || report.Source != original.Group || report.ObservedAt > now || Report(report.MessageId)) { Fail("real observed report lacks canonical original observer/radio/source lineage"); return; }
|
||||
if (m_Reports.Count() >= 32) { Fail("bounded report evidence exhausted"); return; }
|
||||
ERR_NativeResponseLineage proof = new ERR_NativeResponseLineage(); proof.Report = report.Snapshot(); proof.Source = unit; proof.Observer = observer; proof.SourceRadio = unit.Radio; proof.Uplink = unit.Uplink; m_Reports.Insert(proof);
|
||||
PrintFormat("ERR HQ RESPONSE SIGHTING: source=%1 report=M%2 observer=%3 radio=%4 target=%5 observedAt=%6; actual native visual sample", unit.Id, report.MessageId, observer, unit.Radio, m_Player, report.ObservedAt);
|
||||
}
|
||||
void Accepted(ERR_HQ hq, ERR_HQReport report, float now)
|
||||
{
|
||||
if (!m_Started || m_Stopped || hq != m_HQ) return;
|
||||
ERR_NativeResponseLineage proof = Report(report.MessageId); if (!proof) return;
|
||||
if (report.Target != m_Player || report.Source != proof.Report.Source || report.ObservedAt != proof.Report.ObservedAt || vector.Distance(report.Position, proof.Report.Position) > 0.01 || !proof.Uplink || proof.Uplink.Failed || proof.Uplink.Sending || proof.Uplink.Cursor < proof.Uplink.Nodes.Count()) { Fail("accepted report lacks completed actual uplink or immutable observed time"); return; }
|
||||
proof.Accepted = true; proof.AcceptedAt = now;
|
||||
}
|
||||
void Delivered(ERR_HQ hq, ERR_HQUnit unit, ERR_HQRoute route, float now)
|
||||
{
|
||||
if (!m_Started || m_Stopped || hq != m_HQ || !unit.Assignment) return;
|
||||
ERR_NativeResponseLineage proof = Report(unit.Assignment.MessageId); if (!proof || proof.Recipient) return;
|
||||
ERR_NativeResponseUnit original = Original(unit);
|
||||
if (!proof.Accepted || !original || unit == proof.Source || !unit.Assignment || !unit.Waypoint) { Fail("delivered response lacks accepted original recipient/order"); return; }
|
||||
if (unit.Assignment.Target != m_Player || unit.Assignment.Source != proof.Report.Source || unit.Assignment.ObservedAt != proof.Report.ObservedAt || vector.Distance(unit.Assignment.Position, proof.Report.Position) > 0.01) { Fail("delivered response changed immutable report"); return; }
|
||||
if (unit.Radio != original.Radio || !original.Actors.Contains(unit.Radio) || !route.Downlink || route.Nodes.IsEmpty()) { Fail("delivered response lacks original radio/downlink"); return; }
|
||||
ERR_HQRadioNode recipientNode = route.Nodes[route.Nodes.Count() - 1];
|
||||
if (recipientNode.Carrier != unit.Radio || route.Failed || route.Sending || route.Cursor != route.Nodes.Count() - 1) { Fail("delivered response lacks completed actual recipient downlink"); return; }
|
||||
proof.Recipient = unit; proof.RecipientRadio = unit.Radio; proof.DeliveryId = route.MessageId; proof.Waypoint = unit.Waypoint; proof.DeliveredAt = now;
|
||||
foreach (IEntity actor : original.Actors) if (ERR_Actors.CanFight(actor)) { proof.MovingActor = actor; break; }
|
||||
if (proof.MovingActor) proof.DeliveredOrigin = proof.MovingActor.GetOrigin();
|
||||
PrintFormat("ERR HQ RESPONSE DELIVERED: report=M%1 downlink=M%2 source=%3 recipient=%4 radio=%5 waypoint=%6 observedAt=%7", proof.Report.MessageId, route.MessageId, proof.Source.Id, unit.Id, unit.Radio, unit.Waypoint, unit.Assignment.ObservedAt);
|
||||
}
|
||||
int Tick(float now)
|
||||
{
|
||||
if (m_Stopped) return -1;
|
||||
if (!Enabled() || s_Active != this || !m_Mode || !m_Mode.Active() || !m_HQ || now > m_Deadline || m_Failed) { Check(false, "bounded active evidence context: " + m_Failure); return -1; }
|
||||
if (!m_Started)
|
||||
{
|
||||
if (!m_HQ.ERR_ProbeSquadsReady()) return 0;
|
||||
array<IEntity> guns = {}; m_HQ.Mortars().AppendGuns(guns);
|
||||
foreach (IEntity gun : guns) { ERR_MortarBattery battery = m_HQ.Mortars().FindGun(gun); if (!battery || !battery.StartupActivated || !battery.Operational() || !m_HQ.MortarConnected(battery.Group)) return 0; }
|
||||
if (!Check(Arrange(), "original physical force, RTOs and canonical prisoner captured before arranged sighting")) return -1;
|
||||
return 0;
|
||||
}
|
||||
if (!Canonical() || !UnchangedForces()) { Check(false, "canonical player and all original physical group/actor identities retained without added force"); return -1; }
|
||||
if (now >= m_NextDiagnostic)
|
||||
{
|
||||
PrintFormat("ERR HQ RESPONSE PROOF: observed source reports=%1; %2", m_Reports.Count(), m_HQ.ERR_ProbePerception()); m_NextDiagnostic = now + 10000;
|
||||
}
|
||||
foreach (ERR_NativeResponseLineage proof : m_Reports)
|
||||
{
|
||||
if (!proof.Recipient || !proof.MovingActor) continue;
|
||||
if (proof.Recipient.Waypoint != proof.Waypoint || !proof.Recipient.Assignment || proof.Recipient.Assignment.MessageId != proof.Report.MessageId) continue;
|
||||
ERR_NativeResponseJournal uplink = m_Journal.Get(proof.Report.MessageId);
|
||||
ERR_NativeResponseJournal downlink = m_Journal.Get(proof.DeliveryId);
|
||||
if (!uplink || !downlink || !uplink.Report || !uplink.Transmit || !uplink.HQReceive || !downlink.HQTransmit || !downlink.UnitReceive || !downlink.Delivered) continue;
|
||||
if (proof.Recipient.Group.GetCurrentWaypoint() != proof.Waypoint || vector.DistanceXZ(proof.MovingActor.GetOrigin(), proof.DeliveredOrigin) < 3) continue;
|
||||
if (vector.DistanceXZ(proof.MovingActor.GetOrigin(), proof.Report.Position) >= vector.DistanceXZ(proof.DeliveredOrigin, proof.Report.Position) - 1) continue;
|
||||
if (!Check(proof.SourceRadio && proof.RecipientRadio && m_HQ.ERR_ProbeUniqueAssignments(), "actual sighting/report/uplink/HQ receipt/downlink journal lineage reaches distinct original infantry with bounded unique waypoints")) return -1;
|
||||
if (!Check(UnchangedForces(), "original recipient physically moves toward its native Search waypoint with no replacement or additional force")) return -1;
|
||||
Stop(); return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void Stop()
|
||||
{
|
||||
if (m_Stopped) return;
|
||||
m_Stopped = true;
|
||||
if (m_Protected && m_Damage) m_Damage.EnableDamageHandling(m_DamageWasEnabled);
|
||||
m_Protected = false;
|
||||
if (s_Active == this) s_Active = null;
|
||||
}
|
||||
void ~ERR_NativeHQResponseProof() { Stop(); }
|
||||
}
|
||||
#endif
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
#ifdef WORKBENCH
|
||||
modded class ERR_MortarSystem
|
||||
{
|
||||
ERR_MortarBattery ERR_ProbeBattery()
|
||||
{
|
||||
if (m_Batteries.Count() != 1) return null;
|
||||
return m_Batteries[0];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
// Opt-in engine fixture: physical radios and production delivery routes only.
|
||||
#ifdef WORKBENCH
|
||||
modded class ERR_HQ
|
||||
{
|
||||
vector ERR_ProbeResponseOrigin()
|
||||
{
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
if (!unit.ReservedForSupport && unit.Group) return unit.Group.GetOrigin() + "0 0 20";
|
||||
return vector.Zero;
|
||||
}
|
||||
bool ERR_ProbeDeliveredResponse()
|
||||
{
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
if (unit.Assignment && unit.Waypoint && unit.Group != unit.Assignment.Source) return true;
|
||||
return false;
|
||||
}
|
||||
bool ERR_ProbeUniqueAssignments()
|
||||
{
|
||||
array<AIWaypoint> waypoints = {};
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
{
|
||||
if (!unit.Waypoint) continue;
|
||||
if (!unit.Assignment || waypoints.Contains(unit.Waypoint)) return false;
|
||||
waypoints.Insert(unit.Waypoint);
|
||||
}
|
||||
return waypoints.Count() > 0 && Assigned() <= m_Mode.Settings().Get("supportSquadLimit");
|
||||
}
|
||||
string ERR_ProbePerception()
|
||||
{
|
||||
int targets, sightings, radios;
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
{
|
||||
if (unit.Radio) radios++;
|
||||
if (!unit.Utility) continue;
|
||||
array<ref ERR_HQVisualContact> samples = {};
|
||||
ERR_HQObservation.Gather(unit.Utility, GetGame().GetWorld().GetWorldTime(), samples);
|
||||
sightings += samples.Count();
|
||||
if (unit.Utility.m_Perception) targets += unit.Utility.m_Perception.m_aTargets.Count();
|
||||
}
|
||||
return string.Format("%1; radios=%2 targets=%3 fresh visual samples=%4", Status(), radios, targets, sightings);
|
||||
}
|
||||
ERR_HQRoute ERR_ProbeRoute(IEntity carrier = null, bool downlink = false)
|
||||
{
|
||||
if (carrier) return m_Network.Route(carrier, downlink);
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
if (unit.Radio) return m_Network.Route(unit.Radio, downlink);
|
||||
return null;
|
||||
}
|
||||
bool ERR_ProbeSquadsReady()
|
||||
{
|
||||
int infantry;
|
||||
foreach (ERR_HQUnit unit : m_Units)
|
||||
{
|
||||
if (unit.ReservedForSupport) continue;
|
||||
infantry++;
|
||||
if (!unit.Group || ERR_HQRadio.Capability(unit.Radio) != 2) return false;
|
||||
array<AIAgent> agents = {};
|
||||
unit.Group.GetAgents(agents);
|
||||
if (agents.Count() != 6) return false;
|
||||
}
|
||||
return infantry == 6;
|
||||
}
|
||||
}
|
||||
|
||||
class ERR_NativeRadioProbe
|
||||
{
|
||||
protected ref ERR_HQRoute m_Route;
|
||||
protected IEntity m_Carrier;
|
||||
protected int m_Step;
|
||||
|
||||
int Tick(ERR_HQ hq, float now)
|
||||
{
|
||||
if (!hq) return -1;
|
||||
if (m_Step == 0)
|
||||
{
|
||||
if (!hq.ERR_ProbeSquadsReady()) return 0;
|
||||
Print("ERR SMOKE PASS: six physical six-person squads each have a manpack operator");
|
||||
m_Route = hq.ERR_ProbeRoute();
|
||||
if (!m_Route || m_Route.Nodes.IsEmpty()) return -1;
|
||||
m_Carrier = m_Route.Nodes[0].Carrier;
|
||||
m_Route.MessageId = hq.Journal().Message();
|
||||
m_Step = 1;
|
||||
}
|
||||
int result = m_Route.Advance(now, hq.Journal());
|
||||
if (result == 0) return 0;
|
||||
if (m_Step == 3)
|
||||
{
|
||||
if (result >= 0) return -1;
|
||||
Print("ERR SMOKE PASS: removing receiving manpack interrupts an in-flight HQ order");
|
||||
return 1;
|
||||
}
|
||||
if (result < 0) return -1;
|
||||
if (m_Step == 1) Print("ERR SMOKE PASS: physical manpack uplink delivers to HQ");
|
||||
if (m_Step == 2) Print("ERR SMOKE PASS: HQ downlink delivers to a physical receiver");
|
||||
m_Route = hq.ERR_ProbeRoute(m_Carrier, true);
|
||||
if (!m_Route || m_Route.Nodes.IsEmpty()) return -1;
|
||||
m_Route.MessageId = hq.Journal().Message();
|
||||
m_Step++;
|
||||
if (m_Step == 3)
|
||||
{
|
||||
// Start HQ transmission, then remove the actual receiving equipment.
|
||||
if (m_Route.Advance(now, hq.Journal()) != 0) return -1;
|
||||
IEntity receiver = m_Route.Nodes[0].Carrier;
|
||||
InventoryStorageManagerComponent inventory = InventoryStorageManagerComponent.Cast(receiver.FindComponent(InventoryStorageManagerComponent));
|
||||
if (!inventory) return -1;
|
||||
array<IEntity> items = {};
|
||||
inventory.GetItems(items);
|
||||
foreach (IEntity item : items)
|
||||
if (item && item.FindComponent(BaseRadioComponent)) SCR_EntityHelper.DeleteEntityAndChildren(item);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+579
@@ -0,0 +1,579 @@
|
||||
// Explicit, local Workbench diagnostic only. Never compiled into game/server builds.
|
||||
#ifdef WORKBENCH
|
||||
modded class ERR_GameMode
|
||||
{
|
||||
protected bool m_ERRTestPatrolHQ;
|
||||
protected ref ERR_NativePatrolProbe m_ERRHQPatrolMovement;
|
||||
protected ref ERR_NativePatrolHQProbe m_ERRHQPatrolOrder;
|
||||
protected int m_ERRSmokeStep;
|
||||
protected float m_ERRSmokeDeadline;
|
||||
protected float m_ERRSmokeWait;
|
||||
protected IEntity m_ERRSmokePlayer;
|
||||
protected BaseDoorComponent m_ERRGate;
|
||||
protected ERR_LocationComponent m_ERRSmokeRadio;
|
||||
protected bool m_ERRSmokeDone;
|
||||
protected bool m_ERRTestLostHelicopter;
|
||||
protected bool m_ERRTestPlayerDeath;
|
||||
protected bool m_ERRTestRadioNetwork;
|
||||
protected bool m_ERRTestHQResponse;
|
||||
protected bool m_ERRTestMortar;
|
||||
protected bool m_ERRTestMortarFire;
|
||||
protected bool m_ERRTestMapOwnership;
|
||||
protected bool m_ERRTestAircraftControl;
|
||||
protected bool m_ERRTestAircraftCancel;
|
||||
protected bool m_ERRTestAircraftTranslate;
|
||||
protected ref ERR_NativeAircraftControlProbe m_ERRAircraftControl;
|
||||
protected ref ERRM_NativeMapOwnershipProbe m_ERRMapOwnership;
|
||||
protected bool m_ERRTestMortarSalvo;
|
||||
protected ref ERR_NativeMortarSalvoProbe m_ERRMortarSalvo;
|
||||
protected bool m_ERRTestMortarCourier;
|
||||
protected ref ERR_NativeMortarCourierProbe m_ERRMortarCourier;
|
||||
protected bool m_ERRTestMortarTransfer;
|
||||
protected ref ERR_NativeMortarTransferProbe m_ERRMortarTransfer;
|
||||
protected ref ERR_NativeMortarFiringProbe m_ERRMortarFire;
|
||||
protected float m_ERRNextProbeLog;
|
||||
protected ref ERR_NativeRadioProbe m_ERRRadioProbe;
|
||||
protected ref ERR_NativeHQResponseProof m_ERRHQResponseProof;
|
||||
protected ref array<IEntity> m_ERRCacheFolders = {};
|
||||
|
||||
protected override void EOnInit(IEntity owner)
|
||||
{
|
||||
super.EOnInit(owner);
|
||||
string enabled;
|
||||
if (SCR_Global.IsEditMode() || !Replication.IsServer() || !System.GetCLIParam("errSmoke", enabled) || enabled != "1") return;
|
||||
string outcome;
|
||||
m_ERRTestLostHelicopter = System.GetCLIParam("errSmokeOutcome", outcome) && outcome == "lost-helicopter";
|
||||
m_ERRTestRadioNetwork = outcome == "radio-network";
|
||||
m_ERRTestHQResponse = outcome == "hq-response";
|
||||
m_ERRTestMortar = outcome == "mortar-inventory";
|
||||
m_ERRTestMapOwnership = outcome == "map-ownership";
|
||||
m_ERRTestAircraftCancel = outcome == "aircraft-cancel";
|
||||
m_ERRTestAircraftTranslate = outcome == "aircraft-translate";
|
||||
m_ERRTestAircraftControl = outcome == "aircraft-control" || m_ERRTestAircraftCancel || m_ERRTestAircraftTranslate;
|
||||
m_ERRTestMortarFire = outcome == "mortar-fire" || outcome == "mortar-expiry";
|
||||
m_ERRTestMortarCourier = outcome == "mortar-courier";
|
||||
m_ERRTestMortarSalvo = outcome == "mortar-salvo" || outcome == "mortar-refresh";
|
||||
m_ERRTestMortarTransfer = outcome == "mortar-transfer";
|
||||
m_ERRTestPatrolHQ = outcome == "patrol-hq";
|
||||
m_ERRTestPlayerDeath = outcome == "player-death" || m_ERRTestRadioNetwork || m_ERRTestHQResponse || m_ERRTestMortar;
|
||||
m_ERRSmokeDeadline = GetWorld().GetWorldTime() + 150000;
|
||||
if (m_ERRTestAircraftControl) m_ERRSmokeDeadline = GetWorld().GetWorldTime() + 330000;
|
||||
if (m_ERRTestMortarCourier) m_ERRSmokeDeadline = GetWorld().GetWorldTime() + 450000;
|
||||
if (m_ERRTestMortarTransfer) m_ERRSmokeDeadline = GetWorld().GetWorldTime() + 330000;
|
||||
if (m_ERRTestMortarSalvo) m_ERRSmokeDeadline = GetWorld().GetWorldTime() + 330000;
|
||||
if (m_ERRTestMortarFire) m_ERRSmokeDeadline = GetWorld().GetWorldTime() + 330000;
|
||||
if (m_ERRTestPatrolHQ) m_ERRSmokeDeadline = GetWorld().GetWorldTime() + 420000;
|
||||
OnStop().Insert(ERR_StopHQPatrol);
|
||||
OnPhaseChanged().Insert(ERR_SmokePhase);
|
||||
GetGame().GetCallqueue().CallLater(ERR_SmokeTick, 500, true);
|
||||
Print("ERR SMOKE: scripted integration test enabled; gameplay acceptance remains separate");
|
||||
}
|
||||
|
||||
protected bool ERR_Check(bool condition, string name)
|
||||
{
|
||||
if (condition) { Print("ERR SMOKE PASS: " + name); return true; }
|
||||
ERR_StopHQPatrol();
|
||||
Print("ERR SMOKE FAIL: " + name, LogLevel.ERROR);
|
||||
m_ERRSmokeDone = true;
|
||||
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
|
||||
return false;
|
||||
}
|
||||
protected void ERR_StopHQPatrol()
|
||||
{
|
||||
if (m_ERRMapOwnership) m_ERRMapOwnership.Stop();
|
||||
if (m_ERRAircraftControl) m_ERRAircraftControl.Stop();
|
||||
if (m_ERRHQResponseProof) m_ERRHQResponseProof.Stop();
|
||||
if (m_ERRMortarSalvo) m_ERRMortarSalvo.Stop();
|
||||
if (m_ERRMortarCourier) m_ERRMortarCourier.Stop();
|
||||
if (m_ERRMortarTransfer) m_ERRMortarTransfer.Stop();
|
||||
if (m_ERRMortarFire) m_ERRMortarFire.Stop();
|
||||
if (m_ERRHQPatrolMovement) m_ERRHQPatrolMovement.Stop();
|
||||
if (m_ERRHQPatrolOrder) m_ERRHQPatrolOrder.Stop();
|
||||
}
|
||||
protected void ERR_MovePlayer(vector position)
|
||||
{
|
||||
vector transform[4];
|
||||
m_ERRSmokePlayer.GetWorldTransform(transform);
|
||||
transform[3] = ERR_Actors.Ground(position);
|
||||
ChimeraCharacter.Cast(m_ERRSmokePlayer).Teleport(transform);
|
||||
}
|
||||
protected ERR_UnlockPrisonAction ERR_FindLock(IEntity entity)
|
||||
{
|
||||
BaseActionsManagerComponent manager = BaseActionsManagerComponent.Cast(entity.FindComponent(BaseActionsManagerComponent));
|
||||
if (manager)
|
||||
{
|
||||
array<BaseUserAction> actions = {};
|
||||
manager.GetActionsList(actions);
|
||||
foreach (BaseUserAction action : actions)
|
||||
{
|
||||
ERR_UnlockPrisonAction lock = ERR_UnlockPrisonAction.Cast(action);
|
||||
if (lock) return lock;
|
||||
}
|
||||
}
|
||||
IEntity child = entity.GetChildren();
|
||||
while (child)
|
||||
{
|
||||
ERR_UnlockPrisonAction lock = ERR_FindLock(child);
|
||||
if (lock) return lock;
|
||||
child = child.GetSibling();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
protected void ERR_SmokePhase(ERR_Phase previous, ERR_Phase current)
|
||||
{
|
||||
if (m_ERRSmokeDone || !ERR_MissionRules.Terminal(current)) return;
|
||||
if (m_ERRTestAircraftControl)
|
||||
{
|
||||
if (!ERR_Check(current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 221, "actual aircraft controller completed before explicit fixture reset")) return;
|
||||
m_ERRSmokeDone = true;
|
||||
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
|
||||
ERR_StopHQPatrol();
|
||||
if (m_ERRTestAircraftCancel)
|
||||
Print("ERR SMOKE COMPLETE: actual airborne aircraft cancellation retains inputs through settled contact landing and engine-off; isolated mechanism, not HQ dispatch");
|
||||
else if (m_ERRTestAircraftTranslate)
|
||||
Print("ERR SMOKE COMPLETE: actual aircraft controller immutable target translation, observed target hold and contact landing; bounded mechanism, not route support or HQ dispatch");
|
||||
else Print("ERR SMOKE COMPLETE: actual aircraft controller and post-spawn qualification, powered hover and contact landing; isolated physical mechanism, not HQ dispatch");
|
||||
return;
|
||||
}
|
||||
if (m_ERRTestMapOwnership)
|
||||
{
|
||||
if (!ERR_Check(current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 201, "native map ownership probe cleaned up before explicit reset")) return;
|
||||
m_ERRSmokeDone = true;
|
||||
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
|
||||
ERR_StopHQPatrol();
|
||||
Print("ERR SMOKE COMPLETE: native replacement map/config/root ownership and spare deletion; camera authorization observed, render flag not measured");
|
||||
return;
|
||||
}
|
||||
if (m_ERRTestMortarSalvo)
|
||||
{
|
||||
if (!ERR_Check(current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 191, "native configured salvo and unchanged-report replay observation complete before reset")) return;
|
||||
m_ERRSmokeDone = true;
|
||||
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
|
||||
ERR_StopHQPatrol();
|
||||
Print("ERR SMOKE COMPLETE: native full configured mortar salvo, exact original ammunition and unchanged-report replay refusal; fixture reset requested");
|
||||
return;
|
||||
}
|
||||
if (m_ERRTestMortarCourier)
|
||||
{
|
||||
if (!ERR_Check(current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 181, "native finite courier reached an exhausted battery before explicit reset")) return;
|
||||
m_ERRSmokeDone = true;
|
||||
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
|
||||
ERR_StopHQPatrol();
|
||||
Print("ERR SMOKE COMPLETE: native finite courier, real supply uplink/downlink, original patrol walking and exact physical shell delivery; original 27 shells sequestered in fixture boxes; reset requested");
|
||||
return;
|
||||
}
|
||||
if (m_ERRTestMortarTransfer)
|
||||
{
|
||||
if (!ERR_Check(current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 161, "native exact shell transfers and original-owner restoration complete before explicit reset")) return;
|
||||
m_ERRSmokeDone = true;
|
||||
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
|
||||
ERR_StopHQPatrol();
|
||||
Print("ERR SMOKE COMPLETE: native exact mortar inventory transfer, accepted-operation cancellation and original-owner restoration; radio bypassed; fixture reset requested");
|
||||
return;
|
||||
}
|
||||
if (m_ERRTestMortarFire)
|
||||
{
|
||||
if (!ERR_Check(current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 151, "native mortar first shot and stop complete before explicit reset")) return;
|
||||
m_ERRSmokeDone = true;
|
||||
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
|
||||
ERR_StopHQPatrol();
|
||||
Print("ERR SMOKE COMPLETE: native HQ radio mortar projectile and configured cancellation/expiry; fixture reset requested");
|
||||
return;
|
||||
}
|
||||
if (m_ERRTestPatrolHQ)
|
||||
{
|
||||
if (!ERR_Check(current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 133, "HQ patrol preemption and physical resumption before explicit reset")) return;
|
||||
m_ERRSmokeDone = true;
|
||||
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
|
||||
Print("ERR SMOKE COMPLETE: native HQ patrol preemption and physical resumption");
|
||||
return;
|
||||
}
|
||||
if (m_ERRTestPlayerDeath)
|
||||
{
|
||||
if (!ERR_Check(current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 91, "death exhausts round life and reaches defeat after spectator grace")) return;
|
||||
}
|
||||
else if (m_ERRTestLostHelicopter)
|
||||
{
|
||||
if (!ERR_Check(current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 8, "lost extraction helicopter causes defeat")) return;
|
||||
}
|
||||
else if (!ERR_Check(current == ERR_Phase.VICTORY && m_ERRSmokeStep == 7, "scripted escape reaches victory after physical vehicle occupancy")) return;
|
||||
m_ERRSmokeDone = true;
|
||||
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
|
||||
PrintFormat("ERR SMOKE COMPLETE: terminal=%1 lostHelicopterCase=%2 playerDeathCase=%3", current, m_ERRTestLostHelicopter, m_ERRTestPlayerDeath);
|
||||
GetGame().GetCallqueue().CallLater(ERR_EndScreenshot, 4000);
|
||||
}
|
||||
protected bool ERR_CheckRoster()
|
||||
{
|
||||
ERR_PlayerRoster roster = new ERR_PlayerRoster();
|
||||
UUID first = "00e77100-0000-0000-0000-000000000101";
|
||||
UUID second = "00e77100-0000-0000-0000-000000000202";
|
||||
if (!ERR_Check(roster.Bind(101, first) && roster.CanEnter(101, ERR_Phase.PRISON), "new identity may enter prison")) return false;
|
||||
roster.Spawned(101, m_ERRSmokePlayer);
|
||||
if (!ERR_Check(!roster.CanEnter(101, ERR_Phase.PRISON), "spawn consumes identity life")) return false;
|
||||
roster.Disconnect(101);
|
||||
if (!ERR_Check(roster.HasSurvivor() && !roster.Get(101), "disconnect retains living actor and clears connection")) return false;
|
||||
if (!ERR_Check(roster.Bind(202, first) && !roster.CanEnter(202, ERR_Phase.PRISON), "reconnected identity retains spent life")) return false;
|
||||
if (!ERR_Check(!roster.Bind(303, first), "duplicate active identity rejected")) return false;
|
||||
if (!ERR_Check(roster.Bind(101, second) && roster.CanEnter(101, ERR_Phase.RADIO_DEFENCE) && !roster.CanEnter(101, ERR_Phase.EXTRACTION), "recycled connection receives independent life and extraction admission policy")) return false;
|
||||
return true;
|
||||
}
|
||||
protected bool ERR_CheckCompanionCommands()
|
||||
{
|
||||
array<string> replies = {};
|
||||
array<string> commands = {"hq interval show", "hq squads show", "hq radius show", "intel carrying show", "intel reveal show"};
|
||||
array<string> keys = {"reportIntervalSeconds", "supportSquadLimit", "responseRadiusMetres", "aiIntelChancePercent", "intelRevealChancePercent"};
|
||||
for (int i = 0; i < commands.Count(); i++)
|
||||
{
|
||||
Admin().Execute(this, true, commands[i], replies);
|
||||
if (!ERR_Check(replies.Count() == 1 && replies[0] == keys[i] + "=" + Settings().Get(keys[i]).ToString(), "companion command retained: " + commands[i])) return false;
|
||||
}
|
||||
Admin().Execute(this, false, "hq log", replies);
|
||||
if (!ERR_Check(replies.Count() == 1 && replies[0] == "Administrator access required.", "companion journal is behind Core admin authorization")) return false;
|
||||
Admin().Execute(this, true, "hq show", replies);
|
||||
ERR_HQ hq = ERR_HQ.Cast(Command());
|
||||
if (!ERR_Check(hq && replies.Count() == 1 && replies[0] == hq.Status(), "companion HQ status uses the shared command service")) return false;
|
||||
return true;
|
||||
}
|
||||
protected void ERR_PrisonScreenshot() { System.MakeScreenshot("$logs:err-prison"); }
|
||||
protected void ERR_EndScreenshot() { System.MakeScreenshot("$logs:err-result"); }
|
||||
protected void ERR_SmokeTick()
|
||||
{
|
||||
float now = GetWorld().GetWorldTime();
|
||||
if (m_ERRSmokeDone) return;
|
||||
if (now > m_ERRSmokeDeadline) { ERR_Check(false, "timeout at step " + m_ERRSmokeStep.ToString()); return; }
|
||||
if (now < m_ERRSmokeWait) return;
|
||||
if (m_ERRSmokeStep == 220)
|
||||
{
|
||||
int result = m_ERRAircraftControl.Tick(now);
|
||||
if (result < 0) { ERR_Check(false, "native actual aircraft controller fixture"); return; }
|
||||
if (result == 1) { m_ERRSmokeStep = 221; Transition(ERR_Phase.DEFEAT); }
|
||||
return;
|
||||
}
|
||||
if (m_ERRSmokeStep == 200)
|
||||
{
|
||||
int result = m_ERRMapOwnership.Tick(now);
|
||||
if (result < 0) { ERR_Check(false, "native map ownership fixture"); return; }
|
||||
if (result == 1) { m_ERRSmokeStep = 201; Transition(ERR_Phase.DEFEAT); }
|
||||
return;
|
||||
}
|
||||
if (m_ERRSmokeStep == 190)
|
||||
{
|
||||
int result = m_ERRMortarSalvo.Tick(now);
|
||||
if (result < 0) { ERR_Check(false, "native complete mortar salvo fixture; inspect source diagnostics"); return; }
|
||||
if (result == 1) { m_ERRSmokeStep = 191; Transition(ERR_Phase.DEFEAT); }
|
||||
return;
|
||||
}
|
||||
if (m_ERRSmokeStep == 180)
|
||||
{
|
||||
int result = m_ERRMortarCourier.Tick(now);
|
||||
if (result < 0) { ERR_Check(false, "native finite courier fixture"); return; }
|
||||
if (result == 1) { m_ERRSmokeStep = 181; Transition(ERR_Phase.DEFEAT); }
|
||||
return;
|
||||
}
|
||||
if (m_ERRSmokeStep == 160)
|
||||
{
|
||||
int result = m_ERRMortarTransfer.Tick(now);
|
||||
if (result < 0) { ERR_Check(false, "native mortar exact-shell transfer fixture"); return; }
|
||||
if (result == 1) { m_ERRSmokeStep = 161; Transition(ERR_Phase.DEFEAT); }
|
||||
return;
|
||||
}
|
||||
if (m_ERRSmokeStep == 150)
|
||||
{
|
||||
int result = m_ERRMortarFire.Tick(now);
|
||||
if (result < 0) { ERR_Check(false, "native mortar firing probe failed; see diagnostics"); return; }
|
||||
if (result == 1) { m_ERRSmokeStep = 151; Transition(ERR_Phase.DEFEAT); }
|
||||
return;
|
||||
}
|
||||
if (m_ERRSmokeStep == 130 || m_ERRSmokeStep == 132)
|
||||
{
|
||||
int result = m_ERRHQPatrolMovement.Tick(now);
|
||||
if (result < 0) { ERR_StopHQPatrol(); GetGame().GetCallqueue().Remove(ERR_SmokeTick); return; }
|
||||
if (result == 1)
|
||||
{
|
||||
if (m_ERRSmokeStep == 130) { m_ERRHQPatrolOrder = new ERR_NativePatrolHQProbe(this); m_ERRSmokeStep = 131; }
|
||||
else { m_ERRSmokeStep = 133; Transition(ERR_Phase.DEFEAT); }
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (m_ERRSmokeStep == 131)
|
||||
{
|
||||
int result = m_ERRHQPatrolOrder.Tick(now);
|
||||
if (result < 0) { ERR_StopHQPatrol(); GetGame().GetCallqueue().Remove(ERR_SmokeTick); return; }
|
||||
if (result == 1) { m_ERRHQPatrolMovement = new ERR_NativePatrolProbe(this); m_ERRSmokeStep = 132; }
|
||||
return;
|
||||
}
|
||||
if (m_ERRSmokeStep == 0)
|
||||
{
|
||||
array<IEntity> players = {};
|
||||
ERR_Actors.Players(players, true);
|
||||
if (players.Count() != 1 || Phase() != ERR_Phase.PRISON) return;
|
||||
m_ERRSmokePlayer = players[0];
|
||||
if (!ERR_CheckRoster() || !ERR_NativeContractsProbe.Run(this, m_ERRSmokePlayer) || !ERR_CheckCompanionCommands()) return;
|
||||
if (m_ERRTestAircraftControl)
|
||||
{
|
||||
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
||||
m_ERRAircraftControl = new ERR_NativeAircraftControlProbe(this);
|
||||
m_ERRSmokeStep = 220;
|
||||
return;
|
||||
}
|
||||
if (m_ERRTestPatrolHQ)
|
||||
{
|
||||
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
||||
m_ERRHQPatrolMovement = new ERR_NativePatrolProbe(this);
|
||||
m_ERRSmokeStep = 130;
|
||||
return;
|
||||
}
|
||||
if (m_ERRTestMapOwnership)
|
||||
{
|
||||
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
||||
m_ERRMapOwnership = new ERRM_NativeMapOwnershipProbe();
|
||||
m_ERRSmokeStep = 200;
|
||||
return;
|
||||
}
|
||||
if (m_ERRTestMortarSalvo)
|
||||
{
|
||||
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
||||
m_ERRMortarSalvo = new ERR_NativeMortarSalvoProbe(this);
|
||||
m_ERRSmokeStep = 190;
|
||||
return;
|
||||
}
|
||||
if (m_ERRTestMortarCourier)
|
||||
{
|
||||
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
||||
m_ERRMortarCourier = new ERR_NativeMortarCourierProbe(this);
|
||||
m_ERRSmokeStep = 180;
|
||||
return;
|
||||
}
|
||||
if (m_ERRTestMortarTransfer)
|
||||
{
|
||||
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
||||
m_ERRMortarTransfer = new ERR_NativeMortarTransferProbe(this);
|
||||
m_ERRSmokeStep = 160;
|
||||
return;
|
||||
}
|
||||
if (m_ERRTestMortarFire)
|
||||
{
|
||||
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
||||
m_ERRMortarFire = new ERR_NativeMortarFiringProbe(this);
|
||||
m_ERRSmokeStep = 150;
|
||||
return;
|
||||
}
|
||||
if (m_ERRTestMortar)
|
||||
{
|
||||
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
||||
m_ERRSmokeStep = 120;
|
||||
return;
|
||||
}
|
||||
if (m_ERRTestHQResponse)
|
||||
{
|
||||
m_ERRHQResponseProof = new ERR_NativeHQResponseProof(this, m_ERRSmokePlayer);
|
||||
m_ERRSmokeStep = 110;
|
||||
return;
|
||||
}
|
||||
if (m_ERRTestRadioNetwork)
|
||||
{
|
||||
m_ERRRadioProbe = new ERR_NativeRadioProbe();
|
||||
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
||||
m_ERRSmokeStep = 100;
|
||||
return;
|
||||
}
|
||||
if (m_ERRTestPlayerDeath)
|
||||
{
|
||||
m_ERRSmokeStep = 9;
|
||||
SCR_CharacterDamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(SCR_CharacterDamageManagerComponent)).Kill(Instigator.CreateInstigator(m_ERRSmokePlayer));
|
||||
m_ERRSmokeWait = now + 4000;
|
||||
return;
|
||||
}
|
||||
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
||||
if (!ERR_Check(vector.DistanceXZ(m_ERRSmokePlayer.GetOrigin(), PrisonPosition()) < 5, "initial prisoner position")) return;
|
||||
foreach (ERR_LocationComponent location : m_Locations)
|
||||
if (location.Kind() == ERR_LocationKind.RADIO) m_ERRSmokeRadio = location;
|
||||
if (!ERR_Check(m_ERRSmokeRadio && ERR_Actors.Alive(ExtractionVehicle()), "radio and extraction vehicle exist")) return;
|
||||
ERR_UnlockPrisonAction lock;
|
||||
foreach (IEntity tracked : m_OwnedEntities)
|
||||
{
|
||||
if (tracked) lock = ERR_FindLock(tracked);
|
||||
if (lock) break;
|
||||
}
|
||||
if (!ERR_Check(lock != null, "authored gate exposes lock action")) return;
|
||||
ERR_MovePlayer(lock.GetOwner().GetOrigin() + "1 0 1");
|
||||
vector gateLow, gateHigh;
|
||||
lock.GetOwner().GetWorldBounds(gateLow, gateHigh);
|
||||
PrintFormat("ERR GEOMETRY: gate low=%1 high=%2", gateLow, gateHigh);
|
||||
IEntity wall = lock.GetOwner().GetParent().GetChildren();
|
||||
while (wall)
|
||||
{
|
||||
vector wallLow, wallHigh;
|
||||
wall.GetWorldBounds(wallLow, wallHigh);
|
||||
PrintFormat("ERR GEOMETRY: child origin=%1 low=%2 high=%3", wall.GetOrigin(), wallLow, wallHigh);
|
||||
wall = wall.GetSibling();
|
||||
}
|
||||
GetGame().GetCallqueue().CallLater(ERR_PrisonScreenshot, 2000);
|
||||
if (!ERR_Check(lock.CanBePerformedScript(m_ERRSmokePlayer), "lock permits prisoner")) return;
|
||||
m_ERRGate = BaseDoorComponent.Cast(lock.GetOwner().FindComponent(BaseDoorComponent));
|
||||
lock.PerformAction(lock.GetOwner(), m_ERRSmokePlayer);
|
||||
if (!ERR_Check(Phase() == ERR_Phase.RADIO_SEARCH, "lock action advances mission")) return;
|
||||
array<IEntity> cacheStock = {};
|
||||
ERR_Inventory.Items(InventoryStorageManagerComponent.Cast(Sites().Find("everon-supply-1").Container.FindComponent(InventoryStorageManagerComponent)), cacheStock);
|
||||
foreach (IEntity tracked : cacheStock)
|
||||
{
|
||||
if (tracked && tracked.GetPrefabData() && tracked.GetPrefabData().GetPrefabName() == ERR_Resources.ObjectiveDocument()) m_ERRCacheFolders.Insert(tracked);
|
||||
}
|
||||
if (!ERR_Check(m_ERRCacheFolders.Count() == 2, "two guaranteed physical cache documents")) return;
|
||||
if (!ERR_Check(m_Settings.Set("aiIntelChancePercent", 0), "test does not rely on random intel")) return;
|
||||
if (!ERR_Check(m_Settings.Set("radioDefenceSeconds", 30), "isolated test profile defence duration")) return;
|
||||
m_ERRSmokeStep = 1;
|
||||
}
|
||||
else if (m_ERRSmokeStep == 1 || m_ERRSmokeStep == 2)
|
||||
{
|
||||
if (m_ERRSmokeStep == 2 && !ERR_Check(m_ERRGate && m_ERRGate.IsOpen() && m_ERRGate.CanCharacterPass(0.6), "unlocked gate physically permits passage")) return;
|
||||
ERR_MovePlayer(Sites().Find("everon-supply-1").Container.GetOrigin() + "0 0 2");
|
||||
SCR_InventoryStorageManagerComponent inventory = SCR_InventoryStorageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(SCR_InventoryStorageManagerComponent));
|
||||
InventoryStorageManagerComponent source = InventoryStorageManagerComponent.Cast(Sites().Find("everon-supply-1").Container.FindComponent(InventoryStorageManagerComponent));
|
||||
BaseInventoryStorageComponent destination = inventory.FindStorageForItem(m_ERRCacheFolders[m_ERRSmokeStep - 1]);
|
||||
if (!ERR_Check(destination && source.TryMoveItemToStorage(m_ERRCacheFolders[m_ERRSmokeStep - 1], destination), "authored cache document recovered into inventory")) return;
|
||||
m_ERRSmokeStep++;
|
||||
m_ERRSmokeWait = now + 2500;
|
||||
}
|
||||
else if (m_ERRSmokeStep == 3)
|
||||
{
|
||||
if (!ERR_Check(m_ERRSmokeRadio.Knowledge() == 2, "carried intel identifies radio through intel service")) return;
|
||||
if (!ERR_Check(!TuneRadio(m_ERRSmokePlayer, m_ERRSmokeRadio), "radio rejects distant player")) return;
|
||||
ERR_MovePlayer(m_ERRSmokeRadio.Position() + "2 0 0");
|
||||
m_ERRSmokeStep = 30;
|
||||
m_ERRSmokeWait = now + 1000;
|
||||
}
|
||||
else if (m_ERRSmokeStep == 30)
|
||||
{
|
||||
PrintFormat("ERR SMOKE: radio=%1 player=%2", m_ERRSmokeRadio.Position(), m_ERRSmokePlayer.GetOrigin());
|
||||
if (!ERR_Check(TuneRadio(m_ERRSmokePlayer, m_ERRSmokeRadio), "identified radio starts defence")) return;
|
||||
m_ERRSmokeStep = 4;
|
||||
m_ERRSmokeWait = now + 3000;
|
||||
}
|
||||
else if (m_ERRSmokeStep == 4)
|
||||
{
|
||||
if (!ERR_Check(DefenceOccupied(), "occupied defence timer running")) return;
|
||||
ERR_MovePlayer(m_ERRSmokeRadio.Position() + "90 0 0");
|
||||
m_ERRSmokeStep = 5;
|
||||
m_ERRSmokeWait = now + 2500;
|
||||
}
|
||||
else if (m_ERRSmokeStep == 5)
|
||||
{
|
||||
if (!ERR_Check(!DefenceOccupied() && Phase() == ERR_Phase.RADIO_DEFENCE, "leaving area resets defence")) return;
|
||||
ERR_MovePlayer(m_ERRSmokeRadio.Position() + "2 0 0");
|
||||
m_ERRSmokeStep = 6;
|
||||
m_ERRSmokeWait = now + 33000;
|
||||
}
|
||||
else if (m_ERRSmokeStep == 6)
|
||||
{
|
||||
if (!ERR_Check(Phase() == ERR_Phase.EXTRACTION, "continuous occupied defence completes")) return;
|
||||
if (m_ERRTestLostHelicopter)
|
||||
{
|
||||
m_ERRSmokeStep = 8;
|
||||
ERR_Actors.DeleteSpawned(ExtractionVehicle());
|
||||
return;
|
||||
}
|
||||
CompartmentAccessComponent access = CompartmentAccessComponent.Cast(m_ERRSmokePlayer.FindComponent(CompartmentAccessComponent));
|
||||
BaseCompartmentSlot slot = access.FindFreeCompartment(ExtractionVehicle(), ECompartmentType.PILOT);
|
||||
if (!ERR_Check(slot && access.GetInVehicle(ExtractionVehicle(), slot, true, -1, ECloseDoorAfterActions.INVALID, false), "boarding extraction vehicle")) return;
|
||||
m_ERRSmokeStep = 7;
|
||||
m_ERRSmokeWait = now + 3000;
|
||||
}
|
||||
else if (m_ERRSmokeStep == 120)
|
||||
{
|
||||
ERR_MortarBattery battery = ERR_MortarSystem.Get().ERR_ProbeBattery();
|
||||
if (!battery || !battery.StartupActivated) return;
|
||||
if (!ERR_Check(battery.Operational() && battery.Group.GetAgentsCount() == 4, "authored physical mortar and four-person crew initialize")) return;
|
||||
if (!ERR_Check(ERR_HQRadio.Capability(battery.RadioOperator) == 2, "mortar has a dedicated physical manpack operator")) return;
|
||||
if (!ERR_Check(battery.CountShells() == 27, "mortar starts with exactly 27 physical HE shells")) return;
|
||||
array<AIAgent> agents = {};
|
||||
battery.Group.GetAgents(agents);
|
||||
array<IEntity> intelCrew = {};
|
||||
foreach (AIAgent intelAgent : agents)
|
||||
{
|
||||
IEntity intelActor;
|
||||
if (intelAgent) intelActor = intelAgent.GetControlledEntity();
|
||||
if (!ERR_Check(intelActor && !intelCrew.Contains(intelActor), "mortar Intel observation uses distinct actual crew actors")) return;
|
||||
if (!ERR_Check(m_Intel && m_Intel.ERR_ProbeWasRolled(intelActor), "ready mortar crew actor reached its one optional Intel carrying roll")) return;
|
||||
intelCrew.Insert(intelActor);
|
||||
}
|
||||
if (!ERR_Check(intelCrew.Count() == 4, "all four original ready physical crew reached Intel without forcing documents or changing carrying chance")) return;
|
||||
foreach (AIAgent agent : agents)
|
||||
{
|
||||
InventoryStorageManagerComponent inventory = InventoryStorageManagerComponent.Cast(agent.GetControlledEntity().FindComponent(InventoryStorageManagerComponent));
|
||||
array<IEntity> items = {};
|
||||
ERR_Inventory.Items(inventory, items);
|
||||
foreach (IEntity item : items)
|
||||
if (item && item.GetPrefabData() && item.GetPrefabData().GetPrefabName() == ERR_MortarBattery.SHELL) SCR_EntityHelper.DeleteEntityAndChildren(item);
|
||||
}
|
||||
m_ERRSmokeStep = 121;
|
||||
m_ERRSmokeWait = now + 15000;
|
||||
}
|
||||
else if (m_ERRSmokeStep == 121)
|
||||
{
|
||||
ERR_MortarBattery battery = ERR_MortarSystem.Get().ERR_ProbeBattery();
|
||||
if (!ERR_Check(battery.CountShells() == 0 && battery.TotalShots == 0, "depleted mortar inventory stays empty without fabricated ammunition")) return;
|
||||
ERR_Actors.DeleteSpawned(battery.Gun);
|
||||
ERR_Actors.DeleteSpawned(battery.Group);
|
||||
m_ERRSmokeStep = 122;
|
||||
m_ERRSmokeWait = now + 12000;
|
||||
}
|
||||
else if (m_ERRSmokeStep == 122)
|
||||
{
|
||||
ERR_MortarBattery battery = ERR_MortarSystem.Get().ERR_ProbeBattery();
|
||||
if (!ERR_Check(battery && !battery.Gun && !battery.Group && !battery.Operational(), "lost mortar and crew remain lost without replacement spawning")) return;
|
||||
m_ERRSmokeStep = 9;
|
||||
SCR_CharacterDamageManagerComponent damage = SCR_CharacterDamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(SCR_CharacterDamageManagerComponent));
|
||||
damage.EnableDamageHandling(true);
|
||||
damage.Kill(Instigator.CreateInstigator(m_ERRSmokePlayer));
|
||||
m_ERRSmokeWait = now + 4000;
|
||||
}
|
||||
else if (m_ERRSmokeStep == 110)
|
||||
{
|
||||
int result = m_ERRHQResponseProof.Tick(now);
|
||||
if (result < 0) { ERR_Check(false, "native HQ response evidence failed; inspect report lineage diagnostics"); return; }
|
||||
if (result == 0) return;
|
||||
m_ERRSmokeStep = 9;
|
||||
SCR_CharacterDamageManagerComponent damage = SCR_CharacterDamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(SCR_CharacterDamageManagerComponent));
|
||||
damage.EnableDamageHandling(true);
|
||||
damage.Kill(Instigator.CreateInstigator(m_ERRSmokePlayer));
|
||||
m_ERRSmokeWait = now + 4000;
|
||||
}
|
||||
else if (m_ERRSmokeStep == 100)
|
||||
{
|
||||
int result = m_ERRRadioProbe.Tick(ERR_HQ.Cast(Command()), now);
|
||||
if (result == 0) return;
|
||||
if (!ERR_Check(result == 1, "native radio delivery and interruption sequence")) return;
|
||||
m_ERRSmokeStep = 9;
|
||||
SCR_CharacterDamageManagerComponent damage = SCR_CharacterDamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(SCR_CharacterDamageManagerComponent));
|
||||
damage.EnableDamageHandling(true);
|
||||
damage.Kill(Instigator.CreateInstigator(m_ERRSmokePlayer));
|
||||
m_ERRSmokeWait = now + 4000;
|
||||
}
|
||||
else if (m_ERRSmokeStep == 9)
|
||||
{
|
||||
SCR_PlayerController controller = SCR_PlayerController.Cast(GetGame().GetPlayerController());
|
||||
if (!ERR_Check(!ERR_Actors.Alive(m_ERRSmokePlayer), "native damage kills prisoner")) return;
|
||||
if (!ERR_Check(controller && controller.ERR_ObserverState() == ERR_ObserverReason.SPENT_LIFE, "death selects spectator without another life")) return;
|
||||
if (!ERR_Check(controller.ERR_HasObserverCamera() && SCR_ManualCamera.Cast(GetGame().GetCameraManager().CurrentCamera()), "spectator camera activates after native death")) return;
|
||||
m_ERRSmokeStep = 91;
|
||||
}
|
||||
else if (m_ERRSmokeStep == 7)
|
||||
{
|
||||
if (!ERR_Check(CompartmentAccessComponent.GetVehicleIn(m_ERRSmokePlayer) == ExtractionVehicle(), "physical extraction occupancy")) return;
|
||||
// Controlled transport tests the boundary detector, not piloting skill.
|
||||
ExtractionVehicle().SetOrigin(m_EscapeCenter + Vector(m_EscapeRadius + 100, 300, 0));
|
||||
m_ERRSmokeWait = now + 5000;
|
||||
}
|
||||
}
|
||||
void ~ERR_GameMode()
|
||||
{
|
||||
if (GetGame())
|
||||
{
|
||||
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
|
||||
GetGame().GetCallqueue().Remove(ERR_PrisonScreenshot);
|
||||
GetGame().GetCallqueue().Remove(ERR_EndScreenshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#ifndef WORKBENCH
|
||||
modded class ArmaReforgerScripted
|
||||
{
|
||||
override protected ResourceName ERR_DiagnosticHeader(string project)
|
||||
{
|
||||
if (project == "chat") return "{C1E20667DCE324ED}Missions/ERR_Everon.conf";
|
||||
return super.ERR_DiagnosticHeader(project);
|
||||
}
|
||||
|
||||
override protected string ERR_DiagnosticAddon(string project)
|
||||
{
|
||||
if (project == "chat") return "FA804F411BDDF2C6";
|
||||
return super.ERR_DiagnosticAddon(project);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
[WorkbenchPluginAttribute(name: "Validate Rereintroduced scenario", wbModules: {"WorldEditor"})]
|
||||
class ERR_ValidateWorldPlugin : WorldEditorPlugin
|
||||
{
|
||||
protected bool m_Obstructed;
|
||||
protected bool CheckClearance(IEntity entity)
|
||||
{
|
||||
if (entity && entity.GetVObject()) m_Obstructed = true;
|
||||
return !m_Obstructed;
|
||||
}
|
||||
override void Run() { Validate(); }
|
||||
override void RunCommandline() { Validate(); }
|
||||
protected void Validate()
|
||||
{
|
||||
WorldEditor editor = Workbench.GetModule(WorldEditor);
|
||||
if (!editor) { Print("ERR VALIDATION: World Editor missing", LogLevel.ERROR); return; }
|
||||
WorldEditorAPI api = editor.GetApi();
|
||||
if (!api) { Print("ERR VALIDATION: world API missing", LogLevel.ERROR); return; }
|
||||
PrintFormat("ERR VALIDATION: prison terrain=%1, radio terrain=%2, extraction terrain=%3", api.GetTerrainSurfaceY(6090,7160), api.GetTerrainSurfaceY(6500,7200), api.GetTerrainSurfaceY(7000,7500));
|
||||
for (int dx = -6; dx <= 6; dx += 3)
|
||||
for (int dz = -6; dz <= 6; dz += 3)
|
||||
PrintFormat("ERR TERRAIN: x=%1 z=%2 y=%3", 6090 + dx, 7160 + dz, api.GetTerrainSurfaceY(6090 + dx, 7160 + dz));
|
||||
float bestSlope = 1000;
|
||||
vector bestPosition;
|
||||
for (int x = 5800; x <= 6200; x += 10)
|
||||
{
|
||||
for (int z = 6800; z <= 7200; z += 10)
|
||||
{
|
||||
float low = 10000;
|
||||
float high = -10000;
|
||||
for (int sx = -9; sx <= 9; sx += 3)
|
||||
for (int sz = -15; sz <= 9; sz += 3)
|
||||
{
|
||||
float height = api.GetTerrainSurfaceY(x + sx, z + sz);
|
||||
low = Math.Min(low, height);
|
||||
high = Math.Max(high, height);
|
||||
}
|
||||
if (low > 5 && high - low < bestSlope)
|
||||
{
|
||||
m_Obstructed = false;
|
||||
api.GetWorld().QueryEntitiesByAABB(Vector(x - 10, low + 0.1, z - 16), Vector(x + 10, high + 5, z + 10), CheckClearance);
|
||||
if (m_Obstructed) continue;
|
||||
bestSlope = high - low;
|
||||
bestPosition = Vector(x, api.GetTerrainSurfaceY(x, z), z);
|
||||
}
|
||||
}
|
||||
}
|
||||
PrintFormat("ERR SITE CANDIDATE: position=%1 elevationRange=%2", bestPosition, bestSlope);
|
||||
string play;
|
||||
if (System.GetCLIParam("errPlay", play) && play == "1") editor.SwitchToGameMode();
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
GameProject {
|
||||
ID "ERR_Chat"
|
||||
GUID "FA804F411BDDF2C6"
|
||||
TITLE "Escapists Rereintroduced - Chat"
|
||||
Dependencies {
|
||||
"58D0FB3206B6F859"
|
||||
"2528466DE220D91C"
|
||||
"5192138CB9D5B911"
|
||||
"D31039C7C53E348A"
|
||||
"9AAEE9041D79809E"
|
||||
}
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
ERR_ScenarioDefinition {
|
||||
MinimumRadioSources 2
|
||||
Sites {
|
||||
ERR_SiteDefinition {
|
||||
Id "everon-prison-1"
|
||||
Label "Prison"
|
||||
Kind 0
|
||||
Prefab "{BE6B8D56349038A6}Prefabs/ERR/Prison.et"
|
||||
Position 6090 0 7160
|
||||
Guarded 1
|
||||
GuardOffset 35 0 0
|
||||
Container "{40986810D0B1F4AE}Prefabs/ERR/SupplyCache.et"
|
||||
ContainerOffset -4 0 0
|
||||
Stock {
|
||||
ERR_SiteStock { Prefab "{C0F7DD85A86B2900}Prefabs/Weapons/Handguns/PM/Handgun_PM.et" Count 5 }
|
||||
ERR_SiteStock { Prefab "{8B853CDD11BA916E}Prefabs/Weapons/Magazines/Magazine_9x18_PM_8rnd_Ball.et" Count 8 }
|
||||
ERR_SiteStock { Prefab "{13772C903CB5E4F7}Prefabs/Items/Equipment/Maps/Map_Paper_01/PaperMap_01_folded.et" Count 1 }
|
||||
}
|
||||
}
|
||||
ERR_SiteDefinition {
|
||||
Id "everon-supply-1"
|
||||
Label "Southern supply cache"
|
||||
Kind 4
|
||||
Prefab "{7C3D16C17C24D351}Prefabs/ERR/SupplySite.et"
|
||||
Position 6150 0 7050
|
||||
Guarded 1
|
||||
GuardOffset 18 0 0
|
||||
Container "{40986810D0B1F4AE}Prefabs/ERR/SupplyCache.et"
|
||||
ContainerOffset 0 0 0
|
||||
Stock {
|
||||
ERR_SiteStock { Prefab "{FA5C25BF66A53DCF}Prefabs/Weapons/Rifles/AK74/Rifle_AK74.et" Count 8 }
|
||||
ERR_SiteStock { Prefab "{BBB50A815A2F916B}Prefabs/Weapons/Magazines/Magazine_545x39_AK_30rnd_Ball.et" Count 16 }
|
||||
ERR_SiteStock { Prefab "{C3F1FA1E2EC2B345}Prefabs/Items/Medicine/FieldDressing_01/FieldDressing_USSR_01.et" Count 12 }
|
||||
ERR_SiteStock { Prefab "{554159A6740C3F3E}Prefabs/ERR/ObjectiveDocument.et" Count 2 TargetId "everon-radio-1" }
|
||||
}
|
||||
RevealOnEscape 1
|
||||
}
|
||||
ERR_SiteDefinition {
|
||||
Id "everon-radio-1"
|
||||
Label "Radio station"
|
||||
DiscoverRadius 8
|
||||
Kind 1
|
||||
Prefab "{F7E7E3D6ACF0C860}Prefabs/ERR/Radio.et"
|
||||
Position 6500 0 7200
|
||||
Guarded 1
|
||||
GuardOffset 30 0 0
|
||||
}
|
||||
ERR_SiteDefinition {
|
||||
Id "everon-extraction-1"
|
||||
Label "Initial helicopter pickup site"
|
||||
Kind 2
|
||||
Prefab "{033503110AC34EAC}Prefabs/ERR/Extraction.et"
|
||||
Position 7000 0 7500
|
||||
Guarded 1
|
||||
GuardOffset 30 0 0
|
||||
}
|
||||
ERR_SiteDefinition {
|
||||
Id "everon-supply-2"
|
||||
Label "Western supply cache"
|
||||
Kind 4
|
||||
Prefab "{7C3D16C17C24D351}Prefabs/ERR/SupplySite.et"
|
||||
Position 5960 0 7220
|
||||
Guarded 1
|
||||
GuardOffset 16 0 0
|
||||
Patrol {
|
||||
ERR_SitePatrolPoint { Offset 18 0 0 }
|
||||
ERR_SitePatrolPoint { Offset 18 0 24 }
|
||||
ERR_SitePatrolPoint { Offset -6 0 24 }
|
||||
ERR_SitePatrolPoint { Offset -6 0 0 }
|
||||
}
|
||||
Container "{40986810D0B1F4AE}Prefabs/ERR/SupplyCache.et"
|
||||
DiscoverRadius 25
|
||||
RevealOnEscape 1
|
||||
Transport ERR_SiteTransport {
|
||||
Prefab "{259EE7B78C51B624}Prefabs/Vehicles/Wheeled/UAZ469/UAZ469.et"
|
||||
Offset 0 0 -8
|
||||
Heading 180
|
||||
ClearanceRadius 4
|
||||
MaximumTerrainRange 1
|
||||
}
|
||||
Stock {
|
||||
ERR_SiteStock { Prefab "{FA5C25BF66A53DCF}Prefabs/Weapons/Rifles/AK74/Rifle_AK74.et" Count 4 }
|
||||
ERR_SiteStock { Prefab "{BBB50A815A2F916B}Prefabs/Weapons/Magazines/Magazine_545x39_AK_30rnd_Ball.et" Count 8 }
|
||||
ERR_SiteStock { Prefab "{C3F1FA1E2EC2B345}Prefabs/Items/Medicine/FieldDressing_01/FieldDressing_USSR_01.et" Count 6 }
|
||||
ERR_SiteStock { Prefab "{554159A6740C3F3E}Prefabs/ERR/ObjectiveDocument.et" Count 2 TargetId "everon-radio-1" }
|
||||
}
|
||||
}
|
||||
ERR_SiteDefinition {
|
||||
Id "everon-medical-1"
|
||||
Label "Medical stash"
|
||||
Kind 4
|
||||
Prefab "{7C3D16C17C24D351}Prefabs/ERR/SupplySite.et"
|
||||
Position 6340 0 7400
|
||||
Guarded 0
|
||||
GuardOffset 16 0 0
|
||||
Container "{40986810D0B1F4AE}Prefabs/ERR/SupplyCache.et"
|
||||
DiscoverRadius 25
|
||||
RevealOnEscape 0
|
||||
Stock {
|
||||
ERR_SiteStock { Prefab "{C3F1FA1E2EC2B345}Prefabs/Items/Medicine/FieldDressing_01/FieldDressing_USSR_01.et" Count 12 }
|
||||
ERR_SiteStock { Prefab "{527D7C5D2E476BDC}Prefabs/Items/Medicine/SalineBag_01/SalineBag_USSR_01.et" Count 6 }
|
||||
ERR_SiteStock { Prefab "{0D9A5DCF89AE7AA9}Prefabs/Items/Medicine/MorphineInjection_01/MorphineInjection_01.et" Count 6 }
|
||||
ERR_SiteStock { Prefab "{13772C903CB5E4F7}Prefabs/Items/Equipment/Maps/Map_Paper_01/PaperMap_01_folded.et" Count 3 }
|
||||
}
|
||||
}
|
||||
ERR_SiteDefinition {
|
||||
Id "everon-outpost-1"
|
||||
Label "Eastern guard outpost"
|
||||
Kind 3
|
||||
Prefab "{7C3D16C17C24D351}Prefabs/ERR/SupplySite.et"
|
||||
Position 6740 0 7300
|
||||
Guarded 1
|
||||
GuardOffset 16 0 0
|
||||
Patrol {
|
||||
ERR_SitePatrolPoint { Offset 18 0 0 }
|
||||
ERR_SitePatrolPoint { Offset 18 0 24 }
|
||||
ERR_SitePatrolPoint { Offset -6 0 24 }
|
||||
ERR_SitePatrolPoint { Offset -6 0 0 }
|
||||
}
|
||||
Container "{40986810D0B1F4AE}Prefabs/ERR/SupplyCache.et"
|
||||
DiscoverRadius 35
|
||||
RevealOnEscape 0
|
||||
Stock {
|
||||
ERR_SiteStock { Prefab "{FA5C25BF66A53DCF}Prefabs/Weapons/Rifles/AK74/Rifle_AK74.et" Count 2 }
|
||||
ERR_SiteStock { Prefab "{BBB50A815A2F916B}Prefabs/Weapons/Magazines/Magazine_545x39_AK_30rnd_Ball.et" Count 10 }
|
||||
ERR_SiteStock { Prefab "{243948B23D90BECB}Prefabs/Items/Equipment/Binoculars/Binoculars_B8/Binoculars_B8.et" Count 2 }
|
||||
ERR_SiteStock { Prefab "{C3F1FA1E2EC2B345}Prefabs/Items/Medicine/FieldDressing_01/FieldDressing_USSR_01.et" Count 4 }
|
||||
}
|
||||
Props {
|
||||
ERR_SiteProp { Prefab "{4B6AC1D8701C35A1}Prefabs/ERR/FieldCover.et" Offset 0 0 -4 }
|
||||
ERR_SiteProp { Prefab "{4B6AC1D8701C35A1}Prefabs/ERR/FieldCover.et" Offset -4 0 0 Angles 90 0 0 }
|
||||
ERR_SiteProp { Prefab "{4B6AC1D8701C35A1}Prefabs/ERR/FieldCover.et" Offset 4 0 0 Angles 90 0 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
MetaFileClass {
|
||||
Name "{BC310DC0CD27F523}Configs/ERR/EveronSites.conf"
|
||||
Configurations {
|
||||
ResourceClass PC {
|
||||
}
|
||||
ResourceClass HEADLESS : PC {
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
SCR_EntityCatalogMultiList {
|
||||
m_eEntityCatalogType VEHICLE
|
||||
m_aMultiLists {
|
||||
SCR_EntityCatalogMultiListEntry {
|
||||
m_sIdentifier "Mission transport"
|
||||
m_aEntities {
|
||||
SCR_EntityCatalogEntry {
|
||||
m_sEntityPrefab "{259EE7B78C51B624}Prefabs/Vehicles/Wheeled/UAZ469/UAZ469.et"
|
||||
m_aEntityDataList {
|
||||
SCR_EntityCatalogEditorData { m_eValidEditorModes 33 }
|
||||
SCR_EntityCatalogSpawnerData { m_eSlotTypes 24 }
|
||||
}
|
||||
}
|
||||
SCR_EntityCatalogEntry {
|
||||
m_sEntityPrefab "{DF5CCB7C0FF049F4}Prefabs/Vehicles/Helicopters/Mi8MT/Mi8MT_unarmed_transport.et"
|
||||
m_aEntityDataList {
|
||||
SCR_EntityCatalogEditorData { m_eValidEditorModes 33 }
|
||||
SCR_EntityCatalogSpawnerData { m_eSlotTypes VEHICLE_LARGE }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
MetaFileClass {
|
||||
Name "{E4785D3AF1D043C2}Configs/ERR/Factions/MissionVehicles.conf"
|
||||
Configurations {
|
||||
ResourceClass PC {}
|
||||
ResourceClass HEADLESS : PC {}
|
||||
}
|
||||
}
|
||||
+277
@@ -0,0 +1,277 @@
|
||||
SCR_Faction : "{E7B7EA4823DD81F0}Configs/Factions/BLUFOR.conf" {
|
||||
FactionKey "US"
|
||||
UIInfo SCR_FactionUIInfo "{528C961236B7DCD3}" {
|
||||
Name "#AR-Faction_US"
|
||||
Icon "{EB7F65DBC9392557}UI/Textures/Editor/EditableEntities/Factions/EditableEntity_Faction_USA.edds"
|
||||
m_sNameUpper "#AR-Faction_US-UC"
|
||||
}
|
||||
"Identity of soldiers" FactionIdentity "{570D128F4022EF5A}" : "{B600946E9ACC17C2}Configs/Identities/FactionIdentity_US.conf" {
|
||||
}
|
||||
"Radio encryption key" "chickenNuggets"
|
||||
m_sFactionFlag "{46FB2FB2C2125A3C}UI/Textures/Flags/US-flag-base-3it.edds"
|
||||
m_FactionFlagMaterial "{610803CC3F3D6649}Assets/Props/Fabric/Flags/Data/Flag_1_2_USA.emat"
|
||||
m_FactionLabel FACTION_US
|
||||
m_CallsignInfo SCR_FactionCallsignInfo "{5DA0F2A6677ADA9E}" : "{CB7824D572DD0D5C}Configs/Callsigns/Callsigns_US.conf" {
|
||||
}
|
||||
m_iFactionRadioFrequency 48000
|
||||
m_aEntityCatalogs {
|
||||
SCR_EntityCatalogMultiList "{5C68A0EE815FA292}" : "{B56E8B4D4B195559}Configs/EntityCatalog/US/Characters_EntityCatalog_US.conf" {
|
||||
}
|
||||
SCR_EntityCatalogMultiList "{5C9E82F29B327997}" : "{E4785D3AF1D043C2}Configs/ERR/Factions/MissionVehicles.conf" {
|
||||
}
|
||||
SCR_EntityCatalogMultiList "{5C68A0EE8B97CA50}" : "{4E70E7C4C2C17D4A}Configs/EntityCatalog/US/Groups_EntityCatalog_US.conf" {
|
||||
}
|
||||
SCR_EntityCatalog "{5C68A0EEB7DB3E84}" : "{0E4B62F186F97C43}Configs/EntityCatalog/US/WeaponTripod_EntityCatalog_US.conf" {
|
||||
}
|
||||
SCR_EntityCatalogMultiList "{5C68A0EEB3F124F9}" : "{5F7EC52FC40A03E2}Configs/EntityCatalog/US/InventoryItems_EntityCatalog_US.conf" {
|
||||
}
|
||||
SCR_EntityCatalog "{5C9EDDD37394AD40}" : "{0BF36EFF9FFAF92B}Configs/EntityCatalog/US/SupplyContainerItems_EntityCatalog_US.conf" {
|
||||
}
|
||||
}
|
||||
m_aFlagNames +{
|
||||
}
|
||||
m_aBaseCallsigns {
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F493E764B}" {
|
||||
m_sCallsign "#AR-Campaign_PointAlabama"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Alabama_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Alabama_US_UC"
|
||||
m_iSignalIndex 0
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F49F79967}" {
|
||||
m_sCallsign "#AR-Campaign_PointAlaska"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Alaska_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Alaska_US_UC"
|
||||
m_iSignalIndex 1
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F482DA59B}" {
|
||||
m_sCallsign "#AR-Campaign_PointArizona"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Arizona_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Arizona_US_UC"
|
||||
m_iSignalIndex 2
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F483330D2}" {
|
||||
m_sCallsign "#AR-Campaign_PointArkansas"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Arkansas_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Arkansas_US_UC"
|
||||
m_iSignalIndex 3
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F48170DA8}" {
|
||||
m_sCallsign "#AR-Campaign_PointCalifornia"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_California_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_California_US_UC"
|
||||
m_iSignalIndex 4
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F487834E8}" {
|
||||
m_sCallsign "#AR-Campaign_PointColorado"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Colorado_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Colorado_US_UC"
|
||||
m_iSignalIndex 5
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F48410A86}" {
|
||||
m_sCallsign "#AR-Campaign_PointConnecticut"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Connecticut_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Connecticut_US_UC"
|
||||
m_iSignalIndex 6
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F48A4E31F}" {
|
||||
m_sCallsign "#AR-Campaign_PointDelaware"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Delaware_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Delaware_US_UC"
|
||||
m_iSignalIndex 7
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F488C6B57}" {
|
||||
m_sCallsign "#AR-Campaign_PointFlorida"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Florida_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Florida_US_UC"
|
||||
m_iSignalIndex 8
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F4896F764}" {
|
||||
m_sCallsign "#AR-Campaign_PointGeorgia"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Georgia_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Georgia_US_UC"
|
||||
m_iSignalIndex 9
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F48FBD90E}" {
|
||||
m_sCallsign "#AR-Campaign_PointHawaii"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Hawaii_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Hawaii_US_UC"
|
||||
m_iSignalIndex 10
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F48DFF562}" {
|
||||
m_sCallsign "#AR-Campaign_PointIdaho"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Idaho_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Idaho_US_UC"
|
||||
m_iSignalIndex 11
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F7724C851}" {
|
||||
m_sCallsign "#AR-Campaign_PointIllinois"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Illinois_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Illinois_US_UC"
|
||||
m_iSignalIndex 12
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F77097F3F}" {
|
||||
m_sCallsign "#AR-Campaign_PointIndiana"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Indiana_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Indiana_US_UC"
|
||||
m_iSignalIndex 13
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F77102B55}" {
|
||||
m_sCallsign "#AR-Campaign_PointIowa"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Iowa_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Iowa_US_UC"
|
||||
m_iSignalIndex 14
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F777ABD04}" {
|
||||
m_sCallsign "#AR-Campaign_PointKansas"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Kansas_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Kansas_US_UC"
|
||||
m_iSignalIndex 15
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F775EBBC1}" {
|
||||
m_sCallsign "#AR-Campaign_PointKentucky"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Kentucky_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Kentucky_US_UC"
|
||||
m_iSignalIndex 16
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F77A28EB6}" {
|
||||
m_sCallsign "#AR-Campaign_PointLouisiana"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Louisiana_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Louisiana_US_UC"
|
||||
m_iSignalIndex 17
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F77871C23}" {
|
||||
m_sCallsign "#AR-Campaign_PointMaine"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Maine_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Maine_US_UC"
|
||||
m_iSignalIndex 18
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F77EB96A0}" {
|
||||
m_sCallsign "#AR-Campaign_PointMaryland"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Maryland_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Maryland_US_UC"
|
||||
m_iSignalIndex 19
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F77CC2518}" {
|
||||
m_sCallsign "#AR-Campaign_PointMassachusetts"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Massachusetts_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Massachusetts_US_UC"
|
||||
m_iSignalIndex 20
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F77D3715B}" {
|
||||
m_sCallsign "#AR-Campaign_PointMichigan"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Michigan_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Michigan_US_UC"
|
||||
m_iSignalIndex 21
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F7635D418}" {
|
||||
m_sCallsign "#AR-Campaign_PointMinnesota"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Minnesota_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Minnesota_US_UC"
|
||||
m_iSignalIndex 22
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F761C3FF2}" {
|
||||
m_sCallsign "#AR-Campaign_PointMississippi"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Mississippi_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Mississippi_US_UC"
|
||||
m_iSignalIndex 23
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F7660CFAA}" {
|
||||
m_sCallsign "#AR-Campaign_PointMissouri"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Missouri_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Missouri_US_UC"
|
||||
m_iSignalIndex 24
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F764A6E6B}" {
|
||||
m_sCallsign "#AR-Campaign_PointMontana"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Montana_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Montana_US_UC"
|
||||
m_iSignalIndex 25
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F76AD4D35}" {
|
||||
m_sCallsign "#AR-Campaign_PointNebraska"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Nebraska_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Nebraska_US_UC"
|
||||
m_iSignalIndex 26
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F76B1EF3A}" {
|
||||
m_sCallsign "#AR-Campaign_PointNevada"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Nevada_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Nevada_US_UC"
|
||||
m_iSignalIndex 27
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F7695FF2F}" {
|
||||
m_sCallsign "#AR-Campaign_PointNewYork"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_NewYork_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_NewYork_US_UC"
|
||||
m_iSignalIndex 28
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F76FF94B1}" {
|
||||
m_sCallsign "#AR-Campaign_PointOhio"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Ohio_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Ohio_US_UC"
|
||||
m_iSignalIndex 29
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F76C68EED}" {
|
||||
m_sCallsign "#AR-Campaign_PointOklahoma"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Oklahoma_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Oklahoma_US_UC"
|
||||
m_iSignalIndex 30
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F752A9F29}" {
|
||||
m_sCallsign "#AR-Campaign_PointOregon"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Oregon_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Oregon_US_UC"
|
||||
m_iSignalIndex 31
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F750E6FB9}" {
|
||||
m_sCallsign "#AR-Campaign_PointPennsylvania"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Pennsylvania_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Pennsylvania_US_UC"
|
||||
m_iSignalIndex 32
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F7517240F}" {
|
||||
m_sCallsign "#AR-Campaign_PointTexas"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Texas_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Texas_US_UC"
|
||||
m_iSignalIndex 33
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F7574D867}" {
|
||||
m_sCallsign "#AR-Campaign_PointUtah"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Utah_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Utah_US_UC"
|
||||
m_iSignalIndex 34
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F755C4871}" {
|
||||
m_sCallsign "#AR-Campaign_PointVermont"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Vermont_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Vermont_US_UC"
|
||||
m_iSignalIndex 35
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F75A0502B}" {
|
||||
m_sCallsign "#AR-Campaign_PointVirginia"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Virginia_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Virginia_US_UC"
|
||||
m_iSignalIndex 36
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F758ABFD3}" {
|
||||
m_sCallsign "#AR-Campaign_PointWashington"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Washington_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Washington_US_UC"
|
||||
m_iSignalIndex 37
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F75EF8263}" {
|
||||
m_sCallsign "#AR-Campaign_PointWisconsin"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Wisconsin_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Wisconsin_US_UC"
|
||||
m_iSignalIndex 38
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C90D92F75F0F224}" {
|
||||
m_sCallsign "#AR-Campaign_PointWyoming"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Wyoming_US"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Wyoming_US_UC"
|
||||
m_iSignalIndex 39
|
||||
}
|
||||
}
|
||||
m_FactionHomeTerritoryConfig SCR_FactionHomeTerritoryConfig "{5AEE94D54FB58C33}" : "{B548A5B264939A98}Configs/Identities/Territories/FactionHomeTerritory_US.conf" {
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
MetaFileClass {
|
||||
Name "{5C67A1DDE0044806}Configs/ERR/Factions/US.conf"
|
||||
Configurations {
|
||||
ResourceClass PC {}
|
||||
ResourceClass HEADLESS : PC {}
|
||||
}
|
||||
}
|
||||
+276
@@ -0,0 +1,276 @@
|
||||
SCR_Faction : "{B122E3B10BB3404B}Configs/Factions/OPFOR.conf" {
|
||||
FactionKey "USSR"
|
||||
UIInfo SCR_FactionUIInfo "{528C96127F8B6B08}" {
|
||||
Name "#AR-Faction_USSR"
|
||||
Icon "{40B12B0DF911B856}UI/Textures/Editor/EditableEntities/Factions/EditableEntity_Faction_USSR.edds"
|
||||
m_sNameUpper "#AR-Faction_USSR-UC"
|
||||
}
|
||||
"Identity of soldiers" FactionIdentity "{570CFDC6685B6B25}" : "{FBBCD73926989580}Configs/Identities/FactionIdentity_USSR.conf" {
|
||||
}
|
||||
"Radio encryption key" "coldBorscht"
|
||||
m_sFactionFlag "{50824EB9F2C215AB}UI/Textures/Flags/USSR-flag-base-3it.edds"
|
||||
m_FactionFlagMaterial "{B47B52D9DA68905D}Assets/Props/Fabric/Flags/Data/Flag_1_2_SovietArmy.emat"
|
||||
m_FactionLabel FACTION_USSR
|
||||
m_CallsignInfo SCR_FactionCallsignInfo "{5DA0F2A67DFB8809}" : "{958043CE1648EC43}Configs/Callsigns/Callsigns_USSR.conf" {
|
||||
}
|
||||
m_iFactionRadioFrequency 42000
|
||||
m_aEntityCatalogs {
|
||||
SCR_EntityCatalogMultiList "{5C68A0E9BD5E5DD9}" : "{3B679F1799D55F6F}Configs/EntityCatalog/USSR/Characters_EntityCatalog_USSR.conf" {
|
||||
}
|
||||
SCR_EntityCatalogMultiList "{5C9E82F285C09E24}" : "{E4785D3AF1D043C2}Configs/ERR/Factions/MissionVehicles.conf" {
|
||||
}
|
||||
SCR_EntityCatalogMultiList "{5C68A0E9B9AA3C2D}" : "{37D5B75AAC3E72EA}Configs/EntityCatalog/USSR/Groups_EntityCatalog_USSR.conf" {
|
||||
}
|
||||
SCR_EntityCatalog "{5C68A0E9A51460AB}" : "{DDF17C8C140BD1F2}Configs/EntityCatalog/USSR/WeaponTripod_EntityCatalog_USSR.conf" {
|
||||
}
|
||||
SCR_EntityCatalogMultiList "{5C68A0E9AFE1E631}" : "{C53421647C3D0D2E}Configs/EntityCatalog/USSR/InventoryItems_EntityCatalog_USSR.conf" {
|
||||
}
|
||||
SCR_EntityCatalog "{5C9EDDD378BA3298}" : "{B4CA6761AC935808}Configs/EntityCatalog/USSR/SupplyContainerItems_EntityCatalog_USSR.conf" {
|
||||
}
|
||||
}
|
||||
m_iIdentityVoiceSignal 100
|
||||
m_aBaseCallsigns {
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95F3FB520}" {
|
||||
m_sCallsign "#AR-Campaign_PointArena"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Arena_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Arena_RU_UC"
|
||||
m_iSignalIndex 0
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95F0034DB}" {
|
||||
m_sCallsign "#AR-Campaign_PointAvrora"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Avrora_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Avrora_RU_UC"
|
||||
m_iSignalIndex 1
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95F6514E7}" {
|
||||
m_sCallsign "#AR-Campaign_PointBekas"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Bekas_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Bekas_RU_UC"
|
||||
m_iSignalIndex 2
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95F4EE8BF}" {
|
||||
m_sCallsign "#AR-Campaign_PointBizon"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Bizon_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Bizon_RU_UC"
|
||||
m_iSignalIndex 3
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95F5021F9}" {
|
||||
m_sCallsign "#AR-Campaign_PointBolero"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Bolero_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Bolero_RU_UC"
|
||||
m_iSignalIndex 4
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95FBA4C89}" {
|
||||
m_sCallsign "#AR-Campaign_PointCafftan"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Cafftan_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Cafftan_RU_UC"
|
||||
m_iSignalIndex 5
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95F9C00A9}" {
|
||||
m_sCallsign "#AR-Campaign_PointKaban"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Kaban_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Kaban_RU_UC"
|
||||
m_iSignalIndex 6
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95FE17E10}" {
|
||||
m_sCallsign "#AR-Campaign_PointConsul"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Consul_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Consul_RU_UC"
|
||||
m_iSignalIndex 7
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95FCBA475}" {
|
||||
m_sCallsign "#AR-Campaign_PointDimok"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Dimok_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Dimok_RU_UC"
|
||||
m_iSignalIndex 8
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95E2B2963}" {
|
||||
m_sCallsign "#AR-Campaign_PointEffir"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Effir_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Effir_RU_UC"
|
||||
m_iSignalIndex 9
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95E3040F5}" {
|
||||
m_sCallsign "#AR-Campaign_PointFakir"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Fakir_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Fakir_RU_UC"
|
||||
m_iSignalIndex 10
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95E17C67D}" {
|
||||
m_sCallsign "#AR-Campaign_PointGips"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Gips_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Gips_RU_UC"
|
||||
m_iSignalIndex 11
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95E797F8A}" {
|
||||
m_sCallsign "#AR-Campaign_PointGussar"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Gussar_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Gussar_RU_UC"
|
||||
m_iSignalIndex 12
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95E431F83}" {
|
||||
m_sCallsign "#AR-Campaign_PointIgla"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Igla_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Igla_RU_UC"
|
||||
m_iSignalIndex 13
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95EA7F87E}" {
|
||||
m_sCallsign "#AR-Campaign_PointKhek"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Khek_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Khek_RU_UC"
|
||||
m_iSignalIndex 14
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95E88C6B9}" {
|
||||
m_sCallsign "#AR-Campaign_PointLednik"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Lednik_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Lednik_RU_UC"
|
||||
m_iSignalIndex 15
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95EEDE322}" {
|
||||
m_sCallsign "#AR-Campaign_PointMatros"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Matros_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Matros_RU_UC"
|
||||
m_iSignalIndex 16
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95EF6244A}" {
|
||||
m_sCallsign "#AR-Campaign_PointMuskat"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Muskat_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Muskat_RU_UC"
|
||||
m_iSignalIndex 17
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95EDAF04A}" {
|
||||
m_sCallsign "#AR-Campaign_PointNagan"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Nagan_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Nagan_RU_UC"
|
||||
m_iSignalIndex 18
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95D3CCF27}" {
|
||||
m_sCallsign "#AR-Campaign_PointNitka"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Nitka_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Nitka_RU_UC"
|
||||
m_iSignalIndex 19
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95D076483}" {
|
||||
m_sCallsign "#AR-Campaign_PointOmega"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Omega_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Omega_RU_UC"
|
||||
m_iSignalIndex 20
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95D68D0F3}" {
|
||||
m_sCallsign "#AR-Campaign_PointPlazma"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Plazma_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Plazma_RU_UC"
|
||||
m_iSignalIndex 21
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95D4C0F8E}" {
|
||||
m_sCallsign "#AR-Campaign_PointPolyn"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Polyn_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Polyn_RU_UC"
|
||||
m_iSignalIndex 22
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95D56EEEE}" {
|
||||
m_sCallsign "#AR-Campaign_PointProlog"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Prolog_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Prolog_RU_UC"
|
||||
m_iSignalIndex 23
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95DBA92EE}" {
|
||||
m_sCallsign "#AR-Campaign_PointRabkor"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Rabkor_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Rabkor_RU_UC"
|
||||
m_iSignalIndex 24
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95C69680D}" {
|
||||
m_sCallsign "#AR-Campaign_PointRadon"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Radon_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Radon_RU_UC"
|
||||
m_iSignalIndex 25
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95C4FD930}" {
|
||||
m_sCallsign "#AR-Campaign_PointSadko"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Sadko_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Sadko_RU_UC"
|
||||
m_iSignalIndex 26
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95C5255FD}" {
|
||||
m_sCallsign "#AR-Campaign_PointSekret"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Sekret_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Sekret_RU_UC"
|
||||
m_iSignalIndex 27
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95CB621AE}" {
|
||||
m_sCallsign "#AR-Campaign_PointShtanga"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Shtanga_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Shtanga_RU_UC"
|
||||
m_iSignalIndex 28
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95C9545BC}" {
|
||||
m_sCallsign "#AR-Campaign_PointShuka"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Shuka_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Shuka_RU_UC"
|
||||
m_iSignalIndex 29
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95CFE26C3}" {
|
||||
m_sCallsign "#AR-Campaign_PointTabor"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Tabor_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Tabor_RU_UC"
|
||||
m_iSignalIndex 30
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95CDD263C}" {
|
||||
m_sCallsign "#AR-Campaign_PointTenor"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Tenor_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Tenor_RU_UC"
|
||||
m_iSignalIndex 31
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95B27128C}" {
|
||||
m_sCallsign "#AR-Campaign_PointTsikson"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Tsikson_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Tsikson_RU_UC"
|
||||
m_iSignalIndex 32
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95B08D83D}" {
|
||||
m_sCallsign "#AR-Campaign_PointUlan"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Ulan_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Ulan_RU_UC"
|
||||
m_iSignalIndex 33
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95B6D3FC2}" {
|
||||
m_sCallsign "#AR-Campaign_PointVermout"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Vermout_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Vermout_RU_UC"
|
||||
m_iSignalIndex 34
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95B712949}" {
|
||||
m_sCallsign "#AR-Campaign_PointViympel"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Viympel_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Viympel_RU_UC"
|
||||
m_iSignalIndex 35
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95B57D185}" {
|
||||
m_sCallsign "#AR-Campaign_PointYantar"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Yantar_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Yantar_RU_UC"
|
||||
m_iSignalIndex 36
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95BB17114}" {
|
||||
m_sCallsign "#AR-Campaign_PointYolka"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Yolka_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Yolka_RU_UC"
|
||||
m_iSignalIndex 37
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95B97C054}" {
|
||||
m_sCallsign "#AR-Campaign_PointZheton"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Zheton_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Zheton_RU_UC"
|
||||
m_iSignalIndex 38
|
||||
}
|
||||
SCR_MilitaryBaseCallsign "{5C9108F95BFC33E5}" {
|
||||
m_sCallsign "#AR-Campaign_PointZurna"
|
||||
m_sCallsignShort "#AR-Comm_Variable_Base_Zurna_RU"
|
||||
m_sCallsignUpperCase "#AR-Comm_Variable_Base_Zurna_RU_UC"
|
||||
m_iSignalIndex 39
|
||||
}
|
||||
}
|
||||
m_FactionHomeTerritoryConfig SCR_FactionHomeTerritoryConfig "{5AEE94D5517C30BD}" : "{31FC78AF00523A99}Configs/Identities/Territories/FactionHomeTerritory_USSR.conf" {
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
MetaFileClass {
|
||||
Name "{D7A5CFA62A36480D}Configs/ERR/Factions/USSR.conf"
|
||||
Configurations {
|
||||
ResourceClass PC {}
|
||||
ResourceClass HEADLESS : PC {}
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
SCR_GameOverScreenConfig : "{651583FFFB7FC765}Configs/GameOverScreen/BaseGameOverScreensConfig.conf" {
|
||||
m_aGameOverScreens {
|
||||
ERR_EscapeGameOverScreenInfo "{6B193DC8DDC63507}" {
|
||||
m_GameOverScreenId VICTORY
|
||||
m_GameOverContentLayout "{C4507D07F3C54831}UI/layouts/HUD/GameOver/EndScreen/EndScreenContent_NoImage.layout"
|
||||
m_OptionalParams SCR_BaseGameOverScreenInfoOptional "{4984A5C1BFEFC04E}" {
|
||||
m_sTitle "Escape complete"
|
||||
m_sAudioOneShot "SOUND_ONVICTORY"
|
||||
}
|
||||
}
|
||||
ERR_FailureGameOverScreenInfo "{E66F29A7A8AD7438}" {
|
||||
m_GameOverScreenId LOOSE
|
||||
m_GameOverContentLayout "{C4507D07F3C54831}UI/layouts/HUD/GameOver/EndScreen/EndScreenContent_NoImage.layout"
|
||||
m_OptionalParams SCR_BaseGameOverScreenInfoOptional "{3DBF0BDE715945FA}" {
|
||||
m_sTitle "Escape failed"
|
||||
m_sDebriefing "No surviving prisoners can complete the escape, or the extraction helicopter has been lost."
|
||||
m_sAudioOneShot "SOUND_ONDEFEAT"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
MetaFileClass {
|
||||
Name "{F6633922A25F9441}Configs/ERR/GameOverScreens.conf"
|
||||
Configurations {
|
||||
ResourceClass PC {
|
||||
}
|
||||
ResourceClass HEADLESS : PC {
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
ERR_FactionCatalog {
|
||||
PlayerFactionKey "US"
|
||||
EnemyFactionKey "USSR"
|
||||
PrisonerPrefab "{3FF693715C9F8928}Prefabs/ERR/Prisoner.et"
|
||||
GuardGroupPrefab "{D5A4D0D3AE1A3E26}Prefabs/ERR/Patrol.et"
|
||||
ExtractionPrefab "{DF5CCB7C0FF049F4}Prefabs/Vehicles/Helicopters/Mi8MT/Mi8MT_unarmed_transport.et"
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
MetaFileClass {
|
||||
Name "{BE50B97B8E2B36E6}Configs/ERR/VanillaFactions.conf"
|
||||
Configurations {
|
||||
ResourceClass PC {
|
||||
}
|
||||
ResourceClass HEADLESS : PC {
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +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_sGameMode "Escape"
|
||||
m_iPlayerCount 12
|
||||
m_eSaveTypes 0
|
||||
m_bShowInScenarioMenu 1
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
MetaFileClass {
|
||||
Name "{AD62322D5213C440}Missions/ERR_Everon_Base.conf"
|
||||
Configurations {
|
||||
ResourceClass PC {
|
||||
}
|
||||
ResourceClass HEADLESS : PC {
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
GenericEntity {
|
||||
components {
|
||||
ERR_LocationComponent "{C17CA14398A1CD7E}" { m_Kind 2 }
|
||||
RplComponent "{0BF5BAAA5FBFEECE}" { Streamable Disabled }
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
MetaFileClass {
|
||||
Name "{033503110AC34EAC}Prefabs/ERR/Extraction.et"
|
||||
Configurations {
|
||||
EntityTemplateResourceClass PC {
|
||||
}
|
||||
EntityTemplateResourceClass HEADLESS : PC {
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
SCR_FactionManager : "{A242612865F2A48E}Prefabs/MP/Managers/Factions/FactionManager_Base.et" {
|
||||
Factions {
|
||||
SCR_Faction : "{5C67A1DDE0044806}Configs/ERR/Factions/US.conf" {
|
||||
m_sFactionBackground "{F8AC305446D5D480}UI/Textures/DeployMenu/Loadout-BG/Loadout_US_BG.imageset"
|
||||
}
|
||||
SCR_Faction : "{D7A5CFA62A36480D}Configs/ERR/Factions/USSR.conf" {
|
||||
m_sFactionBackground "{21533F2089005056}UI/Textures/DeployMenu/Loadout-BG/Loadout_BG_USSR.imageset"
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
MetaFileClass {
|
||||
Name "{5414041A15DC4B54}Prefabs/ERR/FactionManager.et"
|
||||
Configurations {
|
||||
EntityTemplateResourceClass PC {}
|
||||
EntityTemplateResourceClass HEADLESS : PC {}
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
GenericEntity {
|
||||
components {
|
||||
MeshObject "{A557F6DC012E5101}" {
|
||||
Object "{268DBDABB3C94AD7}Assets/Props/Military/Sandbags/Sandbag_01_short.xob"
|
||||
Materials {
|
||||
MaterialAssignClass "{A557F6DC012E5102}" { SourceMaterial "Sandbags_01_Short" AssignedMaterial "{31529E064AA7C917}Assets/Props/Military/Sandbags/Data/Sandbags_01_short_Burlap.emat" }
|
||||
MaterialAssignClass "{A557F6DC012E5103}" { SourceMaterial "Sandbags_01_MLOD" AssignedMaterial "{2169E8EB73E1C127}Assets/Props/Military/Sandbags/Data/Sandbags_01_Burlap_MLOD.emat" }
|
||||
}
|
||||
}
|
||||
RigidBody "{A557F6DC012E5104}" { ModelGeometry 1 Static 1 }
|
||||
RplComponent "{A557F6DC012E5105}" {}
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
MetaFileClass {
|
||||
Name "{4B6AC1D8701C35A1}Prefabs/ERR/FieldCover.et"
|
||||
Configurations {
|
||||
ResourceClass PC {}
|
||||
ResourceClass HEADLESS : PC {}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
ERR_GameMode : "{0F307326459A1395}Prefabs/MP/Modes/GameMode_Base.et" {
|
||||
components {
|
||||
SCR_GameOverScreenManagerComponent "{56DEC646298AD219}" {
|
||||
m_GameOverScreenConfig SCR_GameOverScreenConfig "{56DEC6462FFDD82D}" : "{F6633922A25F9441}Configs/ERR/GameOverScreens.conf" {}
|
||||
}
|
||||
ERR_ScenarioComponent "{B78E910E88DE30D2}" {
|
||||
m_Definition "{BC310DC0CD27F523}Configs/ERR/EveronSites.conf"
|
||||
}
|
||||
SCR_RespawnSystemComponent "{56B2B4793051E7C9}" {
|
||||
m_SpawnLogic ERR_SpawnLogic "{79AD722B2EFD595F}" {}
|
||||
}
|
||||
}
|
||||
m_bUseSpawnPreload 0
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
MetaFileClass {
|
||||
Name "{978E045E53EF36C8}Prefabs/ERR/GameMode.et"
|
||||
Configurations {
|
||||
EntityTemplateResourceClass PC {
|
||||
}
|
||||
EntityTemplateResourceClass HEADLESS : PC {
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
GenericEntity : "{6D56FED1E55A8F84}Prefabs/Items/Misc/IntelligenceFolder_E_01/IntelligenceFolder_E_01.et" {
|
||||
components {
|
||||
ERR_DocumentComponent "{E4598D0BE2C251A3}" {}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
MetaFileClass {
|
||||
Name "{554159A6740C3F3E}Prefabs/ERR/ObjectiveDocument.et"
|
||||
Configurations {
|
||||
ResourceClass PC {
|
||||
}
|
||||
ResourceClass HEADLESS : PC {
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
SCR_AIGroup : "{E552DABF3636C2AD}Prefabs/Groups/OPFOR/Group_USSR_RifleSquad.et" {
|
||||
m_aUnitPrefabSlots {
|
||||
"{5436629450D8387A}Prefabs/Characters/Factions/OPFOR/USSR_Army/Character_USSR_SL.et"
|
||||
"{612F43A4D5AE765F}Prefabs/Characters/Factions/OPFOR/USSR_Army/Character_USSR_RTO.et"
|
||||
"{23ADBBC31B6A3DC6}Prefabs/Characters/Factions/OPFOR/USSR_Army/Character_USSR_AR.et"
|
||||
"{1C78331E156A3D65}Prefabs/Characters/Factions/OPFOR/USSR_Army/Character_USSR_AT.et"
|
||||
"{333DA6244C7DA34C}Prefabs/Characters/Factions/OPFOR/USSR_Army/Character_USSR_SR.et"
|
||||
"{BF643BE4ADBDFDD3}Prefabs/Characters/Factions/OPFOR/USSR_Army/Character_USSR_LAT.et"
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
MetaFileClass {
|
||||
Name "{D5A4D0D3AE1A3E26}Prefabs/ERR/Patrol.et"
|
||||
Configurations {
|
||||
EntityTemplateResourceClass PC {}
|
||||
EntityTemplateResourceClass HEADLESS {}
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
GenericEntity {
|
||||
components {
|
||||
ERR_LocationComponent "{3B60CBEB3C04333C}" { m_Kind 0 }
|
||||
Hierarchy "{30ADADE77D1EC3B8}" {}
|
||||
RplComponent "{7FA5168C6FBD0C22}" {}
|
||||
}
|
||||
{
|
||||
GenericEntity : "{A67420C1FB91856E}Prefabs/ERR/PrisonWall.et" {
|
||||
ID "2879356FFFBE36E0"
|
||||
coords -6 0 6
|
||||
angles 0 0 0
|
||||
}
|
||||
GenericEntity : "{A67420C1FB91856E}Prefabs/ERR/PrisonWall.et" {
|
||||
ID "C99EF9AFCEBA863D"
|
||||
coords -3 0 6
|
||||
angles 0 0 0
|
||||
}
|
||||
GenericEntity : "{A67420C1FB91856E}Prefabs/ERR/PrisonWall.et" {
|
||||
ID "91048277F04ACA61"
|
||||
coords 0 0 6
|
||||
angles 0 0 0
|
||||
}
|
||||
GenericEntity : "{A67420C1FB91856E}Prefabs/ERR/PrisonWall.et" {
|
||||
ID "8F2200A922FB0A6E"
|
||||
coords 3 0 6
|
||||
angles 0 0 0
|
||||
}
|
||||
GenericEntity : "{A67420C1FB91856E}Prefabs/ERR/PrisonWall.et" {
|
||||
ID "3C442429BBCF5D2F"
|
||||
coords -6 0 -6
|
||||
angles 0 -90 0
|
||||
}
|
||||
GenericEntity : "{A67420C1FB91856E}Prefabs/ERR/PrisonWall.et" {
|
||||
ID "9E855CC802C592CA"
|
||||
coords 6 0 -6
|
||||
angles 0 -90 0
|
||||
}
|
||||
GenericEntity : "{A67420C1FB91856E}Prefabs/ERR/PrisonWall.et" {
|
||||
ID "778DAC42C9208F67"
|
||||
coords -6 0 -3
|
||||
angles 0 -90 0
|
||||
}
|
||||
GenericEntity : "{A67420C1FB91856E}Prefabs/ERR/PrisonWall.et" {
|
||||
ID "7E350EB9053673C4"
|
||||
coords 6 0 -3
|
||||
angles 0 -90 0
|
||||
}
|
||||
GenericEntity : "{A67420C1FB91856E}Prefabs/ERR/PrisonWall.et" {
|
||||
ID "AD17884D5EA37D37"
|
||||
coords -6 0 0
|
||||
angles 0 -90 0
|
||||
}
|
||||
GenericEntity : "{A67420C1FB91856E}Prefabs/ERR/PrisonWall.et" {
|
||||
ID "1A157E23E1A995D8"
|
||||
coords 6 0 0
|
||||
angles 0 -90 0
|
||||
}
|
||||
GenericEntity : "{A67420C1FB91856E}Prefabs/ERR/PrisonWall.et" {
|
||||
ID "3A78A6F3ACB7ACB1"
|
||||
coords -6 0 3
|
||||
angles 0 -90 0
|
||||
}
|
||||
GenericEntity : "{A67420C1FB91856E}Prefabs/ERR/PrisonWall.et" {
|
||||
ID "275B472B565FFF6C"
|
||||
coords 6 0 3
|
||||
angles 0 -90 0
|
||||
}
|
||||
GenericEntity : "{A67420C1FB91856E}Prefabs/ERR/PrisonWall.et" {
|
||||
ID "1B20E11B3FC7B59A"
|
||||
coords -6 0 -6
|
||||
angles 0 0 0
|
||||
}
|
||||
GenericEntity : "{A67420C1FB91856E}Prefabs/ERR/PrisonWall.et" {
|
||||
ID "2735B52246DD6213"
|
||||
coords -3 0 -6
|
||||
angles 0 0 0
|
||||
}
|
||||
GenericEntity : "{A67420C1FB91856E}Prefabs/ERR/PrisonWall.et" {
|
||||
ID "28101A0E33470B38"
|
||||
coords 3 0 -6
|
||||
angles 0 0 0
|
||||
}
|
||||
GenericEntity : "{C3FDC4DBCD58C2F5}Prefabs/ERR/PrisonDoor.et" {
|
||||
ID "54D8F96ECC743335"
|
||||
scale 1.5
|
||||
coords 0 -0.1 -6
|
||||
angles 0 0 0
|
||||
}
|
||||
DecalEntity : "{4AD6EB80A8B1FA3A}Prefabs/Decals/Dirt/Decal_Dirt_Patch_03.et" {
|
||||
ID "FCFBA935CC13E016"
|
||||
components {
|
||||
Hierarchy "{EF4B704D646D1759}" {}
|
||||
}
|
||||
coords 0 0 0
|
||||
angles -90 0 0
|
||||
scale 10
|
||||
FarPlane 1.5
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
MetaFileClass {
|
||||
Name "{BE6B8D56349038A6}Prefabs/ERR/Prison.et"
|
||||
Configurations {
|
||||
EntityTemplateResourceClass PC {
|
||||
}
|
||||
EntityTemplateResourceClass HEADLESS : PC {
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
GenericEntity : "{34B08027DBDE322A}Prefabs/Structures/BuildingParts/Doors/Door_MetalGate_01_Short_LEFT_EXT_ST_B.et" {
|
||||
components {
|
||||
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" }
|
||||
Duration 4
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
MetaFileClass {
|
||||
Name "{C3FDC4DBCD58C2F5}Prefabs/ERR/PrisonDoor.et"
|
||||
Configurations {
|
||||
EntityTemplateResourceClass PC {
|
||||
}
|
||||
EntityTemplateResourceClass HEADLESS : PC {
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
GenericEntity {
|
||||
components {
|
||||
Hierarchy "{4F20A90EDBD625A4}" {}
|
||||
MeshObject "{4F20A90EDBD625A1}" { Object "{0B96D139FEBCA006}Assets/Structures/Walls/Concrete/ConcreteWall_01_3m.xob" }
|
||||
RigidBody "{4F20A90EDBD625A2}" { ModelGeometry 1 Static 1 }
|
||||
RplComponent "{4F20A90EDBD625A3}" {}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
MetaFileClass {
|
||||
Name "{A67420C1FB91856E}Prefabs/ERR/PrisonWall.et"
|
||||
Configurations {
|
||||
EntityTemplateResourceClass PC {
|
||||
}
|
||||
EntityTemplateResourceClass HEADLESS : PC {
|
||||
}
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
SCR_ChimeraCharacter : "{836E7E39AAC5888B}Prefabs/Characters/Factions/BLUFOR/US_Army/Character_US_Base.et" {
|
||||
components {
|
||||
ActionsManagerComponent "{520EA1D2F659CE02}" {
|
||||
additionalActions {
|
||||
ERR_LoadCasualtyAction "{E7016B8C499EC8A3}" {
|
||||
ParentContextList { "Chest" "LowerTorso" }
|
||||
UIInfo SCR_ActionUIInfo "{E7016B8C499EC8A4}" { Name "Load casualty into extraction helicopter" }
|
||||
Duration 5
|
||||
}
|
||||
}
|
||||
}
|
||||
SCR_EditableCharacterComponent "{520EA1D2F659C85C}" {
|
||||
m_UIInfo SCR_EditableEntityUIInfo "{5298E609432D192D}" {
|
||||
Name "Prisoner"
|
||||
}
|
||||
}
|
||||
SCR_CharacterInventoryStorageComponent "{520EA1D2DB118DAC}" {
|
||||
components {
|
||||
SCR_EquipmentStorageComponent "{56B49DA9722B635D}" {
|
||||
InitialStorageSlots {
|
||||
SCR_EquipmentStorageSlot BinocularSlot { Prefab "" }
|
||||
SCR_EquipmentStorageSlot WristwatchSlot {
|
||||
Prefab "{78ED4FEF62BBA728}Prefabs/Items/Equipment/Watches/Watch_SandY184A.et"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BaseLoadoutManagerComponent "{520EA1D2DB11821E}" {
|
||||
Slots {
|
||||
LoadoutSlotInfo Hat { Prefab "{F545C1BE7C99954A}Prefabs/Characters/HeadGear/Hat_Patrol_US_01/Hat_Patrol_US_01.et" }
|
||||
LoadoutSlotInfo Jacket { Prefab "{3CCA7A9BB4FD3197}Prefabs/Characters/Uniforms/Jacket_US_BDU_rolledup.et" }
|
||||
LoadoutSlotInfo Pants { Prefab "{604BB72BE8E023C2}Prefabs/Characters/Uniforms/Pants_US_BDU.et" }
|
||||
SCR_FeetLoadoutSlotInfo Boots { Prefab "{DAAFD15478BDE1C3}Prefabs/Characters/Footwear/CombatBoots_US_01.et" }
|
||||
LoadoutSlotInfo ArmoredVest { Prefab "" }
|
||||
LoadoutSlotInfo Back { Prefab "" }
|
||||
LoadoutSlotInfo Vest { Prefab "" }
|
||||
LoadoutSlotInfo HandWear { Prefab "" }
|
||||
}
|
||||
}
|
||||
SCR_InventoryStorageManagerComponent "{520EA1D2DB118DE5}" {
|
||||
InitialInventoryItems {
|
||||
ItemsInitConfigurationItem "{EA7AC3C2078B68C1}" {
|
||||
TargetStorage "Prefabs/Characters/Uniforms/Pants_US_BDU.et"
|
||||
PrefabsToSpawn {
|
||||
"{A81F501D3EF6F38E}Prefabs/Items/Medicine/FieldDressing_01/FieldDressing_US_01.et"
|
||||
"{A81F501D3EF6F38E}Prefabs/Items/Medicine/FieldDressing_01/FieldDressing_US_01.et"
|
||||
"{D70216B1B2889129}Prefabs/Items/Medicine/Tourniquet_01/Tourniquet_US_01.et"
|
||||
"{0D9A5DCF89AE7AA9}Prefabs/Items/Medicine/MorphineInjection_01/MorphineInjection_01.et"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
MetaFileClass {
|
||||
Name "{3FF693715C9F8928}Prefabs/ERR/Prisoner.et"
|
||||
Configurations {
|
||||
EntityTemplateResourceClass PC {}
|
||||
EntityTemplateResourceClass HEADLESS : PC {}
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
GenericEntity {
|
||||
components {
|
||||
MeshObject "{111C06CB740ED195}" { Object "{77C9FCF3FB1246E9}Assets/Items/Equipment/Radios/Radio_R107M/Radio_R107M_01_item.xob" }
|
||||
RigidBody "{4C174A44D07B91BA}" { ModelGeometry 1 Static 1 }
|
||||
ERR_LocationComponent "{C19CA6CC83470879}" { m_Kind 1 }
|
||||
RplComponent "{54ED7463161A5622}" { Streamable Disabled }
|
||||
ActionsManagerComponent "{B081EC0768636EAB}" {
|
||||
ActionContexts { UserActionContext "{C1A75A5D8B7EF1E4}" { ContextName "ERR_Radio" Radius 2 } }
|
||||
additionalActions { ERR_TuneRadioAction "{7ABBB6B5D73DDD95}" {
|
||||
ParentContextList { "ERR_Radio" }
|
||||
UIInfo SCR_ActionUIInfo "{1B42BB7CEDECB994}" { Name "Contact extraction HQ" }
|
||||
Duration 8
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
MetaFileClass {
|
||||
Name "{F7E7E3D6ACF0C860}Prefabs/ERR/Radio.et"
|
||||
Configurations {
|
||||
EntityTemplateResourceClass PC {
|
||||
}
|
||||
EntityTemplateResourceClass HEADLESS : PC {
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
SCR_ManualCamera {
|
||||
m_bInputEnabled 0
|
||||
m_bStartAtEntity 1
|
||||
m_aComponents {
|
||||
ERR_FollowSurvivorCameraComponent "{965C2E8B2339B926}" {}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
MetaFileClass {
|
||||
Name "{EB1210AAE1BB8F54}Prefabs/ERR/SpectatorCamera.et"
|
||||
Configurations {
|
||||
EntityTemplateResourceClass PC {
|
||||
}
|
||||
EntityTemplateResourceClass HEADLESS : PC {
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
GenericEntity : "{EBBC859CCD94D96D}Prefabs/Props/EmptyStorages/USSR_AmmoBox_empty.et" {
|
||||
components {
|
||||
MeshObject "{6530845A09309E37}" {
|
||||
Object "{819ED3266EE5113B}Assets/Props/Military/AmmoBox/EquipmentBoxWooden_01/EquipmentBoxWooden_Weapon_01.xob"
|
||||
Materials {
|
||||
MaterialAssignClass "{6530845A093091BB}" {
|
||||
SourceMaterial "EquipmentBoxWooden_Weapon_01"
|
||||
AssignedMaterial "{7D2E831AFA5CF50D}Assets/Props/Military/AmmoBox/EquipmentBoxWooden_01/Data/EquipmentBoxWooden_Weapon_01_USSR.emat"
|
||||
}
|
||||
MaterialAssignClass "{6530845A093091B2}" {
|
||||
SourceMaterial "EquipmentBoxWooden_Weapon_01_Decal"
|
||||
}
|
||||
}
|
||||
}
|
||||
SCR_UniversalInventoryStorageComponent "{6530845A09302DE0}" {
|
||||
MaxCumulativeVolume 120000
|
||||
MaxItemSize 120 40 40
|
||||
m_fMaxWeight 80
|
||||
Attributes SCR_ItemAttributeCollection "{6530845A09302DFF}" {
|
||||
ItemDisplayName SCR_InventoryUIInfo "{6530845A09302D03}" {
|
||||
Name "Supply cache"
|
||||
Description "Limited equipment shared between prisoners."
|
||||
}
|
||||
}
|
||||
}
|
||||
InventoryStorageManagerComponent "{18A1D42A468F584F}" {}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
MetaFileClass {
|
||||
Name "{40986810D0B1F4AE}Prefabs/ERR/SupplyCache.et"
|
||||
Configurations {
|
||||
ResourceClass PC {
|
||||
}
|
||||
ResourceClass HEADLESS : PC {
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
GenericEntity {
|
||||
components {
|
||||
ERR_LocationComponent "{8E7CFEA92C1811F0}" { m_Kind 4 }
|
||||
RplComponent "{98A4A31435A9CA04}" { Streamable Disabled }
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
MetaFileClass {
|
||||
Name "{7C3D16C17C24D351}Prefabs/ERR/SupplySite.et"
|
||||
Configurations {
|
||||
ResourceClass PC {
|
||||
}
|
||||
ResourceClass HEADLESS : PC {
|
||||
}
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
class ERR_UnlockPrisonAction : ScriptedUserAction
|
||||
{
|
||||
override bool CanBePerformedScript(IEntity user)
|
||||
{
|
||||
ERR_GameMode mode = ERR_GameMode.Get();
|
||||
SetCannotPerformReason(string.Empty);
|
||||
if (!mode) { SetCannotPerformReason("Mission is not ready"); return false; }
|
||||
if (mode.Phase() != ERR_Phase.PRISON) { SetCannotPerformReason("Prison lock is not available in this mission phase"); return false; }
|
||||
if (!ERR_Actors.CanFight(user)) { SetCannotPerformReason("You must be alive and conscious"); return false; }
|
||||
return true;
|
||||
}
|
||||
override void PerformAction(IEntity pOwnerEntity, IEntity pUserEntity)
|
||||
{
|
||||
ERR_GameMode mode = ERR_GameMode.Get();
|
||||
if (!mode || !mode.UnlockPrison(pUserEntity)) return;
|
||||
BaseDoorComponent door = BaseDoorComponent.Cast(pOwnerEntity.FindComponent(BaseDoorComponent));
|
||||
if (door) door.UseDoorAction(pUserEntity);
|
||||
}
|
||||
}
|
||||
|
||||
class ERR_PrisonDoorAction : SCR_DoorUserAction
|
||||
{
|
||||
// Native AI calls PerformAction directly, bypassing the UI availability query.
|
||||
// Recheck the same permission here; preserve vanilla execution/broadcasting
|
||||
// on both server and clients once the replicated prison phase is unlocked.
|
||||
override void PerformAction(IEntity pOwnerEntity, IEntity pUserEntity)
|
||||
{
|
||||
if (!CanBePerformedScript(pUserEntity)) return;
|
||||
super.PerformAction(pOwnerEntity, pUserEntity);
|
||||
}
|
||||
|
||||
override bool CanBePerformedScript(IEntity user)
|
||||
{
|
||||
ERR_GameMode mode = ERR_GameMode.Get();
|
||||
SetCannotPerformReason(string.Empty);
|
||||
if (!mode) { SetCannotPerformReason("Mission is not ready"); return false; }
|
||||
if (mode.Phase() <= ERR_Phase.PRISON)
|
||||
{
|
||||
SetCannotPerformReason("Pick the prison lock before opening the gate");
|
||||
return false;
|
||||
}
|
||||
// Vanilla owns any obstruction/door-specific reason after unlocking.
|
||||
return super.CanBePerformedScript(user);
|
||||
}
|
||||
}
|
||||
|
||||
class ERR_TuneRadioAction : ScriptedUserAction
|
||||
{
|
||||
override bool CanBePerformedScript(IEntity user)
|
||||
{
|
||||
ERR_GameMode mode = ERR_GameMode.Get();
|
||||
ERR_LocationComponent location = ERR_LocationComponent.Cast(GetOwner().FindComponent(ERR_LocationComponent));
|
||||
SetCannotPerformReason(string.Empty);
|
||||
if (!mode) { SetCannotPerformReason("Mission is not ready"); return false; }
|
||||
if (mode.Phase() != ERR_Phase.RADIO_SEARCH)
|
||||
{
|
||||
string reason = "Radio is not available in this mission phase";
|
||||
if (mode.Phase() == ERR_Phase.PRISON) reason = "Escape the prison first";
|
||||
if (mode.Phase() == ERR_Phase.RADIO_DEFENCE) reason = "Transmission is already in progress; defend the radio";
|
||||
if (mode.Phase() == ERR_Phase.EXTRACTION) reason = "Transmission is complete; reach the extraction helicopter";
|
||||
SetCannotPerformReason(reason);
|
||||
return false;
|
||||
}
|
||||
if (!location) { SetCannotPerformReason("Radio location is unavailable"); return false; }
|
||||
if (location.Knowledge() != 2) { SetCannotPerformReason("Identify this radio through recovered intel or close reconnaissance first"); return false; }
|
||||
if (!ERR_Actors.CanFight(user)) { SetCannotPerformReason("You must be alive and conscious"); return false; }
|
||||
return true;
|
||||
}
|
||||
override void PerformAction(IEntity pOwnerEntity, IEntity pUserEntity)
|
||||
{
|
||||
ERR_GameMode mode = ERR_GameMode.Get();
|
||||
if (mode) mode.TuneRadio(pUserEntity, ERR_LocationComponent.Cast(pOwnerEntity.FindComponent(ERR_LocationComponent)));
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
class ERR_Actors
|
||||
{
|
||||
static bool Alive(IEntity actor)
|
||||
{
|
||||
if (!actor) return false;
|
||||
DamageManagerComponent damage = DamageManagerComponent.Cast(actor.FindComponent(DamageManagerComponent));
|
||||
return damage && !damage.IsDestroyed();
|
||||
}
|
||||
|
||||
static bool CanFight(IEntity actor)
|
||||
{
|
||||
if (!Alive(actor)) return false;
|
||||
CharacterControllerComponent controller = CharacterControllerComponent.Cast(actor.FindComponent(CharacterControllerComponent));
|
||||
return controller && !controller.IsUnconscious();
|
||||
}
|
||||
|
||||
static SCR_AIGroup Group(IEntity actor)
|
||||
{
|
||||
if (!actor) return null;
|
||||
AIControlComponent control = AIControlComponent.Cast(actor.FindComponent(AIControlComponent));
|
||||
if (!control || !control.GetAIAgent()) return null;
|
||||
return SCR_AIGroup.Cast(control.GetAIAgent().GetParentGroup());
|
||||
}
|
||||
|
||||
static bool Player(IEntity actor)
|
||||
{
|
||||
return actor && GetGame().GetPlayerManager().GetPlayerIdFromControlledEntity(actor) > 0;
|
||||
}
|
||||
|
||||
static IEntity Spawn(ResourceName prefab, vector position, vector angles = "0 0 0")
|
||||
{
|
||||
if (!Replication.IsServer() || prefab.IsEmpty()) return null;
|
||||
Resource resource = Resource.Load(prefab);
|
||||
if (!resource || !resource.IsValid()) return null;
|
||||
EntitySpawnParams params = new EntitySpawnParams();
|
||||
params.TransformMode = ETransformMode.WORLD;
|
||||
Math3D.AnglesToMatrix(angles, params.Transform);
|
||||
params.Transform[3] = position;
|
||||
return GetGame().SpawnEntityPrefab(resource, GetGame().GetWorld(), params);
|
||||
}
|
||||
|
||||
static void DeleteSpawned(IEntity entity)
|
||||
{
|
||||
if (!Replication.IsServer() || !entity || Player(entity)) return;
|
||||
SCR_AIGroup group = SCR_AIGroup.Cast(entity);
|
||||
if (group)
|
||||
{
|
||||
// Never remove a group that has acquired a player since mission creation.
|
||||
if (group.GetPlayerCount(true) > 0) return;
|
||||
group.DespawnMembers();
|
||||
}
|
||||
SCR_EntityHelper.DeleteEntityAndChildren(entity);
|
||||
}
|
||||
|
||||
static vector Ground(vector position)
|
||||
{
|
||||
position[1] = GetGame().GetWorld().GetSurfaceY(position[0], position[2]) + 0.1;
|
||||
return position;
|
||||
}
|
||||
|
||||
static void Players(out array<IEntity> players, bool conscious = false)
|
||||
{
|
||||
players.Clear();
|
||||
array<int> ids = {};
|
||||
GetGame().GetPlayerManager().GetPlayers(ids);
|
||||
foreach (int id : ids)
|
||||
{
|
||||
IEntity actor = GetGame().GetPlayerManager().GetPlayerControlledEntity(id);
|
||||
if (Alive(actor) && (!conscious || CanFight(actor))) players.Insert(actor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// One server-owned implementation is installed by the AI addon.
|
||||
class ERR_CommandService
|
||||
{
|
||||
void Tick(float now) {}
|
||||
void Stop() {}
|
||||
void RegisterGroup(SCR_AIGroup group) {}
|
||||
void UnregisterGroup(SCR_AIGroup group) {}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
// Transport-independent administration. Only the owning controller supplies
|
||||
// authorization, derived on the server from its authenticated player ID.
|
||||
class ERR_CommandArguments
|
||||
{
|
||||
static string CoreSetting(string section, string option)
|
||||
{
|
||||
if (section == "mission" && option == "radio") return "radioDefenceSeconds";
|
||||
if (section == "intel" && option == "reveal") return "intelRevealChancePercent";
|
||||
return "";
|
||||
}
|
||||
static bool Integer(string text, out int value)
|
||||
{
|
||||
if (text.IsEmpty() || text.Length() > 6) return false;
|
||||
for (int i = 0; i < text.Length(); i++)
|
||||
if ("0123456789".IndexOf(text.Substring(i, 1)) < 0) return false;
|
||||
value = text.ToInt();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
class ERR_AdminService
|
||||
{
|
||||
protected string Setting(string section, string option)
|
||||
{
|
||||
return ERR_CommandArguments.CoreSetting(section, option);
|
||||
}
|
||||
protected bool Extension(ERR_GameMode mode, string arguments, array<string> replies) { return false; }
|
||||
protected bool TimeCommand(ERR_GameMode mode, array<string> parts, array<string> replies)
|
||||
{
|
||||
if (parts.Count() < 2 || parts[0] != "mission" || (parts[1] != "time" && parts[1] != "speed")) return false;
|
||||
string key = "startTimeMinutes";
|
||||
if (parts[1] == "speed") key = "timeMultiplier";
|
||||
if (parts.Count() == 3 && parts[2] == "show")
|
||||
{
|
||||
if (mode.Time()) replies.Insert(mode.Time().Status());
|
||||
replies.Insert("Next round: " + ERR_TimeService.Clock(mode.Settings().Get("startTimeMinutes")) + ", " + mode.Settings().Get("timeMultiplier").ToString() + "x progression.");
|
||||
return true;
|
||||
}
|
||||
int value;
|
||||
bool valid;
|
||||
if (parts.Count() == 4 && parts[2] == "set")
|
||||
{
|
||||
if (key == "startTimeMinutes") valid = ERR_TimeService.ParseClock(parts[3], value);
|
||||
else valid = ERR_CommandArguments.Integer(parts[3], value);
|
||||
}
|
||||
if (valid && mode.Settings().Set(key, value))
|
||||
replies.Insert("Saved for next round: " + ERR_TimeService.Clock(mode.Settings().Get("startTimeMinutes")) + ", " + mode.Settings().Get("timeMultiplier").ToString() + "x progression. Current round clock unchanged.");
|
||||
else replies.Insert("Invalid time or settings could not be saved. Use mission time set HH:MM (00:00-23:59) or mission speed set N (1-24). Previous setting retained.");
|
||||
return true;
|
||||
}
|
||||
protected string SettingHelp(string key)
|
||||
{
|
||||
if (key == "intelRevealChancePercent")
|
||||
return "Use /esc intel reveal set N (0-100). This is the chance that a first document identifies its location exactly; otherwise it gives a rough location. A second document for that location always identifies it. Changes affect newly recovered documents immediately; known locations and spent documents remain unchanged.";
|
||||
return "";
|
||||
}
|
||||
protected void SettingCommand(ERR_Settings settings, string key, array<string> parts, array<string> replies)
|
||||
{
|
||||
if (parts.Count() == 3 && parts[2] == "show") { replies.Insert(key + "=" + settings.Get(key).ToString()); return; }
|
||||
string help = SettingHelp(key);
|
||||
if (parts.Count() == 3 && parts[2] == "help" && !help.IsEmpty()) { replies.Insert(help); return; }
|
||||
int value;
|
||||
if (parts.Count() == 4 && parts[2] == "set" && ERR_CommandArguments.Integer(parts[3], value) && settings.Set(key, value))
|
||||
replies.Insert(key + "=" + value.ToString() + " saved");
|
||||
else
|
||||
{
|
||||
string failure = "Invalid value or settings could not be saved; previous setting retained.";
|
||||
if (!help.IsEmpty()) failure += " " + help;
|
||||
replies.Insert(failure);
|
||||
}
|
||||
}
|
||||
protected string Help()
|
||||
{
|
||||
return "/esc status | settings | mission abandon | mission radio show|set N | mission date show|set YYYY-MM-DD|authored | mission weather show|set authored|clear|overcast|rain | mission time show|set HH:MM | mission speed show|set N | intel reveal show|set N|help | spectate next|previous";
|
||||
}
|
||||
void Execute(ERR_GameMode mode, bool authorized, string arguments, array<string> replies)
|
||||
{
|
||||
replies.Clear();
|
||||
if (!Replication.IsServer() || !mode) return;
|
||||
if (!authorized) { replies.Insert("Administrator access required."); return; }
|
||||
if (arguments.Length() > 160) { replies.Insert("Command is too long."); return; }
|
||||
arguments = arguments.Trim(); arguments.ToLower();
|
||||
if (arguments == "status") { replies.Insert(mode.Status()); Extension(mode, arguments, replies); return; }
|
||||
if (arguments == "mission abandon")
|
||||
{
|
||||
if (mode.AbandonRound()) replies.Insert("Round abandoned. The normal defeat and restart sequence has begun.");
|
||||
else replies.Insert("Only an active round can be abandoned.");
|
||||
return;
|
||||
}
|
||||
if (!mode.Settings()) { replies.Insert("Mission settings are not ready."); return; }
|
||||
if (arguments == "settings")
|
||||
{
|
||||
foreach (string key : ERR_Settings.Keys()) replies.Insert(key + "=" + mode.Settings().Get(key).ToString());
|
||||
return;
|
||||
}
|
||||
if (Extension(mode, arguments, replies)) return;
|
||||
array<string> parts = {};
|
||||
arguments.Split(" ", parts, true);
|
||||
if (ERR_EnvironmentCommands.Execute(mode.Settings(), mode.Environment(), parts, replies)) return;
|
||||
if (TimeCommand(mode, parts, replies)) return;
|
||||
if (parts.Count() == 3 || parts.Count() == 4)
|
||||
{
|
||||
string key = Setting(parts[0], parts[1]);
|
||||
if (!key.IsEmpty())
|
||||
{
|
||||
SettingCommand(mode.Settings(), key, parts, replies);
|
||||
return;
|
||||
}
|
||||
}
|
||||
replies.Insert(Help());
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user