Skip to content

Commit

Permalink
Added session for map portals (#459)
Browse files Browse the repository at this point in the history
  • Loading branch information
SokyranTheDragon authored May 24, 2024
1 parent d980e3b commit 6390871
Show file tree
Hide file tree
Showing 3 changed files with 258 additions and 0 deletions.
99 changes: 99 additions & 0 deletions Source/Client/Persistent/MapPortalPatches.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using HarmonyLib;
using RimWorld;
using UnityEngine;
using Verse;

namespace Multiplayer.Client.Persistent;

[HarmonyPatch(typeof(Widgets), nameof(Widgets.ButtonTextWorker))]
static class MakeMapPortalCancelButtonRed
{
static void Prefix(string label, ref bool __state)
{
if (MapPortalProxy.drawing == null) return;
if (label != "CancelButton".Translate()) return;

GUI.color = new Color(1f, 0.3f, 0.35f);
__state = true;
}

static void Postfix(bool __state, ref Widgets.DraggableResult __result)
{
if (!__state) return;

GUI.color = Color.white;
if (__result.AnyPressed())
{
MapPortalProxy.drawing.Session?.Remove();
__result = Widgets.DraggableResult.Idle;
}
}
}

[HarmonyPatch(typeof(Widgets), nameof(Widgets.ButtonTextWorker))]
static class MapPortalHandleReset
{
static void Prefix(string label, ref bool __state)
{
if (MapPortalProxy.drawing == null) return;
if (label != "ResetButton".Translate()) return;

__state = true;
}

static void Postfix(bool __state, ref Widgets.DraggableResult __result)
{
if (!__state) return;

if (__result.AnyPressed())
{
MapPortalProxy.drawing.Session?.Reset();
__result = Widgets.DraggableResult.Idle;
}
}
}

[HarmonyPatch(typeof(Dialog_EnterPortal), nameof(Dialog_EnterPortal.TryAccept))]
static class TryAcceptMapPortal
{
static bool Prefix(Dialog_EnterPortal __instance)
{
if (Multiplayer.InInterface && __instance is MapPortalProxy mapPortal)
{
mapPortal.Session?.TryAccept();
return false;
}

return true;
}
}

[HarmonyPatch(typeof(Dialog_EnterPortal), nameof(Dialog_EnterPortal.AddToTransferables))]
static class CancelMapPortalAddItems
{
static bool Prefix(Dialog_EnterPortal __instance)
{
if (__instance is MapPortalProxy { itemsReady: true } mp)
{
// Sets the transferables list back to the session list
// as it gets reset in CalculateAndRecacheTransferables
mp.transferables = mp.Session.transferables;
return false;
}

return true;
}
}

static class OpenMapPortalSessionDialog
{
[MpPrefix(typeof(MapPortal), nameof(MapPortal.GetGizmos), 0)]
static bool Prefix(MapPortal __instance)
{
if (Multiplayer.Client == null)
return true;

MapPortalSession.OpenOrCreateSession(__instance);
return false;
}
}
32 changes: 32 additions & 0 deletions Source/Client/Persistent/MapPortalProxy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using RimWorld;
using UnityEngine;

namespace Multiplayer.Client.Persistent;

public class MapPortalProxy(MapPortal portal) : Dialog_EnterPortal(portal), ISwitchToMap
{
public static MapPortalProxy drawing;
public bool itemsReady = false;

public MapPortalSession Session => portal.Map.MpComp().sessionManager.GetFirstOfType<MapPortalSession>();

public override void DoWindowContents(Rect inRect)
{
drawing = this;
var session = Session;
SyncSessionWithTransferablesMarker.DrawnSessionWithTransferables = session;

try
{
if (session == null)
Close();

base.DoWindowContents(inRect);
}
finally
{
drawing = null;
SyncSessionWithTransferablesMarker.DrawnSessionWithTransferables = null;
}
}
}
127 changes: 127 additions & 0 deletions Source/Client/Persistent/MapPortalSession.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
using System.Collections.Generic;
using System.Linq;
using Multiplayer.API;
using RimWorld;
using Verse;

namespace Multiplayer.Client.Persistent;

public class MapPortalSession : ExposableSession, ISessionWithTransferables, ISessionWithCreationRestrictions
{
public MapPortal portal;
public List<TransferableOneWay> transferables;

public override Map Map => portal.Map;

public MapPortalSession(Map _) : base(null)
{
// Mandatory constructor
}

public MapPortalSession(MapPortal portal) : base(null)
{
this.portal = portal;

AddItems();
}

private void AddItems()
{
var dialog = new MapPortalProxy(portal);

// Init code taken from Dialog_EnterPortal.PostOpen
dialog.CalculateAndRecacheTransferables();

transferables = dialog.transferables;
}

public override bool IsCurrentlyPausing(Map map) => Map == map;

public override FloatMenuOption GetBlockingWindowOptions(ColonistBar.Entry entry)
{
if (entry.map != Map)
return null;

return new FloatMenuOption("MpMapPortalSession".Translate(portal?.Label), () =>
{
SwitchToMapOrWorld(Map);
OpenWindow();
});
}

public static void OpenOrCreateSession(MapPortal portal)
{
var session = portal.Map.MpComp().sessionManager.AllSessions
.OfType<MapPortalSession>()
.FirstOrDefault(s => s.portal == portal);
if (session == null)
CreateSession(portal);
else
session.OpenWindow();
}

[SyncMethod]
private static void CreateSession(MapPortal portal)
{
var map = portal.Map;
var manager = map.MpComp().sessionManager;
var session = manager.GetOrAddSession(new MapPortalSession(portal));

// Shouldn't happen and is here for safety.
if (session == null)
Log.Error($"Failed creating session of type {nameof(MapPortalSession)}.");
else if (MP.IsExecutingSyncCommandIssuedBySelf)
session.OpenWindow();
}

[SyncMethod]
public void TryAccept()
{
// There's not a single situation where TryAccept would return false.
// However, it will likely be used by prefixes added by mods.
if (PrepareDummyDialog().TryAccept())
Remove();
}

[SyncMethod]
public void Reset() => transferables.ForEach(t => t.CountToTransfer = 0);

[SyncMethod]
public void Remove() => Map.MpComp().sessionManager.RemoveSession(this);

public void OpenWindow(bool sound = true)
{
var dialog = PrepareDummyDialog();
if (!sound)
dialog.soundAppear = null;

Find.WindowStack.Add(dialog);
}

private MapPortalProxy PrepareDummyDialog()
{
return new MapPortalProxy(portal)
{
itemsReady = true,
transferables = transferables,
};
}

public override void ExposeData()
{
base.ExposeData();

Scribe_Collections.Look(ref transferables, "transferables", LookMode.Deep);
Scribe_References.Look(ref portal, "portal");
}

public Transferable GetTransferableByThingId(int thingId)
=> transferables.Find(tr => tr.things.Any(t => t.thingIDNumber == thingId));

public void Notify_CountChanged(Transferable tr)
{
// There should not really be a need to clear caches, as this dialog does not really have any.
}

public bool CanExistWith(Session other) => other is not MapPortalSession portalSession || portalSession.portal != portal;
}

0 comments on commit 6390871

Please sign in to comment.