diff --git a/1.5/Assemblies/DoorsExpanded.dll b/1.5/Assemblies/DoorsExpanded.dll new file mode 100644 index 0000000..4bd09d8 Binary files /dev/null and b/1.5/Assemblies/DoorsExpanded.dll differ diff --git a/1.5/Defs/JobDefs/PH_Jobs.xml b/1.5/Defs/JobDefs/PH_Jobs.xml new file mode 100644 index 0000000..861feb5 --- /dev/null +++ b/1.5/Defs/JobDefs/PH_Jobs.xml @@ -0,0 +1,12 @@ + + + + + PH_UseRemoteButton + DoorsExpanded.JobDriver_UseRemoteButton + using TargetA. + false + true + + + diff --git a/1.5/Defs/ResearchProjectDefs/Heron_ResearchProjects.xml b/1.5/Defs/ResearchProjectDefs/Heron_ResearchProjects.xml new file mode 100644 index 0000000..63212f7 --- /dev/null +++ b/1.5/Defs/ResearchProjectDefs/Heron_ResearchProjects.xml @@ -0,0 +1,66 @@ + + + + + Heron_ResearchTab + + + + ProjectHeron_Curtains + + Unlocks simple construction techniques to make rings, simple hooks, and poles for the production of curtains, which open far faster than standard doors, but slowly vent temperatures between rooms. + Neolithic + 200 + 0 + 2 + +
  • ClassicStart
  • +
  • TribalStart
  • +
    +
    + + + ProjectHeron_PrisonDoors + + Provides simple prison doors to keep inmates safe, well, mostly. + 500 + Medieval + 1 + 2 + + + + ProjectHeron_RemoteDoors + + Unlocks remote doors for remote containment as well as buttons and levers to manipulate them. + 800 + Industrial + 1 + 3 + + + + ProjectHeron_Gates + + Hinges and fastening techniques allow for the construction of swinging doors, such as large gates. + 200 + Neolithic + 0 + 3 + +
  • ClassicStart
  • +
    +
    + + + ProjectHeron_BlastDoors + + Allows for the production of blast absorbing pressurized blast doors. + 3000 + Industrial + HiTechResearchBench + 2 + 2 + + +
    diff --git a/1.5/Defs/ResearchProjectDefs/Heron_ResearchTab.xml b/1.5/Defs/ResearchProjectDefs/Heron_ResearchTab.xml new file mode 100644 index 0000000..6866b4a --- /dev/null +++ b/1.5/Defs/ResearchProjectDefs/Heron_ResearchTab.xml @@ -0,0 +1,9 @@ + + + + + Heron_ResearchTab + + + + diff --git a/1.5/Defs/SoundDef/Heron_Sounds.xml b/1.5/Defs/SoundDef/Heron_Sounds.xml new file mode 100644 index 0000000..6f2bac3 --- /dev/null +++ b/1.5/Defs/SoundDef/Heron_Sounds.xml @@ -0,0 +1,150 @@ + + + + + HeronGarageOpen + MapOnly + 1 + +
  • + False + +
  • + Misc/RemoteDoor/GarageOpen +
  • + + 3 + 0.85~1.15 + +
    +
    + + + HeronGarageClose + MapOnly + 1 + +
  • + False + +
  • + Misc/RemoteDoor/GarageClose +
  • + + 3 + 0.85~1.15 + +
    +
    + + + HeronCurtainOpen + MapOnly + 1 + +
  • + False + +
  • + Misc/Curtain/CurtainOpen +
  • + + 3 + 0.85~1.15 + +
    +
    + + + HeronCurtainClose + MapOnly + 1 + +
  • + False + +
  • + Misc/Curtain/CurtainClose +
  • + + 3 + 0.85~1.15 + +
    +
    + + + + HeronJailDoorOpen + MapOnly + 1 + +
  • + False + +
  • + Misc/JailDoor/JailDoorOpen +
  • + + 3 + 0.85~1.15 + +
    +
    + + + HeronJailDoorClose + MapOnly + 1 + +
  • + False + +
  • + Misc/JailDoor/JailDoorClose +
  • + + 3 + 0.85~1.15 + +
    +
    + + + + HeronBlastDoorOpen + MapOnly + 1 + +
  • + False + +
  • + Misc/BlastDoor/BlastDoorOpen +
  • + + 3 + 0.85~1.15 + +
    +
    + + + HeronBlastDoorClose + MapOnly + 1 + +
  • + False + +
  • + Misc/BlastDoor/BlastDoorClose +
  • + + 3 + 0.85~1.15 + +
    +
    + +
    diff --git a/1.5/Defs/ThingDef_Building/Heron_Base.xml b/1.5/Defs/ThingDef_Building/Heron_Base.xml new file mode 100644 index 0000000..28d017d --- /dev/null +++ b/1.5/Defs/ThingDef_Building/Heron_Base.xml @@ -0,0 +1,126 @@ + + + + + + + + +
  • Buildings
  • +
    + MinifiedThing + +
  • Fabric
  • +
  • Leathery
  • +
    +
    + + + +
  • Metallic
  • +
    +
    + + + +
  • Metallic
  • +
  • Woody
  • +
  • Stony
  • +
    +
    + + + + + DoorsExpanded.Building_DoorExpanded + Building + DoorMoveable + 1 + true + + + 250 + 1.0 + + false + true + Normal + + true + BulletImpact_Metal + true + Light + Structure + true + 1.0 + true + RealtimeOnly + + true + true + Door_OpenPowered + Door_ClosePowered + Door_OpenManual + Door_CloseManual + false + + + +
  • + + +
  • PlaceWorker_DoorLearnOpeningSpeed
  • + +
    + + + + HeronInvisibleDoor + + DoorsExpanded.Building_DoorRegionHandler + Building + + Repair + + BlankTex + Graphic_Single + + false + + + + BlankTex + FloorEmplacement + 0 + false + false + false + false + true + false + Normal + false + Light + RealtimeOnly + + true + true + true + + +
  • + + + + diff --git a/1.5/Defs/ThingDef_Building/Heron_Doors.xml b/1.5/Defs/ThingDef_Building/Heron_Doors.xml new file mode 100644 index 0000000..2fbd662 --- /dev/null +++ b/1.5/Defs/ThingDef_Building/Heron_Doors.xml @@ -0,0 +1,661 @@ + + + + + + + PH_DoorDouble + + + 40 + 320 + 1700 + + 50 + (2,1) + + Things/Building/Door/DoorDouble_Mover + Graphic_Single + (2,1) + + + false + + + Things/Building/Door/DoorDouble_MenuIcon + +
  • + Door + true +
  • +
  • + Standard +
  • +
    + +
  • JecsTools.PlaceWorker_Outline
  • +
    +
    + + + PH_DoorTriple + + + 60 + 480 + 2550 + + 75 + (3,1) + + Things/Building/Door/DoorTriple_Mover + Graphic_Single + (3,1) + + + false + + + Things/Building/Door/DoorTriple_MenuIcon + +
  • + Door + true +
  • +
  • + Standard +
  • +
    + +
  • JecsTools.PlaceWorker_Outline
  • +
    +
    + + + PH_AutodoorDouble + + + 40 + 320 + 2200 + + + 80 + 4 + + 50 + (2,1) + + Things/Building/Door/AutodoorDouble_Mover + Graphic_Single + (2,1) + + + false + + + Things/Building/Door/AutodoorDouble_MenuIcon + +
  • + Autodoor + true +
  • +
  • + true + Standard +
  • +
  • + CompPowerTrader + 100 +
  • +
  • + + +
  • Autodoors
  • + + +
  • JecsTools.PlaceWorker_Outline
  • +
    + 6 +
    + + + PH_AutodoorTriple + + + 60 + 480 + 3300 + + + 120 + 6 + + 75 + (3,1) + + Things/Building/Door/AutodoorTriple_Mover + Graphic_Single + (3,1) + + + false + + + Things/Building/Door/AutodoorTriple_MenuIcon + +
  • + Autodoor + true +
  • +
  • + true + Standard +
  • +
  • + CompPowerTrader + 150 +
  • +
  • + + +
  • JecsTools.PlaceWorker_Outline
  • + + +
  • Autodoors
  • +
    + 6 +
    + + + PH_DoorJail + + Sliding jail cell door with strong bars. + + 30 + 300 + 1500 + + + 30 + 2 + + 50 + (1,1) + false + + Things/Building/Door/JailCell_Mover + Graphic_Single + CutoutComplex + (1,1) + + + false + + + + HeronJailDoorOpen + HeronJailDoorClose + + +
  • + Standard + true + 0.8 + false +
  • +
    + +
  • JecsTools.PlaceWorker_Outline
  • +
    + +
  • ProjectHeron_PrisonDoors
  • +
    +
    + + + + + HeronCurtainTribal + + Divides rooms. Curtains are much faster to open than standard doors, but are flammable and slowly vent temperatures between rooms. + + 10 + 50 + 850 + 4.0 + 1.0 + + 25 + (1,1) + false + + HeronCurtainOpen + HeronCurtainClose + true + + + Things/Building/Door/Curtain_Mover + Graphic_Single + (1,1) + + + false + + + +
  • + true +
  • +
  • + Stretch + 6.0 + true +
  • +
    + +
  • ProjectHeron_Curtains
  • +
    +
    + + + HeronCurtainTribalDouble + + + 20 + 100 + 1700 + 4.0 + 2.0 + + 50 + (2,1) + + HeronCurtainOpen + HeronCurtainClose + true + + + Things/Building/Door/CurtainDouble_Mover + Graphic_Single + (2,1) + + + false + + + +
  • + HeronCurtainTribal + true +
  • +
  • + Stretch + 6.0 + true + (0.2,1) +
  • +
    + +
  • ProjectHeron_Curtains
  • +
    +
    + + + HeronCurtainTribalTriple + + + 30 + 150 + 2550 + 4.0 + 3.0 + + 75 + (3,1) + + HeronCurtainOpen + HeronCurtainClose + true + + + Things/Building/Door/CurtainTriple_Mover + Graphic_Single + (3,1) + + + false + + + +
  • + HeronCurtainTribal + true +
  • +
  • + Stretch + 6.0 + true + (0.2,1) +
  • +
    + +
  • ProjectHeron_Curtains
  • +
    +
    + + + + + PH_GateDoubleThick + + + 60 + 500 + 2500 + + 100 + (2,1) + + Things/Building/Door/GateDoubleThick_Mover + Graphic_Multi + (2,2) + + + false + + + Things/Building/Door/Gate_MenuUI + +
  • + Door + true +
  • +
  • + DoubleSwing + false + true +
  • +
    + +
  • JecsTools.PlaceWorker_Outline
  • +
    + +
  • ProjectHeron_Gates
  • +
    +
    + + + + + + PH_DoorBlastSingle + + A heavy door that is far more resilient to damage. + + 50 + 500 + 2000 + 0.25 + 0.5 + + + 40 + 4 + + 75 + (1,1) + false + Heavy + + Things/Building/Door/DoorBlastDoor_MoverSingle + Graphic_Single + (1,1) + + false + + + Things/Building/Door/BlastDoorSingleUI + + HeronBlastDoorOpen + HeronBlastDoorClose + + +
  • + true +
  • +
  • + true + Standard + false + (0,0,1) + + Things/Building/Door/DoorBlastDoorSingle + Graphic_Single + CutoutComplex + (1,1) + + false + + +
  • +
  • + CompPowerTrader + 50 +
  • +
  • + 2.0 +
  • +
    + +
  • JecsTools.PlaceWorker_Outline
  • +
    + +
  • ProjectHeron_BlastDoors
  • +
    + 6 +
    + + + PH_DoorBlastDoor + + A heavy pair of doors that are far more resilient to damage. + + 100 + 1000 + 4000 + 0.25 + 0.5 + + + 80 + 7 + + 150 + (2,1) + Heavy + + Things/Building/Door/DoorBlastDoor_Mover + Graphic_Multi + (2,1) + + false + + + Things/Building/Door/BlastDoorUI + + HeronBlastDoorOpen + HeronBlastDoorClose + + +
  • + true +
  • +
  • + true + Standard + true + false + + Things/Building/Door/DoorBlastDoor_MoverAsync + Graphic_Multi + (2,1) + + false + + + (0,0,1) + + Things/Building/Door/DoorBlastDoor_Frame + Graphic_Multi + CutoutComplex + (2,1) + + false + + + + Things/Building/Door/DoorBlastDoor_FrameAsync + Graphic_Multi + CutoutComplex + (2,1) + + false + + +
  • +
  • + CompPowerTrader + 100 +
  • +
  • + 2.0 +
  • +
    + +
  • JecsTools.PlaceWorker_Outline
  • +
    + +
  • ProjectHeron_BlastDoors
  • +
    + 6 +
    + + + PH_DoorThickBlastDoor + + + 200 + 2000 + 8000 + 0.25 + 0.5 + + + 160 + 14 + + 300 + (3,2) + Heavy + + Things/Building/Door/DoorBlastDoor_Mover + Graphic_Multi + (3,2) + + false + + + Things/Building/Door/BlastDoorUI + + HeronBlastDoorOpen + HeronBlastDoorClose + + +
  • + PH_DoorBlastDoor + true +
  • +
  • + true + Standard + true + false + + Things/Building/Door/DoorBlastDoor_MoverAsync + Graphic_Multi + (3,2) + + false + + + + Things/Building/Door/DoorBlastDoor_Frame + Graphic_Multi + CutoutComplex + (3,2) + + false + + + + Things/Building/Door/DoorBlastDoor_FrameAsync + Graphic_Multi + CutoutComplex + (3,2) + + false + + +
  • +
  • + CompPowerTrader + 200 +
  • +
  • + 2.0 +
  • +
    + +
  • JecsTools.PlaceWorker_Outline
  • +
    + +
  • ProjectHeron_BlastDoors
  • +
    + 6 +
    + +
    diff --git a/1.5/Defs/ThingDef_Building/Heron_RemoteDoorsAndButtons.xml b/1.5/Defs/ThingDef_Building/Heron_RemoteDoorsAndButtons.xml new file mode 100644 index 0000000..0613387 --- /dev/null +++ b/1.5/Defs/ThingDef_Building/Heron_RemoteDoorsAndButtons.xml @@ -0,0 +1,303 @@ + + + + + PH_DoorButton + + A button that connects to doors (functions the same as a lever). + DoorsExpanded.Building_DoorRemoteButton + Building + South + true + (0,0,1) + + false + + Things/Building/RemoteDoorButton/RemoteDoorButton_ON + Graphic_Multi + + (0.9,0.3,0.9) + + + (0.2,0,0.6,0.1) + + 2 + + + false + + Things/Building/RemoteDoorButton/RemoteDoorButton_OFF + Graphic_Multi + + (0.9,0.3,0.9) + + + (0.2,0,0.6,0.1) + + 2 + + BuildingOnTop + PassThroughOnly + 50 + ConstructDirt + Normal + RealtimeOnly + 0.40 + + 80 + 200 + 1 + + true + + 10 + 1 + + BulletImpact_Ground + true + 0.5 + +
  • JecsTools.PlaceWorker_OnTopOfWalls
  • +
    + true + +
  • + CompPowerTrader + 5 +
  • +
    + Structure + +
  • ProjectHeron_RemoteDoors
  • +
    +
    + + + PH_DoorLever + + A lever that connects to doors (functions the same as a button). + DoorsExpanded.Building_DoorRemoteButton + Building + South + true + (0,0,1) + + false + + Things/Building/RemoteDoorLever/RemoteDoorLever_ON + Graphic_Multi + + (0.9,0.3,0.9) + + + (0.2,0,0.6,0.1) + + 2 + + + false + + Things/Building/RemoteDoorLever/RemoteDoorLever_OFF + Graphic_Multi + + (0.9,0.3,0.9) + + + (0.2,0,0.6,0.1) + + 2 + + BuildingOnTop + PassThroughOnly + 50 + ConstructDirt + Normal + RealtimeOnly + 0.40 + + 40 + 100 + 1 + + true + + 5 + 1 + + BulletImpact_Ground + true + 0.5 + +
  • JecsTools.PlaceWorker_OnTopOfWalls
  • +
    + true + +
  • + CompPowerTrader + 1 +
  • +
    + Structure + +
  • ProjectHeron_RemoteDoors
  • +
    +
    + + + PH_DoorRemoteSingle + + Vertical sliding metal door. Uses less power than an autodoor but opens more slowly. + + 20 + 160 + 1000 + 0.25 + + + 20 + 1 + + 25 + (1,1) + false + + HeronGarageOpen + HeronGarageClose + + + Things/Building/Door/DoorRemote_Mover + Graphic_Single + (1,1) + + + false + + + +
  • + true +
  • +
  • + true + StretchVertical + true +
  • +
  • + CompPowerTrader + 15 +
  • +
  • + 2.0 +
  • +
    + +
  • ProjectHeron_RemoteDoors
  • +
    +
    + + + PH_DoorRemoteDouble + + + 40 + 320 + 2000 + 0.25 + + + 40 + 2 + + 50 + (2,1) + + Things/Building/Door/DoorDoubleRemote_Mover + Graphic_Single + (2,1) + + + false + + + + HeronGarageOpen + HeronGarageClose + + +
  • + PH_DoorRemoteSingle + true +
  • +
  • + true + StretchVertical + true +
  • +
  • + CompPowerTrader + 30 +
  • +
  • + 2.0 +
  • +
    + +
  • ProjectHeron_RemoteDoors
  • +
    +
    + + + PH_DoorRemoteTriple + + + 60 + 480 + 3000 + 0.25 + + + 60 + 3 + + 75 + (3,1) + + Things/Building/Door/DoorTripleRemote_Mover + Graphic_Single + (3,1) + + + false + + + + HeronGarageOpen + HeronGarageClose + + +
  • + PH_DoorRemoteSingle + true +
  • +
  • + true + StretchVertical + true +
  • +
  • + CompPowerTrader + 45 +
  • +
  • + 2.0 +
  • +
    + +
  • ProjectHeron_RemoteDoors
  • +
    +
    + +
    diff --git a/1.5/Defs/WorkGiverDefs/PH_WorkGivers.xml b/1.5/Defs/WorkGiverDefs/PH_WorkGivers.xml new file mode 100644 index 0000000..b5014ca --- /dev/null +++ b/1.5/Defs/WorkGiverDefs/PH_WorkGivers.xml @@ -0,0 +1,19 @@ + + + + + PH_UseRemoteButton + + DoorsExpanded.WorkGiver_UseRemoteButton + BasicWorker + 81 + use + using + +
  • Manipulation
  • +
    + true + true +
    + +
    diff --git a/1.5/Languages/ChineseSimplified-by-Juijote.txt b/1.5/Languages/ChineseSimplified-by-Juijote.txt new file mode 100644 index 0000000..e69de29 diff --git a/1.5/Languages/ChineseSimplified/Keyed/ProjectHeron_Keys.xml b/1.5/Languages/ChineseSimplified/Keyed/ProjectHeron_Keys.xml new file mode 100644 index 0000000..339a65e --- /dev/null +++ b/1.5/Languages/ChineseSimplified/Keyed/ProjectHeron_Keys.xml @@ -0,0 +1,25 @@ + + + + 断开按钮或控制杆 + 断开所有连接的按钮或控制杆。 + {0}需要远程打开电源。 + 使用按钮或控制杆 + 切换殖民者是否应该去激活或停用按钮或控制杆。 + 连接电源进行远程安全防护。 + 连接按钮或控制杆进行远程安全防护。 + 连接到按钮或控制杆 + 立即将遥控门连接到按钮 + 在 {0} 处成功设置新按钮或控制杆 + {0}无法设置新按钮 + 在 {0} 取消链接先前连接的按钮或控制杆 + 取消链接先前连接的按钮或控制杆 + 必须连接到遥控门 + 必须接通电源 + 远程安全防护 + 启用此设置后,只能通过对应按钮远程开关此门,此门关闭时,将处于锁定状态,无法出入。 当此设置关闭时,任何人都可以像使用普通门一样自由开关此门。 + + + 必须安装在墙上。 + + diff --git a/1.5/Languages/English/Keyed/ProjectHeron_Keys.xml b/1.5/Languages/English/Keyed/ProjectHeron_Keys.xml new file mode 100644 index 0000000..48539a1 --- /dev/null +++ b/1.5/Languages/English/Keyed/ProjectHeron_Keys.xml @@ -0,0 +1,29 @@ + + + + Doors Expanded + Log level (for debugging) + Normal + Debug + Stack Trace + + Can be opened and closed from a distance with a button or lever. + Disconnect button or lever + Disconnects any connected button or lever. + {0} requires power to be opened remotely. + Use {0} + Toggles whether or not a colonist should make their way over to activate or deactivate {0}. + Connect a power source to secure remotely. + Connect a button or lever to secure remotely. + Connect to a button or lever + Instantly links the remote door to a button or lever + Failed to set new button or lever from {0} + Must be connected to a remote door + Must be connected to power + Secured remotely + With this setting on, the door may only be opened remotely by using the corresponding button or lever. The door will act like it is in a locked state, no entry or exit, while closed. When this setting is off, characters may freely open and close the door like normal doors. + + + Must be placed on a wall. + + diff --git a/1.5/Languages/French-by-qux.txt b/1.5/Languages/French-by-qux.txt new file mode 100644 index 0000000..e69de29 diff --git a/1.5/Languages/French/DefInjected/JobDef/PH_Jobs.xml b/1.5/Languages/French/DefInjected/JobDef/PH_Jobs.xml new file mode 100644 index 0000000..1405b2a --- /dev/null +++ b/1.5/Languages/French/DefInjected/JobDef/PH_Jobs.xml @@ -0,0 +1,6 @@ + + + + Utilise TargetA. + + \ No newline at end of file diff --git a/1.5/Languages/French/DefInjected/ResearchProjectDef/Heron_ResearchProjects.xml b/1.5/Languages/French/DefInjected/ResearchProjectDef/Heron_ResearchProjects.xml new file mode 100644 index 0000000..ac05d32 --- /dev/null +++ b/1.5/Languages/French/DefInjected/ResearchProjectDef/Heron_ResearchProjects.xml @@ -0,0 +1,19 @@ + + + + Rideaux + Déverrouille des techniques de construction simples pour fabriquer des anneaux, des crochets simples et des tringles pour fabriquer des rideaux qui s'ouvrent beaucoup plus rapidement que les portes standard, mais libèrent lentement les températures entre les pièces. + + Portes de prison + Fournit des portes de prison simples pour protéger les détenus. + + Portes automatique + Déverrouille les portes automatique ainsi que les télécommandes pour les contrôler. + + Portails + Les charnières et les techniques de fixation permettent de construire des portes battantes. + + Portes anti-souffle + Permet la fabrication de portes résistantes aux explosions. + + \ No newline at end of file diff --git a/1.5/Languages/French/DefInjected/ResearchTabDef/Heron_ResearchTab.xml b/1.5/Languages/French/DefInjected/ResearchTabDef/Heron_ResearchTab.xml new file mode 100644 index 0000000..fa53eca --- /dev/null +++ b/1.5/Languages/French/DefInjected/ResearchTabDef/Heron_ResearchTab.xml @@ -0,0 +1,6 @@ + + + + Recherche de portes + + \ No newline at end of file diff --git a/1.5/Languages/French/DefInjected/ThingDef/Heron_Doors.xml b/1.5/Languages/French/DefInjected/ThingDef/Heron_Doors.xml new file mode 100644 index 0000000..2157e15 --- /dev/null +++ b/1.5/Languages/French/DefInjected/ThingDef/Heron_Doors.xml @@ -0,0 +1,58 @@ + + + + + + + + + + Porte de prison + Une porte de prison avec de solides barreaux. + + + + Rideaux + Sépare les chambres. Les rideaux s'ouvrent beaucoup plus rapidement que les portes conventionnelles, mais ils sont hautement inflammables et laissent lentement l'air passer entre les pièces. + + + + + + + Portail + + + + Porte anti-souffle + Une porte lourde qui est beaucoup plus résistante aux dommages que les portes classiques. + + Porte anti-souffle + Une paire de portes lourdes qui sont beaucoup plus résistantes aux dommages que les portes classique. + + + + diff --git a/1.5/Languages/French/DefInjected/ThingDef/Heron_RemoteDoorsAndButtons.xml b/1.5/Languages/French/DefInjected/ThingDef/Heron_RemoteDoorsAndButtons.xml new file mode 100644 index 0000000..01d5f36 --- /dev/null +++ b/1.5/Languages/French/DefInjected/ThingDef/Heron_RemoteDoorsAndButtons.xml @@ -0,0 +1,15 @@ + + + + Poignée de porte + Une poignée qui se connecte aux portes (fonctionne comme un levier). + + Levier de porte + Un levier qui se connecte aux portes (fonctionne comme un bouton). + + Porte automatique + Porte métallique coulissante verticale. Utilise moins d'énergie qu'une porte automatique mais s'ouvre plus lentement + + + + diff --git a/1.5/Languages/French/DefInjected/WorkGiverDef/PH_WorkGivers.xml b/1.5/Languages/French/DefInjected/WorkGiverDef/PH_WorkGivers.xml new file mode 100644 index 0000000..a408c8a --- /dev/null +++ b/1.5/Languages/French/DefInjected/WorkGiverDef/PH_WorkGivers.xml @@ -0,0 +1,9 @@ + + + + Utilise TargetA + Utiliser + Utilise + + + \ No newline at end of file diff --git a/1.5/Languages/French/Keyed/ProjectHeron_Keys.xml b/1.5/Languages/French/Keyed/ProjectHeron_Keys.xml new file mode 100644 index 0000000..2802c93 --- /dev/null +++ b/1.5/Languages/French/Keyed/ProjectHeron_Keys.xml @@ -0,0 +1,23 @@ + + + + Peut être ouvert et fermé à distance avec un bouton ou un levier. + Bouton ou levier de déconnexion + Déconnecte tous les boutons ou leviers attachés. + {0}Une alimentation électrique doit être établie pour ouvrir la porte. + Utilise {0}. + Indique si un colon doit passer pour activer ou désactiver {0}. + Connectez une source d'alimentation pour assurer le contrôle à distance. + Connectez un bouton ou un levier pour la télécommande. + Connectez-vous avec un bouton ou un levier + Connecte instantanément la porte avec un bouton + Le nouveau bouton n'a pas pu être défini{0} + Doit être connecté à une porte télécommandée + Doit être connecté à l'alimentation + Télécommandé + Lorsque ce paramètre est activé, la porte ne peut être ouverte qu'à distance en appuyant sur le bouton approprié. La porte se comporte comme si elle était verrouillée, sans entrée ni sortie tant qu'elle est fermée. Lorsque ce paramètre est désactivé, les personnes peuvent ouvrir et fermer librement la porte comme des portes normales. + + + Doit être placé sur un mur. + + diff --git a/1.5/Languages/German/DefInjected/JobDef/PH_Jobs.xml b/1.5/Languages/German/DefInjected/JobDef/PH_Jobs.xml new file mode 100644 index 0000000..f371a6b --- /dev/null +++ b/1.5/Languages/German/DefInjected/JobDef/PH_Jobs.xml @@ -0,0 +1,7 @@ + + + + Benutze TargetA. + + + \ No newline at end of file diff --git a/1.5/Languages/German/DefInjected/ResearchProjectDef/Heron_ResearchProjects.xml b/1.5/Languages/German/DefInjected/ResearchProjectDef/Heron_ResearchProjects.xml new file mode 100644 index 0000000..c40a870 --- /dev/null +++ b/1.5/Languages/German/DefInjected/ResearchProjectDef/Heron_ResearchProjects.xml @@ -0,0 +1,20 @@ + + + + Einfache Vorhänge + Schaltet einfache Konstruktionstechniken zur Herstellung von Ringen, einfachen Haken und Stangen für die Herstellung von Vorhängen frei, die sich weitaus schneller öffnen als Standardtüren, jedoch die Temperaturen zwischen den Räumen langsam ablassen. + + Häftlingskontrolle + Bietet einfache Gefängnistüren, um die Insassen größtenteils zu schützen. + + Fernbediente Türen + Schalte Fernbedienbare Türen für die ferngesteuerte Kontrolle sowie Knöpfe und Hebel frei, um sie zu kontrollieren. + + Scharniere und Tore + Scharniere und Befestigungstechniken ermöglichen den Bau von Pendeltüren, z. B. großen Toren. + + Explosionsschutztüren + Ermöglicht die Herstellung von druckstoßabsorbierenden Türen. + + + \ No newline at end of file diff --git a/1.5/Languages/German/DefInjected/ResearchTabDef/Heron_ResearchTab.xml b/1.5/Languages/German/DefInjected/ResearchTabDef/Heron_ResearchTab.xml new file mode 100644 index 0000000..bc429c8 --- /dev/null +++ b/1.5/Languages/German/DefInjected/ResearchTabDef/Heron_ResearchTab.xml @@ -0,0 +1,7 @@ + + + + Türforschung + + + \ No newline at end of file diff --git a/1.5/Languages/German/DefInjected/ThingDef/Heron_Doors.xml b/1.5/Languages/German/DefInjected/ThingDef/Heron_Doors.xml new file mode 100644 index 0000000..8cd0213 --- /dev/null +++ b/1.5/Languages/German/DefInjected/ThingDef/Heron_Doors.xml @@ -0,0 +1,58 @@ + + + + + + + + + + Gefängnistür + Schiebegefängniszellentür mit starken Metallstangen. + + + + Vorhang (Stamm) + Teilt Räume. Vorhänge lassen sich viel schneller öffnen als Standardtüren, sind jedoch entflammbar und lüften langsam die Temperaturen zwischen den Räumen. + + + + + + + Tor + + + + Explosionsschutztür + Eine schwere Tür, die viel widerstandsfähiger gegen Beschädigungen ist. + + Explosionsschutztür + Ein schweres Türenpaar, das viel widerstandsfähiger gegen Beschädigungen ist. + + + + diff --git a/1.5/Languages/German/DefInjected/ThingDef/Heron_RemoteDoorsAndButtons.xml b/1.5/Languages/German/DefInjected/ThingDef/Heron_RemoteDoorsAndButtons.xml new file mode 100644 index 0000000..c3bd6bf --- /dev/null +++ b/1.5/Languages/German/DefInjected/ThingDef/Heron_RemoteDoorsAndButtons.xml @@ -0,0 +1,15 @@ + + + + Türknopf + Eine Taste, die mit Türen verbunden wird (funktioniert wie ein Hebel). + + Türhebel + Ein Hebel, der mit Türen verbunden wird (funktioniert wie ein Knopf). + + Fernbedienbare Tür + Vertikale Metallschiebetür. Teilt Räume. + + + + diff --git a/1.5/Languages/German/DefInjected/WorkGiverDef/PH_WorkGivers.xml b/1.5/Languages/German/DefInjected/WorkGiverDef/PH_WorkGivers.xml new file mode 100644 index 0000000..691c76d --- /dev/null +++ b/1.5/Languages/German/DefInjected/WorkGiverDef/PH_WorkGivers.xml @@ -0,0 +1,9 @@ + + + + Benutze TargetA + Benutze + Benutzt + + + \ No newline at end of file diff --git a/1.5/Languages/German/Keyed/ProjectHeron_Keys.xml b/1.5/Languages/German/Keyed/ProjectHeron_Keys.xml new file mode 100644 index 0000000..6862325 --- /dev/null +++ b/1.5/Languages/German/Keyed/ProjectHeron_Keys.xml @@ -0,0 +1,23 @@ + + + + Kann mit einem Knopf aus der Ferne geöffnet und geschlossen werden. + Knopf oder Hebel trennen + Trennt alle angeschlossenen Knöpfe oder Hebel. + {0} Zum Öffnen der Tür muss eine Stromversorgung hergestllt werden. + Benutzen Sie {0}. + Schaltet ein oder aus, ob ein Kolonist vorbeikommen soll, um {0} zu aktivieren oder zu deaktivieren. + Schließen Sie eine Stromquelle an, um die Fernbedienbarkeit zu gewährleisten. + Schließen Sie einen Knopf oder Hebel an, um eine Fernbedienbarkeit zu gewährleisten. + Mit einem Knopf oder Hebel verbinden + Verbindet die Tür sofort mit einem Knopf + Neuer Knopf konnte nicht festgelegt werden {0} + Muss an eine Fernbedienbare Tür angeschlossen werden + Muss an die Stromversorgung angeschlossen sein + Ferngesteuert + Wenn diese Einstellung aktiviert ist, kann die Tür nur durch Drücken der entsprechenden Taste aus der Ferne geöffnet werden. Die Tür verhält sich so, als wäre sie verriegelt, kein Ein- oder Ausgang, solange sie geschlossen ist. Wenn diese Einstellung deaktiviert ist, können Menschen die Tür wie normale Türen frei öffnen und schließen.. + + + Muss an eine Wand gestellt werden. + + diff --git a/1.5/Languages/Japanese/DefInjected/JobDef/PH_Jobs.xml b/1.5/Languages/Japanese/DefInjected/JobDef/PH_Jobs.xml new file mode 100644 index 0000000..552ea86 --- /dev/null +++ b/1.5/Languages/Japanese/DefInjected/JobDef/PH_Jobs.xml @@ -0,0 +1,6 @@ + + + + TargetAを使用中 + + \ No newline at end of file diff --git a/1.5/Languages/Japanese/DefInjected/ResearchProjectDef/Heron_ResearchProjects.xml b/1.5/Languages/Japanese/DefInjected/ResearchProjectDef/Heron_ResearchProjects.xml new file mode 100644 index 0000000..b710983 --- /dev/null +++ b/1.5/Languages/Japanese/DefInjected/ResearchProjectDef/Heron_ResearchProjects.xml @@ -0,0 +1,16 @@ + + + + 簡素なカーテン + 標準的なドアよりも素早く開く事ができるカーテンの製造するのに、リングと簡単なフック、ポールを取り付けるための簡単な建築技術のロックを解除します。ただしカーテンで仕切られている部屋同士や屋外の熱がゆっくりと入ってきます。 + + 囚人の隔離 + 基本的な隔離技術です。 + + ちょうつがいとゲート + ちょうつがいと留め金具の技術は、大きなゲートのような開き戸の建設を可能にします。 + + ブラストドア + 爆発の圧力を吸収して内部の人や物を守る防爆扉の建設を可能にします。 + + \ No newline at end of file diff --git a/1.5/Languages/Japanese/DefInjected/ResearchTabDef/Heron_ResearchTab.xml b/1.5/Languages/Japanese/DefInjected/ResearchTabDef/Heron_ResearchTab.xml new file mode 100644 index 0000000..c592e09 --- /dev/null +++ b/1.5/Languages/Japanese/DefInjected/ResearchTabDef/Heron_ResearchTab.xml @@ -0,0 +1,6 @@ + + + + Doors + + \ No newline at end of file diff --git a/1.5/Languages/Japanese/DefInjected/ThingDef/Heron_Doors.xml b/1.5/Languages/Japanese/DefInjected/ThingDef/Heron_Doors.xml new file mode 100644 index 0000000..d37eac0 --- /dev/null +++ b/1.5/Languages/Japanese/DefInjected/ThingDef/Heron_Doors.xml @@ -0,0 +1,59 @@ + + + + + + + + + + 囚人用ドア + 強力な鉄格子を持ちスライドさせて開閉する刑務所用の囚人部屋のドアです. + + + + カーテン (部族) + 部屋を隔てる手動扉です.カーテンは標準的なドアよりも素早く開く事ができますが,可燃性なのと換気口のように仕切られた空間同士の温度が通り抜けます. + + + + + + + ゲート + + + + ブラストドア + 損傷に対してはるかに弾力性のある重いドア。 + + ブラストドア + 損傷に対してはるかに弾力性のある重いドアのペア。 + + + + diff --git a/1.5/Languages/Japanese/DefInjected/ThingDef/Heron_RemoteDoorsAndButtons.xml b/1.5/Languages/Japanese/DefInjected/ThingDef/Heron_RemoteDoorsAndButtons.xml new file mode 100644 index 0000000..025d4ca --- /dev/null +++ b/1.5/Languages/Japanese/DefInjected/ThingDef/Heron_RemoteDoorsAndButtons.xml @@ -0,0 +1,15 @@ + + + + ドアボタン + ドアを操作するボタン(レバーと同じ機能)です. + + ドアレバー + ドアを操作するレバー(ボタンと同じ機能)です. + + 遠隔ドア + 垂直にスライドする金属製のドアです.部屋を分割します. + + + + diff --git a/1.5/Languages/Japanese/DefInjected/WorkGiverDef/PH_WorkGivers.xml b/1.5/Languages/Japanese/DefInjected/WorkGiverDef/PH_WorkGivers.xml new file mode 100644 index 0000000..e036e7d --- /dev/null +++ b/1.5/Languages/Japanese/DefInjected/WorkGiverDef/PH_WorkGivers.xml @@ -0,0 +1,8 @@ + + + + TargetAを使用 + 使用 + 使用 + + \ No newline at end of file diff --git a/1.5/Languages/Japanese/Keyed/ProjectHeron_Keys.xml b/1.5/Languages/Japanese/Keyed/ProjectHeron_Keys.xml new file mode 100644 index 0000000..38318b1 --- /dev/null +++ b/1.5/Languages/Japanese/Keyed/ProjectHeron_Keys.xml @@ -0,0 +1,23 @@ + + + + ボタンで遠くから開閉できます。 + ボタンやレバーを解除 + 接続されているボタンまたはレバーを解除します。 + {0}を遠隔操作で開く必要があります。 + {0}を使用 + 入植者が{0}の操作をできるのか、できないかを切り替えます。 + 遠隔操作で安全を確保するために電源に接続してください。 + ボタンやレバーを接続して遠隔操作で安全を確保します。 + ボタンに接続 + 離れていても操作できるように遠隔ドアをドアボタンやドアレバーに接続します。 + 新しい操作ボタン(座標:{0})に設定できませんでした + 遠隔操作するドアに接続する必要があります + 電源に接続する必要があります + 遠隔ドアを施錠 + この設定がオンの場合、ドアは対応するボタンを押すことによってのみ遠隔操作で開くことができます。閉じられている間、ドアはロックされた状態、つまり出入り口とはならない状態になります。この設定がオフの場合、ポーン(人間や動物)は通常のドアのようにドアを自由に開閉できます。 + + + 壁に置く必要があります。 + + diff --git a/1.5/Languages/Korean-by-MarinHigh.txt b/1.5/Languages/Korean-by-MarinHigh.txt new file mode 100644 index 0000000..e69de29 diff --git a/1.5/Languages/Korean/DefInjected/ThingDef/Heron_Doors.xml b/1.5/Languages/Korean/DefInjected/ThingDef/Heron_Doors.xml new file mode 100644 index 0000000..6700408 --- /dev/null +++ b/1.5/Languages/Korean/DefInjected/ThingDef/Heron_Doors.xml @@ -0,0 +1,59 @@ + + + + + + + + + + 감옥 문 + 튼튼한 창살이 있는 미닫이식 감방 문입니다. + + + + 커튼 (부족민) + 방을 나눕니다. 커튼은 평범한 문보다 훨씬 빨리 열리지만, 불탈 수 있고 서서히 방 사이의 온도를 전달합니다. + + + + + + + 관문 + + + + 방폭 문 + 손상에 훨씬 더 탄력적인 무거운 문. + + 방폭 문 + 손상에 훨씬 더 탄력적인 무거운 한 쌍의 문. + + + + diff --git a/1.5/Languages/Polish-by-sma342.txt b/1.5/Languages/Polish-by-sma342.txt new file mode 100644 index 0000000..e69de29 diff --git a/1.5/Languages/Polish/DefInjected/ThingDef/Heron_Doors.xml b/1.5/Languages/Polish/DefInjected/ThingDef/Heron_Doors.xml new file mode 100644 index 0000000..3be8d0a --- /dev/null +++ b/1.5/Languages/Polish/DefInjected/ThingDef/Heron_Doors.xml @@ -0,0 +1,59 @@ + + + + + + + + + + więzienne drzwi + Przesuwane więzienne drzwi z mocnymi kratami. + + + + kurtyna (plemienna) + Rozdziela pomieszczenia. Kurtyny otwierają się szybciej niż zwykłe drzwi, ale są łatwopalne i powoli przepuszczają temperatury z innych pomieszczeń. + + + + + + + brama + + + + drzwi pancerne + Ciężkie drzwi, które są znacznie bardziej odporne na uszkodzenia. + + drzwi pancerne + Ciężka para drzwi, które są znacznie bardziej odporne na uszkodzenia. + + + + diff --git a/1.5/Languages/Portuguese/DefInjected/ThingDef/Heron_Doors.xml b/1.5/Languages/Portuguese/DefInjected/ThingDef/Heron_Doors.xml new file mode 100644 index 0000000..79c55e9 --- /dev/null +++ b/1.5/Languages/Portuguese/DefInjected/ThingDef/Heron_Doors.xml @@ -0,0 +1,59 @@ + + + + + + + + + + porta de cela + Uma porta de cadeia deslisante com barras bem fortes. + + + + cortina (tribal) + Divide as salas. As cortinas são bem mais rápidas para abrir do que as portas normais, porém são bem inflamáveis e tenta equilibrar a temperatura entre salas. + + + + + + + portão + + + + portão blindado/anti-explosão + Uma porta pesada que é muito mais resistente a danos. + + portão blindado/anti-explosão + Um par de portas pesadas que são muito mais resistentes a danos. + + + + diff --git a/1.5/Languages/PortugueseBrazilian-by-Freewayz.txt b/1.5/Languages/PortugueseBrazilian-by-Freewayz.txt new file mode 100644 index 0000000..e69de29 diff --git a/1.5/Languages/PortugueseBrazilian/DefInjected/ThingDef/Heron_Doors.xml b/1.5/Languages/PortugueseBrazilian/DefInjected/ThingDef/Heron_Doors.xml new file mode 100644 index 0000000..79c55e9 --- /dev/null +++ b/1.5/Languages/PortugueseBrazilian/DefInjected/ThingDef/Heron_Doors.xml @@ -0,0 +1,59 @@ + + + + + + + + + + porta de cela + Uma porta de cadeia deslisante com barras bem fortes. + + + + cortina (tribal) + Divide as salas. As cortinas são bem mais rápidas para abrir do que as portas normais, porém são bem inflamáveis e tenta equilibrar a temperatura entre salas. + + + + + + + portão + + + + portão blindado/anti-explosão + Uma porta pesada que é muito mais resistente a danos. + + portão blindado/anti-explosão + Um par de portas pesadas que são muito mais resistentes a danos. + + + + diff --git a/1.5/Languages/Russian-by-kr33man.txt b/1.5/Languages/Russian-by-kr33man.txt new file mode 100644 index 0000000..e69de29 diff --git a/1.5/Languages/Russian/DefInjected/ResearchProjectDef/Heron_ResearchProjects.xml b/1.5/Languages/Russian/DefInjected/ResearchProjectDef/Heron_ResearchProjects.xml new file mode 100644 index 0000000..84e6ae8 --- /dev/null +++ b/1.5/Languages/Russian/DefInjected/ResearchProjectDef/Heron_ResearchProjects.xml @@ -0,0 +1,17 @@ + + + + простые шторы + Открывает простые строительные технологии для изготовления колец, простых крючков и шестов для производства штор, которые открываются намного быстрее, чем стандартные двери, но медленно пропускают воздух между комнатами. + + содержание заключенных + Базовое + + петли и ворота + Петли и способ скрепления позволяют создавать раздвижные двери, такие как большие ворота. + + взрывозащищенные двери + Позволяет производить герметичные взрывозащищенные двери. + + + \ No newline at end of file diff --git a/1.5/Languages/Russian/DefInjected/ResearchTabDef/Heron_ResearchTab.xml b/1.5/Languages/Russian/DefInjected/ResearchTabDef/Heron_ResearchTab.xml new file mode 100644 index 0000000..80b6f5a --- /dev/null +++ b/1.5/Languages/Russian/DefInjected/ResearchTabDef/Heron_ResearchTab.xml @@ -0,0 +1,7 @@ + + + + исследование дверей + + + \ No newline at end of file diff --git a/1.5/Languages/Russian/DefInjected/ThingDef/Heron_Doors.xml b/1.5/Languages/Russian/DefInjected/ThingDef/Heron_Doors.xml new file mode 100644 index 0000000..323a36d --- /dev/null +++ b/1.5/Languages/Russian/DefInjected/ThingDef/Heron_Doors.xml @@ -0,0 +1,59 @@ + + + + + + + + + + тюремная дверь + Раздвижная дверь тюремной камеры с прочной решеткой. + + + + занавес (племя) + Разделяет комнаты. Занавесы открываются гораздо быстрее обычных дверей, но легко воспламеняются и медленно пропускают воздух между комнатами. + + + + + + + ворота + + + + взрывозащищенная дверь + Тяжелая дверь, более устойчивая к повреждениям. + + взрывозащищенная дверь + Тяжелая пара дверей, которые гораздо более устойчивы к повреждениям. + + + + diff --git a/1.5/Languages/Spanish-by-Crusader.txt b/1.5/Languages/Spanish-by-Crusader.txt new file mode 100644 index 0000000..e69de29 diff --git a/1.5/Languages/Spanish/DefInjected/JobDef/PH_Jobs.xml b/1.5/Languages/Spanish/DefInjected/JobDef/PH_Jobs.xml new file mode 100644 index 0000000..b7f2240 --- /dev/null +++ b/1.5/Languages/Spanish/DefInjected/JobDef/PH_Jobs.xml @@ -0,0 +1,6 @@ + + + + usando TargetA. + + \ No newline at end of file diff --git a/1.5/Languages/Spanish/DefInjected/ResearchProjectDef/Heron_ResearchProjects.xml b/1.5/Languages/Spanish/DefInjected/ResearchProjectDef/Heron_ResearchProjects.xml new file mode 100644 index 0000000..1081590 --- /dev/null +++ b/1.5/Languages/Spanish/DefInjected/ResearchProjectDef/Heron_ResearchProjects.xml @@ -0,0 +1,19 @@ + + + + Cortinas simples + Desbloquea técnicas de construcción simples para hacer anillos, ganchos simples y postes para la producción de cortinas, que se abren mucho más rápido que las puertas estándar, pero que ventilan lentamente las temperaturas entre las habitaciones. + + Contención de prisioneros + Proporciona puertas de prisión simples para mantener a los internos seguros, bueno, en su mayoría. + + Puertas remotas + Desbloquea puertas remotas para contención remota, así como botones y palancas para manipularlas. + + Bisagras y puertas + Las bisagras y las técnicas de fijación permiten la construcción de puertas batientes, como puertas grandes. + + Puertas batientes + Permite la producción de puertas de explosión presurizadas que absorben explosiones. + + \ No newline at end of file diff --git a/1.5/Languages/Spanish/DefInjected/ResearchTabDef/Heron_ResearchTab.xml b/1.5/Languages/Spanish/DefInjected/ResearchTabDef/Heron_ResearchTab.xml new file mode 100644 index 0000000..bb401c3 --- /dev/null +++ b/1.5/Languages/Spanish/DefInjected/ResearchTabDef/Heron_ResearchTab.xml @@ -0,0 +1,6 @@ + + + + Investigación de puertas + + \ No newline at end of file diff --git a/1.5/Languages/Spanish/DefInjected/ThingDef/Heron_Doors.xml b/1.5/Languages/Spanish/DefInjected/ThingDef/Heron_Doors.xml new file mode 100644 index 0000000..54f35a1 --- /dev/null +++ b/1.5/Languages/Spanish/DefInjected/ThingDef/Heron_Doors.xml @@ -0,0 +1,58 @@ + + + + + + + + + + Puerta de prisión + Puerta corredera de celda con fuertes rejas. + + + + Cortina (tribal) + Divide habitaciones. Las cortinas son mucho más rápidas de abrir que las puertas estándar, pero son inflamables y ventilan lentamente las temperaturas entre las habitaciones. + + + + + + + Portón + + + + Puerta blindada + Una puerta pesada que es mucho más resistente al daño. + + Puerta blindada + Un par de puertas pesadas que es mucho más resistente al daño. + + + + diff --git a/1.5/Languages/Spanish/DefInjected/ThingDef/Heron_RemoteDoorsAndButtons.xml b/1.5/Languages/Spanish/DefInjected/ThingDef/Heron_RemoteDoorsAndButtons.xml new file mode 100644 index 0000000..aaf4e69 --- /dev/null +++ b/1.5/Languages/Spanish/DefInjected/ThingDef/Heron_RemoteDoorsAndButtons.xml @@ -0,0 +1,15 @@ + + + + Botón de puerta + Un botón que se conecta a las puertas (funciona igual que una palanca). + + Palanca de puerta + Una palanca que se conecta a las puertas (funciona igual que un botón). + + Puerta remota + Puerta corredera vertical de metal. Divide habitaciones. Se puede abrir y cerrar a distancia con un botón. + + + + diff --git a/1.5/Languages/Spanish/DefInjected/WorkGiverDef/PH_WorkGivers.xml b/1.5/Languages/Spanish/DefInjected/WorkGiverDef/PH_WorkGivers.xml new file mode 100644 index 0000000..2c4c380 --- /dev/null +++ b/1.5/Languages/Spanish/DefInjected/WorkGiverDef/PH_WorkGivers.xml @@ -0,0 +1,8 @@ + + + + usa TargetA + usa + usando + + \ No newline at end of file diff --git a/1.5/Languages/Spanish/Keyed/ProjectHeron_Keys.xml b/1.5/Languages/Spanish/Keyed/ProjectHeron_Keys.xml new file mode 100644 index 0000000..81b63e1 --- /dev/null +++ b/1.5/Languages/Spanish/Keyed/ProjectHeron_Keys.xml @@ -0,0 +1,23 @@ + + + + Se puede abrir y cerrar a distancia con un botón o palanca. + Botón o palanca de desconexión + Desconecta cualquier botón o palanca conectada. + {0} requiere que la energía se abra de forma remota. + Usa {0} + Alterna si un colono debe o no hacer su camino para activar o desactivar {0}. + Conecte una fuente de alimentación para proteger de forma remota. + Conecte un botón o palanca para asegurar de forma remota. + Conectarse a un botón o palanca + Vincula instantáneamente la puerta remota a un botón + No se pudo establecer el nuevo botón desde {0} + Debe estar conectado a una puerta remota + Debe estar conectado a la alimentación + Protegido de forma remota + Con esta configuración, la puerta solo se puede abrir de forma remota presionando el botón correspondiente. La puerta actuará como si estuviera en un estado cerrado, sin entrada ni salida, mientras está cerrada. Cuando esta configuración está desactivada, los personajes pueden abrir y cerrar libremente la puerta como las puertas normales. + + + Debe ser colocado en la pared. + + diff --git a/1.5/Patches/VanillaDoors.xml b/1.5/Patches/VanillaDoors.xml new file mode 100644 index 0000000..02a8174 --- /dev/null +++ b/1.5/Patches/VanillaDoors.xml @@ -0,0 +1,54 @@ + + + + + +
  • + Always + /Defs/ThingDef[defName="Door"]/comps + + /Defs/ThingDef[defName="Door"] + + + + +
  • +
  • + /Defs/ThingDef[defName="Door"]/comps + +
  • + true +
  • + + +
    +
    + + + +
  • + Always + /Defs/ThingDef[defName="Autodoor"]/comps + + /Defs/ThingDef[defName="Autodoor"] + + + + +
  • +
  • + /Defs/ThingDef[defName="Autodoor"]/comps + +
  • + true +
  • +
  • + true + Standard +
  • + + +
    +
    + +
    diff --git a/About/About.xml b/About/About.xml index 2f01fc2..9ff7290 100644 --- a/About/About.xml +++ b/About/About.xml @@ -10,6 +10,7 @@
  • 1.2
  • 1.3
  • 1.4
  • +
  • 1.5
  • @@ -19,7 +20,7 @@ https://github.com/pardeike/HarmonyRimWorld/releases/latest
  • - 1.4.1.1 (10-21-2022) + 1.5.0.0 (04-20-2024) Adds new types and sizes of doors to RimWorld and an extensible framework for other mods to add such doors. @@ -38,11 +39,15 @@ Commissioned by CMDR Toss Antilles. Thank you to my Patrons for supporting me in my efforts. Without you, none of this would be possible. These are the most excellent rim dwellers who support me: -Michael Fisher, Storm D Bain, Luke Salinas, WonkyWoo WeebHoo, Daniel Schott, RainerWingel , Lea Stannard, David Silberstein, Matt Harris, Kiya Nicoll, Paul Fenwick, Elodie , Genaeve , David Turner, Populous25 , Matthew Isom, Charlie Garnham, TinyATuin, Michael Cailler, Jimes Tooper, Landon Cash, Sharp Spook, Don Homer, roxxploxx , Alex Mederer, Justin Andres, Dan Jones, Kaz, Michael Whitehead, iknowdude00, Alexander , Ken Birdwell, Michael Fisher, Storm D Bain, Luke Salinas, WonkyWoo WeebHoo, Daniel Schott, RainerWingel , Lea Stannard, David Silberstein, Matt Harris, Kiya Nicoll, Paul Fenwick, Elodie , Genaeve , David Turner, Populous25 , Matthew Isom, Charlie Garnham, TinyATuin, Michael Cailler, Jimes Tooper, Landon Cash, Sharp Spook, Don Homer, roxxploxx , Alex Mederer, Justin Andres, Dan Jones, Kaz, Michael Whitehead, iknowdude00, Alexander , Ken Birdwell + ======================== Changelog ======================== +1.5.0.0 (04-20-2024) +======================== +Updated for RimWorld 1.5 to use the new MultiTileDoor class instead of invisible doors. Saves from 1.4 to 1.5 will need to rebuild their doors in the new 1.5 version. + 1.4.1.1 (10-21-2022) ======================== Initial update for RimWorld 1.4 for Doors Expanded diff --git a/About/Changelog.txt b/About/Changelog.txt index d545600..95bb1bd 100644 --- a/About/Changelog.txt +++ b/About/Changelog.txt @@ -1,3 +1,7 @@ +1.5.0.0 (04-20-2024) +======================== +Updated for RimWorld 1.5 to use the new MultiTileDoor class instead of invisible doors. Saves from 1.4 to 1.5 will need to rebuild their doors in the new 1.5 version. + 1.4.1.1 (10-21-2022) ======================== Initial update for RimWorld 1.4 for Doors Expanded diff --git a/About/Manifest.xml b/About/Manifest.xml index 08a4f6d..c520cbb 100644 --- a/About/Manifest.xml +++ b/About/Manifest.xml @@ -1,7 +1,7 @@  DoorsExpanded - 1.4.1.1 + 1.5.0.0 https://raw.githubusercontent.com/jecrell/DoorsExpanded/master/About/Manifest.xml diff --git a/About/Version.txt b/About/Version.txt index 3d5fc67..5d7661f 100644 --- a/About/Version.txt +++ b/About/Version.txt @@ -1 +1 @@ -1.4.1.1 +1.5.0.0 diff --git a/LoadFolders.xml b/LoadFolders.xml index 46b13fb..59cf667 100644 --- a/LoadFolders.xml +++ b/LoadFolders.xml @@ -22,4 +22,8 @@
  • /
  • 1.4
  • + +
  • /
  • +
  • 1.5
  • +
    diff --git a/Source/Building_DoorExpanded.cs b/Source/Building_DoorExpanded.cs index 01e556e..ed9d250 100644 --- a/Source/Building_DoorExpanded.cs +++ b/Source/Building_DoorExpanded.cs @@ -8,6 +8,7 @@ using Verse.AI; using Verse.AI.Group; using Verse.Sound; +using static HarmonyLib.Code; namespace DoorsExpanded { @@ -15,268 +16,27 @@ namespace DoorsExpanded /// /// Building_DoorExpanded /// - /// What: A class for multi-celled, larger and more complicated doors. - /// - /// HowWhy: This class is a copy of the Building_Door class without inheriting it. - /// This prevents it from being passed into region checks that cause region - /// link errors. It also spawns in Building_DoorRegionHandler classed Things - /// to act as invisible doors between the spaces of the larger door. This - /// prevents portal errors. + /// What: Originally, a class for multi-celled, larger and more complicated doors. + /// RimWorld 1.5 officially implemented a Building_MultiTileDoor class. This class is + /// somewhat obsolete and exists as an extension of MultiTileDoor. Previous versions of + /// RimWorld required a complete new class. + /// + /// There is still value in the Expanded class, as it allows for swinging doors, stretching doors, + /// and is also extended into remote controlled doors. /// /// - // TODO: Since we're spending so much effort copying and patching things such that a door that's not a - // Building_Door acts like Building_Door with extra features just to avoid RimWorld limitations regarding doors, - // at this point, shouldn't we consider attacking those limitations directly rather than all these workarounds? - public class Building_DoorExpanded : Building + public class Building_DoorExpanded : Building_MultiTileDoor { - // All of these constants are currently the same as those in Building_Door. - private const float OpenTicks = 45f; - private const int CloseDelayTicks = 110; - private const int WillCloseSoonThreshold = 111; - private const int ApproachCloseDelayTicks = 300; - private const int MaxTicksSinceFriendlyTouchToAutoClose = 120; - private const float PowerOffDoorOpenSpeedFactor = 0.25f; - private const float VisualDoorOffsetStart = 0f; - internal const float VisualDoorOffsetEnd = 0.45f; - private const float NotifyFogGridDoorOpenPct = 0.4f; - private List invisDoors = new(); private CompProperties_DoorExpanded props; - private CompPowerTrader powerComp; private CompForbiddable forbiddenComp; - private bool openInt; - private bool holdOpenInt; - private int lastFriendlyTouchTick = -9999; - protected int ticksUntilClose; - protected int ticksSinceOpen; - private bool freePassageWhenClearedReachabilityCache; - private Pawn approachingPawn; - private bool lastForbiddenState; - private bool preventDoorOpenRecursion; - private bool preventDoorTryCloseRecursion; - - [Obsolete("Use Props instead")] - public DoorExpandedDef Def => def as DoorExpandedDef; - - public CompProperties_DoorExpanded Props => - props ??= def.GetDoorExpandedProps() ?? throw new Exception("Missing " + typeof(CompProperties_DoorExpanded)); - - public List InvisDoors => invisDoors; - - public bool Open => props.doorType is DoorType.FreePassage || openInt; - - protected virtual bool OpenInt - { - get => openInt; - set - { - if (openInt == value) - return; - openInt = value; - foreach (var invisDoor in invisDoors) - { - invisDoor.Open = value; - } - } - } - - public virtual bool HoldOpen => holdOpenInt; - - public int TicksUntilClose => ticksUntilClose; - - public int TicksSinceOpen => ticksSinceOpen; - - public bool FreePassage => Open && (HoldOpen || !WillCloseSoon); - - public int TicksTillFullyOpened - { - get - { - var ticksTillFullyOpened = TicksToOpenNow - ticksSinceOpen; - if (ticksTillFullyOpened < 0) - { - ticksTillFullyOpened = 0; - } - return ticksTillFullyOpened; - } - } - - public bool WillCloseSoon - { - get - { - if (!Spawned) - { - return true; - } - if (!Open) - { - return true; - } - if (HoldOpen) - { - return false; - } - if (ticksUntilClose > 0 && ticksUntilClose <= WillCloseSoonThreshold && !BlockedOpenMomentary) - { - return true; - } - if (CanTryCloseAutomatically && !BlockedOpenMomentary) - { - return true; - } - var searchRect = this.OccupiedRect().ExpandedBy(1); - foreach (var c in searchRect) - { - if (!searchRect.IsCorner(c) && c.InBounds(Map)) - { - var thingList = c.GetThingList(Map); - for (var j = 0; j < thingList.Count; j++) - { - if (thingList[j] is Pawn pawn && !pawn.HostileTo(this) && !pawn.Downed && - (pawn.Position == Position || (pawn.pather.Moving && pawn.pather.nextCell == Position))) - { - return true; - } - } - } - } - return true; - } - } - - public bool BlockedOpenMomentary - { - get - { - foreach (var c in this.OccupiedRect()) - { - var thingList = c.GetThingList(Map); - for (var i = 0; i < thingList.Count; i++) - { - var thing = thingList[i]; - if (thing.def.category is ThingCategory.Item or ThingCategory.Pawn) - { - return true; - } - } - } - return false; - } - } - - public bool DoorPowerOn => powerComp is { PowerOn: true }; - - public bool DoorPowerOff => powerComp is { PowerOn: false }; - - public bool SlowsPawns => /*!DoorPowerOn ||*/ TicksToOpenNow > 20; - - public int TicksToOpenNow => DoorOpenTicks(StatRequest.For(this), DoorPowerOn); - - internal bool CanTryCloseAutomatically => FriendlyTouchedRecently && !HoldOpen; - - internal protected virtual bool FriendlyTouchedRecently => - Find.TickManager.TicksGame < lastFriendlyTouchTick + MaxTicksSinceFriendlyTouchToAutoClose; - - public override bool FireBulwark => !Open && base.FireBulwark; - + private const float VisualDoorOffsetStart = 0f; + internal const float VisualDoorOffsetEnd = 0.45f; public virtual bool Forbidden => forbiddenComp?.Forbidden ?? false; - private float OpenPct - { - get - { - // If TicksToOpenNow == 0 (instantaneous open), have to use different logic to avoid NRE. - var ticksToOpenNow = TicksToOpenNow; - if (ticksToOpenNow == 0) - { - return Open ? 1f : 0f; - } - else - { - return Mathf.Clamp01((float)ticksSinceOpen / ticksToOpenNow); - } - } - } - - // This method works for both Building_Door and Building_DoorExpanded. - private static int DoorOpenTicks(StatRequest statRequest, bool doorPowerOn, bool applyPostProcess = true) - { - if (statRequest.Def.GetDoorExpandedProps() is { doorType: DoorType.FreePassage }) - { - return 0; - } - var ticksToOpenNow = OpenTicks / StatDefOf.DoorOpenSpeed.Worker.GetValue(statRequest, applyPostProcess); - if (doorPowerOn) - { - ticksToOpenNow *= PowerOffDoorOpenSpeedFactor; - } - return Mathf.RoundToInt(ticksToOpenNow); - } - - public static float DoorOpenTime(StatRequest statRequest, bool doorPowerOn, bool applyPostProcess) - { - return GenTicks.TicksToSeconds(DoorOpenTicks(statRequest, doorPowerOn, applyPostProcess)); - } - - // This method works for both Building_Door and Building_DoorExpanded. - public static string DoorOpenTimeExplanation(StatRequest statRequest, bool doorPowerOn, StatDef stat) - { - var explanation = new StringBuilder(); - - // Treat powered door open speed as the "normal" speed. - // This is done partly due to the lack of a vanilla "has power" translation key. - var defaultSpeed = OpenTicks * PowerOffDoorOpenSpeedFactor; - explanation.AppendLine("StatsReport_BaseValue".Translate() + ": " + stat.ValueToString(defaultSpeed / GenTicks.TicksPerRealSecond)); - - if (statRequest.Def.GetDoorExpandedProps() is { doorType: DoorType.FreePassage }) - { - explanation.AppendLine($"{DoorType.FreePassage}: x0"); - return explanation.ToString(); - } - - var doorOpenSpeedStat = StatDefOf.DoorOpenSpeed; - var doorOpenSpeed = doorOpenSpeedStat.Worker.GetValue(statRequest); - explanation.AppendLine($"{doorOpenSpeedStat.LabelCap}:"); - var doorOpenSpeedExplanation = doorOpenSpeedStat.Worker.GetExplanationFull(statRequest, doorOpenSpeedStat.toStringNumberSense, doorOpenSpeed); - explanation.AppendLine(" " + string.Join("\n ", - doorOpenSpeedExplanation.Split(new[] { '\n' }, System.StringSplitOptions.RemoveEmptyEntries))); - explanation.AppendLine($" 1/x => x{(1f / doorOpenSpeed).ToStringPercent()}"); - - if (!doorPowerOn) - { - explanation.AppendLine($"{"NoPower".Translate()}: x{(1f / PowerOffDoorOpenSpeedFactor).ToStringPercent()}"); - } - - return explanation.ToString(); - } - - // This method works for both Building_Door and Building_DoorExpanded. - public static bool DoorNeedsPower(ThingDef def) => def.HasComp(typeof(CompPowerTrader)); - - // This method works for both Building_Door and Building_DoorExpanded. - public static bool? DoorIsPoweredOn(Thing thing) - { - if (thing is Building_Door door) - return door.DoorPowerOn; - else if (thing is Building_DoorExpanded doorEx) - return doorEx.DoorPowerOn; - return null; - } - - public override void PostMake() - { - base.PostMake(); - _ = Props; // ensures props is initialized - powerComp = GetComp(); - forbiddenComp = GetComp(); - } + public CompProperties_DoorExpanded Props => + props ??= def.GetDoorExpandedProps() ?? throw new Exception("Missing " + typeof(CompProperties_DoorExpanded)); - public override void PostMapInit() - { - if (Spawned) - SpawnInvisDoorsAsNeeded(Map, this.OccupiedRect()); - } public override void SpawnSetup(Map map, bool respawningAfterLoad) { @@ -286,25 +46,14 @@ public override void SpawnSetup(Map map, bool respawningAfterLoad) // Fortunately once rotated, no further non-1x1 rotations will change the footprint further. // Restricted rotation logic in both patched Designator_Place and Blueprint shouldn't allow invalid rotations by // this point, but better safe than sorry, especially if this is spawned without Designator_Place or Blueprint. - Rotation = DoorRotationAt(def, props, Position, Rotation, map); + Rotation = DoorRotationAt(def, Props, Position, Rotation, map); - // Note: We only want invisDoors to register in the edificeGrid (during its SpawnSetup). - // A harmony patch to EdificeGrid.Register, which is called in Building.SpawnSetup, - // prevents this Building_DoorExpanded from being registered in the edificeGrid. base.SpawnSetup(map, respawningAfterLoad); - // During game loading/initialization, we can't tell whether another door (including invis door) is actually spawned - // (and thus listed in GridsUtility.GetThingList()), since things are initially set to unspawned state and then spawned - // as the game is being initialized, so another door may have SpawnSetup called later than this. - // Therefore, we delay spawning (and the involved sanity checks) of invis doors until game is initialized. - if (!respawningAfterLoad) - { - SpawnInvisDoorsAsNeeded(map, this.OccupiedRect()); - } powerComp = GetComp(); forbiddenComp = GetComp(); - SetForbidden(Forbidden); + ForbidUtility.SetForbidden(this,Forbidden); ClearReachabilityCache(map); if (BlockedOpenMomentary) { @@ -312,550 +61,47 @@ public override void SpawnSetup(Map map, bool respawningAfterLoad) } } - // Note: This method is also filled with sanity checks for invis doors that manifest as warnings. - private void SpawnInvisDoorsAsNeeded(Map map, CellRect occupiedRect) - { - if (TLog.Enabled) - TLog.Log(this, $"{this} (#invisDoors={invisDoors.Count}/{occupiedRect.Area})"); - var invisDoorsToRespawn = new List(); - var invisDoorsToReposition = new List(); - var spawnedInvisDoors = new List(); - var errors = new List(); - - if (invisDoors.Count > 0) - { - var invisDoorCells = new HashSet(); // only for detecting multiple existing invis doors at same cell - for (var i = invisDoors.Count - 1; i >= 0; i--) - { - var invisDoor = invisDoors[i]; - var removeInvisDoor = false; - if (invisDoor is null) - { - errors.Add($"{this}.invisDoors[{i}] is unexpectedly null - removing it"); - removeInvisDoor = true; - } - else if (invisDoor.Destroyed) - { - errors.Add($"{invisDoor} is unexpectedly destroyed - removing it"); - removeInvisDoor = true; - } - else - { - if (!invisDoor.Spawned) - { - // Error msg is added later in this case. - removeInvisDoor = true; - invisDoorsToRespawn.Add(invisDoor); - } - else - { - var cell = invisDoor.Position; - if (!occupiedRect.Contains(cell)) - { - errors.Add($"{invisDoor} has position {cell} outside of {occupiedRect} - destroying it"); - removeInvisDoor = true; - invisDoor.Destroy(); - } - else if (!invisDoorCells.Add(cell)) - { - var existingInvisDoors = cell.GetThingList(map).OfType(); - errors.Add($"{invisDoor} has position {cell} taken by multiple invis doors " + - $"({existingInvisDoors.ToStringSafeEnumerable()}) - destroying it"); - removeInvisDoor = true; - invisDoor.Destroy(); - } - } - } - - if (removeInvisDoor) - { - invisDoors.RemoveAt(i); - } - else - { - if (invisDoor.ParentDoor is null) - { - errors.Add($"{invisDoor} has no parent - reparenting it to {this}"); - invisDoor.ParentDoor = this; - } - else if (invisDoor.ParentDoor != this) - { - errors.Add($"{invisDoor} has different parent - removing it from {this}"); - // Don't destroy this invis door here - let the invis door's parent handle it. - invisDoors.RemoveAt(i); - } - - if (invisDoor.Faction != Faction) - { - errors.Add($"{invisDoor} has different faction ({invisDoor.Faction}) - setting it to {Faction}"); - invisDoor.SetFactionDirect(Faction); - } - } - } - } - - foreach (var cell in occupiedRect) - { - var thingList = cell.GetThingList(map); - // Another spawned overlapping doorEx shouldn't ever by possible due to GenSpawn.SpawningWipes checks, but just in case... - foreach (var existingThing in thingList) - { - if (existingThing != this && existingThing is Building_DoorExpanded existingDoorEx) - { - var existingOccupiedRect = existingDoorEx.OccupiedRect(); - if (occupiedRect.Overlaps(existingOccupiedRect)) - { - errors.Add($"Unexpected {existingDoorEx} (occupying {existingOccupiedRect}) overlaps {this} " + - $"(occupying {occupiedRect}) - refunding it"); - // This should also destroy all the invis doors associated with the refunded doorEx. - GenSpawn.Refund(existingDoorEx, map, occupiedRect); - } - } - } - - Building_DoorRegionHandler invisDoor = null; - var existingInvisDoors = thingList.OfType(); - foreach (var existingInvisDoor in existingInvisDoors) - { - if (existingInvisDoor.ParentDoor != this) - { - errors.Add($"Unexpected {invisDoor} already spawned at {cell} - destroying it"); - // By this point, it's impossible for another spawned doorEx to overlap this doorEx, - // so if existing invis door's parent is another doorEx, assume it's either unspawned or in another location, - // so don't destroy that doorEx. Just destroy - invisDoor.Destroy(); - } - else if (invisDoor is not null) - { - // This isn't redundant with the earlier multiple invis doors check, since it's technically possible for - // some existing invis doors at the cell to not all be within invisDoors, thus warranting this sanity check. - errors.Add($"{invisDoor} has position {cell} taken by multiple invis doors " + - $"({existingInvisDoors.ToStringSafeEnumerable()}) - destroying it"); - invisDoor.Destroy(); - } - else - { - invisDoor = existingInvisDoor; - } - } - - if (invisDoor is null) - { - if (invisDoorsToRespawn.Count > 0) - { - invisDoor = invisDoorsToRespawn.Pop(); - errors.Add($"{invisDoor} is unexpectedly unspawned - respawning it at {cell}"); - } - else - { - invisDoor = (Building_DoorRegionHandler)ThingMaker.MakeThing(HeronDefOf.HeronInvisibleDoor); - invisDoor.ParentDoor = this; - invisDoor.SetFactionDirect(Faction); - } - GenSpawn.Spawn(invisDoor, cell, map); - spawnedInvisDoors.Add(invisDoor); - } - - // Open is the only field that needs to be manually synced with the parent door; - // all other fields in the invis door are unused. - invisDoor.Open = Open; - } - - foreach (var invisDoor in invisDoorsToRespawn) - { - errors.Add($"{invisDoor} is unexpectedly unspawned and is extraneous - destroying it"); - invisDoor.Destroy(); - } - - invisDoors.AddRange(spawnedInvisDoors); - - if (errors.Count > 0) - { - var errorMsg = $"[Doors Expanded] Encountered errors when spawning invis doors for {this}:\n" + errors.ToLineList("\t"); - if (spawnedInvisDoors.Count > 0) - errorMsg += "\nSpawned invis doors:\n" + spawnedInvisDoors.Select(invisDoor => invisDoor.ToString()).ToLineList("\t"); - Log.Error(errorMsg); - } - } - - public override void DeSpawn(DestroyMode mode = DestroyMode.Vanish) - { - TLog.Log(this); - var spawnedInvisDoors = invisDoors.FindAll(invisDoor => invisDoor.Spawned); - foreach (var invisDoor in spawnedInvisDoors) - { - // If this parent door is respawned later, it will always recreate the invis doors, - // so destroy (rather than just despawn) existing invis doors. - // And invis doors are always despawned/destroyed in the default Vanish mode. - invisDoor.Destroy(); - } - invisDoors.Clear(); - var map = Map; - // Note: To be safe, this room notifying is done after all invis doors are despawned. - // See also HarmonyPatches.InvisDoorRoomNotifyContainedThingSpawnedOrDespawnedPrefix. - foreach (var invisDoor in spawnedInvisDoors) - { - // Following partially copied from Thing.DeSpawn. - map.regionGrid.GetValidRegionAt_NoRebuild(Position)?.Room?.Notify_ContainedThingSpawnedOrDespawned(invisDoor); - } - // Following conditions copied from Building.DeSpawn. - if (mode is not DestroyMode.WillReplace && def.MakeFog) - { - // FogGrid.Notify_FogBlockerRemoved needs to be called on all invis door positions (same as OccupiedRect()), - // or else there can be some cases where despawning a large door doesn't properly defog rooms. - // Building.DeSpawn only calls this on the single Position location. - // See also HarmonyPatches.InvisDoorMakeFogTranspiler. - // Following is kinda inefficient, but this isn't perfomance critical code, so it shouldn't matter. - foreach (var c in this.OccupiedRect()) - { - map.fogGrid.Notify_FogBlockerRemoved(c); - } - } - base.DeSpawn(mode); - ClearReachabilityCache(map); - } - - public override void Destroy(DestroyMode mode = DestroyMode.Vanish) - { - TLog.Log(this); - base.Destroy(mode); - } - - public override void ExposeData() - { - base.ExposeData(); - Scribe_Collections.Look(ref invisDoors, nameof(invisDoors), LookMode.Reference); - invisDoors ??= new List(); // in case a save file somehow has missing or null invisDoors - Scribe_Values.Look(ref openInt, "open", false); - Scribe_Values.Look(ref holdOpenInt, "holdOpen", false); - Scribe_Values.Look(ref lastFriendlyTouchTick, nameof(lastFriendlyTouchTick), 0); - Scribe_References.Look(ref approachingPawn, nameof(approachingPawn)); - if (Scribe.mode is LoadSaveMode.LoadingVars) - { - // Ensure props is available for Open usage since PostMake hasn't been called yet. - // base.ExposeData() ensures that def is available, which is required for props initialization. - _ = Props; - if (Open) - { - ticksSinceOpen = TicksToOpenNow; - } - } - } - - // Simulated to be "virtual" via Harmony patch on Thing.SetFactionDirect. - public new void SetFactionDirect(Faction newFaction) - { - if (Faction == newFaction) - return; - base.SetFactionDirect(newFaction); - foreach (var invisDoor in invisDoors) - { - invisDoor.SetFactionDirect(newFaction); - } - } - - public override void SetFaction(Faction newFaction, Pawn recruiter = null) - { - // This check prevents redundant calls from all the invis doors' SetFaction. - if (Faction == newFaction) - return; - base.SetFaction(newFaction, recruiter); - foreach (var invisDoor in invisDoors) - { - invisDoor.SetFaction(newFaction, recruiter); - } - if (Spawned) - { - ClearReachabilityCache(Map); - } - } - - public override void Tick() - { - // Workaround for MinifyEverything issue where reinstalling doors sometimes causes a transient and harmless NRE - // in GridsUtility.GetThingList. This also effects vanilla doors, which are fixed in a harmony patch - // (see HarmonyPatches.BuildingDoorTickPrefix). - if (!Spawned) - return; - - base.Tick(); - - var occupiedRect = this.OccupiedRect(); - var map = Map; - - // Periodic sanity checks. - if (invisDoors.Count(invisDoor => invisDoor is { Spawned: true }) != occupiedRect.Area || - this.IsHashIntervalTick(GenTicks.TickLongInterval)) - { - SpawnInvisDoorsAsNeeded(map, occupiedRect); - } - - if (FreePassage != freePassageWhenClearedReachabilityCache) - { - ClearReachabilityCache(map); - } - if (!Open) - { - if (ticksSinceOpen > 0) - { - ticksSinceOpen--; - } - if ((Find.TickManager.TicksGame + thingIDNumber.HashOffset()) % TemperatureTuning.Door_TempEqualizeIntervalClosed == 0) - { - GenTemperature.EqualizeTemperaturesThroughBuilding(this, props.tempEqualizeRate, twoWay: false); - } - } - else - { - if (ticksSinceOpen < TicksToOpenNow) - { - ticksSinceOpen++; - } - var isPawnPresent = false; - foreach (var c in occupiedRect) - { - var thingList = c.GetThingList(map); - for (var i = 0; i < thingList.Count; i++) - { - if (thingList[i] is Pawn pawn) - { - CheckFriendlyTouched(pawn); - isPawnPresent = true; - } - } - } - if (ticksUntilClose > 0) - { - if (isPawnPresent) - { - ticksUntilClose = CloseDelayTicks; - } - ticksUntilClose--; - if (ticksUntilClose <= 0 && !HoldOpen && !DoorTryClose()) - { - ticksUntilClose = 1; - } - } - else if (CanTryCloseAutomatically) - { - ticksUntilClose = CloseDelayTicks; - } - if ((Find.TickManager.TicksGame + thingIDNumber.HashOffset()) % TemperatureTuning.Door_TempEqualizeIntervalOpen == 0) - { - GenTemperature.EqualizeTemperaturesThroughBuilding(this, TemperatureTuning.Door_TempEqualizeRate, twoWay: false); - } - if (OpenPct >= NotifyFogGridDoorOpenPct && approachingPawn is not null) - { - // Following is kinda inefficient, but this isn't perfomance critical code, so it shouldn't matter. - foreach (var invisDoor in invisDoors) - { - map.fogGrid.Notify_PawnEnteringDoor(invisDoor, approachingPawn); - } - approachingPawn = null; - } - } - } - - public void CheckFriendlyTouched(Pawn p) - { - if (!p.HostileTo(this) && PawnCanOpen(p)) - { - lastFriendlyTouchTick = Find.TickManager.TicksGame; - } - } - - public void Notify_PawnApproaching(Pawn p, int moveCost) - { - CheckFriendlyTouched(p); - var pawnCanOpen = PawnCanOpen(p); - if (pawnCanOpen || Open) - { - approachingPawn = p; - } - if (pawnCanOpen && !SlowsPawns) - { - var ticksToClose = Mathf.Max(ApproachCloseDelayTicks, moveCost + 1); - DoorOpen(ticksToClose); - } - } - - public void Notify_ForbiddenInputChanged() - { - var forbidden = Forbidden; - if (forbidden != lastForbiddenState) - { - SetForbidden(forbidden); - } - } - - private void SetForbidden(bool forbidden) - { - lastForbiddenState = forbidden; - foreach (var invisDoor in invisDoors) - { - invisDoor.SetForbidden(forbidden); - } - } - - public bool CanPhysicallyPass(Pawn p) - { - return FreePassage || PawnCanOpen(p) || (Open && p.HostileTo(this)); - } - - public virtual bool PawnCanOpen(Pawn p) - { - if (Map is { } map && map.Parent.doorsAlwaysOpenForPlayerPawns && p.Faction == Faction.OfPlayer) - { - return true; - } - if (p.GetLord() is { LordJob: { } lordJob } && lordJob.CanOpenAnyDoor(p)) - { - return true; - } - if (WildManUtility.WildManShouldReachOutsideNow(p)) - { - return true; - } - if (p.RaceProps.FenceBlocked && !def.building.roamerCanOpen && (!p.roping.IsRopedByPawn || !PawnCanOpen(p.roping.RopedByPawn))) - { - return false; - } - if (Faction is null) - { - return true; - } - if (p.guest is { Released: true }) - { - return true; - } - return GenAI.MachinesLike(Faction, p); - } - - public override bool BlocksPawn(Pawn p) => !Open && !PawnCanOpen(p); - - protected internal void DoorOpen(int ticksToClose = CloseDelayTicks) - { - // For compatibility with other mods that patch Building_Door.DoorOpen, - // which is only internally called within Building_Door, need to call DoorOpen on each invis door. - // However doing so would end up calling this DoorOpen recursively. - // preventDoorOpenRecursion is used to prevent this unwanted recursion. - if (preventDoorOpenRecursion) - return; - preventDoorOpenRecursion = true; - try - { - foreach (var invisDoor in invisDoors) - invisDoor.DoorOpen(ticksToClose); - } - finally - { - preventDoorOpenRecursion = false; - } - - if (Open) - { - ticksUntilClose = ticksToClose; - } - else - { - ticksUntilClose = TicksToOpenNow + ticksToClose; - OpenInt = true; - CheckClearReachabilityCacheBecauseOpenedOrClosed(); - if (DoorPowerOn) - { - def.building.soundDoorOpenPowered?.PlayOneShot(new TargetInfo(Position, Map)); - } - else - { - def.building.soundDoorOpenManual?.PlayOneShot(new TargetInfo(Position, Map)); - } - } - } - - protected internal bool DoorTryClose() + public static Rot4 DoorRotationAt(ThingDef def, CompProperties_DoorExpanded props, IntVec3 loc, Rot4 rot, Map map) { - // For compatibility with other mods that patch Building_Door.DoorTryClose, - // which is only internally called within Building_Door, need to call DoorTryClose on each invis door. - // However doing so would end up calling this DoorTryClose recursively. - // preventDoorTryCloseRecursion is used to prevent this unwanted recursion. - if (preventDoorTryCloseRecursion) - return false; - preventDoorTryCloseRecursion = true; - try - { - foreach (var invisDoor in invisDoors) - invisDoor.DoorTryClose(); - } - finally - { - preventDoorTryCloseRecursion = false; - } - - if (HoldOpen || BlockedOpenMomentary) - { - return false; - } - OpenInt = false; - CheckClearReachabilityCacheBecauseOpenedOrClosed(); - if (DoorPowerOn) - { - def.building.soundDoorClosePowered?.PlayOneShot(new TargetInfo(Position, Map)); - } - else + if (!def.rotatable) { - def.building.soundDoorCloseManual?.PlayOneShot(new TargetInfo(Position, Map)); + var size = def.Size; + if ((size.x == 1 && size.z == 1) || props.doorType is DoorType.StretchVertical or DoorType.Stretch) + rot = DoorUtility.DoorRotationAt(loc, map, false); } - return true; - } - - public void StartManualOpenBy(Pawn opener) - { - DoorOpen(); - } - - public void StartManualCloseBy(Pawn closer) - { - ticksUntilClose = CloseDelayTicks; - } - - // This exists to expose draw vectors for debugging purposes. - internal class DebugDrawVectors - { - public float openPct; - public Vector3 offsetVector, scaleVector, graphicVector; + if (!props.rotatesSouth && rot == Rot4.South) + rot = Rot4.North; + return rot; } - internal DebugDrawVectors debugDrawVectors = new(); - - public override void Draw() + protected override void DrawAt(Vector3 drawLoc, bool flip = false) { - var drawPos = DrawPos; - drawPos.y = AltitudeLayer.DoorMoveable.AltitudeFor(); - var rotation = DoorRotationAt(def, props, Position, Rotation, Map); + drawLoc.y = AltitudeLayer.DoorMoveable.AltitudeFor(); + var rotation = DoorRotationAt(def, Props, Position, Rotation, Map); Rotation = rotation; var openPct = OpenPct; for (var i = 0; i < 2; i++) { var flipped = i != 0; - var graphic = (!flipped && props.doorAsync is { } doorAsyncGraphic) + var graphic = (!flipped && Props.doorAsync is { } doorAsyncGraphic) ? doorAsyncGraphic.GraphicColoredFor(this) : Graphic; - Draw(def, props, graphic, drawPos, rotation, openPct, flipped, - i == 0 && DebugViewSettingsMoreWrite.writeDoors ? debugDrawVectors : null); - graphic.ShadowGraphic?.DrawWorker(drawPos, rotation, def, this, 0f); + Draw(def, Props, graphic, drawLoc, rotation, openPct, flipped, null); + //i == 0 && DebugViewSettingsMoreWrite.writeDoors ? debugDrawVectors : null); + graphic.ShadowGraphic?.DrawWorker(drawLoc, rotation, def, this, 0f); if (props.singleDoor) break; } if (props.doorFrame is not null) { - DrawFrameParams(def, props, drawPos, rotation, false, out var fMesh, out var fMatrix); + DrawFrameParams(def, Props, drawLoc, rotation, false, out var fMesh, out var fMatrix); Graphics.DrawMesh(fMesh, fMatrix, props.doorFrame.GraphicColoredFor(this).MatAt(rotation), 0); if (props.doorFrameSplit is not null) { - DrawFrameParams(def, props, drawPos, rotation, true, out fMesh, out fMatrix); + DrawFrameParams(def, Props, drawLoc, rotation, true, out fMesh, out fMatrix); Graphics.DrawMesh(fMesh, fMatrix, props.doorFrameSplit.GraphicColoredFor(this).MatAt(rotation), 0); } } @@ -964,7 +210,7 @@ private static void DrawDoubleSwingParams(ThingDef def, CompProperties_DoorExpan rotQuat = rotation.AsQuat; offsetVector = rotQuat * offsetVector; - var offsetMod = (VisualDoorOffsetStart + props.doorOpenMultiplier * openPct) * def.Size.x; + var offsetMod = ((VisualDoorOffsetStart + props.doorOpenMultiplier * openPct) * def.Size.x); offsetVector *= offsetMod; if (verticalRotation) @@ -1008,6 +254,7 @@ private static void DrawStandardParams(ThingDef def, CompProperties_DoorExpanded scaleVector = new Vector3(drawSize.x * persMod, 1f, drawSize.y * persMod); } + private static void DrawFrameParams(ThingDef def, CompProperties_DoorExpanded props, Vector3 drawPos, Rot4 rotation, bool split, out Mesh mesh, out Matrix4x4 matrix) @@ -1072,63 +319,19 @@ private static void DrawFrameParams(ThingDef def, CompProperties_DoorExpanded pr matrix = Matrix4x4.TRS(graphicVector, rotQuat, scaleVector); } - public static Rot4 DoorRotationAt(ThingDef def, CompProperties_DoorExpanded props, IntVec3 loc, Rot4 rot, Map map) - { - if (!def.rotatable) - { - var size = def.Size; - if ((size.x == 1 && size.z == 1) || props.doorType is DoorType.StretchVertical or DoorType.Stretch) - rot = Building_Door.DoorRotationAt(loc, map); - } - if (!props.rotatesSouth && rot == Rot4.South) - rot = Rot4.North; - return rot; - } - - public override IEnumerable GetGizmos() - { - foreach (var gizmo in base.GetGizmos()) - { - yield return gizmo; - } - if (Faction == Faction.OfPlayer) - { - yield return new Command_Toggle - { - defaultLabel = "CommandToggleDoorHoldOpen".Translate(), - defaultDesc = "CommandToggleDoorHoldOpenDesc".Translate(), - hotKey = KeyBindingDefOf.Misc3, - icon = TexCommand.HoldOpen, - isActive = () => holdOpenInt, - toggleAction = () => holdOpenInt = !holdOpenInt, - }; - if (DebugSettings.godMode) - { - yield return new Command_Toggle - { - defaultLabel = "DEV: Open", - defaultDesc = "debug".Translate(), - hotKey = KeyBindingDefOf.Misc3, - icon = TexCommand.HoldOpen, - isActive = () => OpenInt, - toggleAction = () => OpenInt = !OpenInt, - }; - } - } - } - private void ClearReachabilityCache(Map map) { map.reachability.ClearCache(); - freePassageWhenClearedReachabilityCache = FreePassage; + //freePassageWhenClearedReachabilityCache = FreePassage; } - private void CheckClearReachabilityCacheBecauseOpenedOrClosed() + // This exists to expose draw vectors for debugging purposes. + internal class DebugDrawVectors { - if (Spawned) - { - Map.reachability.ClearCacheForHostile(this); - } + public float openPct; + public Vector3 offsetVector, scaleVector, graphicVector; } + + internal DebugDrawVectors debugDrawVectors = new(); } } diff --git a/Source/Building_DoorRegionHandler.cs b/Source/Building_DoorRegionHandler.cs index aa94917..21df54b 100644 --- a/Source/Building_DoorRegionHandler.cs +++ b/Source/Building_DoorRegionHandler.cs @@ -12,181 +12,24 @@ namespace DoorsExpanded /// /// Door Region Handler /// - /// What: These are children doors spawned inside a larger door. - /// - /// Why: This class is used instead of rewriting the base RimWorld code for - /// handling regions. Regions do not handle large doors well. So this class - /// will add smaller, invisible doors, inside a bigger door. + /// Now deprecated code. This will exist only to destroy previously existing invisible doors. + /// Previously this was used in RimWorld as a way to have a door spread across as many tiles as needed. + /// A 3x5 door would simply be filled with invisible doors that acted all at once. + /// + /// As this is no longer necessary, previously saved DoorRegionHandlers will now simply delete after + /// their first tick. /// /// + [Obsolete("No longer in use. RimWorld 1.5 introduced the MultiTileDoor and Invisible Doors inside " + + "larger frames are no longer needed.")] public class Building_DoorRegionHandler : Building_Door { - private Building_DoorExpanded parentDoor; - - public Building_DoorExpanded ParentDoor - { - get => parentDoor; - set => parentDoor = value; - } - - // A harmony patch ensures that null or empty mouseover labels are not displayed. - public override string LabelMouseover => null; - - public override string Label => parentDoor.Label; - - public override string LabelShort => parentDoor.LabelShort; - - // Following Building_Door fields are going to be synced with Building_DoorExpanded (handled in that class): - // private bool openInt - since bool Open property is inlined and thus cannot be harmony patched - - // Other fields are ignored. - - // Following Building_Door non-virtual properties/methods are harmony patched to delegate to ParentDoor: - // public bool FreePassage - // public int TicksTillFullyOpened - // public bool WillCloseSoon - // - Note: Although only used by Building_Door.FreePassage, it could be used outside of vanilla code. - // public bool BlockedOpenMomentary - // public bool SlowsPawns - // public int TicksToOpenNow - // public void CheckFriendlyTouched(Pawn p) - // public void Notify_PawnApproaching(Pawn p, int moveCost) - // public bool CanPhysicallyPass(Pawn p) - // protected void DoorOpen(int ticksToClose) - in case another subclass calls this - // protected bool DoorTryClose() - in case another subclass calls this - // public void StartManualOpenBy(Pawn opener) - // public void StartManualCloseBy(Pawn closer) - // - Note: This is inlined, so can only patch its caller Pawn_PathFollower.TryEnterNextPathCell - - // Following Building_Door methods are left as-is (they might have some extraneous yet non-harmful behavior, which is fine): - // public override void PostMake() - // public override void DeSpawn(DestroyMode mode = DestroyMode.Vanish) - - // Other methods are overriden here. - - public new bool Open - { - get => Building_Door_openInt(this); - set => Building_Door_openInt(this) = value; - } - - private static readonly AccessTools.FieldRef Building_Door_openInt = - AccessTools.FieldRefAccess("openInt"); - - private static readonly AccessTools.FieldRef Building_Door_holdOpenInt = - AccessTools.FieldRefAccess("holdOpenInt"); - - private static readonly AccessTools.FieldRef Thing_positionInt = - AccessTools.FieldRefAccess("positionInt"); - - public override bool FireBulwark => ParentDoor.FireBulwark; - - public override void SpawnSetup(Map map, bool respawningAfterLoad) - { - TLog.Log(this); - // See HarmonyPatches.InvisDoorSpawnSetupTranspiler. - base.SpawnSetup(map, respawningAfterLoad); - } - - public override void DeSpawn(DestroyMode mode = DestroyMode.Vanish) - { - TLog.Log(this); - // This check is necessary to prevent errors during operations that despawn all things in the same cell, - // since despawning/destroying parent doors also destroys their invis doors. - if (Spawned) - base.DeSpawn(mode); - } - - public override void Destroy(DestroyMode mode = DestroyMode.Vanish) - { - TLog.Log(this); - // This check is necessary to prevent errors during operations that destroy all things in the same cell, - // since despawning/destroying parent doors also destroys their invis doors. - if (!Destroyed) - base.Destroy(mode); - } - - public override void ExposeData() - { - base.ExposeData(); - // This roundabout way of setting parentDoor is to handle the case where parentDoor is no longer a Building_DoorExpanded - // (potentially due to XML def/patch changes), in which case, we invalidate the position to prevent spawning this invis door. - var tempParentDoor = (ILoadReferenceable)parentDoor; - Scribe_References.Look(ref tempParentDoor, nameof(parentDoor)); - parentDoor = tempParentDoor as Building_DoorExpanded; - if (tempParentDoor is not null && parentDoor is null) - { - if (TLog.Enabled) - TLog.Log(this, $"{this} has invalid parentDoor type {tempParentDoor.GetType()} - invalidating position to avoid spawning"); - Thing_positionInt(this) = IntVec3.Invalid; - } - } - - public override void SetFaction(Faction newFaction, Pawn recruiter = null) - { - // This check is necessary to prevent infinite loop between this method and Building_DoorExpanded.SetFaction. - if (newFaction == Faction) - return; - base.SetFaction(newFaction, recruiter); - ParentDoor.SetFaction(newFaction, recruiter); - } - public override void Tick() { - // Sanity checks. These are inexpensive and thus done every tick. - var parentDoor = ParentDoor; - if (parentDoor is not { Spawned: true }) - { - var stateStr = parentDoor is null ? "null" : parentDoor.Destroyed ? "destroyed" : "unspawned"; - Log.Error($"{this}.ParentDoor is unexpectedly {stateStr} - destroying this"); - Destroy(); - return; - } - if (Faction != parentDoor.Faction) - SetFaction(parentDoor.Faction); - - // Some mods (such as OpenedDoorsDontBlockLight) directly read ticksSinceOpen or ticksUntilClose fields, - // since no public accessor exists for those fields, so for compatibility with such mods, copy them from parent door here. - ticksSinceOpen = parentDoor.TicksSinceOpen; - ticksUntilClose = parentDoor.TicksUntilClose; - - // See HarmonyPatches.InvisDoorTickTranspiler. - base.Tick(); - } - - public override void PostApplyDamage(DamageInfo dinfo, float totalDamageDealt) - { - // Note: The invis door def has useHitPoints=true, so invis doors never take damage. - // A harmony patch allows PathFinder.IsDestroyable to still return true for invis doors. - ParentDoor.TakeDamage(dinfo); - } - - public override bool PawnCanOpen(Pawn p) => ParentDoor.PawnCanOpen(p); - - public override bool BlocksPawn(Pawn p) => ParentDoor.BlocksPawn(p); - - // Only for exposing public access to Building_Door.DoorOpen. - public new void DoorOpen(int ticksToClose) => base.DoorOpen(ticksToClose); - - // Only for exposing public access to Building_Door.DoorTryClose. - public new void DoorTryClose() => base.DoorTryClose(); - - public override void Draw() - { - // Do nothing, not even call base.Draw(), since this is an invisible door. - } - - public override IEnumerable GetGizmos() - { - // This invis door shouldn't be selectable to get gizmos, but just in case, return empty. - return Enumerable.Empty(); - } - - public override string ToString() - { - if (parentDoor is null) - return base.ToString() + " (NO PARENT)"; - return base.ToString() + $" ({parentDoor}.invisDoors[{parentDoor.InvisDoors.IndexOf(this)}])"; + Log.Warning($"{this} remains from RimWorld v1.4 - destroying this to remain in-line with new" + + "RW 1.5 MultiTileDoor code for DoorsExpanded"); + Destroy(); + return; } } } diff --git a/Source/Building_DoorRemote.cs b/Source/Building_DoorRemote.cs index 739d746..0f0288b 100644 --- a/Source/Building_DoorRemote.cs +++ b/Source/Building_DoorRemote.cs @@ -33,34 +33,38 @@ public bool SecuredRemotely { securedRemotely = value; // Reactive update due to Forbidden depending on SecuredRemotely (via ForcedClosed). - Notify_ForbiddenInputChanged(); + ForbidUtility.SetForbidden(this, value); } } - protected override bool OpenInt + protected bool OpenInt { set { - base.OpenInt = value; + base.openInt = value; // Reactive update due to Forbidden depending on OpenInt (via Open via ForcedClosed). - Notify_ForbiddenInputChanged(); + ForbidUtility.SetForbidden(this, value); + } + } + + protected bool HoldOpenInt + { + set + { + base.holdOpenInt = value; } } // When secured remotely, only care about whether its held open remotely; // else can be held open either remotely or by gizmo. - public override bool HoldOpen => securedRemotely ? HoldOpenRemotely : HoldOpenRemotely || base.HoldOpen; + public bool HoldOpen => securedRemotely ? HoldOpenRemotely : HoldOpenRemotely || base.HoldOpen; public bool HoldOpenRemotely => Button is { ButtonOn: true }; public bool ForcedClosed => SecuredRemotely && !Open; - public override bool Forbidden => ForcedClosed || base.Forbidden; + public bool Forbidden => ForcedClosed || ForbidUtility.IsForbidden(this, this.Faction); - // For purposes of determining whether a door can be closed automatically, - // treat a powered door that's linked to an enabled button (NeedsPower is false) as always being "friendly touched". - internal protected override bool FriendlyTouchedRecently => - button is { NeedsPower: false } && DoorPowerOn || base.FriendlyTouchedRecently; public override void ExposeData() { @@ -75,12 +79,11 @@ public override void ExposeData() private static readonly float LockDrawY = AltitudeLayer.MetaOverlays.AltitudeFor() + Altitudes.AltInc * 6; // from OverlayDrawer.RenderQuestionMarkOverlay - public override void Draw() + protected override void DrawAt(Vector3 drawLoc, bool flip = false) { if (ForcedClosed) { // This is based off OverlayDrawer.RenderQuestionMarkOverlay/RenderPulsingOverlayInternal, with customized parameters. - var drawLoc = DrawPos; drawLoc.y = LockDrawY; var sineInput = (Time.realtimeSinceStartup + 397f * (thingIDNumber % 571)) * LockPulseFrequency; var alpha = (Mathf.Sin(sineInput) + 1f) * 0.5f; @@ -90,7 +93,7 @@ public override void Draw() drawBatch.DrawMesh(MeshPool.plane05, Matrix4x4.TRS(drawLoc, Quaternion.identity, Vector3.one), material, layer: 0, renderInstanced: true); } - base.Draw(); + base.DrawAt(drawLoc, flip); } private static readonly AccessTools.FieldRef OverlayDrawer_drawBatch = @@ -161,7 +164,7 @@ public override IEnumerable GetGizmos() public void Notify_ButtonPushed() { - if (DoorPowerOff) + if (powerComp is CompPowerTrader power && !power.PowerOn) { Messages.Message("PH_CannotOpenRemotelyWithoutPower".Translate(Label), this, MessageTypeDefOf.RejectInput); return; diff --git a/Source/CompProperties_DoorExpanded.cs b/Source/CompProperties_DoorExpanded.cs index e08a316..45065b5 100644 --- a/Source/CompProperties_DoorExpanded.cs +++ b/Source/CompProperties_DoorExpanded.cs @@ -71,7 +71,11 @@ public override void ResolveReferences(ThingDef parentDef) if (typeof(Building_DoorRemote).IsAssignableFrom(parentDef.thingClass)) { var remoteDoorDescription = "PH_RemoteDoor".Translate(); - if (!parentDef.description.Contains(remoteDoorDescription)) + if(parentDef.description is null) + { + parentDef.description = remoteDoorDescription; + } + else if(!parentDef.description.Contains(remoteDoorDescription)) { parentDef.description += " " + remoteDoorDescription; } @@ -106,6 +110,7 @@ public override void ResolveReferences(ThingDef parentDef) .FindAll(def => typeof(Building_DoorRemoteButton).IsAssignableFrom(def.thingClass)); buildingProps.relatedBuildCommands = cachedDoorRemoteDefs; } + } [ThreadStatic] diff --git a/Source/DebugInspectorPatches.cs b/Source/DebugInspectorPatches.cs deleted file mode 100644 index 099dcc1..0000000 --- a/Source/DebugInspectorPatches.cs +++ /dev/null @@ -1,455 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Reflection; -using System.Reflection.Emit; -using System.Text; -using HarmonyLib; -using RimWorld; -using UnityEngine; -using Verse; -using Verse.AI; - -namespace DoorsExpanded -{ - public static class DebugViewSettingsMoreDraw - { - public static bool drawDoorTempEqualization; - } - - public static class DebugViewSettingsMoreWrite - { - public static bool writeTemperature; - public static bool writeEdificeGrid; - public static bool writeDoors; - public static bool writePatchCallRegistry; - } - - public static class DebugInspectorPatches - { - public static void PatchDebugInspector() - { - //var harmony = HarmonyPatches.harmony; - //var type = typeof(DebugInspectorPatches); - //harmony.Patch(original: AccessTools.Constructor(typeof(Dialog_DebugSettingsMenu)), - // transpiler: new HarmonyMethod(type, nameof(AddMoreDebugViewSettingsTranspiler))); - //harmony.Patch(original: AccessTools.Method(typeof(Dialog_DebugSettingsMenu), "DoListingItems"), - // transpiler: new HarmonyMethod(type, nameof(AddMoreDebugViewSettingsTranspiler)), - // postfix: new HarmonyMethod(type, nameof(AddMoreDebugViewSettingsPostfix))); - //harmony.Patch(original: AccessTools.Method(typeof(EditWindow_DebugInspector), "CurrentDebugString"), - // transpiler: new HarmonyMethod(type, nameof(EditWindowDebugInspectorTranspiler))); - //harmony.Patch(original: AccessTools.Method(typeof(District), "DebugString"), - // postfix: new HarmonyMethod(type, nameof(DistrictMoreDebugString))); - //harmony.Patch(original: AccessTools.Method(typeof(Room), nameof(Room.DebugString)), - // postfix: new HarmonyMethod(type, nameof(RoomMoreDebugString))); - //harmony.Patch(original: AccessTools.Method(typeof(DoorsDebugDrawer), nameof(DoorsDebugDrawer.DrawDebug)), - // postfix: new HarmonyMethod(type, nameof(AddMoreDoorsDebugDrawer))); - } - - private static Dictionary patchCallRegistry; - - // Note: Callers of RegisterPatch and RegisterPatchCalled need to have #define PATCH_CALL_REGISTRY in their file, - // since its callers of Conditional-attributed methods that are ellided rather than the method itself. - [Conditional("PATCH_CALL_REGISTRY")] - public static void RegisterPatch(string name) - { - if (name is not null) - { - patchCallRegistry ??= new Dictionary(); - patchCallRegistry[name] = false; - } - } - - [Conditional("PATCH_CALL_REGISTRY")] - public static void RegisterPatchCalled(string name) => patchCallRegistry[name] = true; - - // Dialog_DebugSettingsMenu constructor (for RW 1.2) - // Dialog_DebugSettingsMenu.DoListingItems - public static IEnumerable AddMoreDebugViewSettingsTranspiler( - IEnumerable instructions) - { - // This transforms the following code: - // typeof(DebugViewSettings).GetFields() - // into: - // AddMoreDebugViewSettings(typeof(DebugViewSettings).GetFields()) - - var instructionList = instructions.AsList(); - var debugViewSettingsIndex = instructionList.FindIndex(instr => instr.OperandIs(typeof(DebugViewSettings))); - if (debugViewSettingsIndex >= 0) - { - var getFieldsIndex = instructionList.FindIndex(debugViewSettingsIndex + 1, instr => instr.Calls(methodof_Type_GetFields)); - instructionList.SafeInsertRange(getFieldsIndex + 1, new[] - { - new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(DebugInspectorPatches), nameof(AddMoreDebugViewSettings))), - }); - } - return instructionList; - } - - private static FieldInfo[] AddMoreDebugViewSettings(FieldInfo[] fields) - { - var fieldList = fields.ToList(); - var fieldsToInsert = typeof(DebugViewSettingsMoreWrite).GetFields().AsEnumerable(); - if (patchCallRegistry is null) - fieldsToInsert = fieldsToInsert.Where(field => field != fieldof_MoreDebugViewSettings_writePatchCallRegistry); - fieldList.InsertRange(fieldList.IndexOf(fieldof_DebugViewSettings_writePathCosts) + 1, fieldsToInsert); - fieldList.InsertRange(fieldList.IndexOf(fieldof_DebugViewSettings_drawDoorsDebug) + 1, - typeof(DebugViewSettingsMoreDraw).GetFields()); - return fieldList.ToArray(); - } - - private static readonly MethodInfo methodof_Type_GetFields = AccessTools.Method(typeof(Type), nameof(Type.GetFields)); - private static readonly FieldInfo fieldof_DebugViewSettings_drawDoorsDebug = - AccessTools.Field(typeof(DebugViewSettings), nameof(DebugViewSettings.drawDoorsDebug)); - private static readonly FieldInfo fieldof_DebugViewSettings_writePathCosts = - AccessTools.Field(typeof(DebugViewSettings), nameof(DebugViewSettings.writePathCosts)); - private static readonly FieldInfo fieldof_MoreDebugViewSettings_writePatchCallRegistry = - AccessTools.Field(typeof(DebugViewSettingsMoreWrite), nameof(DebugViewSettingsMoreWrite.writePatchCallRegistry)); - - public static void AddMoreDebugViewSettingsPostfix() - { - // We do dynamic patching here to try to avoid the performance overhead in the patched methods when toggled off. - if (temperatureEqualizationMethodsPatched != DebugViewSettingsMoreDraw.drawDoorTempEqualization) - { - foreach (var pair in temperatureEqualizationMethods) - { - var origMethod = pair.Key; - var patchMethod = pair.Value; - if (temperatureEqualizationMethodsPatched) - HarmonyPatches.harmony.Unpatch(origMethod, patchMethod); - else - HarmonyPatches.harmony.Patch(origMethod, transpiler: new HarmonyMethod(patchMethod)); - } - temperatureEqualizationMethodsPatched = DebugViewSettingsMoreDraw.drawDoorTempEqualization; - } - } - - private static bool temperatureEqualizationMethodsPatched = false; - private static readonly Dictionary temperatureEqualizationMethods = new() - { - [AccessTools.Method(typeof(RoomTempTracker), nameof(RoomTempTracker.EqualizeTemperature))] = - AccessTools.Method(typeof(DebugInspectorPatches), nameof(RoomTempTracker_EqualizeTemperature_Transpiler)), - [AccessTools.Method(typeof(GenTemperature), nameof(GenTemperature.EqualizeTemperaturesThroughBuilding))] = - AccessTools.Method(typeof(DebugInspectorPatches), nameof(GenTemperature_EqualizeTemperaturesThroughBuilding_Transpiler)), - }; - - public static IEnumerable RoomTempTracker_EqualizeTemperature_Transpiler(IEnumerable instructions) - { - // This transforms the following code: - // this.room.Temperature += WallEqualizationTempChangePerInterval() - // into: - // RoomTempTracker_EqualizeTemperature_SetRoomTemperature(this.room, - // this.room.Temperature + WallEqualizationTempChangePerInterval()) - - return Transpilers.MethodReplacer(instructions, - AccessTools.PropertySetter(typeof(Room), nameof(Room.Temperature)), - AccessTools.Method(typeof(DebugInspectorPatches), - nameof(RoomTempTracker_EqualizeTemperature_SetRoomTemperature))); - } - - private static void RoomTempTracker_EqualizeTemperature_SetRoomTemperature(Room room, float temperature) - { - SetRoomTemperatureAndDrawDelta(room, temperature, Color.red); - } - - public static IEnumerable GenTemperature_EqualizeTemperaturesThroughBuilding_Transpiler( - IEnumerable instructions) - { - // This transforms the following code: - // room.Temperature += tempDelta - // into: - // SetRoomTemperatureAndDrawDelta(room, room.Temperature + tempDelta) - - return Transpilers.MethodReplacer(instructions, - AccessTools.PropertySetter(typeof(Room), nameof(Room.Temperature)), - AccessTools.Method(typeof(DebugInspectorPatches), - nameof(GenTemperature_EqualizeTemperaturesThroughBuilding_SetRoomTemperature))); - } - - private static void GenTemperature_EqualizeTemperaturesThroughBuilding_SetRoomTemperature(Room room, float temperature) - { - SetRoomTemperatureAndDrawDelta(room, temperature, Color.white); - } - - private static void SetRoomTemperatureAndDrawDelta(Room room, float temperature, Color color) - { - if (room.IsDoorway) - { - var tempDelta = temperature - room.Temperature; - var door = room.FirstRegion.door; - if (door is Building_DoorRegionHandler) - Log.Message($"[{color}] temperature of {door}: {room.Temperature} => {temperature}"); - if (tempDelta != 0f) - MoteMaker.ThrowText(room.ExtentsClose.CenterVector3, room.Map, $"{tempDelta:+0.##;-0.##}", color); - } - room.Temperature = temperature; - } - - // EditWindow_DebugInspector.CurrentDebugString - public static IEnumerable EditWindowDebugInspectorTranspiler(IEnumerable instructions, - MethodBase method, ILGenerator ilGen) - { - var methodof_UI_MouseCell = AccessTools.Method(typeof(UI), nameof(UI.MouseCell)); - var methodof_object_ToString = AccessTools.Method(typeof(object), nameof(ToString)); - var instructionList = instructions.AsList(); - var locals = new Locals(method, ilGen); - - // Assume first found StringBuilder var (currently the only StringBuilder var) is the one we want. - var stringBuilderVar = locals.FromStloc(instructionList.Find(instr => - locals.IsStloc(instr, out var local) && local.LocalType == typeof(StringBuilder))); - - var mouseCellIndex = instructionList.FindIndex(instr => instr.Calls(methodof_UI_MouseCell)); - var mouseCellVar = locals.FromStloc(instructionList[mouseCellIndex + 1]); - - var mouseCellToStringIndex = instructionList.FindSequenceIndex( - instr => locals.IsLdloca(instr, mouseCellVar), - instr => instr.Is(OpCodes.Constrained, typeof(IntVec3)), - instr => instr.Calls(methodof_object_ToString)); - instructionList.SafeReplaceRange(mouseCellToStringIndex, 3, new[] - { - new CodeInstruction(OpCodes.Call, - AccessTools.Method(typeof(DebugInspectorPatches), nameof(MousePositionToString))), - }); - - var writePathCostsFlagIndex = instructionList.FindIndex( - instr => instr.LoadsField(fieldof_DebugViewSettings_writePathCosts)); - var nextFlagIndex = instructionList.FindIndex(writePathCostsFlagIndex + 1, IsDebugViewSettingFlagAccess); - // Note: Not using SafeInsertRange, since labels need to be transferred to a new instruction in the middle. - instructionList.InsertRange(nextFlagIndex - 1, new[] - { - stringBuilderVar.ToLdloc(), - mouseCellVar.ToLdloc(), - new CodeInstruction(OpCodes.Call, - AccessTools.Method(typeof(DebugInspectorPatches), nameof(WriteMorePathCostsDebugOutput))), - stringBuilderVar.ToLdloc().TransferLabelsAndBlocksFrom(instructionList[nextFlagIndex]), - mouseCellVar.ToLdloc(), - new CodeInstruction(OpCodes.Call, - AccessTools.Method(typeof(DebugInspectorPatches), nameof(WriteMoreDebugOutput))), - }); - - return instructionList; - } - - private static string MousePositionToString() - { - var mousePos = UI.MouseMapPosition(); - return $"({mousePos.x:000.00}, {mousePos.z:000.00})"; - } - - private static bool IsDebugViewSettingFlagAccess(CodeInstruction instruction) => - instruction.opcode == OpCodes.Ldsfld && ((FieldInfo)instruction.operand).DeclaringType == typeof(DebugViewSettings); - - private static void WriteMorePathCostsDebugOutput(StringBuilder debugString, IntVec3 mouseCell) - { - if (Find.Selector.SingleSelectedObject is Pawn pawn) - { - debugString.AppendLine($"CalculatedCostAt({mouseCell}, false, {pawn.Position}): " + - pawn.Map.pathing.For(pawn).pathGrid.CalculatedCostAt(mouseCell, perceivedStatic: false, pawn.Position)); - debugString.AppendLine($"CostToMoveIntoCell({pawn}, {mouseCell}): " + CostToMoveIntoCell(pawn, mouseCell)); - using var pawnPath = pawn.Map.pathFinder.FindPath(pawn.Position, mouseCell, pawn); - debugString.AppendLine($"FindPath({pawn.Position}, {mouseCell}, {pawn}).TotalCost: " + pawnPath.TotalCost); - } - } - - private static readonly Func CostToMoveIntoCell = - (Func)Delegate.CreateDelegate(typeof(Func), - AccessTools.Method(typeof(Pawn_PathFollower), "CostToMoveIntoCell", new[] { typeof(Pawn), typeof(IntVec3) })); - - private static void WriteMoreDebugOutput(StringBuilder debugString, IntVec3 mouseCell) - { - var map = Find.CurrentMap; - - if (DebugViewSettingsMoreWrite.writeTemperature) - { - debugString.AppendLine("---"); - debugString.AppendLine("Temperature: " + - (GenTemperature.TryGetTemperatureForCell(mouseCell, map, out var temperature) ? $"{temperature:f1}" : "")); - } - - if (DebugViewSettingsMoreWrite.writeEdificeGrid) - { - debugString.AppendLine("---"); - debugString.AppendLine("Building at edifice grid: " + map.edificeGrid[mouseCell]); - } - - if (DebugViewSettingsMoreWrite.writeDoors) - { - debugString.AppendLine("---"); - var pawn = Find.Selector.SingleSelectedObject as Pawn; - if (pawn is not null) - { - debugString.AppendLine("From selected pawn " + pawn + " to door"); - debugString.AppendLine("- CaresAboutForbidden(p, false): " + ForbidUtility.CaresAboutForbidden(pawn, false)); - //debugString.AppendLine("- mindState.maxDistToSquadFlag: " + pawn.mindState.maxDistToSquadFlag); - } - - foreach (var thing in map.thingGrid.ThingsAt(mouseCell)) - { - if (thing is Building_Door door) - { - debugString.AppendLine("Door: " + door); - debugString.AppendLine("- Open: " + door.Open); - debugString.AppendLine("- HoldOpen: " + door.HoldOpen); - debugString.AppendLine("- FreePassage: " + door.FreePassage); - debugString.AppendLine("- WillCloseSoon: " + door.WillCloseSoon); - debugString.AppendLine("- BlockedOpenMomentary: " + door.BlockedOpenMomentary); - debugString.AppendLine("- SlowsPawns: " + door.SlowsPawns); - debugString.AppendLine("- TicksToOpenNow: " + door.TicksToOpenNow); - debugString.AppendLine("- FriendlyTouchedRecently: " + - methodof_Building_Door_FriendlyTouchedRecently.Invoke(door, Array.Empty())); - debugString.AppendLine("- lastFriendlyTouchTick: " + Building_Door_lastFriendlyTouchTick(door)); - debugString.AppendLine("- ticksUntilClose: " + Building_Door_ticksUntilClose(door)); - debugString.AppendLine("- ticksSinceOpen: " + Building_Door_ticksSinceOpen(door)); - debugString.AppendLine("- IsForbidden(player): " + door.IsForbidden(Faction.OfPlayer)); - debugString.AppendLine("- def.Fillage: " + door.def.Fillage); - debugString.AppendLine("- CanBeSeenOver: " + door.CanBeSeenOver()); - debugString.AppendLine("- BaseBlockChance: " + door.BaseBlockChance()); - - if (pawn is not null) - { - debugString.AppendLine("- For selected pawn: " + pawn); - debugString.AppendLine(" - CanPhysicallyPass(p): " + door.CanPhysicallyPass(pawn)); - debugString.AppendLine(" - PawnCanOpen(p): " + door.PawnCanOpen(pawn)); - debugString.AppendLine(" - BlocksPawn(p): " + door.BlocksPawn(pawn)); - debugString.AppendLine(" - p.HostileTo(this): " + pawn.HostileTo(door)); - debugString.AppendLine(" - PathWalkCostFor(p): " + door.PathWalkCostFor(pawn)); - debugString.AppendLine(" - IsDangerousFor(p): " + door.IsDangerousFor(pawn)); - debugString.AppendLine(" - IsForbidden(p): " + door.IsForbidden(pawn)); - debugString.AppendLine(" - Position.IsForbidden(p): " + door.Position.IsForbidden(pawn)); - debugString.AppendLine(" - Position.InAllowedArea(p): " + door.Position.InAllowedArea(pawn)); - //debugString.AppendLine(" - Position.InHorDistOf(p.DutyLocation,p.mindState.maxDistToSquadFlag): " + - // (pawn.mindState.maxDistToSquadFlag > 0f ? - // door.Position.InHorDistOf(pawn.DutyLocation(), pawn.mindState.maxDistToSquadFlag).ToString() : - // "N/A")); - //debugString.AppendLine(" - IsForbidden(p.Faction): " + door.IsForbidden(pawn.Faction)); - //debugString.AppendLine(" - IsForbidden(p.HostFaction): " + door.IsForbidden(pawn.HostFaction)); - //debugString.AppendLine(" - pawn.Lord.extraForbiddenThings.Contains(this): " + - // (pawn.GetLord()?.extraForbiddenThings?.Contains(door) ?? false)); - debugString.AppendLine(" - IsForbiddenToPass(p): " + door.IsForbiddenToPass(pawn)); - } - - if (door is Building_DoorRegionHandler invisDoor) - { - var parentDoor = invisDoor.ParentDoor; - debugString.AppendLine("- ParentDoor: " + parentDoor); - debugString.AppendLine(" - DrawPos: " + parentDoor.DrawPos); - debugString.AppendLine(" - debugDrawVectors.openPct: " + parentDoor.debugDrawVectors?.openPct); - debugString.AppendLine(" - debugDrawVectors.offsetVector: " + parentDoor.debugDrawVectors?.offsetVector); - debugString.AppendLine(" - debugDrawVectors.scaleVector: " + parentDoor.debugDrawVectors?.scaleVector); - debugString.AppendLine(" - debugDrawVectors.graphicVector: " + parentDoor.debugDrawVectors?.graphicVector); - debugString.AppendLine(" - Open: " + parentDoor.Open); - debugString.AppendLine(" - HoldOpen: " + parentDoor.HoldOpen); - debugString.AppendLine(" - FreePassage: " + parentDoor.FreePassage); - debugString.AppendLine(" - WillCloseSoon: " + parentDoor.WillCloseSoon); - debugString.AppendLine(" - BlockedOpenMomentary: " + parentDoor.BlockedOpenMomentary); - debugString.AppendLine(" - SlowsPawns: " + parentDoor.SlowsPawns); - debugString.AppendLine(" - TicksToOpenNow: " + parentDoor.TicksToOpenNow); - debugString.AppendLine(" - FriendlyTouchedRecently: " + parentDoor.FriendlyTouchedRecently); - debugString.AppendLine(" - lastFriendlyTouchTick: " + Building_DoorExpanded_lastFriendlyTouchTick(parentDoor)); - debugString.AppendLine(" - ticksUntilClose: " + parentDoor.TicksUntilClose); - debugString.AppendLine(" - ticksSinceOpen: " + parentDoor.TicksSinceOpen); - debugString.AppendLine(" - Forbidden: " + parentDoor.Forbidden); - debugString.AppendLine(" - def.Fillage: " + parentDoor.def.Fillage); - debugString.AppendLine(" - CanBeSeenOver: " + parentDoor.CanBeSeenOver()); - debugString.AppendLine(" - BaseBlockChance: " + parentDoor.BaseBlockChance()); - - if (parentDoor is Building_DoorRemote parentDoorRemote) - { - debugString.AppendLine(" - Button: " + parentDoorRemote.Button); - debugString.AppendLine(" - SecuredRemotely: " + parentDoorRemote.SecuredRemotely); - debugString.AppendLine(" - HoldOpenRemotely: " + parentDoorRemote.HoldOpenRemotely); - debugString.AppendLine(" - ForcedClosed: " + parentDoorRemote.ForcedClosed); - } - - if (pawn is not null) - { - debugString.AppendLine(" - For selected pawn: " + pawn); - //debugString.AppendLine(" - CanPhysicallyPass(p): " + parentDoor.CanPhysicallyPass(pawn)); - debugString.AppendLine(" - PawnCanOpen(p): " + parentDoor.PawnCanOpen(pawn)); - debugString.AppendLine(" - BlocksPawn(p): " + parentDoor.BlocksPawn(pawn)); - debugString.AppendLine(" - p.HostileTo(this): " + pawn.HostileTo(parentDoor)); - debugString.AppendLine(" - PathWalkCostFor(p): " + parentDoor.PathWalkCostFor(pawn)); - debugString.AppendLine(" - IsDangerousFor(p): " + parentDoor.IsDangerousFor(pawn)); - debugString.AppendLine(" - IsForbidden(p): " + parentDoor.IsForbidden(pawn)); - debugString.AppendLine(" - Position.IsForbidden(p): " + parentDoor.Position.IsForbidden(pawn)); - debugString.AppendLine(" - Position.InAllowedArea(p): " + parentDoor.Position.InAllowedArea(pawn)); - //debugString.AppendLine(" - Position.InHorDistOf(p.DutyLocation,p.mindState.maxDistToSquadFlag): " + - // (pawn.mindState.maxDistToSquadFlag > 0f ? - // parentDoor.Position.InHorDistOf(pawn.DutyLocation(), pawn.mindState.maxDistToSquadFlag).ToString() : - // "N/A")); - //debugString.AppendLine(" - IsForbidden(p.Faction): " + parentDoor.IsForbidden(pawn.Faction)); - //debugString.AppendLine(" - IsForbidden(p.HostFaction): " + parentDoor.IsForbidden(pawn.HostFaction)); - //debugString.AppendLine(" - pawn.Lord.extraForbiddenThings.Contains(this): " + - // (pawn.GetLord()?.extraForbiddenThings?.Contains(parentDoor) ?? false)); - } - } - var room = door.GetRoom(); - if (room is not null) - { - debugString.AppendLine("- " + room.DebugString().Replace("\n ", "\n - ").Replace("\n\n", "\n")); - } - } - else if (thing is Building_DoorRemoteButton remoteButton) - { - debugString.AppendLine("RemoteButton: " + remoteButton); - debugString.AppendLine("- LinkedDoors: " + remoteButton.LinkedDoors.ToStringSafeEnumerable()); - debugString.AppendLine("- ButtonOn: " + remoteButton.ButtonOn); - debugString.AppendLine("- NeedsToBeSwitched: " + remoteButton.NeedsToBeSwitched); - } - } - } - - if (DebugViewSettingsMoreWrite.writePatchCallRegistry) - { - debugString.AppendLine("---"); - debugString.AppendLine("Harmony Patch Call Registry:"); - foreach (var pair in patchCallRegistry) - debugString.AppendLine($"- {pair.Key}: {pair.Value}"); - } - } - - private static readonly MethodInfo methodof_Building_Door_FriendlyTouchedRecently = - AccessTools.PropertyGetter(typeof(Building_Door), "FriendlyTouchedRecently"); - private static readonly AccessTools.FieldRef Building_Door_lastFriendlyTouchTick = - AccessTools.FieldRefAccess("lastFriendlyTouchTick"); - private static readonly AccessTools.FieldRef Building_DoorExpanded_lastFriendlyTouchTick = - AccessTools.FieldRefAccess("lastFriendlyTouchTick"); - private static readonly AccessTools.FieldRef Building_Door_ticksUntilClose = - AccessTools.FieldRefAccess("ticksUntilClose"); - private static readonly AccessTools.FieldRef Building_Door_ticksSinceOpen = - AccessTools.FieldRefAccess("ticksSinceOpen"); - - // District.DebugString - public static string DistrictMoreDebugString(string result, District __instance) - { - return $"{result}\n Neighbors=\n - {__instance.Neighbors.Join(delimiter: "\n - ")}"; - } - - // Room.DebugString - public static string RoomMoreDebugString(string result, Room __instance) - { - return $"{result}\n IsDoorway={__instance.IsDoorway}\n TouchesMapEdge={__instance.TouchesMapEdge}" + - $"\n UsesOutdoorTemperature={__instance.UsesOutdoorTemperature}"; - } - - // DoorsDebugDrawer.DrawDebug - public static void AddMoreDoorsDebugDrawer() - { - if (DebugViewSettingsMoreDraw.drawDoorTempEqualization) - { - var mouseCell = UI.MouseCell(); - var things = mouseCell.GetThingList(Find.CurrentMap); - foreach (var thing in things) - { - if (thing is Building_Door and not Building_DoorRegionHandler || thing is Building_DoorExpanded) - { - var adjCells = GenAdj.CellsAdjacentCardinal(thing); - foreach (var cell in adjCells) - CellRenderer.RenderCell(cell, 0.3f); - } - } - } - } - } -} diff --git a/Source/Directory.Build.props b/Source/Directory.Build.props new file mode 100644 index 0000000..79b4109 --- /dev/null +++ b/Source/Directory.Build.props @@ -0,0 +1,77 @@ + + + + + RW1.3;RW1.4;RW1.4Unstable; RW1.4Unstable + RW1.5 + net48 + true + 10 + false + $([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)\..\About\Version.txt")) + + true + + + + none + true + TRACE + + + + + ..\1.3\Assemblies\ + + + + + + + + ..\1.4\Assemblies\ + + + + + + + + ..\1.4\Assemblies\ + + + + + + + + ..\1.5\Assemblies\ + + + + + + + + + ..\1.5\Assemblies\ + + + + + + + + + + + + + + + + + diff --git a/Source/DoorExpandedDef.cs b/Source/DoorExpandedDef.cs index 5981ec7..b3f7e23 100644 --- a/Source/DoorExpandedDef.cs +++ b/Source/DoorExpandedDef.cs @@ -23,11 +23,12 @@ public class DoorExpandedDef : ThingDef public bool singleDoor = false; public DoorType doorType = DoorType.Standard; public bool rotatesSouth = true; - [Obsolete("Use CompProperties_DoorExpanded.tempEqualizeRate instead (note: tempEqualizeRate is the inverse of confusingly named " + + [Obsolete("Use CompProperties_DoorExpanded.tempEqualizeRate instead (note: tempEqualizeRate is the inverse of " + "tempLeakRate, then multiplied by TemperatureTuning.Door_TempEqualizeIntervalClosed)")] public int tempLeakRate = TemperatureTuning.Door_TempEqualizeIntervalClosed; - public float doorOpenMultiplier = Building_DoorExpanded.VisualDoorOffsetEnd; - [Obsolete("Use the DoorOpenSpeed stat instead (note: DoorOpenSpeed stat is the inverse of confusingly named doorOpenSpeedRate)")] + [Obsolete("No longer in use")] + public float doorOpenMultiplier = 0f; // Building_DoorExpanded.VisualDoorOffsetEnd; + [Obsolete("Use the DoorOpenSpeed stat instead (note: DoorOpenSpeed stat is the inverse of doorOpenSpeedRate)")] public float doorOpenSpeedRate = 1.0f; public GraphicData doorFrame; public Vector3 doorFrameOffset; @@ -63,7 +64,7 @@ public override void ResolveReferences() // tempLeakRate is counterintuitive whereby lower tempLeakRate resulted in faster temperature equalization rate. // So tempLeakRate is deprecated in favor of a new props.tempEqualizeRate, such that: tempEqualizeRate = TemperatureTuning.Door_TempEqualizeIntervalClosed / tempLeakRate, - doorOpenMultiplier = doorOpenMultiplier, + //doorOpenMultiplier = doorOpenMultiplier, doorFrame = doorFrame, doorFrameOffset = doorFrameOffset, doorFrameSplit = doorFrameSplit, diff --git a/Source/DoorsExpandedMod.cs b/Source/DoorsExpandedMod.cs index 31d9e53..b64785c 100644 --- a/Source/DoorsExpandedMod.cs +++ b/Source/DoorsExpandedMod.cs @@ -19,7 +19,7 @@ public class DoorsExpandedMod : Mod public DoorsExpandedMod(ModContentPack content) : base(content) { - HarmonyPatches.EarlyPatches(); + //HarmonyPatches.EarlyPatches(); Removed in 1.5 Settings = GetSettings(); } diff --git a/Source/HarmonyPatches.cs b/Source/HarmonyPatches.cs index 8f2c1c9..29d0a87 100644 --- a/Source/HarmonyPatches.cs +++ b/Source/HarmonyPatches.cs @@ -25,7 +25,6 @@ public static class HarmonyPatchesOnStartup static HarmonyPatchesOnStartup() { HarmonyPatches.Patches(); - DebugInspectorPatches.PatchDebugInspector(); } } @@ -34,424 +33,10 @@ public static class HarmonyPatches { internal static Harmony harmony = new("rimworld.jecrell.doorsexpanded"); - // Early patching before any XML Def/Patch loading and StaticConstructorOnStartup code. - // This is called from DoorsExpandedMod constructor for earliest possible patching. - public static void EarlyPatches() - { - // The MinifyEverything mod attempts to make all ThingDefs having a minifiedDef. - // This includes our invisible doors (def HeronInvisibleDoor and class Building_DoorRegionHandler), - // and users have reported that this can result in minified invisible doors somehow (how, I don't know...) - // So this is a hack to undo MinifyEverything's AddMinifiedFor behavior for invisible doors. - // This patch also must be applied earlier that MinifyEverything's StaticConstructorOnStartup-based patching, - // since that's when its AddMinifiedFor is called. - Patch(original: AccessTools.Method(typeof(ThingDefGenerator_Buildings), "NewBlueprintDef_Thing"), - postfix: nameof(InvisDoorNewBlueprintDefThingPostfix)); - } - - // ThingDefGenerator_Buildings.NewBlueprintDef_Thing - public static void InvisDoorNewBlueprintDefThingPostfix(ThingDef def, bool isInstallBlueprint) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(InvisDoorNewBlueprintDefThingPostfix)); - if (def == HeronDefOf.HeronInvisibleDoor && isInstallBlueprint) - { - def.blueprintDef = null; - var installBlueprintDef = def.installBlueprintDef; - def.installBlueprintDef = null; - def.minifiedDef = null; - // ThingDefGenerator_Buildings.NewBlueprintDef_Thing is called within MinifyEverything's AddMinifiedFor, - // which then adds installBlueprintDef to the DefDatabase. So must remove from DefDatabase afterwards. - LongEventHandler.ExecuteWhenFinished(() => - { - if (DefDatabase.GetNamedSilentFail(installBlueprintDef.defName) is not null) - DefDatabaseThingDefRemove(installBlueprintDef); - Log.Message($"[Doors Expanded] Detected minifiedDef for def {def} with installBlueprintDef {installBlueprintDef}, " + - "likely added by MinifyEverything mod - removed to avoid minifiable invisible doors"); - }); - } - } - - private static readonly Action DefDatabaseThingDefRemove = - (Action)AccessTools.Method(typeof(DefDatabase), "Remove", new[] { typeof(ThingDef) }) - .CreateDelegate(typeof(Action)); - public static void Patches() { var rwAssembly = typeof(Building_Door).Assembly; - // Historical note: There used to be patches on the following methods that are no longer necessary since: - // a) edificeGrid (and thus GridsUtility.GetEdifice) should now never return a Building_DoorExpanded thing - // b) forbidden state is now synced between Building_DoorExpanded and its invis doors - // c) were workarounds for bugs that have since been fixed - // GenPath.ShouldNotEnterCell - // GenSpawn.SpawnBuildingAsPossible - // ForbidUtility.IsForbiddenToPass - // PathFinder.GetBuildingCost - // PathFinder.BlocksDiagonalMovement - // PawnPathUtility.TryFindLastCellBeforeBlockingDoor - // PawnPathUtility.FirstBlockingBuilding - // RegionTypeUtility.GetExpectedRegionType - // JobGiver_Manhunter.TryGiveJob - // JobGiver_SeekAllowedArea.TryGiveJob - - // Notes on what methods don't need patching despite referencing Building_Door: - // SymbolResolver_AncientComplex_Defences.Resolve - logic regarding doors seems to have no effect? - // SymbolResolver_EdgeWalls.TrySpawnWall - just checks whether door exists at cell - // Blueprint_Door.Draw - used for normal doors, while we have our own blueprint drawing logic - // CompAbilityEffect_WithDest.CanTeleportThingTo - would call invisDoor.Open which delegates to parentDoor - // CompSpawner.TryFindSpawnCell - would call invisDoor.FreePassage which delegates to parentDoor - // DoorsDebugDrawer.DrawDebug - would highlight invis doors, which is fine - // Fire.DoComplexCalcs - just checks whether door exists at cell - // ForbidUtility.IsForbiddenToPass - // - actually no door-specific logic - // - ultimately delegates to our patched CompForbiddable.Forbidden - // SignalAction_OpenDoor.DoAction - would call invisDoor.StartManualOpenBy which delegates to parentDoor - // ThingDefGenerator_Buildings.NewBlueprintDef_Thing - see DoorExpandedBlueprintSpawnSetupPrefix - // Verb_Jump.ValidJumpTarget - would call invisDoor.Open which delegates to parentDoor - // AttackTargetFinder.FindBestReachableMeleeTarget - would call invisDoor.CanPhysicallyPass which delegates to parentDoor - // GenPath.ShouldNotEnterCell - // - would call invisDoor.IsForbidden which ultimately delegates to our patched CompForbiddable.Forbidden - // - would call invisDoor.PawnCanOpen which delegates to parentDoor - // PathFinder.GetBuildingCost - // - would call invisDoor.IsForbiddenToPass (see above) - // - would call invisDoor properties/methods which delegate to parentDoor - // PathFinder.BlocksDiagonalMovement - just checks whether door exists at cell - // Pawn_PathFollower.PawnCanOccupy - would (ultimately) call invisDoor properties/methods which delegate to parentDoor - // Pawn_PathFollower.NextCellDoorToWaitForOrManuallyOpen - ditto - // Pawn_PathFollower.TryEnterNextPathCell - ditto - // Pawn_PathFollower.SetupMoveIntoNextCell - ditto - // Pawn_PathFollower.NeedNewPath - ditto - // PawnPathUtility.FirstBlockingBuilding - would call invisDoor properties/methods which delegate to parentDoor - // PawnPathUtility.TryFindLastCellBeforeBlockingDoor - ditto - // AnimalPenBlueprintEnclosureCalculator.PassCheck - delegates to AnimalPenEnclosureCalculator.RoamerCanPass - // AnimalPenEnclosureCalculator.RoamerCanPass - // - would call invisDoor.FreePassage which delegates to parentDoor - // - this system handles e.g. 2x2 doors just fine, so would work just fine with invis doors - // CellFinder.TryFindBestPawnStandCell - would call invisDoor properties/methods which delegate to parentDoor - // CellFinder.GetAdjacentCardinalCellsForBestStandCell - ditto - // FogGrid.Notify_PawnEnteringDoor - see FogGrid.FloodUnfogAdjacent patch below - // GridsUtility.GetDoor - see below - // Region.door - see below - - // Notes on what methods don't need patching despite calling GridUtility.GetDoor (would be invis door), - // excluding anything already mentioned above: - // ComplexWorker.FindBestSpawnLocation - just checks whether door exists at cell - // BaseGenUtility.AnyDoorAdjacentCardinalTo - ditto - // SymbolResolver_ExtraDoor.WallHasDoor - ditto - // SymbolResolver_ExtraDoor.GetDistanceToExistingDoors - ditto - // SymbolResolver_Filth.CanPlaceFilth - ditto - // SymbolResolver_OutdoorsPath.Resolve - ditto - // SymbolResolver_OutdoorsPath.CanTraverse - ditto - // SymbolResolver_OutdoorsPath.CanPlacePath - ditto - // SymbolResolver_Street.CausesStreet - ditto - // SymbolResolver_TerrorBuildings.Resolve - ditto - // BreachingUtility.BlocksBreaching - ditto - // ComplexThreatWorker_SleepingThreat.CanSpawnAt - ditto - // GenStep_PrisonerWillingToJoin.ScatterAt - ditto - // MeditationUtility.AllMeditationSpotCandidates - // RCellFinder.CanWanderToCell - ditto - // Toils_Interpersonal.GotoInteractablePosition - ditto - // WorkGiver_CleanFilth.JobOnThing - gets cell's door's region; should work for our invis doors - // TouchPathEndModeUtility.IsCornerTouchAllowed - just checks whether door exists at cell - // Graphic_LinkedAsymmetric.Print - ditto - // Graphic_LinkedAsymmetric.ShouldLinkWith - ditto - // Pawn.InteractionCell - ditto - // RegionMaker.TryGenerateRegionFrom - would have invis door assigned to region.door (see below) - // RegionTypeUtility.GetExpectedRegionType - just checks whether door exists at cell - // ThingUtility.InteractionCellWhenAt - ditto - - // Notes on what methods don't need patching despite referencing Region.door (would be invis door): - // Region.Allows - // - lookups by door position - // - would call invisDoor.IsForbiddenToPass (see above) - // - would call invisDoor properties/methods which delegate to parentDoor - // Building_OrbitalTradeBeacon.TradeableCellsAround - just checks whether door exists at cell - // JobGiver_ConfigurableHostilityResponse.TryGetFleeJob - would call invisDoor.Open which delegates to parentDoor - // JobGiver_PrisonerEscape.ShouldStartEscaping - would call invisDoor.FreePassage which delegates to parentDoor - // SelfDefenseUtility.ShouldStartFleeing - would call invisDoor.Open which delegates to parentDoor - // RegionCostCalculator.GetRegionDistance - // - calls PathFinder.GetBuildingCost - // - would call invisDoor.IsForbiddenToPass (see above) - // - would call invisDoor.PawnCanOpen which delegates to parentDoor - // - checks whether door exists at cell - // AnimalPenEnclosureCalculator.EnterRegion - delegates to AnimalPenEnclosureCalculator.RoamerCanPass - // AnimalPenEnclosureCalculator.ProcessRegion - ditto - // AnimalPenEnclosureStateCalculator.VisitPassableDoorway/VisitImpassableDoorway - same door as above - // GenClamor.DoClamor - would call invisDoor.Open which delegates to parentDoor - // Region.IsDoorway - see below - - // Notes on what methods don't need patching despite calling Region.IsDoorway (would be invis door): - // - for normal/invis doors, the region is a single cell - // JobGiver_SeekAllowedArea.TryGiveJob - just checks whether doors exists at cell - // JobGiver_SeekSafeTemperature.ClosestRegionWithinTemperatureRange - ditto - // AnimalPenEnclosureCalculator.EnterRegion/ProcessRegion - see above notes on AnimalPenEnclosureCalculator - // CellFinderLoose.GetFleeDestToolUser - just checks whether doors exists at cell - // District.IsDoorway - // - for normal/invis doors, the single region (and cell) comprises the whole district - // - RCellFinder.TryFindRandomSpotJustOutsideColony - // - just checks whether doors exists at cell - // - Room.IsDoorway - see below - // GenClosest.RegionwiseBFSWorker - just checks whether doors exists at cell - // RegionTraverser.ShouldCountRegion - makes BFS avoid counting door regions for limit purposes, so works as-is - // Room.Notify_ContainedThingSpawnedOrDespawned - // - handles adjacent doors just fine - // - but still needs patching for other reasons - see InvisDoorRoomNotifyContainedThingSpawnedOrDespawnedPrefix - // RoomTempTracker.RegenerateEqualizationData - handles adjacent doors just fine - - // Notes on what methods don't need patching despite calling Room.IsDoorway (would be invis door): - // - for normal/invis doors, the single district (and region and cell) comprises the whole room - // Need_RoomSize.SpacePerceptibleNow - just checks whether doors exists at cell - // Pawn_StyleObserverTracker.StyleObserverTick - ditto - // WanderRoomUtility.IsValidWanderDest - ditto - // AutoBuildRoofAreaSetter.TryGenerateAreaNow - ditto - // Room.Notify_ContainedThingSpawnedOrDespawned - see above - // RoomTempTracker.EqualizeTemperature - // - excludes doors from RoomTempTracker equalization except in a specific edge case (surrounded on all four sides - // by other doors or walls in a "closed" system) to address the exploit described in - // https://www.reddit.com/r/RimWorld/comments/b1wz9a/rimworld_temperature_physics_allow_you_to_build/ - - // Notes on what methods don't need patching despite ThingDef.IsDoor potentially being confused between invis doors - // and parent doors (ThingDef.IsDoor will be patched to also return true for parent doors): - // BreachingUtility.IsWorthBreachingBuilding - fine for both invis doors and parent doors - // CompForbiddable.CompGetGizmosExtra - method only called for parent doors - // JobGiver_MineRandom.TryGiveJob - IsDoor only called for invis doors - // PlantUtility.CanEverPlantAt - IsDoor only called for invis doors - // Sketch.ExposeData - just checks whether door and wall exists in same cell - // SketchGenUtility.GetDoor - // - SketchResolver_AddThingsCentral.CanPlaceAt - just checks whether doors exists at cell - // StatWorker.ShouldShowFor - method and IsDoor should only be called for parent doors - // TargetingParameters.CanTarget - fine for both invis doors and parent doors - // AnimalPenBlueprintEnclosureCalculator.PassCheck - see above - // Building.GetGizmos - method only called for parent doors - // DebugOutputsGeneral.ThingFillageAndPassability - debug method doesn't matter - // GenPlace.PlaceSpotQualityAt - just checks whether doors exists at cell - // SectionLayer_IndoorMask.Regenerate - IsDoor only called for invis doors - // ThingDef.AffectsRegions - fine since should return same value for invis doors and their parents - // ThingDef.AffectsReachability - ditto - // ThingDef.CanAffectLinker - ditto - - // Notes on what methods don't need patching despite def.Fillage/fillPercent being potentially used on invis doors - // (always none), but that method ultimately use def.Fillage on every thing at the cell (including parent door) - // to the net effect of no negative impact: - // SymbolResolver_Clear.Resolve - // BeautyUtility.CellBeauty - // GenConstruct.BlocksConstruction - // Skyfaller.SpawnThings - // BuildingsDamageSectionLayerUtility.UsesLinkableCornersAndEdges - // DamageWorker.ExplosionAffectCell - // FogGrid.Unfog - see also InvisDoorDefMakeFogTranspiler - // Projectile.CanHit - see also DoorExpandedProjectileCheckForFreeIntercept - // RegionTypeUtility.GetExpectedRegionType - // ThingDef.BlocksPlanting - - // Notes on what methods don't need patching despite def.Fillage/fillPercent being potentially used on invis doors - // for other reasons: - // Designator_RemoveFloor.CanDesignateCell - // - only for determining Impassible def.passibility (walls), and doesn't return false for invis doors, which is fine - // Designator_SmoothSurface.CanDesignateCell, SmoothSurfaceDesignatorUtility.CanSmoothFloorUnder - // - only for determining whether floors can be smoothed, SmoothSurfaceDesignatorUtility.CanSmoothFloorUnder returns - // true for invis doors, which is fine - // ShipLandingArea.RecalculateBlockingThing - will end up selecting parent door as firstBlockingThing, which is fine - // Sketch.CanBeSeenOver, SketchResolver_FloorFill.ResolveInt/FloorFillRoom - // - assume that ThingDefs used in Sketch objects are never invis doors - // PawnPathUtility.FirstBlockingBuilding - only for determining PassThroughOnly passibility, which doors should never have - // Building.SpawnSetup/DeSpawn - // - this will do nothing for an invis door, and ultimately be called for the parent door, such that for parent doors - // with full fillage, the dirty flag logic will be correct - // CoverGrid.Register/DeRegister/RecalculateCell - // - this will do nothing for an invis door, and ultimately be called for the parent door, - // with the algorithm using all cells within the parent door, such that the net effect is parent doors, - // not invis doors, will be in the cover grid - // DebugOutputsGeneral.ThingFillageAndPassability/ThingFillPercents - debug methods don't matter - // SectionLayer_Snow.Filled - unused method - // SnowGrid.CanCoexistWithSnow - // - see InvisDoorCanHaveSnowTranspiler - // - only other use of the method is for determining snow depth at cell and is ultimately called for the parent door - // Thing.FireBulwark - overridden in Bulding_DoorExpanded and Building_DoorRegionHandler - // Verb.CanHitFromCellIgnoringRange, Verb_LaunchProjectile.TryCastShot - target should never be an invis door - // ShotReport.HitReportFor - ditto - // ThingDef.SpecialDisplayStats - should never be shown for invis door - - // Notes on what methods don't need patching despite referencing RegionType.Portal (indicates region is a door): - // WorkGiver_CleanFilth.JobOnThing - see above - // RCellFinder.SpotToChewStandingNear - just checks whether doors exists at cell - // CellFinderLoose.CanFleeToLocation - ditto - // RegionMaker.TryGenerateRegionFrom - logic that associates Region.Portal with doors (region.door in this case) - // RegionTypeUtility.GetExpectedRegionType - ditto - // RegionTypeUtility.IsOneCellRegion/AllowsMultipleRegionsPerDistrict - // - enforces that door regions are single cell, which is fine with the invis doors that comprise parent doors - // RegionTempTracker.RegenerateEqualizationData - see above - - // Notes on usage of patch priority: - // - Destructive prefix patches (prefix patch that returns false, preventing remaining non-postfix/finalizer logic) - // that simply prevent a method from running based off a simple filter have high priority yet NOT highest priority, - // because poorly written destructive prefix patches that replicate and modify original logic typically have normal - // priority, and for mod authors that are aware of the harmony priority system and use highest priority, assume they - // know what they're doing and let their prefix patches run before our high priority ones. - // - Prefix patches that delegate calls to invis door methods to parent door methods have highest (first) priority - // to get ahead of other mods' destructive prefix patches. Unfortunately, our Building_Door patches must be destructive - // prefix patches, and there's no safe way to redirect other mods' Building_Door patches to Building_DoorExpanded patches. - - // See comments in Building_DoorRegionHandler. - Patch(original: AccessTools.PropertyGetter(typeof(Building_Door), nameof(Building_Door.FreePassage)), - prefix: nameof(InvisDoorFreePassagePrefix), - priority: Priority.First); - Patch(original: AccessTools.PropertyGetter(typeof(Building_Door), nameof(Building_Door.TicksTillFullyOpened)), - prefix: nameof(InvisDoorTicksTillFullyOpenedPrefix), - priority: Priority.First); - Patch(original: AccessTools.PropertyGetter(typeof(Building_Door), nameof(Building_Door.WillCloseSoon)), - prefix: nameof(InvisDoorWillCloseSoonPrefix), - priority: Priority.First); - Patch(original: AccessTools.PropertyGetter(typeof(Building_Door), nameof(Building_Door.BlockedOpenMomentary)), - prefix: nameof(InvisDoorBlockedOpenMomentaryPrefix), - priority: Priority.First); - Patch(original: AccessTools.PropertyGetter(typeof(Building_Door), nameof(Building_Door.SlowsPawns)), - prefix: nameof(InvisDoorSlowsPawnsPrefix), - priority: Priority.First); - Patch(original: AccessTools.PropertyGetter(typeof(Building_Door), nameof(Building_Door.TicksToOpenNow)), - prefix: nameof(InvisDoorTicksToOpenNowPrefix), - priority: Priority.First); - Patch(original: AccessTools.Method(typeof(Building_Door), nameof(Building_Door.CheckFriendlyTouched)), - prefix: nameof(InvisDoorCheckFriendlyTouchedPrefix), - priority: Priority.First); - Patch(original: AccessTools.Method(typeof(Building_Door), nameof(Building_Door.Notify_PawnApproaching)), - prefix: nameof(InvisDoorNotifyPawnApproachingPrefix), - priority: Priority.First); - Patch(original: AccessTools.Method(typeof(Building_Door), nameof(Building_Door.CanPhysicallyPass)), - prefix: nameof(InvisDoorCanPhysicallyPassPrefix), - priority: Priority.First); - Patch(original: AccessTools.Method(typeof(Building_Door), "DoorOpen"), - prefix: nameof(InvisDoorDoorOpenPrefix), - priority: Priority.First); - Patch(original: AccessTools.Method(typeof(Building_Door), "DoorTryClose"), - prefix: nameof(InvisDoorDoorTryClosePrefix), - priority: Priority.First); - Patch(original: AccessTools.Method(typeof(Building_Door), nameof(Building_Door.StartManualOpenBy)), - prefix: nameof(InvisDoorStartManualOpenByPrefix), - priority: Priority.First); - // Note: Building_Door.StartManualCloseBy gets inlined, but Harmony 2 can prevent this, so that it can be patched. - Patch(original: AccessTools.Method(typeof(Building_Door), nameof(Building_Door.StartManualCloseBy)), - prefix: nameof(InvisDoorStartManualCloseByPrefix), - priority: Priority.First); - Patch(original: AccessTools.Method(typeof(Building_Door), nameof(Building_Door.SpawnSetup)), - transpiler: nameof(InvisDoorSpawnSetupTranspiler)); - Patch(original: AccessTools.Method(typeof(Building_Door), nameof(Building_Door.Tick)), - transpiler: nameof(InvisDoorTickTranspiler)); - - // Patches to redirect access from invis door def to its parent door def. - Patch(original: AccessTools.Method(typeof(GenStep_Terrain), nameof(GenStep_Terrain.Generate)), - transpiler: nameof(InvisDoorDefFillageTranspiler)); - Patch(original: AccessTools.Method(typeof(GenGrid), nameof(GenGrid.CanBeSeenOver), new[] { typeof(Building) }), - transpiler: nameof(InvisDoorDefFillageTranspiler)); - Patch(original: AccessTools.Method(typeof(GridsUtility), nameof(GridsUtility.Filled)), - transpiler: nameof(InvisDoorDefFillageTranspiler)); - Patch(original: AccessTools.Method(typeof(Projectile), "ImpactSomething"), - transpiler: nameof(InvisDoorDefFillageTranspiler)); - Patch(original: AccessTools.Method(rwAssembly.GetType("Verse.SectionLayer_IndoorMask"), "HideRainPrimary"), - transpiler: nameof(InvisDoorDefFillageTranspiler)); - Patch(original: AccessTools.Method(typeof(RitualPosition_ThingDef), nameof(RitualPosition_ThingDef.IsUsableThing), - new[] { typeof(Thing), typeof(IntVec3), typeof(TargetInfo) }), - transpiler: nameof(InvisDoorDefFillageTranspiler)); - foreach (var original in typeof(FloodFillerFog).FindLambdaMethods(nameof(FloodFillerFog.FloodUnfog), typeof(bool))) - { - Patch(original, - transpiler: nameof(InvisDoorDefMakeFogTranspiler)); - } - Patch(original: AccessTools.Method(typeof(FogGrid), "FloodUnfogAdjacent"), - transpiler: nameof(InvisDoorDefMakeFogTranspiler)); - Patch(original: AccessTools.Method(typeof(SnowGrid), "CanHaveSnow"), - transpiler: nameof(InvisDoorCanHaveSnowTranspiler)); - - // Note: Not using AccessTools.TypeByName, since an invalid loaded assembly (e.g. wrong referenced assembly version) can cause - // AccessTools.TypeByName to fail as shown in the following example logs - // (note how there are no listed DoorsExpanded patches from InvisDoorBlockLightTranspiler onwards in above PatchAll): - // https://gist.github.com/HugsLibRecordKeeper/1f5317c8f1643df4593ad981a7e038df - // https://gist.github.com/HugsLibRecordKeeper/fa0fc17b6ddff4eb871e4ec946f7b834 - // Using GenTypes.GetTypeInAnyAssembly instead, even if it's slower on first call for a given type name, - // since GenTypes.GetTypeInAnyAssembly only "valid" loaded assemblies (as determined via ModAssemblyHandler.AssemblyIsUsable). - if (GenTypes.GetTypeInAnyAssembly("OpenedDoorsDontBlockLight.GlowFlooder_Patch") is - Type openedDoorsDontBlockLightGlowFlooderPatch) - { - foreach (var original in AccessTools.GetDeclaredMethods(openedDoorsDontBlockLightGlowFlooderPatch)) - { - if (original.GetParameters().Any(param => param.ParameterType == typeof(Thing))) - { - Patch(original, - transpiler: nameof(InvisDoorCanHaveSnowTranspiler)); - } - } - // Note: The transpiler in OpenedDoorsDontBlockLight.GlowFlooder_Patch matches on thing.def.blockLight - // and replaces that with a call to a custom method that checks thing.def.blockLight, - // so we don't want to patch GlowFlooder.AddFloodGlowFor ourselves in this case. - } - else - { - Patch(original: AccessTools.Method(typeof(GlowFlooder), nameof(GlowFlooder.AddFloodGlowFor)), - transpiler: nameof(InvisDoorBlockLightTranspiler)); - } - - Patch(original: AccessTools.Method(typeof(SectionLayer_LightingOverlay), nameof(SectionLayer_LightingOverlay.Regenerate)), - transpiler: nameof(InvisDoorBlockLightTranspiler)); - - // Other patches for invis doors. - Patch(original: AccessTools.Method(typeof(PathGrid), nameof(PathGrid.CalculatedCostAt)), - transpiler: nameof(InvisDoorCalculatedCostAtTranspiler)); - Patch(original: AccessTools.Method(typeof(GenSpawn), nameof(GenSpawn.WipeExistingThings)), - prefix: nameof(InvisDoorWipeExistingThingsPrefix), - priority: Priority.VeryHigh); - Patch(original: AccessTools.Method(typeof(GenSpawn), nameof(GenSpawn.SpawningWipes)), - prefix: nameof(InvisDoorSpawningWipesPrefix), - priority: Priority.VeryHigh); - Patch(original: AccessTools.Method(typeof(PathFinder), nameof(PathFinder.IsDestroyable)), - postfix: nameof(InvisDoorIsDestroyablePostfix)); - Patch(original: AccessTools.Method(typeof(Room), nameof(Room.Notify_ContainedThingSpawnedOrDespawned)), - prefix: nameof(InvisDoorRoomNotifyContainedThingSpawnedOrDespawnedPrefix), - priority: Priority.VeryHigh); - Patch(original: AccessTools.Method(typeof(CompForbiddable), "UpdateOverlayHandle"), - prefix: nameof(InvisDoorCompForbiddableUpdateOverlayHandlePrefix), - priority: Priority.VeryHigh); - Patch(original: AccessTools.Method(typeof(MouseoverReadout), nameof(MouseoverReadout.MouseoverReadoutOnGUI)), - transpiler: nameof(MouseoverReadoutTranspiler)); - - // Patches for door expanded doors themselves. - Patch(original: AccessTools.Method(typeof(CoverUtility), nameof(CoverUtility.BaseBlockChance), new[] { typeof(Thing) }), - transpiler: nameof(DoorExpandedBaseBlockChanceTranspiler)); - Patch(original: AccessTools.Method(typeof(GenGrid), nameof(GenGrid.CanBeSeenOver), new[] { typeof(Building) }), - transpiler: nameof(DoorExpandedCanBeSeenOverTranspiler)); - Patch(original: AccessTools.Method(typeof(EdificeGrid), nameof(EdificeGrid.Register)), - prefix: nameof(DoorExpandedEdificeGridRegisterPrefix), - priority: Priority.VeryHigh); - Patch(original: AccessTools.PropertyGetter(typeof(Room), nameof(Room.IsDoorway)), - transpiler: nameof(DoorExpandedRoomIsDoorwayTranspiler)); - // Despite ThingDef.IsDoor being a small method that's potentially inlined, Harmony 2 allows patching it. - Patch(original: AccessTools.PropertyGetter(typeof(ThingDef), nameof(ThingDef.IsDoor)), - postfix: nameof(DoorExpandedThingDefIsDoorPostfix)); - Patch(original: AccessTools.PropertySetter(typeof(CompForbiddable), nameof(CompForbiddable.Forbidden)), - transpiler: nameof(DoorExpandedSetForbiddenTranspiler), - transpilerRelated: nameof(DoorExpandedSetForbidden)); - Patch(original: AccessTools.Method(typeof(RegionAndRoomUpdater), "ShouldBeInTheSameRoom"), - postfix: nameof(DoorExpandedShouldBeInTheSameRoomPostfix)); - Patch(original: AccessTools.Method(typeof(GenTemperature), nameof(GenTemperature.EqualizeTemperaturesThroughBuilding)), - transpiler: nameof(DoorExpandedEqualizeTemperaturesThroughBuildingTranspiler), - transpilerRelated: nameof(DoorExpandedGetAdjacentCellsForTemperature)); - Patch(AccessTools.Method(typeof(RoomTempTracker), "RegenerateEqualizationData"), - transpiler: nameof(DoorExpandedRegenerateEqualizationDataTranspiler)); - Patch(original: AccessTools.Method(typeof(Projectile), "CheckForFreeIntercept"), - transpiler: nameof(DoorExpandedProjectileCheckForFreeIntercept)); - Patch(original: AccessTools.Method(typeof(TrashUtility), nameof(TrashUtility.TrashJob)), - transpiler: nameof(DoorExpandedTrashJobTranspiler)); - Patch(original: AccessTools.Method(typeof(Thing), nameof(Thing.SetFactionDirect)), - prefix: nameof(DoorExpandedSetFactionDirectPrefix), - priority: Priority.VeryHigh); - Patch(original: AccessTools.Method(typeof(PrisonBreakUtility), nameof(PrisonBreakUtility.InitiatePrisonBreakMtbDays)), - transpiler: nameof(DoorExpandedInitiatePrisonBreakMtbDaysTranspiler), - transpilerRelated: nameof(DoorExpandedInitiatePrisonBreakMtbDaysAddAllRegionsInDoor)); - Patch(original: AccessTools.Method(typeof(BeautyUtility), nameof(BeautyUtility.FillBeautyRelevantCells)), - transpiler: nameof(DoorExpandedRoomNeighborRegionsTranspiler), - transpilerRelated: nameof(GetDoorNeighborRegions)); - Patch(original: AccessTools.Method(typeof(IdeoUtility), "GetJoinedRooms"), - transpiler: nameof(DoorExpandedRoomNeighborRegionsTranspiler), - transpilerRelated: nameof(GetDoorNeighborRegions)); - // Patches for ghost (pre-placement) and blueprints for door expanded. foreach (var original in typeof(Designator_Place).FindLambdaMethods(nameof(Designator_Place.DoExtraGuiControls), typeof(void))) { @@ -475,48 +60,19 @@ public static void Patches() // TODO: Instead of patching this, consider patching ThingDefGenerator_Buildings.NewBlueprintDef_Thing in EarlyPatches to // allow custom Blueprint for (re)install blueprints, and using custom Blueprint and Blueprint_Install subclasses // (potentially also a Blueprint_Install subclass for Building_Door to keep applying the rotation fix for vanilla doors). - Patch(original: AccessTools.Method(typeof(Thing), nameof(Thing.DrawAt)), + Patch(original: AccessTools.Method(typeof(Thing), "DrawAt"), prefix: nameof(DoorExpandedThingDrawAtPrefix)); // Patches related to door remotes. Patch(original: AccessTools.Method(typeof(FloatMenuMakerMap), "AddJobGiverWorkOrders"), transpiler: nameof(DoorRemoteAddJobGiverWorkOrdersTranspiler), transpilerRelated: nameof(TranslateCustomizeUseDoorRemoteJobLabel)); - - // Workaround for MinifyEverything issue where reinstalling doors sometimes causes a transient and harmless NRE - // in GridsUtility.GetThingList. This patch fixes the issue for vanilla doors. - // Doors Expanded doors are fixed in Building_DoorExpanded.Tick. - Patch(original: AccessTools.Method(typeof(Building_Door), nameof(Building_Door.Tick)), - prefix: nameof(BuildingDoorTickPrefix), - priority: Priority.VeryHigh); - - // Patch CompBreakdownable to consider CompProperties_BreakdownableCustom. - Patch(original: AccessTools.Method(typeof(CompBreakdownable), nameof(CompBreakdownable.CheckForBreakdown)), - transpiler: nameof(CompBreakdownableCheckForBreakdownTranspiler), - transpilerRelated: nameof(CompBreakdownableCustomMTBUnit)); - - // Backwards compatibility patches. - Patch(original: AccessTools.Method(typeof(BackCompatibility), nameof(BackCompatibility.GetBackCompatibleType)), - prefix: nameof(DoorExpandedGetBackCompatibleType), - priority: Priority.VeryHigh); - Patch(original: AccessTools.Method(typeof(BackCompatibility), nameof(BackCompatibility.CheckSpawnBackCompatibleThingAfterLoading)), - prefix: nameof(DoorExpandedCheckSpawnBackCompatibleThingAfterLoading), - priority: Priority.VeryHigh); - Patch(original: AccessTools.Method(typeof(GenTypes), nameof(GenTypes.GetTypeNameWithoutIgnoredNamespaces)), - prefix: nameof(DoorExpandedGetTypeNameWithoutIgnoredNamespacesPrefix), - priority: Priority.VeryHigh); - - // Following isn't actually a Harmony patch, but bundling this patch here anyway. - DefaultDoorMassPatch(); } private static void Patch(MethodInfo original, string prefix = null, string postfix = null, string transpiler = null, string transpilerRelated = null, int priority = Priority.Normal, string[] before = null, string[] after = null, bool? debug = null) { - DebugInspectorPatches.RegisterPatch(prefix); - DebugInspectorPatches.RegisterPatch(postfix); - DebugInspectorPatches.RegisterPatch(transpilerRelated); harmony.Patch(original, NewHarmonyMethod(prefix, priority, before, after, debug), NewHarmonyMethod(postfix, priority, before, after, debug), @@ -587,926 +143,6 @@ private static bool IsDoorExpandedDef(Def def) => private static bool IsVanillaDoorDef(Def def) => def is ThingDef thingDef && typeof(Building_Door).IsAssignableFrom(thingDef.thingClass); - private static bool IsInvisDoorDef(Def def) => - def is ThingDef thingDef && typeof(Building_DoorRegionHandler).IsAssignableFrom(thingDef.thingClass); - - private static Thing GetActualDoor(Thing thing) => - thing is Building_DoorRegionHandler invisDoor ? invisDoor.ParentDoor : thing; - - // Building_Door.FreePassage - public static bool InvisDoorFreePassagePrefix(Building_Door __instance, ref bool __result) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(InvisDoorFreePassagePrefix)); - if (__instance is Building_DoorRegionHandler invisDoor) - { - __result = invisDoor.ParentDoor?.FreePassage ?? true; - return false; - } - return true; - } - - // Building_Door.TicksTillFullyOpened - public static bool InvisDoorTicksTillFullyOpenedPrefix(Building_Door __instance, ref int __result) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(InvisDoorTicksTillFullyOpenedPrefix)); - if (__instance is Building_DoorRegionHandler invisDoor) - { - __result = invisDoor.ParentDoor?.TicksTillFullyOpened ?? 0; - return false; - } - return true; - } - - // Building_Door.WillCloseSoon - public static bool InvisDoorWillCloseSoonPrefix(Building_Door __instance, ref bool __result) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(InvisDoorWillCloseSoonPrefix)); - if (__instance is Building_DoorRegionHandler invisDoor) - { - __result = invisDoor.ParentDoor?.WillCloseSoon ?? false; - return false; - } - return true; - } - - // Building_Door.BlockedOpenMomentary - public static bool InvisDoorBlockedOpenMomentaryPrefix(Building_Door __instance, ref bool __result) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(InvisDoorBlockedOpenMomentaryPrefix)); - if (__instance is Building_DoorRegionHandler invisDoor) - { - __result = invisDoor.ParentDoor?.BlockedOpenMomentary ?? false; - return false; - } - return true; - } - - // Building_Door.SlowsPawns - public static bool InvisDoorSlowsPawnsPrefix(Building_Door __instance, ref bool __result) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(InvisDoorSlowsPawnsPrefix)); - if (__instance is Building_DoorRegionHandler invisDoor) - { - __result = invisDoor.ParentDoor?.SlowsPawns ?? false; - return false; - } - return true; - } - - // Building_Door.TicksToOpenNow - public static bool InvisDoorTicksToOpenNowPrefix(Building_Door __instance, ref int __result) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(InvisDoorTicksToOpenNowPrefix)); - if (__instance is Building_DoorRegionHandler invisDoor) - { - __result = invisDoor.ParentDoor?.TicksToOpenNow ?? 0; - return false; - } - return true; - } - - // Building_Door.CheckFriendlyTouched - public static bool InvisDoorCheckFriendlyTouchedPrefix(Building_Door __instance, Pawn p) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(InvisDoorCheckFriendlyTouchedPrefix)); - if (__instance is Building_DoorRegionHandler invisDoor) - { - invisDoor.ParentDoor?.CheckFriendlyTouched(p); - return false; - } - return true; - } - - // Building_Door.Notify_PawnApproaching - public static bool InvisDoorNotifyPawnApproachingPrefix(Building_Door __instance, Pawn p, int moveCost) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(InvisDoorNotifyPawnApproachingPrefix)); - if (__instance is Building_DoorRegionHandler invisDoor) - { - invisDoor.ParentDoor?.Notify_PawnApproaching(p, moveCost); - return false; - } - return true; - } - - // Building_Door.CanPhysicallyPass - public static bool InvisDoorCanPhysicallyPassPrefix(Building_Door __instance, Pawn p, ref bool __result) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(InvisDoorCanPhysicallyPassPrefix)); - if (__instance is Building_DoorRegionHandler invisDoor) - { - __result = invisDoor.ParentDoor?.CanPhysicallyPass(p) ?? true; - return false; - } - return true; - } - - // Building_Door.DoorOpen - public static bool InvisDoorDoorOpenPrefix(Building_Door __instance, int ticksToClose) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(InvisDoorDoorOpenPrefix)); - if (__instance is Building_DoorRegionHandler invisDoor) - { - invisDoor.ParentDoor?.DoorOpen(ticksToClose); - return false; - } - return true; - } - - // Building_Door.DoorTryClose - public static bool InvisDoorDoorTryClosePrefix(Building_Door __instance) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(InvisDoorDoorTryClosePrefix)); - if (__instance is Building_DoorRegionHandler invisDoor) - { - invisDoor.ParentDoor?.DoorTryClose(); - return false; - } - return true; - } - - // Building_Door.StartManualOpenBy - public static bool InvisDoorStartManualOpenByPrefix(Building_Door __instance, Pawn opener) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(InvisDoorStartManualOpenByPrefix)); - if (__instance is Building_DoorRegionHandler invisDoor) - { - invisDoor.ParentDoor?.StartManualOpenBy(opener); - return false; - } - return true; - } - - // Building_Door.StartManualCloseBy - public static bool InvisDoorStartManualCloseByPrefix(Building_Door __instance, Pawn closer) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(InvisDoorStartManualCloseByPrefix)); - if (__instance is Building_DoorRegionHandler invisDoor) - { - invisDoor.ParentDoor?.StartManualCloseBy(closer); - return false; - } - return true; - } - - // Building_Door.SpawnSetup - public static IEnumerable InvisDoorSpawnSetupTranspiler(IEnumerable instructions, ILGenerator ilGen) - { - // Building_Door.SpawnSetup calls BlockedOpenMomentary, which will be delegating to Building_DoorExpanded. - // If this is a Building_DoorRegionHandler, that Building_DoorExpanded may not be spawned yet, so we want to avoid this. - // (It also calls ClearReachabilityCache, which is redundant with Building_DoorExpanded, although not harmful). - // So if this is a Building_DoorRegionHandler, exit early after the call to Building.SpawnSetup. - // Historical: this used to be done in Building_DoorRegionHandler.SpawnSetup override, but the way it was done - // no longer seems to work in RW 1.3.3116+ (due to the Unity update?); hence, this Harmony patch. - return InvisDoorBaseCallTranspiler(instructions, ilGen, AccessTools.Method(typeof(Building), nameof(Building.SpawnSetup))); - } - - // Building_Door.Tick - public static IEnumerable InvisDoorTickTranspiler(IEnumerable instructions, ILGenerator ilGen) - { - // If this is a Building_DoorRegionHandler, all Tick logic is delegated to Building_DoorExpanded, - // which syncs its fields with its invis doors as needed. So all the Tick logic in Building_Door itself should be skipped, - // other than that in Building.Tick, or rather ThingWithComps.Tick, since Building.Tick doesn't exist. - // Not replicating the logic in ThingWithComps.Tick, in case the logic changes or another mod patches that method. - // Historical: this used to be done in Building_DoorRegionHandler.Tick override, but the way it was done - // no longer seems to work in RW 1.3.3116+ (due to the Unity update?); hence, this Harmony patch. - return InvisDoorBaseCallTranspiler(instructions, ilGen, AccessTools.Method(typeof(Building), nameof(Building.Tick))); - } - - // Generic transpiler that transforms all following instances of code: - // base.(...) - // into: - // base.(...) - // if (this is Building_DoorRegionHandler) - // return; - private static IEnumerable InvisDoorBaseCallTranspiler(IEnumerable instructions, ILGenerator ilGen, - MethodInfo baseMethod) - { - var instructionsList = instructions.AsList(); - - var baseCallIndex = instructionsList.FindIndex(instr => instr.Calls(baseMethod)); - var afterBaseCallLabel = ilGen.DefineLabel(); - var afterBaseCallInstr = instructionsList[baseCallIndex + 1]; - instructionsList.SafeInsertRange(baseCallIndex + 1, new[] - { - new CodeInstruction(OpCodes.Ldarg_0), - new CodeInstruction(OpCodes.Isinst, typeof(Building_DoorRegionHandler)), - new CodeInstruction(OpCodes.Brfalse, afterBaseCallLabel), - new CodeInstruction(OpCodes.Ret), - }); - afterBaseCallInstr.labels.Add(afterBaseCallLabel); - - return instructionsList; - } - - // GenStep_Terrain.Generate - // GenGrid.CanBeSeenOver - // GridsUtility.Filled - // Projectile.ImpactSomething - // SectionLayer_IndoorMask.HideRainPrimary - public static IEnumerable InvisDoorDefFillageTranspiler( - IEnumerable instructions) => - GetActualDoorForDefTranspiler(instructions, - AccessTools.PropertyGetter(typeof(ThingDef), nameof(ThingDef.Fillage))); - - // FloodFillerFog.FloodUnfog - // FogGrid.FloodUnfogAdjacent - public static IEnumerable InvisDoorDefMakeFogTranspiler(IEnumerable instructions) => - GetActualDoorForDefTranspiler(instructions, - AccessTools.PropertyGetter(typeof(ThingDef), nameof(ThingDef.MakeFog))); - - // SnowGrid.CanHaveSnow - public static IEnumerable InvisDoorCanHaveSnowTranspiler(IEnumerable instructions) - { - // Unlike the other thing.def. transpilers, the accessing of the defMember Fillage happens - // in another method, so we have to just replace thing.def with GetActualDoor(thing).def. - foreach (var instruction in instructions) - { - if (instruction.LoadsField(fieldof_Thing_def)) - { - yield return new CodeInstruction(OpCodes.Call, methodof_GetActualDoor); - } - yield return instruction; - } - } - - // GlowFlooder.AddFloodGlowFor - // SectionLayer_LightingOverlay.Regenerate - public static IEnumerable InvisDoorBlockLightTranspiler(IEnumerable instructions) => - GetActualDoorForDefTranspiler(instructions, AccessTools.Field(typeof(ThingDef), nameof(ThingDef.blockLight))); - - private static IEnumerable GetActualDoorForDefTranspiler(IEnumerable instructions, - MemberInfo defMember) - { - // This transforms the following code: - // thing.def. - // into: - // GetActualDoor(thing).def. - - var enumerator = instructions.GetEnumerator(); - // There must be at least one instruction, so we can ignore first MoveNext() value. - enumerator.MoveNext(); - var prevInstruction = enumerator.Current; - while (enumerator.MoveNext()) - { - var instruction = enumerator.Current; - if (prevInstruction.LoadsField(fieldof_Thing_def) && instruction.OperandIs(defMember)) - { - yield return new CodeInstruction(OpCodes.Call, methodof_GetActualDoor); - } - yield return prevInstruction; - prevInstruction = instruction; - } - yield return prevInstruction; - } - - private static readonly FieldInfo fieldof_Thing_def = AccessTools.Field(typeof(Thing), nameof(Thing.def)); - - // PathGrid.CalculatedCostAt - public static IEnumerable InvisDoorCalculatedCostAtTranspiler(IEnumerable instructions, - MethodBase method, ILGenerator ilGen) - { - // This removes the slowdown from consecutive invis doors of the same Building_DoorExpanded thing. - // It keeps the slowdown from consecutive "actual" doors - // (non-Building_DoorRegionhandler Building_Door or Building_DoorExpanded). - - // This transforms the following code: - // if (thing is Building_Door && prevCell.IsValid) - // { - // Building edifice = prevCell.GetEdifice(map); - // if (edifice is not null && edifice is Building_Door) - // { - // consecutiveDoors = true; - // } - // } - // ... - // into: - // if (thing is Building_Door && prevCell.IsValid) - // { - // Building edifice = prevCell.GetEdifice(map); - // if (edifice is not null && edifice is Building_Door) - // { - // if (GetActualDoor(thing) != GetActualDoor(edifice)) - // consecutiveDoors = true; - // } - // } - // ... - - var instructionList = instructions.AsList(); - var locals = new Locals(method, ilGen); - - var searchIndex = 0; - var firstDoorVar = GetIsinstDoorVar(locals, instructionList, ref searchIndex); - var secondDoorVar = GetIsinstDoorVar(locals, instructionList, ref searchIndex); - var condBranchToAfterFlagIndex = instructionList.FindIndex(searchIndex, instr => instr.operand is Label); - var afterFlagLabel = (Label)instructionList[condBranchToAfterFlagIndex].operand; - instructionList.SafeInsertRange(condBranchToAfterFlagIndex + 1, new[] - { - firstDoorVar.ToLdloc(), - new CodeInstruction(OpCodes.Call, methodof_GetActualDoor), - secondDoorVar.ToLdloc(), - new CodeInstruction(OpCodes.Call, methodof_GetActualDoor), - new CodeInstruction(OpCodes.Beq, afterFlagLabel), - }); - - return instructionList; - } - - private static readonly MethodInfo methodof_GetActualDoor = - AccessTools.Method(typeof(HarmonyPatches), nameof(GetActualDoor)); - - // Get x from instruction sequence: ldloc.s ; isinst Building_Door. - private static LocalVar GetIsinstDoorVar(Locals locals, List instructionList, ref int startIndex) - { - var isinstDoorIndex = instructionList.FindIndex(startIndex, IsinstDoorInstruction); - startIndex = isinstDoorIndex + 1; - return locals.FromLdloc(instructionList[isinstDoorIndex - 1]); - } - - private static bool IsinstDoorInstruction(CodeInstruction instruction) => - instruction.Is(OpCodes.Isinst, typeof(Building_Door)); - - // GenSpawn.WipeExistingThings - public static bool InvisDoorWipeExistingThingsPrefix(BuildableDef thingDef) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(InvisDoorWipeExistingThingsPrefix)); - // Only allow vanilla to run if this is not an invisible door. - return !IsInvisDoorDef(thingDef); - } - - // GenSpawn.SpawningWipes - public static bool InvisDoorSpawningWipesPrefix(BuildableDef newEntDef, BuildableDef oldEntDef, ref bool __result) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(InvisDoorSpawningWipesPrefix)); - if (IsInvisDoorDef(newEntDef) || IsInvisDoorDef(oldEntDef)) - { - __result = false; // false, meaning, don't wipe the old thing when you spawn - return false; - } - return true; - } - - // PathFinder.IsDestroyable - public static bool InvisDoorIsDestroyablePostfix(bool result, Thing th) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(InvisDoorIsDestroyablePostfix)); - // Our invis doors have useHitPoints=false, so would ordinarily be considered non-destroyable, - // when we do want the pathfinder to consider them destroyable. - return result || th is Building_DoorRegionHandler; - } - - // Room.Notify_ContainedThingSpawnedOrDespawned - public static bool InvisDoorRoomNotifyContainedThingSpawnedOrDespawnedPrefix(Thing th) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(InvisDoorRoomNotifyContainedThingSpawnedOrDespawnedPrefix)); - // If an invis door's DeSpawn is somehow called before the parent door's DeSpawn is called, - // this can result in a NRE since region links are cleared prematurely - // (specifically RegionLink.GetOtherRegion can return null, unexpectedly for the algorithm). - // So skip if the given Thing is a spawned invis door, and let parent door's DeSpawn handle calling - // Room.Notify_ContainedThingSpawnedOrDespawned for each invis door's Room after they're all despawned. - return th is not Building_DoorRegionHandler { Spawned: true }; - } - - // CompForbiddable.UpdateOverlayHandle - public static bool InvisDoorCompForbiddableUpdateOverlayHandlePrefix(ThingWithComps ___parent) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(InvisDoorCompForbiddableUpdateOverlayHandlePrefix)); - // CompForbidden, which invis doors include, no longer toggles the forbidden overlay from a PostDraw method as of RW 1.3+. - // It now toggles the overlay from a new UpdateOverlayHandle method that's called by the Forbidden setter and PostSpawnSetup. - // Building_DoorRegionHandler overrides Draw to do nothing, which prevents CompForbidden.PostDraw from toggling the overlay - // for our invis doors. This no longer works with the removal of PostDraw (though should still override Draw to do nothing). - // We need to patch UpdateOverlayHandle to prevent invis doors from showing the forbidden overlay. - return ___parent is not Building_DoorRegionHandler; - } - - // MouseoverReadout.MouseoverReadoutOnGUI - public static IEnumerable MouseoverReadoutTranspiler(IEnumerable instructions, - MethodBase method, ILGenerator ilGen) - { - // This transpiler makes MouseoverReadout skip things with null or empty LabelMouseover. - // In particular, this makes it skip invis doors, which have null LabelMouseover. - - // This transforms the following code: - // for (...) - // { - // var thing = ...; - // if (...) - // { - // var rect = ...; - // var labelMouseover = thing.LabelMouseover; - // Widgets.Label(rect, labelMouseover); - // y += YInterval; - // } - // } - // into: - // for (...) - // { - // var thing = ...; - // if (... && !thing.LabelMouseover.IsNullOrEmpty()) - // { - // var rect = ...; - // var labelMouseover = thing.LabelMouseover; - // Widgets.Label(rect, labelMouseover); - // y += YInterval; - // } - // } - - var methodof_Entity_get_LabelMouseover = - AccessTools.PropertyGetter(typeof(Entity), nameof(Entity.LabelMouseover)); - var instructionList = instructions.AsList(); - var locals = new Locals(method, ilGen); - - var labelMouseoverIndex = instructionList.FindIndex(instr => instr.Calls(methodof_Entity_get_LabelMouseover)); - // This relies on the fact that there's a conditional within the loop that acts as a loop continue, - // and we're going to piggyback on that. - var loopContinueLabelIndex = instructionList.FindIndex(labelMouseoverIndex + 1, instr => instr.labels.Count > 0); - var loopContinueLabel = instructionList[loopContinueLabelIndex].labels[0]; - // Unfortunately, we can't simply do a brtrue to loopContinueLabel after a string.IsNullOrEmpty check, - // since we there is at least the LabelMouseover value that needs to be popped from the CIL stack. - // While we can deduce the exact amount of stack pops needed, this is fragile, so we insert the check - // right after the afore-mentioned conditional, such that there's no need for stack pops. - var loopContinueBranchIndex = instructionList.FindLastIndex(labelMouseoverIndex - 1, - instr => instr.OperandIs(loopContinueLabel)); - // We also need the var that has the Thing on which Entity.LabelMouseover is called. - // Assume this is just a ldloc(.s) right before the Entity.LabelMouseover callvirt. - var thingVar = locals.FromLdloc(instructionList[labelMouseoverIndex - 1]); - - instructionList.SafeInsertRange(loopContinueBranchIndex + 1, new[] - { - thingVar.ToLdloc(), - new CodeInstruction(OpCodes.Callvirt, methodof_Entity_get_LabelMouseover), - new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(string), nameof(string.IsNullOrEmpty))), - new CodeInstruction(OpCodes.Brtrue, loopContinueLabel), - }); - - return instructionList; - } - - // CoverUtility.BaseBlockChance - public static IEnumerable DoorExpandedBaseBlockChanceTranspiler( - IEnumerable instructions) - { - // Since invis grid's fillPercent is 0, coverGrid should never contain them, - // and CoverUtility.BaseBlockChance is only called on things in the coverGrid. - // Rather, they'd contain the invis doors' parents that have >0.01 fillPercent, - // so we need to handle door expanded doors. - return DoorExpandedIsOpenDoorTranspiler(instructions); - } - - // GenGrid.CanBeSeenOver - public static IEnumerable DoorExpandedCanBeSeenOverTranspiler(IEnumerable instructions) - { - // GenGrid.CanBeenSeenOver is only either called for edifices (never Building_DoorExpanded) - // or by code that's responsible for setting dirty flags on caches. The latter should already be - // done by such code also being called for invis doors, so this patch probably isn't necessary, - // but better safe than sorry. - return DoorExpandedIsOpenDoorTranspiler(instructions); - } - - // EdificeGrid.Register - public static bool DoorExpandedEdificeGridRegisterPrefix(Building ed) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(DoorExpandedEdificeGridRegisterPrefix)); - // Only the door expanded's invis doors are registered in the edifice grid, - // not the parent door expanded itself. - return ed is not Building_DoorExpanded; - } - - // Room.IsDoorway - public static IEnumerable DoorExpandedRoomIsDoorwayTranspiler(IEnumerable instructions) - { - // Room.IsDoorway only returns true for 1x1 doors since it requires exactly 1 region requirement, and our patch to - // RegionAndRoomUpdater.ShouldBeInTheSameRoom makes it so a larger door is a single room of multiple single cell districts - // (each containing 1 region). This needs to be fixed to return true for our larger doors. More specifically, the district - // count check needs to be changed to allow at least one district rather than exactly 1 district. - // - - // This transforms the following code: - // if (districts.Count != 1) - // return false; - // into: - // if (districts.Count == 0) - // return false; - - var instructionList = instructions.AsList(); - - // Must match ldc.i4.1 and bne.un.s exactly (or equivalent instructions). - var index = instructionList.FindSequenceIndex( - instr => instr.LoadsConstant(1), - instr => instr.opcode == OpCodes.Bne_Un || instr.opcode == OpCodes.Bne_Un_S); - instructionList.SafeReplaceRange(index, 2, new[] - { - new CodeInstruction(OpCodes.Brfalse, instructionList[index + 1].operand), // branch if 0 - }); - - return instructionList; - } - - // ThingDef.IsDoor - public static bool DoorExpandedThingDefIsDoorPostfix(bool result, ThingDef __instance) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(DoorExpandedThingDefIsDoorPostfix)); - // Treat Building_DoorExpanded as a door even if it doesn't derive from Building_Door. - // This allows doors stats to appear for them, among other features. - // However, because GhostUtility.GhostGraphicFor has hardcoded path for thingDef.IsDoor, - // this prevents our DoorExpandedDrawGhostThingTranspiler from working properly. - return result || IsDoorExpandedDef(__instance); - } - - // CompForbiddable.Forbidden - public static IEnumerable DoorExpandedSetForbiddenTranspiler( - IEnumerable instructions) - { - foreach (var instruction in instructions) - { - if (IsinstDoorInstruction(instruction)) - { - // CompForbiddable instance's parent is on top of CIL stack. - yield return new CodeInstruction(OpCodes.Call, - AccessTools.Method(typeof(HarmonyPatches), nameof(DoorExpandedSetForbidden))); - } - yield return instruction; - } - } - - private static ThingWithComps DoorExpandedSetForbidden(ThingWithComps thing) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(DoorExpandedSetForbidden)); - if (thing is Building_DoorExpanded doorEx) - { - doorEx.Notify_ForbiddenInputChanged(); - } - return thing; - } - - // RegionAndRoomUpdater.ShouldBeInTheSameRoom - public static bool DoorExpandedShouldBeInTheSameRoomPostfix(bool result, District a, District b) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(DoorExpandedShouldBeInTheSameRoomPostfix)); - // All the invis doors each comprise a room. - // They should all be combined into a single Room at least for the purposes of temperature management. - if (result) - return true; - if (GetRoomDoor(a) is Building_DoorRegionHandler invisDoorA && - GetRoomDoor(b) is Building_DoorRegionHandler invisDoorB) - { - return invisDoorA.ParentDoor == invisDoorB.ParentDoor; - } - return false; - } - - private static Building_Door GetRoomDoor(District district) - { - if (!district.IsDoorway) - return null; - return district.Regions[0].door; - } - - // GenTemperature.EqualizeTemperaturesThroughBuilding - public static IEnumerable DoorExpandedEqualizeTemperaturesThroughBuildingTranspiler( - IEnumerable instructions, MethodBase method, ILGenerator ilGen) - { - // GenTemperature.EqualizeTemperaturesThroughBuildingTranspiler doesn't handle buildings that are larger than 1x1. - // For the twoWay=false case (which is the one we care about), the algorithm for finding surrounding temperatures - // only looks at the cardinal directions from the building's singular position cell. - // We need it look at all cells surrounding the building's OccupiedRect, excluding corners. - - // This transforms the following code: - // int roomCount = 0; - // float temperatureSum = 0f; - // if (twoWay) - // ... - // else - // { - // for (int j = 0; j < 4; j++) - // { - // IntVec3 c = b.Position + GenAdj.CardinalDirections[j]; - // if (c.InBounds(b.Map)) - // ... - // } - // } - // if (roomCount == 0) - // return; - // into: - // int roomCount = 0; - // float temperatureSum = 0f; - // if (twoWay) - // ... - // else - // { - // IntVec3[] adjCells = GetAdjacentCellsForTemperature(b); - // for (int j = 0; j < adjCells.Length; j++) - // { - // IntVec3 c = adjCells[j]; - // if (c.InBounds(b.Map)) - // ... - // } - // } - // if (roomCount == 0) - // return; - - var instructionList = instructions.AsList(); - var locals = new Locals(method, ilGen); - var adjCellsVar = locals.DeclareLocal(typeof(IntVec3[])); - //void DebugInstruction(string label, int index) - //{ - // Log.Message($"{label} @ {index}: " + - // ((index >= 0 && index < instructionList.Count) ? instructionList[index].ToString() : "invalid index")); - //} - - var twoWayArgIndex = instructionList.FindIndex(instr => instr.opcode == OpCodes.Ldarg_2); - // Assume the next brfalse(.s) operand is a label to the twoWay=false branch. - var twoWayArgFalseBranchIndex = instructionList.FindIndex(twoWayArgIndex + 1, - instr => instr.IsBrfalse()); - var twoWayArgFalseLabel = (Label)instructionList[twoWayArgFalseBranchIndex].operand; - var twoWayArgFalseIndex = instructionList.FindIndex(twoWayArgFalseBranchIndex + 1, - instr => instr.labels.Contains(twoWayArgFalseLabel)); - // Assume next stloc(.s) is storing to the loop index var. - var loopIndexVar = locals.FromStloc(instructionList[instructionList.FindIndex(twoWayArgFalseIndex + 1, - instr => locals.IsStloc(instr))]); - - var newInstructions = new[] - { - new CodeInstruction(OpCodes.Ldarg_0), // Building b - new CodeInstruction(OpCodes.Call, - AccessTools.Method(typeof(HarmonyPatches), nameof(DoorExpandedGetAdjacentCellsForTemperature))), - adjCellsVar.ToStloc(), - }; - instructionList.SafeInsertRange(twoWayArgFalseIndex, newInstructions); - - var buildingArgIndex = instructionList.FindIndex(twoWayArgFalseIndex + newInstructions.Length, - instr => instr.opcode == OpCodes.Ldarg_0); - var currentCellStoreIndex = instructionList.FindIndex(buildingArgIndex + 1, - instr => locals.IsStloc(instr)); - newInstructions = new[] - { - adjCellsVar.ToLdloc(), - loopIndexVar.ToLdloc(), - new CodeInstruction(OpCodes.Ldelem, typeof(IntVec3)), - }; - instructionList.SafeReplaceRange(buildingArgIndex, currentCellStoreIndex - buildingArgIndex, newInstructions); - - var loopEndIndexIndex = instructionList.FindIndex(buildingArgIndex + newInstructions.Length, - instr => instr.opcode == OpCodes.Ldc_I4_4); - instructionList.SafeReplaceRange(loopEndIndexIndex, 1, new[] - { - adjCellsVar.ToLdloc(), - new CodeInstruction(OpCodes.Ldlen), - }); - - return instructionList; - } - - private static IntVec3[] DoorExpandedGetAdjacentCellsForTemperature(Building building) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(DoorExpandedGetAdjacentCellsForTemperature)); - var size = building.def.Size; - if (size.x == 1 && size.z == 1) - { - var position = building.Position; - return new[] - { - position + GenAdj.CardinalDirections[0], - position + GenAdj.CardinalDirections[1], - position + GenAdj.CardinalDirections[2], - position + GenAdj.CardinalDirections[3], - }; - } - else - { - var adjCells = GenAdj.CellsAdjacentCardinal(building).ToArray(); - // Ensure GenTemperature.beqRooms is large enough. - if (((Room[])fieldof_GenTemperature_beqRooms.GetValue(null)).Length < adjCells.Length) - { - fieldof_GenTemperature_beqRooms.SetValue(null, new Room[adjCells.Length]); - } - return adjCells; - } - } - - private static readonly FieldInfo fieldof_GenTemperature_beqRooms = - AccessTools.Field(typeof(GenTemperature), "beqRooms"); - - // RoomTempTracker.RegenerateEqualizationData - public static IEnumerable DoorExpandedRegenerateEqualizationDataTranspiler(IEnumerable instructions, - MethodBase method, ILGenerator ilGen) - { - // RoomTempTracker.RegenerateEqualizationData has logic to include only cells on the other side of walls bordering the room - // (technically, any impassable building with full fillage) for wall equalization purposes, and it explicitly ignores cells - // on the other side of external doors, unless it's blocked by another wall. However, the detection of whether the door is - // on the border of the room is faulty, since it assumes that there is only a single door when examining its region neighbors. - // This means that if another door is right on the other side of border door, that other door is erroneously included. - // This probably isn't a huge deal in vanilla, where layering doors one after another is rare, but that's always the case for - // some our doors, so it should be fixed. - // Also, a door (which itself comprises a 1x1 room) includes cells on the other side of the walls that neighbor it, - // and doors that aren't adjacent to walls don't include any cells. It looks pretty weird, but it's not a big deal, - // since doors themselves are excluded from being affected by RoomTempTracker (they are instead equalized via - // GenTemperature.EqualizeTemperaturesThroughBuilding). - // There's one exceptional case which is addressed in RoomTempTracker.EqualizeTemperature to address the exploit described in - // https://www.reddit.com/r/RimWorld/comments/b1wz9a/rimworld_temperature_physics_allow_you_to_build/ - - // This transforms the following code: - // Region region = intVec.GetRegion(map); - // if (...) - // { - // ... - // Region regionA = ... - // Region regionB = ... - // if (regionA.Room != room && !regionA.IsDoorway) - // { - // ... - // } - // if (regionB.Room != room && !regionB.IsDoorway) - // { - // ... - // } - // ... - // } - // into: - // Region region = intVec.GetRegion(map); - // if (...) - // { - // ... - // Region regionA = ... - // Region regionB = ... - // if (regionA.Room != room && regionA != region) - // { - // ... - // } - // if (regionB.Room != room && regionB != region) - // { - // ... - // } - // ... - // } - - var methodof_GridsUtility_GetRegion = AccessTools.Method(typeof(GridsUtility), nameof(GridsUtility.GetRegion)); - var methodof_Region_get_IsDoorway = AccessTools.PropertyGetter(typeof(Region), nameof(Region.IsDoorway)); - var instructionList = instructions.AsList(); - var locals = new Locals(method, ilGen); - - var getRegionIndex = instructionList.FindIndex(instr => instr.Calls(methodof_GridsUtility_GetRegion)); - var regionStoreIndex = instructionList.FindIndex(getRegionIndex + 1, locals.IsStloc); - var regionVar = locals.FromStloc(instructionList[regionStoreIndex]); - - var index = regionStoreIndex + 1; - while (true) - { - index = instructionList.FindIndex(index, instr => instr.Calls(methodof_Region_get_IsDoorway)); - if (index == -1) - break; - var newInstructions = new[] - { - regionVar.ToLdloc(), - new CodeInstruction(OpCodes.Ceq), - }; - instructionList.SafeReplaceRange(index, 1, newInstructions); - index += newInstructions.Length; - } - - return instructionList; - } - - // Projectile.CheckForFreeIntercept - public static IEnumerable DoorExpandedProjectileCheckForFreeIntercept( - IEnumerable instructions) - { - // Projectile.CheckForFreeIntercept has code that looks like: - // if (thing.def.Fillage is FillCategory.Full) - // { - // if (thing is not Building_Door { Open: true }) - // ... - // Invis doors have Fillage is FillCategory.None, while parent doors are not Building_Door. - // So we need to patch the Building_Door check to work for Building_DoorExpanded. - return DoorExpandedIsOpenDoorTranspiler(instructions); - } - - // TrashUtility.TrashJob - public static IEnumerable DoorExpandedTrashJobTranspiler(IEnumerable instructions) - { - // This prevents enemies from setting Building_DoorExpanded's on fire. - return DoorExpandedIsDoorTranspiler(instructions); - } - - // Thing.SetFactionDirect - private static bool DoorExpandedSetFactionDirectPrefix(Thing __instance, Faction newFaction) - { - if (__instance is Building_DoorExpanded doorEx) - { - // Simulate virtual call. This also involves preventing infinite loop from "override" method calling "base" method. - if (setFactionDirectAlreadyCalled.TryAdd(__instance, true)) - { - try - { - doorEx.SetFactionDirect(newFaction); - } - finally - { - setFactionDirectAlreadyCalled.TryRemove(__instance, out _); - } - return false; - } - } - return true; - } - - private static readonly ConcurrentDictionary setFactionDirectAlreadyCalled = new(); - - // PrisonBreakUtility.InitiatePrisonBreakMtbDays - public static IEnumerable DoorExpandedInitiatePrisonBreakMtbDaysTranspiler( - IEnumerable instructions) - { - // InitiatePrisonBreakMtbDays increases prisoner escape chance for each bordering door (specifically a portal region), - // and for larger doors, it would count each bordering invis door of the same parent door. To prevent this, when an invis door - // region is encountered, add all regions of that invis door's room (i.e. all regions of the parent door) to tmpRegions - // (the set of regions already counted) to prevent them from being counted in subsequent iterations of the method's region loop. - - // This transforms the following code: - // tmpRegions.Add(otherRegion); - // into: - // DoorExpandedInitiatePrisonBreakMtbDaysAddAllRegionsInDoor(tmpRegions, otherRegion); - - // Assume that HashSet.Add call is operating on tmpRegions (hard to verify without CIL stack tracking). - return Transpilers.MethodReplacer(instructions, - AccessTools.Method(typeof(HashSet), nameof(HashSet.Add)), - AccessTools.Method(typeof(HarmonyPatches), nameof(DoorExpandedInitiatePrisonBreakMtbDaysAddAllRegionsInDoor))); - } - - private static bool DoorExpandedInitiatePrisonBreakMtbDaysAddAllRegionsInDoor(HashSet tmpRegions, Region otherRegion) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(DoorExpandedInitiatePrisonBreakMtbDaysAddAllRegionsInDoor)); - var room = otherRegion.Room; - if (room is null) // shouldn't be possible, but if this somehow happens, fallback to the existing behavior - { - return tmpRegions.Add(otherRegion); - } - else - { - var success = false; - foreach (var region in room.Regions) - { - if (tmpRegions.Add(region)) - success = true; - } - return success; - } - } - - // BeautyUtility.FillBeautyRelevantCells - // IdeoUtility.GetJoinedRooms - public static IEnumerable DoorExpandedRoomNeighborRegionsTranspiler(IEnumerable instructions) - { - // The methods being patched assume a door's room only has a single region when looking for neighbors, which isn't true - // for our larger doors composed of a region per cell. This patch fixes the door neighbor logic. - - // This transforms the following code: - // room.FirstRegion.Neighbors - // into: - // GetDoorNeighborRegions(room); - - var methodof_Room_get_FirstRegion = AccessTools.PropertyGetter(typeof(Room), nameof(Room.FirstRegion)); - var methodof_Region_get_Neighbors = AccessTools.PropertyGetter(typeof(Region), nameof(Region.Neighbors)); - var instructionList = instructions.AsList(); - - var index = 0; - while (true) - { - index = instructionList.FindSequenceIndex(index, - instr => instr.Calls(methodof_Room_get_FirstRegion), - instr => instr.Calls(methodof_Region_get_Neighbors)); - if (index == -1) - break; - var newInstructions = new[] - { - new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(HarmonyPatches), nameof(GetDoorNeighborRegions))), - }; - instructionList.SafeReplaceRange(index, 2, newInstructions); - index += newInstructions.Length; - } - - return instructionList; - } - - private static IEnumerable GetDoorNeighborRegions(Room room) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(GetDoorNeighborRegions)); - var firstRegion = room.FirstRegion; - // If invis door, get neighbors of every region in the parent door. - // Exclude regions in the parent door itself from the returned set of neighbors. - if (firstRegion.door is Building_DoorRegionHandler) - { - var doorRegions = room.Regions.ToArray(); - var neighborRegions = new HashSet(); - foreach (var region in doorRegions) - neighborRegions.AddRange(region.Neighbors); - neighborRegions.ExceptWith(doorRegions); - return neighborRegions; - } - else - { - return firstRegion.Neighbors; - } - } // Designator_Place.DoExtraGuiControls (internal lambda) // Designator_Place.HandleRotationShortcuts @@ -1569,9 +205,8 @@ public static IEnumerable DoorExpandedDesignatorPlaceRotateAgai } private static void DoorExpandedRotateAgainIfNeeded(Designator_Place designatorPlace, ref Rot4 placingRot, - RotationDirection rotDirection) + RotationDirection rotDirection) { - DebugInspectorPatches.RegisterPatchCalled(nameof(DoorExpandedRotateAgainIfNeeded)); // If placingRot is South and rotatesSouth is false, rotate again. if (placingRot == Rot4.South && designatorPlace.PlacingDef.GetDoorExpandedProps() is { rotatesSouth: false }) { @@ -1589,8 +224,7 @@ public static IEnumerable DoorExpandedDrawGhostThingTranspiler( private static void DoorExpandedDrawGhostGraphicFromDef(Graphic graphic, Vector3 loc, Rot4 rot, ThingDef thingDef, float extraRotation) { - DebugInspectorPatches.RegisterPatchCalled(nameof(DoorExpandedDrawGhostGraphicFromDef)); - if (thingDef.GetDoorExpandedProps() is {} doorExProps) + if (thingDef.GetDoorExpandedProps() is { } doorExProps) { // Always delegate door expanded graphics to our custom code. for (var i = 0; i < 2; i++) @@ -1647,7 +281,6 @@ public static IEnumerable DoorExpandedGhostGraphicForTranspiler // Blueprint.SpawnSetup public static void DoorExpandedBlueprintSpawnSetupPrefix(Blueprint __instance, Map map) { - DebugInspectorPatches.RegisterPatchCalled(nameof(DoorExpandedBlueprintSpawnSetupPrefix)); ref var blueprint = ref __instance; // This needs to be a prefix (as opposed to a postfix), since Thing.SpawnSetup has logic which depends on rotation. if (blueprint.def.entityDefToBuild is ThingDef thingDef && @@ -1689,7 +322,6 @@ public static void DoorExpandedBlueprintSpawnSetupPrefix(Blueprint __instance, M // ThingWithComps.Draw public static bool DoorExpandedThingDrawAtPrefix(Thing __instance) { - DebugInspectorPatches.RegisterPatchCalled(nameof(DoorExpandedThingDrawAtPrefix)); if (__instance is Blueprint blueprint) { if (blueprint.def.entityDefToBuild is ThingDef thingDef && @@ -1714,7 +346,7 @@ public static bool DoorExpandedThingDrawAtPrefix(Thing __instance) { // Since it's convenient to do so, we'll also "fix" (re)install blueprints for Building_Door thingClass, // in case another mod makes them (re)installable. - blueprint.Rotation = Building_Door.DoorRotationAt(blueprint.Position, blueprint.Map); + blueprint.Rotation = DoorUtility.DoorRotationAt(blueprint.Position, blueprint.Map, false); } } return true; @@ -1724,6 +356,7 @@ public static bool DoorExpandedThingDrawAtPrefix(Thing __instance) MethodInvoker.GetHandler(AccessTools.Method(typeof(ThingWithComps), "Comps_PostDraw")); private static readonly object[] emptyObjArray = Array.Empty(); + // FloatMenuMakerMap.AddJobGiverWorkOrders public static IEnumerable DoorRemoteAddJobGiverWorkOrdersTranspiler(IEnumerable instructions) { @@ -1768,203 +401,11 @@ public static IEnumerable DoorRemoteAddJobGiverWorkOrdersTransp private static TaggedString TranslateCustomizeUseDoorRemoteJobLabel(string translationKey, WorkGiver_Scanner scanner, Job job, Thing thing) { - DebugInspectorPatches.RegisterPatchCalled(nameof(TranslateCustomizeUseDoorRemoteJobLabel)); if (scanner is WorkGiver_UseRemoteButton) return "PH_UseButtonOrLever".Translate(thing.Label); // Following is copied from FloatMenuMakerMap.AddJobGiverWorkOrders. return translationKey.Translate(scanner.PostProcessedGerund(job), thing.Label); } - // Building_Door.Tick - public static bool BuildingDoorTickPrefix(Building_Door __instance) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(BuildingDoorTickPrefix)); - return __instance.Spawned; - } - - // CompBreakdownable.CheckForBreakdown - public static IEnumerable CompBreakdownableCheckForBreakdownTranspiler(IEnumerable instructions) - { - // This transforms the following code: - // Rand.MTBEventOccurs(..., 1f, ...) - // into: - // Rand.MTBEventOccurs(..., CompBreakdownableMTBUnit(this.props), ...) - - foreach (var instruction in instructions) - { - if (instruction.Is(OpCodes.Ldc_R4, 1f)) - { - yield return new CodeInstruction(OpCodes.Ldarg_0); - yield return new CodeInstruction(OpCodes.Ldfld, - AccessTools.Field(typeof(ThingComp), nameof(ThingComp.props))); - yield return new CodeInstruction(OpCodes.Call, - AccessTools.Method(typeof(HarmonyPatches), nameof(CompBreakdownableCustomMTBUnit))); - } - else - { - yield return instruction; - } - } - } - - private static float CompBreakdownableCustomMTBUnit(CompProperties compProps) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(CompBreakdownableCustomMTBUnit)); - return compProps is CompProperties_BreakdownableCustom custom ? custom.breakdownMTBUnit : 1f; - } - - // BackCompatibility.GetBackCompatibleType - public static bool DoorExpandedGetBackCompatibleType(Type baseType, string providedClassName, XmlNode node, ref Type __result) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(DoorExpandedGetBackCompatibleType)); - // To accommodate changes in the specific Building_DoorExpanded class - // (like blast doors from Building_DoorExpanded to Building_DoorRemote, or autodoors from Building_Door to Building_DoorRemote), - // always return a door's actual def's thingClass (which should by set by CompProperties_DoorExpanded). - if (baseType == typeof(Thing) && node["def"] is XmlNode defNode && - (providedClassName == Building_Door_FullName || providedClassName == Building_DoorExpanded_FullName)) - { - __result = DefDatabase.GetNamedSilentFail(defNode.InnerText).thingClass; - return false; - } - return true; - } - - private static readonly string Building_Door_FullName = typeof(Building_Door).Name; // omit "RimWorld." prefix - private static readonly string Building_DoorExpanded_FullName = typeof(Building_DoorExpanded).FullName; - - // BackCompatibility.CheckSpawnBackCompatibleThingAfterLoading - public static bool DoorExpandedCheckSpawnBackCompatibleThingAfterLoading(Thing thing, ref bool __result) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(DoorExpandedCheckSpawnBackCompatibleThingAfterLoading)); - // If invis doors somehow become minified (there are reports this can somehow happen with the MinifyEverything mod), - // destroy them when loading them. - if (thing is MinifiedThing minifiedThing) - { - var innerContainer = minifiedThing.GetDirectlyHeldThings(); - List invisDoors = null; - innerContainer.RemoveAll(innerThing => - { - if (innerThing.def == HeronDefOf.HeronInvisibleDoor) - { - invisDoors ??= new List(); - invisDoors.Add(innerThing); - return true; - } - return false; - }); - if (invisDoors is not null && innerContainer.Count == 0) - { - minifiedThing.Destroy(); - __result = true; // true => avoid spawning - Log.Warning($"[Doors Expanded] Found and destroyed minified invis door(s) during loading: " + invisDoors.ToStringSafeEnumerable()); - return false; - } - } - return true; - } - - // GenTypes.GetTypeNameWithoutIgnoredNamespaces - public static bool DoorExpandedGetTypeNameWithoutIgnoredNamespacesPrefix(Type type, ref string __result) - { - DebugInspectorPatches.RegisterPatchCalled(nameof(DoorExpandedGetTypeNameWithoutIgnoredNamespacesPrefix)); - // Ensure doors patched to have thingClass be Building_DoorExpanded or subclass, which includes door defs with - // CompProperties_DoorExpanded, are saved with Class="Building_Door" in the save file, such that if Doors Expanded - // is removed by the user, such doors revert to vanilla behavior (rather than disappearing altogether). When loading a save, - // the above DoorExpandedGetBackCompatibleType patch converts Building_Door to the proper Building_DoorExpanded type. - // Note: GenTypes.GetTypeNameWithoutIgnoredNamespaces is being patched rather than its caller - // Scribe_Deep.Look(ref T, bool, string, params object[]) since the latter can't currently be patched by Harmony - // (generic argument limitations). One consequence of this that only the door's Type is available, not the door itself, - // so we can't just filter for only one-cell patched doors such as autodoors (unless it's encoded in the door's Type itself). - if (typeof(Building_DoorExpanded).IsAssignableFrom(type)) - { - __result = nameof(Building_Door); - return false; - } - return true; - } - - // Generic transpiler that transforms all following instances of code: - // thing is Building_Door door && door.Open - // into: - // IsOpenDoor(thing) - private static IEnumerable DoorExpandedIsOpenDoorTranspiler(IEnumerable instructions) - { - var instructionList = instructions.AsList(); - - var searchIndex = 0; - var isinstDoorIndex = instructionList.FindIndex(IsinstDoorInstruction); - while (isinstDoorIndex >= 0) - { - searchIndex = isinstDoorIndex + 1; - var doorOpenIndex = instructionList.FindIndex(searchIndex, - instr => instr.Calls(methodof_Building_Door_get_Open)); - var nextIsinstDoorIndex = instructionList.FindIndex(searchIndex, IsinstDoorInstruction); - if (doorOpenIndex >= 0 && (nextIsinstDoorIndex < 0 || doorOpenIndex < nextIsinstDoorIndex)) - { - instructionList.SafeReplaceRange(isinstDoorIndex, doorOpenIndex - isinstDoorIndex + 1, new[] - { - new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(HarmonyPatches), nameof(IsOpenDoor))), - }); - nextIsinstDoorIndex = instructionList.FindIndex(searchIndex, IsinstDoorInstruction); - } - isinstDoorIndex = nextIsinstDoorIndex; - } - - return instructionList; - } - - private static readonly MethodInfo methodof_Building_Door_get_Open = - AccessTools.PropertyGetter(typeof(Building_Door), nameof(Building_Door.Open)); - - private static bool IsOpenDoor(Thing thing) => - thing is Building_Door { Open: true } || - thing is Building_DoorExpanded { Open: true }; - - // Generic transpiler that transforms all following instances of code: - // thing is Building_Door - // into: - // IsDoor(thing) - private static IEnumerable DoorExpandedIsDoorTranspiler(IEnumerable instructions) - { - foreach (var instruction in instructions) - { - if (IsinstDoorInstruction(instruction)) - { - yield return new CodeInstruction(OpCodes.Call, - AccessTools.Method(typeof(HarmonyPatches), nameof(IsDoor))); - } - else - { - yield return instruction; - } - } - } - - private static bool IsDoor(Thing thing) => thing is Building_Door or Building_DoorExpanded; - - public const float DefaultDoorMass = 20f; - - public static void DefaultDoorMassPatch() - { - // Although doors, including our custom doors, aren't uninstallable (minifiable) by default, - // we're defining masses for custom doors, so we might as well define a default for all doors - // if another mod hasn't already defined one yet. - // This way, if another mod like MinifyEverything does make doors uninstallable, they'll have a reasonable mass - // that looks consistent with the mass of our custom doors. - // This patch is done in code during StaticConstructorOnStartup rather than an XML patch since: - // a) A later-in-load-order mod's XML patch that also patches door mass without checking if it already exists, - // would result in an error, albeit a harmless one. - // b) StaticConstructorOnStartup happens after XML patches for all mods are applied - // and after ResolveReferences code for all mods is run, helping ensure default is only applied when necessary. - foreach (var thingDef in DefDatabase.AllDefs) - { - if (typeof(Building_Door).IsAssignableFrom(thingDef.thingClass) && - thingDef.thingClass != typeof(Building_DoorRegionHandler) && - !thingDef.statBases.StatListContains(StatDefOf.Mass)) - { - StatUtility.SetStatValueInList(ref thingDef.statBases, StatDefOf.Mass, DefaultDoorMass); - } - } - } } } diff --git a/Source/HeronDefOf.cs b/Source/HeronDefOf.cs index b018dec..0d641de 100644 --- a/Source/HeronDefOf.cs +++ b/Source/HeronDefOf.cs @@ -6,14 +6,8 @@ namespace DoorsExpanded [DefOf] public static class HeronDefOf { - public static ThingDef HeronInvisibleDoor; - public static JobDef PH_UseRemoteButton; - public static StatDef DoorOpenTime; - public static StatDef PoweredDoorOpenTime; - public static StatDef UnpoweredDoorOpenTime; - static HeronDefOf() => DefOfHelper.EnsureInitializedInCtor(typeof(HeronDefOf)); } } diff --git a/Source/ProjectHeron.csproj b/Source/ProjectHeron.csproj index 7031cf4..854bd3d 100644 --- a/Source/ProjectHeron.csproj +++ b/Source/ProjectHeron.csproj @@ -1,7 +1,7 @@  - RW1.3;RW1.3Unstable;RW1.4Unstable + RW1.3;RW1.3Unstable;RW1.4Unstable;RW1.5Unstable RW1.3 DoorsExpanded net472 @@ -53,6 +53,7 @@ + diff --git a/Source/ProjectHeron.sln b/Source/ProjectHeron.sln index 42eb7a9..b9213fe 100644 --- a/Source/ProjectHeron.sln +++ b/Source/ProjectHeron.sln @@ -15,6 +15,7 @@ Global RW1.3|Any CPU = RW1.3|Any CPU RW1.3Unstable|Any CPU = RW1.3Unstable|Any CPU RW1.4Unstable|Any CPU = RW1.4Unstable|Any CPU + RW1.5Unstable|Any CPU = RW1.5Unstable|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {3A4FB893-1148-4E16-B548-867647DF1724}.RW1.3|Any CPU.ActiveCfg = RW1.3|Any CPU @@ -23,6 +24,8 @@ Global {3A4FB893-1148-4E16-B548-867647DF1724}.RW1.3Unstable|Any CPU.Build.0 = RW1.3Unstable|Any CPU {3A4FB893-1148-4E16-B548-867647DF1724}.RW1.4Unstable|Any CPU.ActiveCfg = RW1.4Unstable|Any CPU {3A4FB893-1148-4E16-B548-867647DF1724}.RW1.4Unstable|Any CPU.Build.0 = RW1.4Unstable|Any CPU + {3A4FB893-1148-4E16-B548-867647DF1724}.RW1.5Unstable|Any CPU.ActiveCfg = RW1.5Unstable|Any CPU + {3A4FB893-1148-4E16-B548-867647DF1724}.RW1.5Unstable|Any CPU.Build.0 = RW1.5Unstable|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Source/StatWorker_DoorOpenTime.cs b/Source/StatWorker_DoorOpenTime.cs deleted file mode 100644 index 08e2cd0..0000000 --- a/Source/StatWorker_DoorOpenTime.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Collections.Generic; -using RimWorld; -using Verse; - -namespace DoorsExpanded -{ - public class StatWorker_DoorOpenTime : StatWorker - { - public override float GetValueUnfinalized(StatRequest req, bool applyPostProcess = true) - { - return Building_DoorExpanded.DoorOpenTime(req, DoorPowerOn(req), applyPostProcess); - } - - public override string GetExplanationUnfinalized(StatRequest req, ToStringNumberSense numberSense) - { - return Building_DoorExpanded.DoorOpenTimeExplanation(req, DoorPowerOn(req), stat); - } - - public override bool ShouldShowFor(StatRequest req) - { - if (req.Def is ThingDef { IsDoor: true } thingDef) - { - if (!Building_DoorExpanded.DoorNeedsPower(thingDef)) - return stat == HeronDefOf.DoorOpenTime; - if (!req.HasThing) - return stat != HeronDefOf.DoorOpenTime; - if (stat == HeronDefOf.DoorOpenTime) - return true; - if (Building_DoorExpanded.DoorIsPoweredOn(req.Thing) is bool doorPowerOn) - return doorPowerOn ? stat == HeronDefOf.UnpoweredDoorOpenTime : stat == HeronDefOf.PoweredDoorOpenTime; - } - return false; - } - - private bool DoorPowerOn(StatRequest req) - { - if (stat == HeronDefOf.PoweredDoorOpenTime) - return true; - if (stat == HeronDefOf.UnpoweredDoorOpenTime) - return false; - return Building_DoorExpanded.DoorIsPoweredOn(req.Thing) ?? Building_DoorExpanded.DoorNeedsPower((ThingDef)req.Def); - } - - public override IEnumerable GetInfoCardHyperlinks(StatRequest statRequest) - { - if (statRequest.Thing is { Stuff: { } thingStuff }) - yield return new Dialog_InfoCard.Hyperlink(thingStuff); - else if (statRequest.StuffDef is { } stuffDef) - yield return new Dialog_InfoCard.Hyperlink(stuffDef); - } - } -} diff --git a/Textures/Things/Building/Door/DoorDoubleRemote_Blueprint.png b/Textures/Things/Building/Door/DoorDoubleRemote_Blueprint.png new file mode 100644 index 0000000..522864f Binary files /dev/null and b/Textures/Things/Building/Door/DoorDoubleRemote_Blueprint.png differ diff --git a/Textures/Things/Building/Door/DoorRemote_Blueprint.png b/Textures/Things/Building/Door/DoorRemote_Blueprint.png new file mode 100644 index 0000000..6335e15 Binary files /dev/null and b/Textures/Things/Building/Door/DoorRemote_Blueprint.png differ diff --git a/Textures/Things/Building/Door/DoorTripleRemote_Blueprint.png b/Textures/Things/Building/Door/DoorTripleRemote_Blueprint.png new file mode 100644 index 0000000..ad2cfbc Binary files /dev/null and b/Textures/Things/Building/Door/DoorTripleRemote_Blueprint.png differ