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:
Kamron Batman 2021-09-12 22:23:15 -07:00 committed by GitHub
parent 52dacec68f
commit c5e35c9b69
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 104 additions and 86 deletions

View file

@ -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

View file

@ -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)
{ {

View file

@ -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,

View file

@ -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();

View file

@ -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()
{ {

View file

@ -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)
} }
} }

View file

@ -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);
}
} }
} }
} }

View file

@ -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)
{ {