fix: Uses client bug to make addon containers work (#776)
* Fixes tooltip for container furniture so it works. This doesn't work on ClassicUO. * Fixes double click opening container furniture by double clicking the addon component. * Fixes the context menu not showing up sometimes on the addon piece. Note: You cannot drop anything into the addon component since it might be far away and it is not a real container. There is no easy fix for this without building an entire custom container class for addons or changing how items drag/drop entirely. Requires closing/opening container gumps as it switches from one component to another that shares the same content. Also requires sharing tooltips, invalidated properties, process delta changes, etc. Not worth the effort. <img width="444" alt="Screen_Shot_2021-09-12_at_4 27 28_PM" src="https://user-images.githubusercontent.com/3953314/133011752-c5b54cf2-2c7b-45c0-882d-0365ef62e686.png">
This commit is contained in:
parent
52dacec68f
commit
c5e35c9b69
8 changed files with 104 additions and 86 deletions
|
|
@ -763,7 +763,7 @@ namespace Server.Items
|
||||||
{
|
{
|
||||||
if (Core.ML)
|
if (Core.ML)
|
||||||
{
|
{
|
||||||
if (ParentsContain<BankBox>()) // Root Parent is the Mobile. Parent could be another containter.
|
if (ParentsContain<BankBox>()) // Root Parent is the Mobile. Parent could be another container.
|
||||||
{
|
{
|
||||||
list.Add(
|
list.Add(
|
||||||
1073841, // Contents: ~1_COUNT~/~2_MAXCOUNT~ items, ~3_WEIGHT~ stones
|
1073841, // Contents: ~1_COUNT~/~2_MAXCOUNT~ items, ~3_WEIGHT~ stones
|
||||||
|
|
|
||||||
|
|
@ -1332,39 +1332,36 @@ namespace Server
|
||||||
|
|
||||||
if (openers != null)
|
if (openers != null)
|
||||||
{
|
{
|
||||||
lock (openers)
|
for (var i = 0; i < openers.Count; ++i)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < openers.Count; ++i)
|
var mob = openers[i];
|
||||||
|
|
||||||
|
var range = GetUpdateRange(mob);
|
||||||
|
|
||||||
|
if (mob.Map != map || !mob.InRange(worldLoc, range))
|
||||||
{
|
{
|
||||||
var mob = openers[i];
|
openers.RemoveAt(i--);
|
||||||
|
}
|
||||||
var range = GetUpdateRange(mob);
|
else
|
||||||
|
{
|
||||||
if (mob.Map != map || !mob.InRange(worldLoc, range))
|
if (mob == rootParent || mob == tradeRecip)
|
||||||
{
|
{
|
||||||
openers.RemoveAt(i--);
|
continue;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
var ns = mob.NetState;
|
||||||
|
|
||||||
|
if (ns != null && mob.CanSee(this))
|
||||||
{
|
{
|
||||||
if (mob == rootParent || mob == tradeRecip)
|
ns.SendContainerContentUpdate(this);
|
||||||
{
|
SendOPLPacketTo(ns);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ns = mob.NetState;
|
|
||||||
|
|
||||||
if (ns != null && mob.CanSee(this))
|
|
||||||
{
|
|
||||||
ns.SendContainerContentUpdate(this);
|
|
||||||
SendOPLPacketTo(ns);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (openers.Count == 0)
|
if (openers.Count == 0)
|
||||||
{
|
{
|
||||||
contParent.Openers = null;
|
contParent.Openers = null;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1719,6 +1716,7 @@ namespace Server
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private bool GetFlag(ImplFlag flag) => (m_Flags & flag) != 0;
|
private bool GetFlag(ImplFlag flag) => (m_Flags & flag) != 0;
|
||||||
|
|
||||||
public BounceInfo GetBounce() => LookupCompactInfo()?.m_Bounce;
|
public BounceInfo GetBounce() => LookupCompactInfo()?.m_Bounce;
|
||||||
|
|
@ -2408,7 +2406,7 @@ namespace Server
|
||||||
}
|
}
|
||||||
|
|
||||||
#nullable enable
|
#nullable enable
|
||||||
public void InvalidateProperties()
|
public virtual void InvalidateProperties()
|
||||||
{
|
{
|
||||||
if (!ObjectPropertyList.Enabled)
|
if (!ObjectPropertyList.Enabled)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2696,9 +2696,9 @@ namespace Server.Gumps
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
{
|
{
|
||||||
@from.SendGump(
|
from.SendGump(
|
||||||
new AdminGump(
|
new AdminGump(
|
||||||
@from,
|
from,
|
||||||
AdminGumpPage.AccountDetails_Information,
|
AdminGumpPage.AccountDetails_Information,
|
||||||
0,
|
0,
|
||||||
null,
|
null,
|
||||||
|
|
@ -2710,9 +2710,9 @@ namespace Server.Gumps
|
||||||
}
|
}
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
@from.SendGump(
|
from.SendGump(
|
||||||
new AdminGump(
|
new AdminGump(
|
||||||
@from,
|
from,
|
||||||
AdminGumpPage.AccountDetails_Characters,
|
AdminGumpPage.AccountDetails_Characters,
|
||||||
0,
|
0,
|
||||||
null,
|
null,
|
||||||
|
|
@ -2724,9 +2724,9 @@ namespace Server.Gumps
|
||||||
}
|
}
|
||||||
case 2:
|
case 2:
|
||||||
{
|
{
|
||||||
@from.SendGump(
|
from.SendGump(
|
||||||
new AdminGump(
|
new AdminGump(
|
||||||
@from,
|
from,
|
||||||
AdminGumpPage.AccountDetails_Comments,
|
AdminGumpPage.AccountDetails_Comments,
|
||||||
0,
|
0,
|
||||||
null,
|
null,
|
||||||
|
|
@ -2738,23 +2738,23 @@ namespace Server.Gumps
|
||||||
}
|
}
|
||||||
case 3:
|
case 3:
|
||||||
{
|
{
|
||||||
@from.SendGump(
|
from.SendGump(
|
||||||
new AdminGump(@from, AdminGumpPage.AccountDetails_Tags, 0, null, null, m_State)
|
new AdminGump(from, AdminGumpPage.AccountDetails_Tags, 0, null, null, m_State)
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 13:
|
case 13:
|
||||||
{
|
{
|
||||||
@from.SendGump(
|
from.SendGump(
|
||||||
new AdminGump(@from, AdminGumpPage.AccountDetails_Access, 0, null, null, m_State)
|
new AdminGump(from, AdminGumpPage.AccountDetails_Access, 0, null, null, m_State)
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 14:
|
case 14:
|
||||||
{
|
{
|
||||||
@from.SendGump(
|
from.SendGump(
|
||||||
new AdminGump(
|
new AdminGump(
|
||||||
@from,
|
from,
|
||||||
AdminGumpPage.AccountDetails_Access_ClientIPs,
|
AdminGumpPage.AccountDetails_Access_ClientIPs,
|
||||||
0,
|
0,
|
||||||
null,
|
null,
|
||||||
|
|
@ -2766,9 +2766,9 @@ namespace Server.Gumps
|
||||||
}
|
}
|
||||||
case 15:
|
case 15:
|
||||||
{
|
{
|
||||||
@from.SendGump(
|
from.SendGump(
|
||||||
new AdminGump(
|
new AdminGump(
|
||||||
@from,
|
from,
|
||||||
AdminGumpPage.AccountDetails_Access_Restrictions,
|
AdminGumpPage.AccountDetails_Access_Restrictions,
|
||||||
0,
|
0,
|
||||||
null,
|
null,
|
||||||
|
|
@ -2780,14 +2780,14 @@ namespace Server.Gumps
|
||||||
}
|
}
|
||||||
case 4:
|
case 4:
|
||||||
{
|
{
|
||||||
@from.Prompt = new AddCommentPrompt(m_State as Account);
|
from.Prompt = new AddCommentPrompt(m_State as Account);
|
||||||
@from.SendMessage("Enter the new account comment.");
|
from.SendMessage("Enter the new account comment.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 5:
|
case 5:
|
||||||
{
|
{
|
||||||
@from.Prompt = new AddTagNamePrompt(m_State as Account);
|
from.Prompt = new AddTagNamePrompt(m_State as Account);
|
||||||
@from.SendMessage("Enter the new tag name.");
|
from.SendMessage("Enter the new tag name.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 6:
|
case 6:
|
||||||
|
|
@ -2897,9 +2897,9 @@ namespace Server.Gumps
|
||||||
}
|
}
|
||||||
case 8:
|
case 8:
|
||||||
{
|
{
|
||||||
@from.SendGump(
|
from.SendGump(
|
||||||
new AdminGump(
|
new AdminGump(
|
||||||
@from,
|
from,
|
||||||
AdminGumpPage.AccountDetails_ChangePassword,
|
AdminGumpPage.AccountDetails_ChangePassword,
|
||||||
0,
|
0,
|
||||||
null,
|
null,
|
||||||
|
|
@ -2911,9 +2911,9 @@ namespace Server.Gumps
|
||||||
}
|
}
|
||||||
case 9:
|
case 9:
|
||||||
{
|
{
|
||||||
@from.SendGump(
|
from.SendGump(
|
||||||
new AdminGump(
|
new AdminGump(
|
||||||
@from,
|
from,
|
||||||
AdminGumpPage.AccountDetails_ChangeAccess,
|
AdminGumpPage.AccountDetails_ChangeAccess,
|
||||||
0,
|
0,
|
||||||
null,
|
null,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Server.ContextMenus;
|
||||||
using Server.Engines.Craft;
|
using Server.Engines.Craft;
|
||||||
|
|
||||||
namespace Server.Items
|
namespace Server.Items
|
||||||
|
|
@ -141,10 +143,7 @@ namespace Server.Items
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnDoubleClick(Mobile from)
|
public override void OnDoubleClick(Mobile from) => _addon?.OnComponentUsed(this, from);
|
||||||
{
|
|
||||||
_addon?.OnComponentUsed(this, from);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnLocationChange(Point3D old)
|
public override void OnLocationChange(Point3D old)
|
||||||
{
|
{
|
||||||
|
|
@ -162,6 +161,11 @@ namespace Server.Items
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void GetProperties(ObjectPropertyList list) => _addon?.GetProperties(list);
|
||||||
|
|
||||||
|
public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list) =>
|
||||||
|
_addon?.GetContextMenuEntries(from, list);
|
||||||
|
|
||||||
public override void OnAfterDelete()
|
public override void OnAfterDelete()
|
||||||
{
|
{
|
||||||
base.OnAfterDelete();
|
base.OnAfterDelete();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
|
using System;
|
||||||
|
using System.Buffers.Binary;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Server.ContextMenus;
|
using Server.ContextMenus;
|
||||||
|
using Server.Network;
|
||||||
|
|
||||||
namespace Server.Items
|
namespace Server.Items
|
||||||
{
|
{
|
||||||
|
|
@ -19,11 +22,11 @@ namespace Server.Items
|
||||||
|
|
||||||
[SerializableField(0)]
|
[SerializableField(0)]
|
||||||
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
|
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
|
||||||
public BaseAddonContainer _addon;
|
private BaseAddonContainer _addon;
|
||||||
|
|
||||||
[SerializableField(1)]
|
[SerializableField(1)]
|
||||||
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
|
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
|
||||||
public Point3D _offset;
|
private Point3D _offset;
|
||||||
|
|
||||||
[Hue]
|
[Hue]
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
|
|
@ -65,9 +68,6 @@ namespace Server.Items
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list) =>
|
|
||||||
_addon?.GetContextMenuEntries(from, list);
|
|
||||||
|
|
||||||
public override void OnMapChange()
|
public override void OnMapChange()
|
||||||
{
|
{
|
||||||
if (_addon != null)
|
if (_addon != null)
|
||||||
|
|
@ -76,6 +76,11 @@ namespace Server.Items
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void GetProperties(ObjectPropertyList list) => _addon?.GetProperties(list);
|
||||||
|
|
||||||
|
public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list) =>
|
||||||
|
_addon?.GetContextMenuEntries(from, list);
|
||||||
|
|
||||||
public override void OnAfterDelete()
|
public override void OnAfterDelete()
|
||||||
{
|
{
|
||||||
base.OnAfterDelete();
|
base.OnAfterDelete();
|
||||||
|
|
@ -83,6 +88,17 @@ namespace Server.Items
|
||||||
_addon?.Delete();
|
_addon?.Delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void SendWorldPacketTo(NetState ns, ReadOnlySpan<byte> world = default)
|
||||||
|
{
|
||||||
|
Span<byte> buffer = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket();
|
||||||
|
var length = OutgoingItemPackets.CreateWorldItem(buffer, this);
|
||||||
|
// Use an itemid of a real container
|
||||||
|
BinaryPrimitives.WriteUInt16BigEndian(buffer[7..9], (ushort)(_addon?.ItemID ?? 0x9AB));
|
||||||
|
ns.Send(buffer[..length]);
|
||||||
|
|
||||||
|
base.SendWorldPacketTo(ns, world);
|
||||||
|
}
|
||||||
|
|
||||||
[AfterDeserialization]
|
[AfterDeserialization]
|
||||||
private void AfterDeserialization()
|
private void AfterDeserialization()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,29 @@
|
||||||
namespace Server.Items
|
namespace Server.Items
|
||||||
{
|
{
|
||||||
[Serializable(0)]
|
[Serializable(0)]
|
||||||
public partial class ArcaneBookshelfEastAddon : BaseAddon
|
public partial class ArcaneBookshelfEastAddon : BaseAddonContainer
|
||||||
{
|
{
|
||||||
[Constructible]
|
[Constructible]
|
||||||
public ArcaneBookshelfEastAddon()
|
public ArcaneBookshelfEastAddon() : base(0x3084)
|
||||||
{
|
{
|
||||||
AddComponent(new AddonComponent(0x3084), 0, 0, 0);
|
AddComponent(new AddonContainerComponent(0x3085), -1, 0, 0);
|
||||||
AddComponent(new AddonComponent(0x3085), -1, 0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override BaseAddonDeed Deed => new ArcaneBookshelfEastDeed();
|
public override BaseAddonContainerDeed Deed => new ArcaneBookshelfEastDeed();
|
||||||
|
public override bool RetainDeedHue => true;
|
||||||
|
public override int DefaultGumpID => 0x107;
|
||||||
|
public override int DefaultDropSound => 0x42;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable(0)]
|
[Serializable(0)]
|
||||||
public partial class ArcaneBookshelfEastDeed : BaseAddonDeed
|
public partial class ArcaneBookshelfEastDeed : BaseAddonContainerDeed
|
||||||
{
|
{
|
||||||
[Constructible]
|
[Constructible]
|
||||||
public ArcaneBookshelfEastDeed()
|
public ArcaneBookshelfEastDeed()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override BaseAddon Addon => new ArcaneBookshelfEastAddon();
|
public override BaseAddonContainer Addon => new ArcaneBookshelfEastAddon();
|
||||||
public override int LabelNumber => 1073371; // arcane bookshelf (east)
|
public override int LabelNumber => 1073371; // arcane bookshelf (east)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -187,6 +187,19 @@ namespace Server.Items
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void InvalidateProperties()
|
||||||
|
{
|
||||||
|
base.InvalidateProperties();
|
||||||
|
|
||||||
|
if (_components != null)
|
||||||
|
{
|
||||||
|
foreach (var component in _components)
|
||||||
|
{
|
||||||
|
component.InvalidateProperties();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handles v0 with old Enum -> Int casting
|
// Handles v0 with old Enum -> Int casting
|
||||||
private void Deserialize(IGenericReader reader, int version)
|
private void Deserialize(IGenericReader reader, int version)
|
||||||
{
|
{
|
||||||
|
|
@ -332,6 +345,10 @@ namespace Server.Items
|
||||||
|
|
||||||
public virtual void OnComponentUsed(AddonContainerComponent c, Mobile from)
|
public virtual void OnComponentUsed(AddonContainerComponent c, Mobile from)
|
||||||
{
|
{
|
||||||
|
if (!Deleted)
|
||||||
|
{
|
||||||
|
OnDoubleClick(from);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,28 +16,9 @@ namespace Server.Items
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int DefaultMaxWeight
|
public override int DefaultMaxWeight => IsSecure ? 0 : base.DefaultMaxWeight;
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (IsSecure)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.DefaultMaxWeight;
|
public override bool IsAccessibleTo(Mobile m) => BaseHouse.CheckAccessible(m, this) && base.IsAccessibleTo(m);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool IsAccessibleTo(Mobile m)
|
|
||||||
{
|
|
||||||
if (!BaseHouse.CheckAccessible(m, this))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.IsAccessibleTo(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CheckHold(Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight)
|
public override bool CheckHold(Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue