Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added session for map portals #459

Merged
merged 1 commit into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
Loading