diff --git a/dev/Configuration/UserInterfaceSettings.cs b/dev/Configuration/UserInterfaceSettings.cs
index 3eca3ab9..38b5090b 100644
--- a/dev/Configuration/UserInterfaceSettings.cs
+++ b/dev/Configuration/UserInterfaceSettings.cs
@@ -30,12 +30,13 @@ public class UserInterfaceSettings : ASettingsSection
private bool m_AlwaysRun;
private bool m_MenuBarDisabled;
- private int m_SpeechColor;
- private int m_EmoteColor;
- private int m_PartyMsgColor;
- private int m_GuildMsgColor;
+ private int m_SpeechColor = 4 * Utility.RandomValue(0, 99) * 5;
+ private int m_EmoteColor = 646;
+ private int m_PartyMsgPrivateColor = 58;
+ private int m_PartyMsgColor = 68;
+ private int m_GuildMsgColor = 70;
private bool m_IgnoreGuildMsg;
- private int m_AllianceMsgColor;
+ private int m_AllianceMsgColor = 487;
private bool m_IgnoreAllianceMsg;
private bool m_CrimeQuery;
@@ -122,6 +123,12 @@ public int EmoteColor
set { SetProperty(ref m_EmoteColor, Clamp(value, 0, HueData.HueCount - 1)); }
}
+ public int PartyPrivateMsgColor
+ {
+ get { return m_PartyMsgPrivateColor; }
+ set { SetProperty(ref m_PartyMsgPrivateColor, Clamp(value, 0, HueData.HueCount - 1)); }
+ }
+
public int PartyMsgColor
{
get { return m_PartyMsgColor; }
diff --git a/dev/Core/UI/UserInterfaceService.cs b/dev/Core/UI/UserInterfaceService.cs
index 0218a4e7..581e9f41 100644
--- a/dev/Core/UI/UserInterfaceService.cs
+++ b/dev/Core/UI/UserInterfaceService.cs
@@ -230,7 +230,7 @@ public void Update(double totalMS, double frameMS)
for (int i = 0; i < m_Controls.Count; i++)
{
AControl c = m_Controls[i];
- if (!c.IsInitialized)
+ if (!c.IsInitialized && !c.IsDisposed)
c.Initialize();
c.Update(totalMS, frameMS);
}
@@ -640,7 +640,7 @@ private void ClipMouse(ref Point position)
position.X = -8;
if (position.Y < -8)
position.Y = -8;
- if (position.Y >= Width + 8)
+ if (position.X >= Width + 8)
position.X = Width + 8;
if (position.Y >= Height + 8)
position.Y = Height + 8;
diff --git a/dev/Ultima/Data/ChatMode.cs b/dev/Ultima/Data/ChatMode.cs
index 980ea431..90a2e239 100644
--- a/dev/Ultima/Data/ChatMode.cs
+++ b/dev/Ultima/Data/ChatMode.cs
@@ -4,6 +4,7 @@ public enum ChatMode {
Whisper,
Emote,
Party,
+ PartyPrivate,
Guild,
Alliance
}
diff --git a/dev/Ultima/Data/MessageTypes.cs b/dev/Ultima/Data/MessageTypes.cs
index 1e97dc77..52a77ed2 100644
--- a/dev/Ultima/Data/MessageTypes.cs
+++ b/dev/Ultima/Data/MessageTypes.cs
@@ -9,8 +9,11 @@
*
***************************************************************************/
+using System;
+
namespace UltimaXNA.Ultima.Data
{
+ [Flags]
public enum MessageTypes
{
Normal = 0x00,
@@ -23,11 +26,13 @@ public enum MessageTypes
Whisper = 0x08,
Yell = 0x09,
Spell = 0x0A,
-
Guild = 0x0D,
Alliance = 0x0E,
Command = 0x0F,
-
- EncodedTriggers = 0xC0
+ ///
+ /// This is used for display only. This is not in the UO protocol. Do not send msgs of this type to the server.
+ ///
+ PartyDisplayOnly = 0x10,
+ EncodedTriggers = 0xC0 // 0x40 + 0x80
}
}
diff --git a/dev/Ultima/Network/Client/AsciiSpeechPacket.cs b/dev/Ultima/Network/Client/AsciiSpeechPacket.cs
index 181df574..e189f473 100644
--- a/dev/Ultima/Network/Client/AsciiSpeechPacket.cs
+++ b/dev/Ultima/Network/Client/AsciiSpeechPacket.cs
@@ -26,7 +26,7 @@ public AsciiSpeechPacket(MessageTypes type, int font, int hue, string lang, stri
int triggerCount; int[] triggers;
SpeechData.GetSpeechTriggers(text, lang, out triggerCount, out triggers);
if (triggerCount > 0)
- type = (MessageTypes)(type | MessageTypes.EncodedTriggers);
+ type = type | MessageTypes.EncodedTriggers;
Stream.Write((byte)type);
Stream.Write((short)hue);
diff --git a/dev/Ultima/Network/Client/Extensions/GuildLocationQueryPacket.cs b/dev/Ultima/Network/Client/Extensions/GuildLocationQueryPacket.cs
new file mode 100644
index 00000000..da6b456f
--- /dev/null
+++ b/dev/Ultima/Network/Client/Extensions/GuildLocationQueryPacket.cs
@@ -0,0 +1,22 @@
+/***************************************************************************
+ * GuildLocationQueryPacket.cs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ ***************************************************************************/
+using UltimaXNA.Core.Network.Packets;
+
+namespace UltimaXNA.Ultima.Network.Client.Extensions {
+ ///
+ /// MapUO Protocol: Requests the position of all guild members.
+ ///
+ public class GuildLocationQueryPacket : SendPacket {
+ public GuildLocationQueryPacket()
+ : base(0xF0, "Query Guild Member Locations") {
+ Stream.Write((byte)0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dev/Ultima/Network/Client/Extensions/PartyLocationQueryPacket.cs b/dev/Ultima/Network/Client/Extensions/PartyLocationQueryPacket.cs
new file mode 100644
index 00000000..c2f2b2d6
--- /dev/null
+++ b/dev/Ultima/Network/Client/Extensions/PartyLocationQueryPacket.cs
@@ -0,0 +1,22 @@
+/***************************************************************************
+ * PartyLocationQueryPacket.cs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ ***************************************************************************/
+using UltimaXNA.Core.Network.Packets;
+
+namespace UltimaXNA.Ultima.Network.Client.Extensions {
+ ///
+ /// MapUO Protocol: Requests the position of all party members.
+ ///
+ public class PartyLocationQueryPacket : SendPacket {
+ public PartyLocationQueryPacket()
+ : base(0xF0, "Query Party Member Locations") {
+ Stream.Write((byte)0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dev/Ultima/Network/Client/Partying/PartyAcceptPacket.cs b/dev/Ultima/Network/Client/Partying/PartyAcceptPacket.cs
new file mode 100644
index 00000000..cfd38b95
--- /dev/null
+++ b/dev/Ultima/Network/Client/Partying/PartyAcceptPacket.cs
@@ -0,0 +1,21 @@
+/***************************************************************************
+ * PartyAcceptPacket.cs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ ***************************************************************************/
+using UltimaXNA.Core.Network.Packets;
+
+namespace UltimaXNA.Ultima.Network.Client.Partying {
+ public class PartyAcceptPacket : SendPacket {
+ public PartyAcceptPacket(Serial invitingPartyLeader)
+ : base(0xbf, "Party Join Accept") {
+ Stream.Write((short)6);
+ Stream.Write((byte)8);
+ Stream.Write(invitingPartyLeader);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dev/Ultima/Network/Client/Partying/PartyDeclinePacket.cs b/dev/Ultima/Network/Client/Partying/PartyDeclinePacket.cs
new file mode 100644
index 00000000..69987a4e
--- /dev/null
+++ b/dev/Ultima/Network/Client/Partying/PartyDeclinePacket.cs
@@ -0,0 +1,21 @@
+/***************************************************************************
+ * PartyDeclinePacket.cs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ ***************************************************************************/
+using UltimaXNA.Core.Network.Packets;
+
+namespace UltimaXNA.Ultima.Network.Client.Partying {
+ public class PartyDeclinePacket : SendPacket {
+ public PartyDeclinePacket(Serial invitingPartyLeader)
+ : base(0xbf, "Party Join Decline") {
+ Stream.Write((short)6);
+ Stream.Write((byte)9);
+ Stream.Write(invitingPartyLeader);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dev/Ultima/Network/Client/Partying/PartyLeavePacket.cs b/dev/Ultima/Network/Client/Partying/PartyLeavePacket.cs
new file mode 100644
index 00000000..8de41c75
--- /dev/null
+++ b/dev/Ultima/Network/Client/Partying/PartyLeavePacket.cs
@@ -0,0 +1,22 @@
+/***************************************************************************
+ * PartyLeavePacket.cs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ ***************************************************************************/
+using UltimaXNA.Core.Network.Packets;
+using UltimaXNA.Ultima.World;
+
+namespace UltimaXNA.Ultima.Network.Client.Partying {
+ public class PartyLeavePacket : SendPacket {
+ public PartyLeavePacket()
+ : base(0xbf, "Leave Party") {
+ Stream.Write((short)6);
+ Stream.Write((byte)2);
+ Stream.Write(WorldModel.PlayerSerial);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dev/Ultima/Network/Client/Partying/PartyLootEnablePacket.cs b/dev/Ultima/Network/Client/Partying/PartyLootEnablePacket.cs
new file mode 100644
index 00000000..590f351a
--- /dev/null
+++ b/dev/Ultima/Network/Client/Partying/PartyLootEnablePacket.cs
@@ -0,0 +1,21 @@
+/***************************************************************************
+ * PartyCanLootPacket.cs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ ***************************************************************************/
+using UltimaXNA.Core.Network.Packets;
+
+namespace UltimaXNA.Ultima.Network.Client.Partying {
+ class PartyCanLootPacket : SendPacket {
+ public PartyCanLootPacket(bool isLootable)
+ : base(0xbf, "Party Can Loot") {
+ Stream.Write((short)6);
+ Stream.Write((byte)6);
+ Stream.Write(isLootable);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dev/Ultima/Network/Client/Partying/PartyPrivateMessagePacket.cs b/dev/Ultima/Network/Client/Partying/PartyPrivateMessagePacket.cs
new file mode 100644
index 00000000..3c9225b1
--- /dev/null
+++ b/dev/Ultima/Network/Client/Partying/PartyPrivateMessagePacket.cs
@@ -0,0 +1,22 @@
+/***************************************************************************
+ * PartyPrivateMessagePacket.cs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ ***************************************************************************/
+using UltimaXNA.Core.Network.Packets;
+
+namespace UltimaXNA.Ultima.Network.Client.Partying {
+ public class PartyPrivateMessagePacket : SendPacket {
+ public PartyPrivateMessagePacket(Serial memberSerial, string msg)
+ : base(0xbf, "Private Party Message") {
+ Stream.Write((short)6);
+ Stream.Write((byte)3);
+ Stream.Write(memberSerial);
+ Stream.WriteBigUniNull(msg);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dev/Ultima/Network/Client/Partying/PartyPublicMessagePacket.cs b/dev/Ultima/Network/Client/Partying/PartyPublicMessagePacket.cs
new file mode 100644
index 00000000..93af4fc3
--- /dev/null
+++ b/dev/Ultima/Network/Client/Partying/PartyPublicMessagePacket.cs
@@ -0,0 +1,21 @@
+/***************************************************************************
+ * PartyPublicMessagePacket.cs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ ***************************************************************************/
+using UltimaXNA.Core.Network.Packets;
+
+namespace UltimaXNA.Ultima.Network.Client.Partying {
+ public class PartyPublicMessagePacket : SendPacket {
+ public PartyPublicMessagePacket(string msg)
+ : base(0xbf, "Public Party Message") {
+ Stream.Write((short)6);
+ Stream.Write((byte)4);
+ Stream.WriteBigUniNull(msg);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dev/Ultima/Network/Client/Partying/PartyRemoveMemberPacket.cs b/dev/Ultima/Network/Client/Partying/PartyRemoveMemberPacket.cs
new file mode 100644
index 00000000..70a40166
--- /dev/null
+++ b/dev/Ultima/Network/Client/Partying/PartyRemoveMemberPacket.cs
@@ -0,0 +1,21 @@
+/***************************************************************************
+ * PartyRemoveMemberPacket.cs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ ***************************************************************************/
+using UltimaXNA.Core.Network.Packets;
+
+namespace UltimaXNA.Ultima.Network.Client.Partying {
+ public class PartyRemoveMemberPacket : SendPacket {
+ public PartyRemoveMemberPacket(Serial serial)
+ : base(0xbf, "Remove Party Member") {
+ Stream.Write((short)6);
+ Stream.Write((byte)2);
+ Stream.Write(serial);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dev/Ultima/Network/Client/Partying/PartyRequestAddTargetPacket.cs b/dev/Ultima/Network/Client/Partying/PartyRequestAddTargetPacket.cs
new file mode 100644
index 00000000..330794ee
--- /dev/null
+++ b/dev/Ultima/Network/Client/Partying/PartyRequestAddTargetPacket.cs
@@ -0,0 +1,21 @@
+/***************************************************************************
+ * PartyRequestAddTargetPacket.cs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ ***************************************************************************/
+using UltimaXNA.Core.Network.Packets;
+
+namespace UltimaXNA.Ultima.Network.Client.Partying {
+ class PartyRequestAddTargetPacket : SendPacket {
+ public PartyRequestAddTargetPacket()
+ : base(0xbf, "Add Party Member") {
+ Stream.Write((short)6);
+ Stream.Write((byte)1);
+ Stream.Write(0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dev/Ultima/Network/Client/Partying/PartyRequestRemoveTargetPacket.cs b/dev/Ultima/Network/Client/Partying/PartyRequestRemoveTargetPacket.cs
new file mode 100644
index 00000000..6b0e7a8c
--- /dev/null
+++ b/dev/Ultima/Network/Client/Partying/PartyRequestRemoveTargetPacket.cs
@@ -0,0 +1,21 @@
+/***************************************************************************
+ * PartyRequestRemoveTargetPacket.cs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ ***************************************************************************/
+using UltimaXNA.Core.Network.Packets;
+
+namespace UltimaXNA.Ultima.Network.Client.Partying {
+ public class PartyRequestRemoveTargetPacket : SendPacket {
+ public PartyRequestRemoveTargetPacket()
+ : base(0xbf, "Remove Party Member") {
+ Stream.Write((short)6);
+ Stream.Write((byte)2);
+ Stream.Write(0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dev/Ultima/Network/Server/CustomHousePacket.cs b/dev/Ultima/Network/Server/CustomHousePacket.cs
index d3eab9dc..b13c3689 100644
--- a/dev/Ultima/Network/Server/CustomHousePacket.cs
+++ b/dev/Ultima/Network/Server/CustomHousePacket.cs
@@ -1,13 +1,6 @@
/***************************************************************************
* CustomHousePacket.cs
*
- * begin : February 24, 2010
- * email : poplicola@ultimaxna.com
- *
- ***************************************************************************/
-
-/***************************************************************************
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
@@ -17,48 +10,41 @@
#region usings
using UltimaXNA.Core.Network;
using UltimaXNA.Core.Network.Packets;
-using UltimaXNA.Ultima.World;
+using UltimaXNA.Ultima.World.Data;
#endregion
-namespace UltimaXNA.Ultima.Network.Server
-{
- public class CustomHousePacket : RecvPacket
- {
- readonly Serial m_houseSerial;
- public Serial HouseSerial { get { return m_houseSerial; } }
+namespace UltimaXNA.Ultima.Network.Server {
+ public class CustomHousePacket : RecvPacket {
+ readonly Serial m_HouseSerial;
+ public Serial HouseSerial => m_HouseSerial;
- readonly int m_revisionHash;
- public int RevisionHash { get { return m_revisionHash; } }
+ readonly int m_RevisionHash;
+ public int RevisionHash => m_RevisionHash;
- readonly int m_numPlanes;
- public int PlaneCount { get { return m_numPlanes; } }
+ readonly int m_NumPlanes;
+ public int PlaneCount => m_NumPlanes;
- CustomHousePlane[] m_planes;
- public CustomHousePlane[] Planes { get { return m_planes; } }
+ CustomHousePlane[] m_Planes;
+ public CustomHousePlane[] Planes => m_Planes;
public CustomHousePacket(PacketReader reader)
- : base(0xD8, "Custom House Packet")
- {
+ : base(0xD8, "Custom House Packet") {
byte CompressionType = reader.ReadByte();
- if (CompressionType != 3)
- {
- m_houseSerial = Serial.Null;
+ if (CompressionType != 3) {
+ m_HouseSerial = Serial.Null;
return;
}
reader.ReadByte(); // unknown, always 0?
- m_houseSerial = reader.ReadInt32();
- m_revisionHash = reader.ReadInt32();
-
+ m_HouseSerial = reader.ReadInt32();
+ m_RevisionHash = reader.ReadInt32();
// this is for compression type 3 only
int bufferLength = reader.ReadInt16();
int trueBufferLength = reader.ReadInt16();
- m_numPlanes = reader.ReadByte();
+ m_NumPlanes = reader.ReadByte();
// end compression type 3
-
- m_planes = new CustomHousePlane[m_numPlanes];
- for (int i = 0; i < m_numPlanes; i++)
- {
- m_planes[i] = new CustomHousePlane(reader);
+ m_Planes = new CustomHousePlane[m_NumPlanes];
+ for (int i = 0; i < m_NumPlanes; i++) {
+ m_Planes[i] = new CustomHousePlane(reader);
}
}
}
diff --git a/dev/Ultima/Network/Server/GeneralInfo/CloseGumpInfo.cs b/dev/Ultima/Network/Server/GeneralInfo/CloseGumpInfo.cs
new file mode 100644
index 00000000..cc93b4da
--- /dev/null
+++ b/dev/Ultima/Network/Server/GeneralInfo/CloseGumpInfo.cs
@@ -0,0 +1,16 @@
+using UltimaXNA.Core.Network;
+
+namespace UltimaXNA.Ultima.Network.Server.GeneralInfo {
+ ///
+ /// Subcommand 0x04: Close a generic gump.
+ ///
+ class CloseGumpInfo : IGeneralInfo {
+ public readonly int GumpTypeID;
+ public readonly int GumpButtonID;
+
+ public CloseGumpInfo(PacketReader reader) {
+ GumpTypeID = reader.ReadInt32();
+ GumpButtonID = reader.ReadInt32();
+ }
+ }
+}
diff --git a/dev/Ultima/Network/Server/GeneralInfo/ContextMenuInfo.cs b/dev/Ultima/Network/Server/GeneralInfo/ContextMenuInfo.cs
new file mode 100644
index 00000000..ae0fe5ee
--- /dev/null
+++ b/dev/Ultima/Network/Server/GeneralInfo/ContextMenuInfo.cs
@@ -0,0 +1,28 @@
+using UltimaXNA.Core.Network;
+using UltimaXNA.Ultima.Data;
+
+namespace UltimaXNA.Ultima.Network.Server.GeneralInfo {
+ ///
+ /// Subcommand 0x14: A context menu.
+ ///
+ class ContextMenuInfo : IGeneralInfo {
+ public readonly ContextMenuData Menu;
+
+ public ContextMenuInfo(PacketReader reader) {
+ reader.ReadByte(); // unknown, always 0x00
+ int subcommand = reader.ReadByte(); // 0x01 for 2D, 0x02 for KR
+ Menu = new ContextMenuData(reader.ReadInt32());
+ int contextMenuChoiceCount = reader.ReadByte();
+ for (int i = 0; i < contextMenuChoiceCount; i++) {
+ int iUniqueID = reader.ReadUInt16();
+ int iClilocID = reader.ReadUInt16() + 3000000;
+ int iFlags = reader.ReadUInt16(); // 0x00=enabled, 0x01=disabled, 0x02=arrow, 0x20 = color
+ int iColor = 0;
+ if ((iFlags & 0x20) == 0x20) {
+ iColor = reader.ReadUInt16();
+ }
+ Menu.AddItem(iUniqueID, iClilocID, iFlags, iColor);
+ }
+ }
+ }
+}
diff --git a/dev/Ultima/Network/Server/GeneralInfo/ExtendedStatsInfo.cs b/dev/Ultima/Network/Server/GeneralInfo/ExtendedStatsInfo.cs
new file mode 100644
index 00000000..3b2baf7c
--- /dev/null
+++ b/dev/Ultima/Network/Server/GeneralInfo/ExtendedStatsInfo.cs
@@ -0,0 +1,45 @@
+using UltimaXNA.Core.Diagnostics.Tracing;
+using UltimaXNA.Core.Network;
+using UltimaXNA.Ultima.Data;
+
+namespace UltimaXNA.Ultima.Network.Server.GeneralInfo {
+ ///
+ /// Subcommand 0x19: the serial of the mobile which the extended stats must be applied to.
+ ///
+ class ExtendedStatsInfo :IGeneralInfo {
+
+ public readonly Serial Serial;
+ public readonly StatLocks Locks;
+
+ public ExtendedStatsInfo(PacketReader reader) {
+ int clientFlag = reader.ReadByte(); // (0x2 for 2D client, 0x5 for KR client)
+ Serial = reader.ReadInt32();
+ byte unknown0 = reader.ReadByte(); // (always 0)
+ byte lockFlags = reader.ReadByte();
+ // Lock flags = bitflags 00SSDDII
+ // 00 = up
+ // 01 = down
+ // 10 = locked
+ // FF = update mobile status animation ( KR only )
+ if (lockFlags != 0xFF) {
+ int strengthLock = (lockFlags >> 4) & 0x03;
+ int dexterityLock = (lockFlags >> 2) & 0x03;
+ int inteligenceLock = (lockFlags) & 0x03;
+ Locks = new StatLocks(strengthLock, dexterityLock, inteligenceLock);
+ }
+ if (clientFlag == 5) {
+ Tracer.Warn("ClientFlags == 5 in GeneralInfoPacket ExtendedStats 0x19. This is not a KR client.");
+ // If(Lock flags = 0xFF) //Update mobile status animation
+ // BYTE[1] Status // Unveryfied if lock flags == FF the locks will be handled here
+ // BYTE[1] unknown (0x00)
+ // BYTE[1] Animation
+ // BYTE[1] unknown (0x00)
+ // BYTE[1] Frame
+ // else
+ // BYTE[1] unknown (0x00)
+ // BYTE[4] unknown (0x00000000)
+ // endif
+ }
+ }
+ }
+}
diff --git a/dev/Ultima/Network/Server/GeneralInfo/HouseRevisionInfo.cs b/dev/Ultima/Network/Server/GeneralInfo/HouseRevisionInfo.cs
new file mode 100644
index 00000000..18706643
--- /dev/null
+++ b/dev/Ultima/Network/Server/GeneralInfo/HouseRevisionInfo.cs
@@ -0,0 +1,17 @@
+using UltimaXNA.Core.Network;
+using UltimaXNA.Ultima.Data;
+
+namespace UltimaXNA.Ultima.Network.Server.GeneralInfo {
+ ///
+ /// Subcommand 0x1D: The revision hash of a custom house.
+ ///
+ class HouseRevisionInfo : IGeneralInfo {
+ public readonly HouseRevisionState Revision;
+
+ public HouseRevisionInfo(PacketReader reader) {
+ Serial s = reader.ReadInt32();
+ int hash = reader.ReadInt32();
+ Revision = new HouseRevisionState(s, hash);
+ }
+ }
+}
diff --git a/dev/Ultima/Network/Server/GeneralInfo/IGeneralInfo.cs b/dev/Ultima/Network/Server/GeneralInfo/IGeneralInfo.cs
new file mode 100644
index 00000000..3e95cda5
--- /dev/null
+++ b/dev/Ultima/Network/Server/GeneralInfo/IGeneralInfo.cs
@@ -0,0 +1,13 @@
+/***************************************************************************
+ * IGeneralInfo.cs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ ***************************************************************************/
+
+namespace UltimaXNA.Ultima.Network.Server.GeneralInfo {
+ public interface IGeneralInfo { }
+}
diff --git a/dev/Ultima/Data/MapDiffInfo.cs b/dev/Ultima/Network/Server/GeneralInfo/MapDiffInfo.cs
similarity index 52%
rename from dev/Ultima/Data/MapDiffInfo.cs
rename to dev/Ultima/Network/Server/GeneralInfo/MapDiffInfo.cs
index a041c1d5..0b00d685 100644
--- a/dev/Ultima/Data/MapDiffInfo.cs
+++ b/dev/Ultima/Network/Server/GeneralInfo/MapDiffInfo.cs
@@ -1,20 +1,20 @@
using UltimaXNA.Core.Network;
-namespace UltimaXNA.Ultima.Data
-{
- public class MapDiffInfo
- {
+namespace UltimaXNA.Ultima.Network.Server.GeneralInfo {
+ ///
+ /// Subcommand 0x18: The count of map diffs that were received.
+ /// As of 6.0.0.0, this is only used to inform the client of the number of active maps.
+ ///
+ public class MapDiffInfo : IGeneralInfo {
public readonly int MapCount;
public readonly int[] MapPatches;
public readonly int[] StaticPatches;
- public MapDiffInfo(PacketReader reader)
- {
+ public MapDiffInfo(PacketReader reader) {
MapCount = reader.ReadInt32();
MapPatches = new int[MapCount];
StaticPatches = new int[MapCount];
- for (int i = 0; i < MapCount; i++)
- {
+ for (int i = 0; i < MapCount; i++) {
StaticPatches[i] = reader.ReadInt32();
MapPatches[i] = reader.ReadInt32();
}
diff --git a/dev/Ultima/Network/Server/GeneralInfo/MapIndexInfo.cs b/dev/Ultima/Network/Server/GeneralInfo/MapIndexInfo.cs
new file mode 100644
index 00000000..6b42c2bb
--- /dev/null
+++ b/dev/Ultima/Network/Server/GeneralInfo/MapIndexInfo.cs
@@ -0,0 +1,13 @@
+using UltimaXNA.Core.Network;
+
+namespace UltimaXNA.Ultima.Network.Server.GeneralInfo {
+ ///
+ /// Subcommand 0x08: The index of the map the player is located within.
+ ///
+ class MapIndexInfo : IGeneralInfo {
+ public readonly byte MapID;
+ public MapIndexInfo(PacketReader reader) {
+ MapID = reader.ReadByte();
+ }
+ }
+}
diff --git a/dev/Ultima/Network/Server/GeneralInfo/PartyInfo.cs b/dev/Ultima/Network/Server/GeneralInfo/PartyInfo.cs
new file mode 100644
index 00000000..7f1af742
--- /dev/null
+++ b/dev/Ultima/Network/Server/GeneralInfo/PartyInfo.cs
@@ -0,0 +1,43 @@
+using UltimaXNA.Core.Diagnostics.Tracing;
+using UltimaXNA.Core.Network;
+
+namespace UltimaXNA.Ultima.Network.Server.GeneralInfo
+{
+ ///
+ /// Subcommand 0x06: Party info.
+ ///
+ class PartyInfo : IGeneralInfo {
+ public const byte CommandPartyList = 0x01;
+ public const byte CommandRemoveMember = 0x02;
+ public const byte CommandPrivateMessage = 0x03;
+ public const byte CommandPublicMessage = 0x04;
+ public const byte CommandInvitation = 0x07;
+
+ public readonly byte SubsubCommand;
+ public readonly IGeneralInfo Info;
+
+ public PartyInfo(PacketReader reader) {
+ SubsubCommand = reader.ReadByte();
+ switch (SubsubCommand) {
+ case CommandPartyList:
+ Info = new PartyMemberListInfo(reader);
+ break;
+ case CommandRemoveMember:
+ Info = new PartyRemoveMemberInfo(reader);
+ break;
+ case CommandPrivateMessage:
+ Info = new PartyMessageInfo(reader, true);
+ break;
+ case CommandPublicMessage:
+ Info = new PartyMessageInfo(reader, false);
+ break;
+ case CommandInvitation://PARTY INVITE PROGRESS
+ Info = new PartyInvitationInfo(reader);
+ break;
+ default:
+ Tracer.Warn($"Unhandled Subsubcommand {SubsubCommand:X2} in PartyInfo.");
+ break;
+ }
+ }
+ }
+}
diff --git a/dev/Ultima/Network/Server/GeneralInfo/PartyInvitationInfo.cs b/dev/Ultima/Network/Server/GeneralInfo/PartyInvitationInfo.cs
new file mode 100644
index 00000000..f8818e64
--- /dev/null
+++ b/dev/Ultima/Network/Server/GeneralInfo/PartyInvitationInfo.cs
@@ -0,0 +1,24 @@
+/***************************************************************************
+ * PartyInvitationInfo.cs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ ***************************************************************************/
+
+using UltimaXNA.Core.Network;
+
+namespace UltimaXNA.Ultima.Network.Server.GeneralInfo {
+ ///
+ /// Subcommand 0x06 / 0x07: Invitation to joint a party.
+ ///
+ public class PartyInvitationInfo : IGeneralInfo {
+ public readonly int PartyLeaderSerial;
+
+ public PartyInvitationInfo(PacketReader reader) {
+ PartyLeaderSerial = reader.ReadInt32();
+ }
+ }
+}
diff --git a/dev/Ultima/Network/Server/GeneralInfo/PartyMemberListInfo.cs b/dev/Ultima/Network/Server/GeneralInfo/PartyMemberListInfo.cs
new file mode 100644
index 00000000..8cbe9e1f
--- /dev/null
+++ b/dev/Ultima/Network/Server/GeneralInfo/PartyMemberListInfo.cs
@@ -0,0 +1,29 @@
+/***************************************************************************
+ * PartyMemberListInfo.cs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ ***************************************************************************/
+
+using UltimaXNA.Core.Network;
+
+namespace UltimaXNA.Ultima.Network.Server.GeneralInfo {
+ ///
+ /// Subcommand 0x06 / 0x01: Party menber list.
+ ///
+ public class PartyMemberListInfo : IGeneralInfo {
+ public readonly int Count;
+ public readonly int[] Serials;
+
+ public PartyMemberListInfo(PacketReader reader) {
+ Count = reader.ReadByte();
+ Serials = new int[Count];
+ for (int i = 0; i < Count; i++) {
+ Serials[i] = reader.ReadInt32();
+ }
+ }
+ }
+}
diff --git a/dev/Ultima/Network/Server/GeneralInfo/PartyMessageInfo.cs b/dev/Ultima/Network/Server/GeneralInfo/PartyMessageInfo.cs
new file mode 100644
index 00000000..a4a39044
--- /dev/null
+++ b/dev/Ultima/Network/Server/GeneralInfo/PartyMessageInfo.cs
@@ -0,0 +1,28 @@
+/***************************************************************************
+ * PartyMessageInfo.cs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ ***************************************************************************/
+
+using UltimaXNA.Core.Network;
+
+namespace UltimaXNA.Ultima.Network.Server.GeneralInfo {
+ ///
+ /// Subcommand 0x06 / 0x03 and 0x06 / 0x04: Party message.
+ ///
+ public class PartyMessageInfo : IGeneralInfo {
+ public readonly bool IsPrivate;
+ public readonly Serial Source;
+ public readonly string Message;
+
+ public PartyMessageInfo(PacketReader reader, bool isPrivate) {
+ IsPrivate = isPrivate;
+ Source = (Serial)reader.ReadInt32();
+ Message = reader.ReadUnicodeString();
+ }
+ }
+}
diff --git a/dev/Ultima/Network/Server/GeneralInfo/PartyRemoveMemberInfo.cs b/dev/Ultima/Network/Server/GeneralInfo/PartyRemoveMemberInfo.cs
new file mode 100644
index 00000000..6c0d6acd
--- /dev/null
+++ b/dev/Ultima/Network/Server/GeneralInfo/PartyRemoveMemberInfo.cs
@@ -0,0 +1,31 @@
+/***************************************************************************
+ * PartyRemoveMemberInfo.cs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ ***************************************************************************/
+
+using UltimaXNA.Core.Network;
+
+namespace UltimaXNA.Ultima.Network.Server.GeneralInfo {
+ ///
+ /// Subcommand 0x06 / 0x03 and 0x06 / 0x02: Remove party member.
+ ///
+ public class PartyRemoveMemberInfo : IGeneralInfo {
+ public readonly int Count;
+ public readonly int RemovedMember;
+ public readonly int[] Serials;
+
+ public PartyRemoveMemberInfo(PacketReader reader) {
+ Count = reader.ReadByte();
+ RemovedMember = reader.ReadInt32();
+ Serials = new int[Count];
+ for (int i = 0; i < Count; i++) {
+ Serials[i] = reader.ReadInt32();
+ }
+ }
+ }
+}
diff --git a/dev/Ultima/Network/Server/GeneralInfo/SpellBookContentsInfo.cs b/dev/Ultima/Network/Server/GeneralInfo/SpellBookContentsInfo.cs
new file mode 100644
index 00000000..90ee0058
--- /dev/null
+++ b/dev/Ultima/Network/Server/GeneralInfo/SpellBookContentsInfo.cs
@@ -0,0 +1,20 @@
+using UltimaXNA.Core.Network;
+using UltimaXNA.Ultima.Data;
+
+namespace UltimaXNA.Ultima.Network.Server.GeneralInfo {
+ ///
+ /// Subcommand 0x1B: the contents of a spellbook.
+ ///
+ class SpellBookContentsInfo : IGeneralInfo {
+ public readonly SpellbookData Spellbook;
+
+ public SpellBookContentsInfo(PacketReader reader) {
+ ushort unknown = reader.ReadUInt16(); // always 1
+ Serial serial = reader.ReadInt32();
+ ushort itemID = reader.ReadUInt16();
+ ushort spellbookType = reader.ReadUInt16(); // 1==regular, 101=necro, 201=paladin, 401=bushido, 501=ninjitsu, 601=spellweaving
+ ulong spellBitfields = reader.ReadUInt32() + (((ulong)reader.ReadUInt32()) << 32); // first bit of first byte = spell #1, second bit of first byte = spell #2, first bit of second byte = spell #8, etc
+ Spellbook = new SpellbookData(serial, itemID, spellbookType, spellBitfields);
+ }
+ }
+}
diff --git a/dev/Ultima/Network/Server/GeneralInfoPacket.cs b/dev/Ultima/Network/Server/GeneralInfoPacket.cs
index 9762a4cc..e30b4c2f 100644
--- a/dev/Ultima/Network/Server/GeneralInfoPacket.cs
+++ b/dev/Ultima/Network/Server/GeneralInfoPacket.cs
@@ -11,202 +11,59 @@
using UltimaXNA.Core.Diagnostics.Tracing;
using UltimaXNA.Core.Network;
using UltimaXNA.Core.Network.Packets;
-using UltimaXNA.Ultima.Data;
+using UltimaXNA.Ultima.Network.Server.GeneralInfo;
#endregion
-namespace UltimaXNA.Ultima.Network.Server
-{
- public class GeneralInfoPacket : RecvPacket
- {
- public readonly short Subcommand;
+namespace UltimaXNA.Ultima.Network.Server {
+ public class GeneralInfoPacket : RecvPacket {
+ public const int CloseGump = 0x04;
+ public const int Party = 0x06;
+ public const int SetMap = 0x08;
+ public const int ContextMenu = 0x14;
+ public const int MapDiff = 0x18;
+ public const int ExtendedStats = 0x19;
+ public const int SpellBookContents = 0x1B;
+ public const int HouseRevision = 0x1D;
+ public const int AOSAbilityIconConfirm = 0x21;
- ///
- /// Subcommand 0x04: Close a generic gump.
- ///
- public int CloseGumpTypeID
- {
- get;
- private set;
- }
- public int CloseGumpButtonID
- {
- get;
- private set;
- }
-
- ///
- /// Subcommand 0x08: The index of the map the player is located within.
- ///
- public byte MapID
- {
- get;
- private set;
- }
-
- ///
- /// Subcommand 0x14: A context menu.
- ///
- public ContextMenuData ContextMenu
- {
- get;
- private set;
- }
-
- ///
- /// Subcommand 0x18: The count of map diffs that were received.
- ///
- public MapDiffInfo MapDiffs
- {
- get;
- private set;
- }
-
- ///
- /// Subcommand 0x1B: the contents of a spellbook.
- ///
- public SpellbookData Spellbook
- {
- get;
- private set;
- }
-
- ///
- /// Subcommand 0x19: the serial of the mobile which the extended stats must be applied to.
- ///
- public Serial ExtendedStatsSerial;
- public StatLocks ExtendedStatsLocks
- {
- get;
- private set;
- }
-
- ///
- /// Subcommand 0x1D: The revision hash of a custom house.
- ///
- public HouseRevisionState HouseRevisionState
- {
- get;
- private set;
- }
+ public readonly short Subcommand;
+ public readonly IGeneralInfo Info;
public GeneralInfoPacket(PacketReader reader)
- : base(0xBF, "General Information")
- {
+ : base(0xBF, "General Information") {
Subcommand = reader.ReadInt16();
-
- switch (Subcommand)
- {
- case 0x04: // Close generic gump
- CloseGumpTypeID = reader.ReadInt32();
- CloseGumpButtonID = reader.ReadInt32();
+ switch (Subcommand) {
+ case CloseGump:
+ Info = new CloseGumpInfo(reader);
break;
- case 0x06:
- // party system, not implemented.
+ case Party:
+ Info = new PartyInfo(reader);
break;
- case 0x8: // Set cursor color / set map
- MapID = reader.ReadByte();
+ case SetMap:
+ Info = new MapIndexInfo(reader);
break;
- case 0x14: // return context menu
- receiveContextMenu(reader);
+ case ContextMenu:
+ Info = new ContextMenuInfo(reader);
break;
- case 0x18: // Number of maps
- receiveMapDiffManifest(reader);
+ case MapDiff:
+ Info = new MapDiffInfo(reader);
break;
- case 0x1D: // House revision
- receiveHouseRevisionState(reader);
+ case ExtendedStats:
+ Info = new ExtendedStatsInfo(reader);
break;
- case 0x19: // Extended stats
- receiveExtendedStats(reader);
+ case SpellBookContents:
+ Info = new SpellBookContentsInfo(reader);
break;
- case 0x1B:
- receiveSpellBookContents(reader);
+ case HouseRevision:
+ Info = new HouseRevisionInfo(reader);
break;
- case 0x21: // (AOS) Ability icon confirm.
+ case AOSAbilityIconConfirm: // (AOS) Ability icon confirm.
// no data, just (bf 00 05 00 21)
break;
default:
- // do nothing. This unhandled subcommand will raise an error in UltimaClient.cs.
+ Tracer.Warn($"Unhandled Subcommand {Subcommand:X2} in GeneralInfoPacket.");
break;
}
}
-
- private void receiveSpellBookContents(PacketReader reader)
- {
- ushort unknown = reader.ReadUInt16(); // always 1
- Serial serial = (Serial)reader.ReadInt32();
- ushort itemID = reader.ReadUInt16();
- ushort spellbookType = reader.ReadUInt16(); // 1==regular, 101=necro, 201=paladin, 401=bushido, 501=ninjitsu, 601=spellweaving
- ulong spellBitfields = reader.ReadUInt32() + (((ulong)reader.ReadUInt32()) << 32); // first bit of first byte = spell #1, second bit of first byte = spell #2, first bit of second byte = spell #8, etc
-
- Spellbook = new SpellbookData(serial, itemID, spellbookType, spellBitfields);
- }
-
- void receiveExtendedStats(PacketReader reader)
- {
- int clientFlag = reader.ReadByte(); // (0x2 for 2D client, 0x5 for KR client)
- ExtendedStatsSerial = (Serial)reader.ReadInt32();
- byte unknown0 = reader.ReadByte(); // (always 0)
- byte lockFlags = reader.ReadByte();
- // Lock flags = 00SSDDII ( in binary )
- // 00 = up
- // 01 = down
- // 10 = locked
- // FF = update mobile status animation ( KR only )
- if (lockFlags != 0xFF)
- {
- int strengthLock = (lockFlags >> 4) & 0x03;
- int dexterityLock = (lockFlags >> 2) & 0x03;
- int inteligenceLock = (lockFlags) & 0x03;
- ExtendedStatsLocks = new StatLocks(strengthLock, dexterityLock, inteligenceLock);
- }
-
- if (clientFlag == 5)
- {
- Tracer.Warn("ClientFlags == 5 in GeneralInfoPacket ExtendedStats 0x19. This is not a KR client.");
- // If(Lock flags = 0xFF) //Update mobile status animation
- // BYTE[1] Status // Unveryfied if lock flags == FF the locks will be handled here
- // BYTE[1] unknown (0x00)
- // BYTE[1] Animation
- // BYTE[1] unknown (0x00)
- // BYTE[1] Frame
- // else
- // BYTE[1] unknown (0x00)
- // BYTE[4] unknown (0x00000000)
- // endif
- }
- }
-
- void receiveHouseRevisionState(PacketReader reader)
- {
- Serial s = reader.ReadInt32();
- int hash = reader.ReadInt32();
- HouseRevisionState = new HouseRevisionState(s, hash);
- }
-
- void receiveMapDiffManifest(PacketReader reader)
- {
- MapDiffs = new MapDiffInfo(reader);
- }
-
- void receiveContextMenu(PacketReader reader)
- {
- reader.ReadByte(); // unknown, always 0x00
- int subcommand = reader.ReadByte(); // 0x01 for 2D, 0x02 for KR
- ContextMenu = new ContextMenuData(reader.ReadInt32());
- int contextMenuChoiceCount = reader.ReadByte();
-
- for (int i = 0; i < contextMenuChoiceCount; i++)
- {
- int iUniqueID = reader.ReadUInt16();
- int iClilocID = reader.ReadUInt16() + 3000000;
- int iFlags = reader.ReadUInt16(); // 0x00=enabled, 0x01=disabled, 0x02=arrow, 0x20 = color
- int iColor = 0;
- if ((iFlags & 0x20) == 0x20)
- {
- iColor = reader.ReadUInt16();
- }
- ContextMenu.AddItem(iUniqueID, iClilocID, iFlags, iColor);
- }
- }
}
}
diff --git a/dev/Ultima/Player/Partying/PartyMember.cs b/dev/Ultima/Player/Partying/PartyMember.cs
new file mode 100644
index 00000000..dadd52be
--- /dev/null
+++ b/dev/Ultima/Player/Partying/PartyMember.cs
@@ -0,0 +1,31 @@
+using UltimaXNA.Ultima.World;
+using UltimaXNA.Ultima.World.Entities.Mobiles;
+
+namespace UltimaXNA.Ultima.Player.Partying
+{
+ public class PartyMember
+ {
+ public readonly Serial Serial;
+ string m_CachedName;
+ public Mobile Mobile => WorldModel.Entities.GetObject(Serial, false);
+
+ public string Name
+ {
+ get
+ {
+ Mobile mobile = Mobile;
+ if (Mobile != null)
+ {
+ m_CachedName = Mobile.Name;
+ }
+ return m_CachedName;
+ }
+ }
+
+ public PartyMember(Serial serial)
+ {
+ Serial = serial;
+ m_CachedName = Name;
+ }
+ }
+}
diff --git a/dev/Ultima/Player/Partying/PartySystem.cs b/dev/Ultima/Player/Partying/PartySystem.cs
new file mode 100644
index 00000000..aeb538c1
--- /dev/null
+++ b/dev/Ultima/Player/Partying/PartySystem.cs
@@ -0,0 +1,251 @@
+/***************************************************************************
+ * PartySystem.cs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ ***************************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UltimaXNA.Core.Network;
+using UltimaXNA.Core.UI;
+using UltimaXNA.Ultima.Network.Client;
+using UltimaXNA.Ultima.Network.Client.Partying;
+using UltimaXNA.Ultima.Network.Server.GeneralInfo;
+using UltimaXNA.Ultima.UI;
+using UltimaXNA.Ultima.UI.WorldGumps;
+using UltimaXNA.Ultima.World;
+
+namespace UltimaXNA.Ultima.Player.Partying
+{
+ public class PartySystem
+ {
+ Serial m_LeaderSerial;
+ Serial m_InvitingPartyLeader;
+ List m_PartyMembers = new List();
+ bool m_AllowPartyLoot;
+
+ public Serial LeaderSerial => m_LeaderSerial;
+ public List Members => m_PartyMembers;
+ public bool InParty => m_PartyMembers.Count > 1;
+ public bool PlayerIsLeader => InParty && PlayerState.Partying.LeaderSerial == WorldModel.PlayerSerial;
+
+ public bool AllowPartyLoot
+ {
+ get
+ {
+ return m_AllowPartyLoot;
+ }
+ set
+ {
+ m_AllowPartyLoot = value;
+ INetworkClient network = ServiceRegistry.GetService();
+ network.Send(new PartyCanLootPacket(m_AllowPartyLoot));
+ }
+ }
+
+ public void ReceivePartyMemberList(PartyMemberListInfo info)
+ {
+ bool wasInParty = InParty;
+ m_PartyMembers.Clear();
+ for (int i = 0; i < info.Count; i++)
+ AddMember(info.Serials[i]);
+ RefreshPartyGumps();
+ if (InParty && !wasInParty)
+ {
+ AllowPartyLoot = m_AllowPartyLoot;
+ }
+ }
+
+ public void ReceiveRemovePartyMember(PartyRemoveMemberInfo info)
+ {
+ m_PartyMembers.Clear();
+ for (int i = 0; i < info.Count; i++)
+ AddMember(info.Serials[i]);
+ RefreshPartyGumps();
+ }
+
+ public void ReceiveInvitation(PartyInvitationInfo info)
+ {
+ m_InvitingPartyLeader = info.PartyLeaderSerial;
+ }
+
+ public void AddMember(Serial serial)
+ {
+ int index = m_PartyMembers.FindIndex(p => p.Serial == serial);
+ if (index != -1)
+ {
+ m_PartyMembers.RemoveAt(index);
+ }
+ m_PartyMembers.Add(new PartyMember(serial));
+ INetworkClient network = ServiceRegistry.GetService();
+ network.Send(new MobileQueryPacket(MobileQueryPacket.StatusType.BasicStatus, serial));
+ }
+
+ public PartyMember GetMember(int index)
+ {
+ if (index >= 0 && index < m_PartyMembers.Count)
+ return m_PartyMembers[index];
+ return null;
+ }
+
+ public PartyMember GetMember(Serial serial)
+ {
+ return m_PartyMembers.Find(p => p.Serial == serial);
+ }
+
+ public void LeaveParty()
+ {
+ INetworkClient network = ServiceRegistry.GetService();
+ network.Send(new PartyLeavePacket());
+ m_PartyMembers.Clear();
+ m_LeaderSerial = Serial.Null;
+ UserInterfaceService ui = ServiceRegistry.GetService();
+ ui.RemoveControl();
+ }
+
+ public void DoPartyCommand(string text)
+ {
+ // I do this a little differently than the legacy client. With legacy, if you type "/add this other player,
+ // please ?" the client will detect the first word is "add" and request an add player target. Instead, I
+ // interpret this as a message, and send the message "add this other player, please?" as a party message.
+ INetworkClient network = ServiceRegistry.GetService();
+ WorldModel world = ServiceRegistry.GetService();
+ string command = text.ToLower();
+ bool commandHandled = false;
+ switch (command)
+ {
+ case "help":
+ ShowPartyHelp();
+ commandHandled = true;
+ break;
+ case "add":
+ RequestAddPartyMemberTarget();
+ commandHandled = true;
+ break;
+ case "rem":
+ case "remove":
+ if (InParty && PlayerIsLeader)
+ {
+ world.Interaction.ChatMessage("Who would you like to remove from your party?", 3, 10, false);
+ network.Send(new PartyRequestRemoveTargetPacket());
+ }
+ commandHandled = true;
+ break;
+ case "accept":
+ if (!InParty && m_InvitingPartyLeader.IsValid)
+ {
+ network.Send(new PartyAcceptPacket(m_InvitingPartyLeader));
+ m_LeaderSerial = m_InvitingPartyLeader;
+ m_InvitingPartyLeader = Serial.Null;
+ }
+ commandHandled = true;
+ break;
+ case "decline":
+ if (!InParty && m_InvitingPartyLeader.IsValid)
+ {
+ network.Send(new PartyDeclinePacket(m_InvitingPartyLeader));
+ m_InvitingPartyLeader = Serial.Null;
+ }
+ commandHandled = true;
+ break;
+ case "quit":
+ LeaveParty();
+ commandHandled = true;
+ break;
+ }
+ if (!commandHandled)
+ {
+ network.Send(new PartyPublicMessagePacket(text));
+ }
+ }
+
+ internal void BeginPrivateMessage(int serial)
+ {
+ PartyMember member = GetMember((Serial)serial);
+ if (member != null)
+ {
+ ChatControl chat = ServiceRegistry.GetService();
+ chat.SetModeToPartyPrivate(member.Name, member.Serial);
+ }
+ }
+
+ public void SendPartyPrivateMessage(Serial serial, string text)
+ {
+ WorldModel world = ServiceRegistry.GetService();
+ PartyMember recipient = GetMember(serial);
+ if (recipient != null)
+ {
+ INetworkClient network = ServiceRegistry.GetService();
+ network.Send(new PartyPrivateMessagePacket(serial, text));
+ world.Interaction.ChatMessage($"To {recipient.Name}: {text}", 3, Settings.UserInterface.PartyPrivateMsgColor, false);
+ }
+ else
+ {
+ world.Interaction.ChatMessage("They are no longer in your party.", 3, Settings.UserInterface.PartyPrivateMsgColor, false);
+ }
+ }
+
+ internal void RequestAddPartyMemberTarget()
+ {
+ INetworkClient network = ServiceRegistry.GetService();
+ WorldModel world = ServiceRegistry.GetService();
+ if (!InParty)
+ {
+ m_LeaderSerial = WorldModel.PlayerSerial;
+ network.Send(new PartyRequestAddTargetPacket());
+ }
+ else if (InParty && PlayerIsLeader)
+ {
+ network.Send(new PartyRequestAddTargetPacket());
+ }
+ else if (InParty && !PlayerIsLeader)
+ {
+ world.Interaction.ChatMessage("You may only add members to the party if you are the leader.", 3, 10, false);
+ }
+ }
+
+ public void RefreshPartyGumps()
+ {
+ UserInterfaceService ui = ServiceRegistry.GetService();
+ ui.RemoveControl();
+ for (int i = 0; i < Members.Count; i++)
+ {
+ ui.AddControl(new PartyHealthTrackerGump(Members[i]), 5, 40 + (48 * i));
+ }
+ Gump gump;
+ if ((gump = ui.GetControl()) != null)
+ {
+ int x = gump.X;
+ int y = gump.Y;
+ ui.RemoveControl();
+ ui.AddControl(new PartyGump(), x, y);
+ }
+ }
+
+ public void RemoveMember(Serial serial)
+ {
+ INetworkClient network = ServiceRegistry.GetService();
+ network.Send(new PartyRemoveMemberPacket(serial));
+ int index = m_PartyMembers.FindIndex(p => p.Serial == serial);
+ if (index != -1)
+ {
+ m_PartyMembers.RemoveAt(index);
+ }
+ }
+
+ public void ShowPartyHelp()
+ {
+ WorldModel m_world = ServiceRegistry.GetService();
+ m_world.Interaction.ChatMessage("/add - add a new member or create a party", 3, 51, true);
+ m_world.Interaction.ChatMessage("/rem - kick a member from your party", 3, 51, true);
+ m_world.Interaction.ChatMessage("/accept - join a party", 3, 51, true);
+ m_world.Interaction.ChatMessage("/decline - decline a party invitation", 3, 51, true);
+ m_world.Interaction.ChatMessage("/quit - leave your current party", 3, 51, true);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dev/Ultima/Player/PlayerState.cs b/dev/Ultima/Player/PlayerState.cs
index 743e2ba0..817ed58b 100644
--- a/dev/Ultima/Player/PlayerState.cs
+++ b/dev/Ultima/Player/PlayerState.cs
@@ -1,36 +1,26 @@
-
-namespace UltimaXNA.Ultima.Player
-{
- class PlayerState
- {
- private static readonly PlayerState m_Instance;
+using UltimaXNA.Ultima.Player.Partying;
- private JournalData m_Journal;
- private SkillData m_Skills;
- private StatLockData m_StatLocks;
+namespace UltimaXNA.Ultima.Player {
+ class PlayerState {
+ static readonly PlayerState m_Instance;
- static PlayerState()
- {
+ JournalData m_Journal;
+ SkillData m_Skills;
+ StatLockData m_StatLocks;
+ PartySystem m_Partying;
+
+ static PlayerState() {
m_Instance = new PlayerState();
m_Instance.m_Journal = new JournalData();
m_Instance.m_Skills = new SkillData();
m_Instance.m_StatLocks = new StatLockData();
+ m_Instance.m_Partying = new PartySystem();
}
- public static JournalData Journaling
- {
- get { return m_Instance.m_Journal; }
- }
-
- public static SkillData Skills
- {
- get { return m_Instance.m_Skills; }
- }
-
- public static StatLockData StatLocks
- {
- get { return m_Instance.m_StatLocks; }
- }
+ public static JournalData Journaling => m_Instance.m_Journal;
+ public static SkillData Skills => m_Instance.m_Skills;
+ public static StatLockData StatLocks => m_Instance.m_StatLocks;
+ public static PartySystem Partying => m_Instance.m_Partying;
}
}
diff --git a/dev/Ultima/Resources/TileMatrixDataPatch.cs b/dev/Ultima/Resources/TileMatrixDataPatch.cs
index a745a69e..1c55472f 100644
--- a/dev/Ultima/Resources/TileMatrixDataPatch.cs
+++ b/dev/Ultima/Resources/TileMatrixDataPatch.cs
@@ -16,6 +16,7 @@
using UltimaXNA.Core.Windows;
using UltimaXNA.Ultima.Data;
using UltimaXNA.Ultima.IO;
+using UltimaXNA.Ultima.Network.Server.GeneralInfo;
#endregion
namespace UltimaXNA.Ultima.Resources
diff --git a/dev/Ultima/UI/LoginGumps/LoginGump.cs b/dev/Ultima/UI/LoginGumps/LoginGump.cs
index 29897142..721a1e1a 100644
--- a/dev/Ultima/UI/LoginGumps/LoginGump.cs
+++ b/dev/Ultima/UI/LoginGumps/LoginGump.cs
@@ -73,10 +73,10 @@ public LoginGump(Action onLogin)
// flag graphic
AddControl(new GumpPic(this, 0, 0, 0x15A0, 0));
// buttons on the left side
- int y = 450;
AddControl(new ButtonResizable(this, 10, 450, 100, 23, "CREDITS", OnClickCredits));
+ int y = 420;
foreach (Tuple button in m_Buttons) {
- AddControl(new ButtonResizable(this, 10, 420, 100, 23, button.Item1, button.Item2));
+ AddControl(new ButtonResizable(this, 10, y, 100, 23, button.Item1, button.Item2));
y -= 30;
}
IsUncloseableWithRMB = true;
diff --git a/dev/Ultima/UI/WorldGumps/ChatControl.cs b/dev/Ultima/UI/WorldGumps/ChatControl.cs
index 07233ef5..147d52a2 100644
--- a/dev/Ultima/UI/WorldGumps/ChatControl.cs
+++ b/dev/Ultima/UI/WorldGumps/ChatControl.cs
@@ -20,6 +20,9 @@
using UltimaXNA.Ultima.UI.Controls;
using UltimaXNA.Ultima.World;
using UltimaXNA.Ultima.Data;
+using UltimaXNA.Ultima.Player;
+using UltimaXNA.Ultima.Network.Client;
+using UltimaXNA.Core.Network;
#endregion
namespace UltimaXNA.Ultima.UI.WorldGumps
@@ -31,11 +34,11 @@ class ChatControl : AControl
private TextEntry m_TextEntry;
private List m_TextEntries;
private List> m_MessageHistory;
-
private InputManager m_Input;
private WorldModel m_World;
-
private int m_MessageHistoryIndex = -1;
+ private Serial m_PrivateMessageSerial = Serial.Null;
+ private string m_PrivateMessageName;
private ChatMode m_Mode = ChatMode.Default;
private ChatMode Mode
@@ -47,7 +50,7 @@ private ChatMode Mode
switch (value)
{
case ChatMode.Default:
- m_TextEntry.LeadingHtmlTag = string.Format("",
+ m_TextEntry.LeadingHtmlTag = string.Format("",
Utility.GetColorFromUshort(Resources.HueData.GetHue(Settings.UserInterface.SpeechColor, 1)));
m_TextEntry.LeadingText = string.Empty;
m_TextEntry.Text = string.Empty;
@@ -58,25 +61,31 @@ private ChatMode Mode
m_TextEntry.LeadingText = "Whisper: ";
m_TextEntry.Text = string.Empty;
break;
- case ChatMode.Emote: // emote
+ case ChatMode.Emote:
m_TextEntry.LeadingHtmlTag = string.Format("",
Utility.GetColorFromUshort(Resources.HueData.GetHue(Settings.UserInterface.EmoteColor, 1)));
m_TextEntry.LeadingText = "Emote: ";
m_TextEntry.Text = string.Empty;
break;
- case ChatMode.Party: // party
+ case ChatMode.Party:
m_TextEntry.LeadingHtmlTag = string.Format("",
Utility.GetColorFromUshort(Resources.HueData.GetHue(Settings.UserInterface.PartyMsgColor, 1)));
m_TextEntry.LeadingText = "Party: ";
m_TextEntry.Text = string.Empty;
break;
- case ChatMode.Guild: // guild
+ case ChatMode.PartyPrivate:
+ m_TextEntry.LeadingHtmlTag = string.Format("",
+ Utility.GetColorFromUshort(Resources.HueData.GetHue(Settings.UserInterface.PartyPrivateMsgColor, 1)));
+ m_TextEntry.LeadingText = $"To {m_PrivateMessageName}: ";
+ m_TextEntry.Text = string.Empty;
+ break;
+ case ChatMode.Guild:
m_TextEntry.LeadingHtmlTag = string.Format("",
Utility.GetColorFromUshort(Resources.HueData.GetHue(Settings.UserInterface.GuildMsgColor, 1)));
m_TextEntry.LeadingText = "Guild: ";
m_TextEntry.Text = string.Empty;
break;
- case ChatMode.Alliance: // alliance
+ case ChatMode.Alliance:
m_TextEntry.LeadingHtmlTag = string.Format("",
Utility.GetColorFromUshort(Resources.HueData.GetHue(Settings.UserInterface.AllianceMsgColor, 1)));
m_TextEntry.LeadingText = "Alliance: ";
@@ -101,6 +110,13 @@ public ChatControl(AControl parent, int x, int y, int width, int height)
IsUncloseableWithRMB = true;
}
+ public void SetModeToPartyPrivate(string name, Serial serial)
+ {
+ m_PrivateMessageName = name;
+ m_PrivateMessageSerial = serial;
+ Mode = ChatMode.PartyPrivate;
+ }
+
public override void Update(double totalMS, double frameMS)
{
if (m_TextEntry == null)
@@ -154,26 +170,25 @@ public override void Update(double totalMS, double frameMS)
Mode = ChatMode.Default;
}
- // if in default, only switch mode if there is a single command char (;, :, etc) followed by any other char.
- // in not in default, only switch mode if the single command char is the only char entered.
- if ((Mode == ChatMode.Default && m_TextEntry.Text.Length == 2) ||
+ // only switch mode if the single command char is the only char entered.
+ if ((Mode == ChatMode.Default && m_TextEntry.Text.Length == 1) ||
(Mode != ChatMode.Default && m_TextEntry.Text.Length == 1))
{
switch (m_TextEntry.Text[0])
{
- case ':': // emote
+ case ':':
Mode = ChatMode.Emote;
break;
- case ';': // whisper
+ case ';':
Mode = ChatMode.Whisper;
break;
- case '/': // party
+ case '/':
Mode = ChatMode.Party;
break;
- case '\\': // guild
+ case '\\':
Mode = ChatMode.Guild;
break;
- case '|': // alliance
+ case '|':
Mode = ChatMode.Alliance;
break;
}
@@ -195,74 +210,110 @@ public override void Draw(SpriteBatchUI spriteBatch, Point position)
public override void OnKeyboardReturn(int textID, string text)
{
+ // local variables
+ ChatMode sentMode = Mode;
+ MessageTypes speechType = MessageTypes.Normal;
+ int hue = 0;
+ // save this message and reset chat for next entry
m_TextEntry.Text = string.Empty;
m_MessageHistory.Add(new Tuple(Mode, text));
m_MessageHistoryIndex = m_MessageHistory.Count;
- m_World.Interaction.SendSpeech(text, Mode);
Mode = ChatMode.Default;
+ // send the message and display it locally.
+ switch (sentMode)
+ {
+ case ChatMode.Default:
+ speechType = MessageTypes.Normal;
+ hue = Settings.UserInterface.SpeechColor;
+ break;
+ case ChatMode.Whisper:
+ speechType = MessageTypes.Whisper;
+ hue = Settings.UserInterface.SpeechColor;
+ break;
+ case ChatMode.Emote:
+ speechType = MessageTypes.Emote;
+ hue = Settings.UserInterface.EmoteColor;
+ break;
+ case ChatMode.Party:
+ PlayerState.Partying.DoPartyCommand(text);
+ return;
+ case ChatMode.PartyPrivate:
+ PlayerState.Partying.SendPartyPrivateMessage(m_PrivateMessageSerial, text);
+ return;
+ case ChatMode.Guild:
+ speechType = MessageTypes.Guild;
+ hue = Settings.UserInterface.GuildMsgColor;
+ break;
+ case ChatMode.Alliance:
+ speechType = MessageTypes.Alliance;
+ hue = Settings.UserInterface.AllianceMsgColor;
+ break;
+ }
+ INetworkClient network = ServiceRegistry.GetService();
+ network.Send(new AsciiSpeechPacket(speechType, 0, hue + 2, "ENU", text));
}
public void AddLine(string text, int font, int hue, bool asUnicode)
{
- m_TextEntries.Add(new ChatLineTimed(string.Format("{0}",
- text, asUnicode ? "uni" : "ascii", font, Utility.GetColorFromUshort(Resources.HueData.GetHue(hue, -1))),
+ m_TextEntries.Add(new ChatLineTimed(string.Format("{0}",
+ text, asUnicode ? "uni" : "ascii", font, Utility.GetColorFromUshort(Resources.HueData.GetHue(hue, -1))),
Width));
}
- }
- class ChatLineTimed
- {
- string m_text;
- public string Text { get { return m_text; } }
- float m_createdTime = float.MinValue;
- bool m_isExpired;
- public bool IsExpired { get { return m_isExpired; } }
- float m_alpha;
- public float Alpha { get { return m_alpha; } }
- private int m_width = 0;
+ class ChatLineTimed
+ {
+ string m_text;
+ public string Text { get { return m_text; } }
+ float m_createdTime = float.MinValue;
+ bool m_isExpired;
+ public bool IsExpired { get { return m_isExpired; } }
+ float m_alpha;
+ public float Alpha { get { return m_alpha; } }
+ private int m_width = 0;
- const float Time_Display = 10000.0f;
- const float Time_Fadeout = 4000.0f;
+ const float Time_Display = 10000.0f;
+ const float Time_Fadeout = 4000.0f;
- private RenderedText m_Texture;
- public int TextHeight { get { return m_Texture.Height; } }
+ private RenderedText m_Texture;
+ public int TextHeight { get { return m_Texture.Height; } }
- public ChatLineTimed(string text, int width)
- {
- m_text = text;
- m_isExpired = false;
- m_alpha = 1.0f;
- m_width = width;
+ public ChatLineTimed(string text, int width)
+ {
+ m_text = text;
+ m_isExpired = false;
+ m_alpha = 1.0f;
+ m_width = width;
- m_Texture = new RenderedText(m_text, m_width);
- }
+ m_Texture = new RenderedText(m_text, m_width);
+ }
- public void Update(double totalMS, double frameMS)
- {
- if (m_createdTime == float.MinValue)
- m_createdTime = (float)totalMS;
- float time = (float)totalMS - m_createdTime;
- if (time > Time_Display)
- m_isExpired = true;
- else if (time > (Time_Display - Time_Fadeout))
+ public void Update(double totalMS, double frameMS)
{
- m_alpha = 1.0f - ((time) - (Time_Display - Time_Fadeout)) / Time_Fadeout;
+ if (m_createdTime == float.MinValue)
+ m_createdTime = (float)totalMS;
+ float time = (float)totalMS - m_createdTime;
+ if (time > Time_Display)
+ m_isExpired = true;
+ else if (time > (Time_Display - Time_Fadeout))
+ {
+ m_alpha = 1.0f - ((time) - (Time_Display - Time_Fadeout)) / Time_Fadeout;
+ }
}
- }
- public void Draw(SpriteBatchUI sb, Point position)
- {
- m_Texture.Draw(sb, position, Utility.GetHueVector(0, false, (m_alpha < 1.0f), true));
- }
+ public void Draw(SpriteBatchUI sb, Point position)
+ {
+ m_Texture.Draw(sb, position, Utility.GetHueVector(0, false, (m_alpha < 1.0f), true));
+ }
- public void Dispose()
- {
- m_Texture = null;
- }
+ public void Dispose()
+ {
+ m_Texture = null;
+ }
- public override string ToString()
- {
- return m_text;
+ public override string ToString()
+ {
+ return m_text;
+ }
}
}
}
diff --git a/dev/Ultima/UI/WorldGumps/PaperDollGump.cs b/dev/Ultima/UI/WorldGumps/PaperDollGump.cs
index f2385263..017dada1 100644
--- a/dev/Ultima/UI/WorldGumps/PaperDollGump.cs
+++ b/dev/Ultima/UI/WorldGumps/PaperDollGump.cs
@@ -10,6 +10,7 @@
***************************************************************************/
#region usings
+
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using UltimaXNA.Core.Graphics;
@@ -21,7 +22,8 @@
using UltimaXNA.Ultima.UI.Controls;
using UltimaXNA.Ultima.World;
using UltimaXNA.Ultima.World.Entities.Mobiles;
-#endregion
+
+#endregion usings
namespace UltimaXNA.Ultima.UI.WorldGumps
{
@@ -135,6 +137,10 @@ private void BuildGump()
// AddControl(new GumpPic(this, 158, 200, 0x2B34, 0));
// LastControl.MouseDoubleClickEvent += SpecialMoves_MouseDoubleClickEvent;
+ // PARTY MANIFEST CALLER
+ AddControl(new GumpPic(this, 44, 195, 2002, 0));
+ LastControl.MouseDoubleClickEvent += PartyManifest_MouseDoubleClickEvent;
+
// equipment slots for hat/earrings/neck/ring/bracelet
AddControl(new EquipmentSlot(this, 2, 76, Mobile, EquipLayer.Helm));
AddControl(new EquipmentSlot(this, 2, 76 + 22 * 1, Mobile, EquipLayer.Earrings));
@@ -154,7 +160,6 @@ private void BuildGump()
}
// name and title
- AddControl(new HtmlGumpling(this, 34, 259, 180, 42, 0, 0, string.Format("{0}", Title)));
AddControl(new HtmlGumpling(this, 35, 260, 180, 42, 0, 0, string.Format("{0}", Title)));
}
@@ -164,7 +169,16 @@ public override void Dispose()
m_VirtueMenuButton.MouseDoubleClickEvent -= VirtueMenu_MouseDoubleClickEvent;
base.Dispose();
}
-
+ private void PartyManifest_MouseDoubleClickEvent(AControl control, int x, int y, MouseButton button)
+ {
+ if (button == MouseButton.Left)
+ {
+ if (UserInterface.GetControl() == null)
+ UserInterface.AddControl(new PartyGump(), 200, 40);
+ else
+ UserInterface.RemoveControl();
+ }
+ }
private void SpecialMoves_MouseDoubleClickEvent(AControl control, int x, int y, MouseButton button)
{
if (button == MouseButton.Left)
diff --git a/dev/Ultima/UI/WorldGumps/PartyGump.cs b/dev/Ultima/UI/WorldGumps/PartyGump.cs
new file mode 100644
index 00000000..e9d5d7dd
--- /dev/null
+++ b/dev/Ultima/UI/WorldGumps/PartyGump.cs
@@ -0,0 +1,124 @@
+/***************************************************************************
+ * PartyGump.cs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ ***************************************************************************/
+using UltimaXNA.Ultima.Player;
+using UltimaXNA.Ultima.UI.Controls;
+using UltimaXNA.Ultima.World;
+
+namespace UltimaXNA.Ultima.UI.WorldGumps
+{
+ public class PartyGump : Gump {
+ const int ButtonIndexLoot = 0;
+ const int ButtonIndexLeave = 1;
+ const int ButtonIndexAdd = 2;
+ const int ButtonIndexKick = 100;
+ const int ButtonIndexTell = 200;
+
+ public PartyGump()
+ : base(0, 0) {
+ IsMoveable = true;
+ AddControl(new ResizePic(this, 0, 0, 2600, 350, PlayerState.Partying.InParty ? 500 : 425));
+ AddControl(new TextLabelAscii(this, 105, 15, 2, 902, "Party Manifest"));
+ AddControl(new TextLabelAscii(this, 34, 51, 1, 902, "Kick"));
+ AddControl(new TextLabelAscii(this, 84, 51, 1, 902, "Tell"));
+ AddControl(new TextLabelAscii(this, 160, 45, 2, 902, "Member Name"));
+ int lineY = 0;
+ bool playerIsLeader = PlayerState.Partying.LeaderSerial == WorldModel.PlayerSerial;
+ for (int i = 0; i < 10; i++)
+ {
+ if (i < PlayerState.Partying.Members.Count)
+ {
+ bool memberIsPlayer = PlayerState.Partying.GetMember(i).Serial == WorldModel.PlayerSerial;
+ if (!memberIsPlayer)
+ {
+ if (playerIsLeader)
+ {
+ AddControl(new Button(this, 35, 70 + lineY, 4017, 4018, ButtonTypes.Activate, PlayerState.Partying.Members[i].Serial, ButtonIndexKick + i));// KICK BUTTON
+ }
+ AddControl(new Button(this, 85, 70 + lineY, 4029, 4030, ButtonTypes.Activate, PlayerState.Partying.Members[i].Serial, ButtonIndexTell + i));// tell BUTTON
+ }
+ AddControl(new ResizePic(this, 130, 70 + lineY, 3000, 195, 25));
+ AddControl(new HtmlGumpling(this, 130, 72 + lineY, 195, 20, 0, 0, $"{PlayerState.Partying.Members[i].Name}"));
+ }
+ else
+ {
+ AddControl(new ResizePic(this, 130, 70 + lineY, 3000, 195, 25));
+ }
+ lineY += 30;
+ }
+ if (PlayerState.Partying.InParty)
+ {
+ int lootGumpUp = PlayerState.Partying.AllowPartyLoot ? 4008 : 4002;
+ string txtLootStatus = PlayerState.Partying.AllowPartyLoot ? "Party CAN loot me" : "Party CANNOT loot me";
+ AddControl(new TextLabelAscii(this, 100, 75 + lineY, 2, 902, txtLootStatus));
+ AddControl(new Button(this, 65, 75 + lineY, lootGumpUp, lootGumpUp + 2, ButtonTypes.Activate, ButtonIndexLoot, 0));
+ (LastControl as Button).GumpOverID = lootGumpUp + 1;
+ lineY += 25;
+ string txtLeave = (playerIsLeader) ? "Disband the party" : "Leave the party";
+ AddControl(new TextLabelAscii(this, 100, 75 + lineY, 2, 902, txtLeave));
+ AddControl(new Button(this, 65, 75 + lineY, 4017, 4019, ButtonTypes.Activate, 0, ButtonIndexLeave));
+ (LastControl as Button).GumpOverID = 4018;
+ lineY += 25;
+ AddControl(new TextLabelAscii(this, 100, 75 + lineY, 2, 902, "Add new member"));
+ AddControl(new Button(this, 65, 75 + lineY, 4005, 4007, ButtonTypes.Activate, 0, ButtonIndexAdd));
+ (LastControl as Button).GumpOverID = 4006;
+ /* msx752 proposed feature addition: Mark an enemy.
+ lineY += 25;
+ AddControl(new TextLabelAscii(this, 100, 75 + lineY, 2, 902, "Mark an enemy"));
+ if (playerIsLeader)
+ {
+ AddControl(new Button(this, 65, 75 + lineY, 4005, 4006, ButtonTypes.Activate, 0, ButtonIndexSetTarget));
+ (LastControl as Button).GumpOverID = 4007;
+ }*/
+ }
+ else
+ {
+ AddControl(new TextLabelAscii(this, 100, 75 + lineY, 2, 902, "Create a party"));
+ AddControl(new Button(this, 65, 75 + lineY, 4005, 4007, ButtonTypes.Activate, 0, ButtonIndexAdd));
+ (LastControl as Button).GumpOverID = 4006;
+ }
+ }
+
+ public override void OnButtonClick(int buttonID) {
+ if (buttonID == -1) {
+ return;
+ }
+ bool playerInParty = PlayerState.Partying.Members.Count > 1;
+ bool playerIsLeader = PlayerState.Partying.LeaderSerial == WorldModel.PlayerSerial;
+ if (buttonID >= ButtonIndexTell) {
+ int serial = PlayerState.Partying.GetMember(buttonID - ButtonIndexTell).Serial;
+ PlayerState.Partying.BeginPrivateMessage(serial);
+ }
+ else if (buttonID >= ButtonIndexKick) {
+ int serial = PlayerState.Partying.GetMember(buttonID - ButtonIndexKick).Serial;
+ PlayerState.Partying.RemoveMember(serial);
+ }
+ else if (buttonID == ButtonIndexLoot && playerInParty) {
+ if (PlayerState.Partying.AllowPartyLoot) {
+ PlayerState.Partying.AllowPartyLoot = false;
+ }
+ else {
+ PlayerState.Partying.AllowPartyLoot = true;
+ }
+ PlayerState.Partying.RefreshPartyGumps();
+ }
+ else if (buttonID == ButtonIndexLeave) {
+ if (playerInParty)
+ {
+ PlayerState.Partying.LeaveParty();
+ }
+ }
+ else if (buttonID == ButtonIndexAdd) {
+ if (!playerInParty || playerIsLeader) {
+ PlayerState.Partying.RequestAddPartyMemberTarget();
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/dev/Ultima/UI/WorldGumps/PartyHealthTrackerGump.cs b/dev/Ultima/UI/WorldGumps/PartyHealthTrackerGump.cs
new file mode 100644
index 00000000..be9c1ac6
--- /dev/null
+++ b/dev/Ultima/UI/WorldGumps/PartyHealthTrackerGump.cs
@@ -0,0 +1,101 @@
+/***************************************************************************
+ * PartyHealthTrackerGump.cs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ ***************************************************************************/
+
+using UltimaXNA.Core.Input;
+using UltimaXNA.Core.UI;
+using UltimaXNA.Ultima.Player;
+using UltimaXNA.Ultima.Player.Partying;
+using UltimaXNA.Ultima.UI.Controls;
+using UltimaXNA.Ultima.World;
+using UltimaXNA.Ultima.World.Entities.Mobiles;
+
+namespace UltimaXNA.Ultima.UI.WorldGumps {
+ class PartyHealthTrackerGump : Gump
+ {
+ Serial m_Serial;
+
+ Button btnPrivateMsg;
+ GumpPic[] m_BarBGs;
+ GumpPicWithWidth[] m_Bars;
+ TextLabel m_Name;
+
+ public PartyHealthTrackerGump(PartyMember member)
+ : base(member.Serial, 0)
+ {
+ while (UserInterface.GetControl() != null)
+ {
+ UserInterface.GetControl(member.Serial).Dispose();
+ }
+ IsMoveable = false;
+ IsUncloseableWithRMB = true;
+ m_Serial = member.Serial;
+ //AddControl(m_Background = new ResizePic(this, 0, 0, 3000, 131, 48));//I need opacity %1 background
+
+ AddControl(m_Name = new TextLabel(this, 1, 0, 1, member.Name));
+ //m_Background.MouseDoubleClickEvent += Background_MouseDoubleClickEvent; //maybe private message calling?
+ m_BarBGs = new GumpPic[3];
+ int sameX = 15;
+ int sameY = 3;
+ if (WorldModel.Entities.GetPlayerEntity().Serial != member.Serial)//you can't send a message to self
+ {
+ AddControl(btnPrivateMsg = new Button(this, 0, 20, 11401, 11402, ButtonTypes.Activate, member.Serial, 0));//private party message / use bandage ??
+ }
+ AddControl(m_BarBGs[0] = new GumpPic(this, sameX, 15 + sameY, 9750, 0));
+ AddControl(m_BarBGs[1] = new GumpPic(this, sameX, 24 + sameY, 9750, 0));
+ AddControl(m_BarBGs[2] = new GumpPic(this, sameX, 33 + sameY, 9750, 0));
+ m_Bars = new GumpPicWithWidth[3];
+ AddControl(m_Bars[0] = new GumpPicWithWidth(this, sameX, 15 + sameY, 40, 0, 1f));//I couldn't find correct visual
+ AddControl(m_Bars[1] = new GumpPicWithWidth(this, sameX, 24 + sameY, 9751, 0, 1f));//I couldn't find correct visual
+ AddControl(m_Bars[2] = new GumpPicWithWidth(this, sameX, 33 + sameY, 41, 0, 1f));//I couldn't find correct visual
+
+ // bars should not handle mouse input, pass it to the background gump.
+ for (int i = 0; i < m_BarBGs.Length; i++)//???
+ {
+ m_Bars[i].HandlesMouseInput = false;
+ }
+ }
+
+ public override void OnButtonClick(int buttonID)
+ {
+ if (buttonID == 0)//private message
+ {
+ PlayerState.Partying.BeginPrivateMessage(btnPrivateMsg.ButtonParameter);
+ }
+ }
+
+ public override void Update(double totalMS, double frameMS)
+ {
+ PartyMember member = PlayerState.Partying.GetMember(m_Serial);
+ if (member == null)
+ {
+ Dispose();
+ return;
+ }
+ m_Name.Text = member.Name;
+ Mobile mobile = member.Mobile;
+ if (mobile == null)
+ {
+ m_Bars[0].PercentWidthDrawn = m_Bars[1].PercentWidthDrawn = m_Bars[2].PercentWidthDrawn = 0f;
+ }
+ else
+ {
+ m_Bars[0].PercentWidthDrawn = ((float)mobile.Health.Current / mobile.Health.Max);
+ m_Bars[1].PercentWidthDrawn = ((float)mobile.Mana.Current / mobile.Mana.Max);
+ m_Bars[2].PercentWidthDrawn = ((float)mobile.Stamina.Current / mobile.Stamina.Max);
+ // I couldn't find correct visual
+ //if (Mobile.Flags.IsBlessed)
+ // m_Bars[0].GumpID = 0x0809;
+ //else if (Mobile.Flags.IsPoisoned)
+ // m_Bars[0].GumpID = 0x0808;
+ }
+ base.Update(totalMS, frameMS);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dev/Ultima/UI/WorldGumps/VendorBuyGump.cs b/dev/Ultima/UI/WorldGumps/VendorBuyGump.cs
index 66effeca..81217b1d 100644
--- a/dev/Ultima/UI/WorldGumps/VendorBuyGump.cs
+++ b/dev/Ultima/UI/WorldGumps/VendorBuyGump.cs
@@ -45,6 +45,7 @@ class VendorBuyGump : Gump
public VendorBuyGump(AEntity vendorBackpack, VendorBuyListPacket packet)
: base(0, 0)
{
+
// sanity checking: don't show buy gumps for empty containers.
if (!(vendorBackpack is Container) || ((vendorBackpack as Container).Contents.Count <= 0) || (packet.Items.Count <= 0))
{
@@ -67,11 +68,13 @@ public VendorBuyGump(AEntity vendorBackpack, VendorBuyListPacket packet)
AddControl(m_OKButton = new Button(this, 220, 333, 0x907, 0x908, ButtonTypes.Activate, 0, 0));
m_OKButton.GumpOverID = 0x909;
m_OKButton.MouseClickEvent += okButton_MouseClickEvent;
+
}
public override void Dispose()
{
- m_OKButton.MouseClickEvent -= okButton_MouseClickEvent;
+ if (m_OKButton != null)
+ m_OKButton.MouseClickEvent -= okButton_MouseClickEvent;
base.Dispose();
}
@@ -172,7 +175,7 @@ private void BuildShopContents(AEntity vendorBackpack, VendorBuyListPacket packe
m_ScrollBar.Value = 0;
}
- private const string c_Format =
+ private const string c_Format =
"" +
"{0}
{1}gp, {3} available.";
diff --git a/dev/Ultima/World/CustomHousing.cs b/dev/Ultima/World/Data/CustomHouse.cs
similarity index 54%
rename from dev/Ultima/World/CustomHousing.cs
rename to dev/Ultima/World/Data/CustomHouse.cs
index 1dae0eef..8f6f6088 100644
--- a/dev/Ultima/World/CustomHousing.cs
+++ b/dev/Ultima/World/Data/CustomHouse.cs
@@ -1,5 +1,5 @@
/***************************************************************************
- * CustomHousing.cs
+ * CustomHouse.cs
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -9,77 +9,27 @@
***************************************************************************/
using System.Collections.Generic;
-using UltimaXNA.Core.Network;
-using UltimaXNA.Core.Network.Compression;
using UltimaXNA.Ultima.Resources;
-namespace UltimaXNA.Ultima.World
-{
- class CustomHousing
- {
- static Dictionary m_customHouses = new Dictionary();
-
- public static bool IsHashCurrent(Serial serial, int hash)
- {
- if (m_customHouses.ContainsKey(serial))
- {
- CustomHouse h = m_customHouses[serial];
- if (h.Hash == hash)
- return true;
- else
- return false;
- }
- else
- {
- return false;
- }
-
- }
-
- public static CustomHouse GetCustomHouseData(Serial serial)
- {
- return m_customHouses[serial];
- }
-
- public static void UpdateCustomHouseData(Serial serial, int hash, int planecount, CustomHousePlane[] planes)
- {
- CustomHouse house;
- if (m_customHouses.ContainsKey(serial))
- {
- house = m_customHouses[serial];
-
- }
- else
- {
- house = new CustomHouse(serial);
- m_customHouses.Add(serial, house);
- }
- house.Update(hash, planecount, planes);
- }
- }
-
- class CustomHouse
- {
+namespace UltimaXNA.Ultima.World.Data {
+ class CustomHouse {
public Serial Serial;
public int Hash;
int m_planeCount;
CustomHousePlane[] m_planes;
- public CustomHouse(Serial serial)
- {
+ public CustomHouse(Serial serial) {
Serial = serial;
}
- public void Update(int hash, int planecount, CustomHousePlane[] planes)
- {
+ public void Update(int hash, int planecount, CustomHousePlane[] planes) {
Hash = hash;
m_planeCount = planecount;
m_planes = planes;
}
- public StaticTile[] GetStatics(int width, int height)
- {
+ public StaticTile[] GetStatics(int width, int height) {
List statics = new List();
// Custom Houses are sent in 'planes' of four different types. We determine which type we're looking at the index and the size.
@@ -89,18 +39,15 @@ public StaticTile[] GetStatics(int width, int height)
int numTilesInLastPlane = 0;
int zIndex = 0;
- for (int plane = 0; plane < m_planeCount; plane++)
- {
+ for (int plane = 0; plane < m_planeCount; plane++) {
int numTiles = m_planes[plane].ItemData.Length >> 1;
if ((plane == m_planeCount - 1) &&
(numTiles != sizeFloor) &&
- (numTiles != sizeWalls))
- {
+ (numTiles != sizeWalls)) {
numTiles = m_planes[plane].ItemData.Length / 5;
int index = 0;
- for (int j = 0; j < numTiles; j++)
- {
+ for (int j = 0; j < numTiles; j++) {
StaticTile s = new StaticTile();
s.ID = (short)((m_planes[plane].ItemData[index++] << 8) + m_planes[plane].ItemData[index++]);
int x = (sbyte)m_planes[plane].ItemData[index++];
@@ -112,21 +59,18 @@ public StaticTile[] GetStatics(int width, int height)
statics.Add(s);
}
}
- else
- {
+ else {
int iWidth = width, iHeight = height;
int iX = 0, iY = 0;
int x = 0, y = 0, z = 0;
- if (plane == 0)
- {
+ if (plane == 0) {
zIndex = 0;
iWidth += 1;
iHeight += 1;
}
- else if (numTiles == sizeFloor)
- {
+ else if (numTiles == sizeFloor) {
if (numTilesInLastPlane != sizeFloor)
zIndex = 1;
else
@@ -136,8 +80,7 @@ public StaticTile[] GetStatics(int width, int height)
iX = 1;
iY = 1;
}
- else if (numTiles == sizeWalls)
- {
+ else if (numTiles == sizeWalls) {
if (numTilesInLastPlane != sizeWalls)
zIndex = 1;
else
@@ -146,27 +89,35 @@ public StaticTile[] GetStatics(int width, int height)
- switch (zIndex)
- {
- case 0: z = 0; break;
- case 1: z = 7; break;
- case 2: z = 27; break;
- case 3: z = 47; break;
- case 4: z = 67; break;
- default: continue;
+ switch (zIndex) {
+ case 0:
+ z = 0;
+ break;
+ case 1:
+ z = 7;
+ break;
+ case 2:
+ z = 27;
+ break;
+ case 3:
+ z = 47;
+ break;
+ case 4:
+ z = 67;
+ break;
+ default:
+ continue;
}
int index = 0;
- for (int j = 0; j < numTiles; j++)
- {
+ for (int j = 0; j < numTiles; j++) {
StaticTile s = new StaticTile();
s.ID = (short)((m_planes[plane].ItemData[index++] << 8) + m_planes[plane].ItemData[index++]);
s.X = (byte)(x + iX);
s.Y = (byte)(y + iY);
s.Z = (sbyte)z;
y++;
- if (y >= iHeight)
- {
+ if (y >= iHeight) {
y = 0;
x++;
}
@@ -178,24 +129,4 @@ public StaticTile[] GetStatics(int width, int height)
return statics.ToArray();
}
}
-
- public class CustomHousePlane
- {
- public readonly int Index;
- public readonly bool IsFloor;
- public readonly byte[] ItemData;
-
- public CustomHousePlane(PacketReader reader)
- {
- byte[] data = reader.ReadBytes(4);
- Index = data[0];
- int uncompressedsize = data[1] + ((data[3] & 0xF0) << 4);
- int compressedLength = data[2] + ((data[3] & 0xF) << 8);
- ItemData = new byte[uncompressedsize];
- ZlibCompression.Unpack(ItemData, ref uncompressedsize, reader.ReadBytes(compressedLength), compressedLength);
-
- IsFloor = ((Index & 0x20) == 0x20);
- Index &= 0x1F;
- }
- }
}
diff --git a/dev/Ultima/World/Data/CustomHousePlane.cs b/dev/Ultima/World/Data/CustomHousePlane.cs
new file mode 100644
index 00000000..0623c314
--- /dev/null
+++ b/dev/Ultima/World/Data/CustomHousePlane.cs
@@ -0,0 +1,21 @@
+using UltimaXNA.Core.Network;
+using UltimaXNA.Core.Network.Compression;
+
+namespace UltimaXNA.Ultima.World.Data {
+ public class CustomHousePlane {
+ public readonly int Index;
+ public readonly bool IsFloor;
+ public readonly byte[] ItemData;
+
+ public CustomHousePlane(PacketReader reader) {
+ byte[] data = reader.ReadBytes(4);
+ Index = data[0];
+ int uncompressedsize = data[1] + ((data[3] & 0xF0) << 4);
+ int compressedLength = data[2] + ((data[3] & 0xF) << 8);
+ ItemData = new byte[uncompressedsize];
+ ZlibCompression.Unpack(ItemData, ref uncompressedsize, reader.ReadBytes(compressedLength), compressedLength);
+ IsFloor = ((Index & 0x20) == 0x20);
+ Index &= 0x1F;
+ }
+ }
+}
diff --git a/dev/Ultima/World/Data/CustomHousing.cs b/dev/Ultima/World/Data/CustomHousing.cs
new file mode 100644
index 00000000..1cbd0977
--- /dev/null
+++ b/dev/Ultima/World/Data/CustomHousing.cs
@@ -0,0 +1,39 @@
+/***************************************************************************
+ * CustomHousing.cs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ ***************************************************************************/
+
+using System.Collections.Generic;
+
+namespace UltimaXNA.Ultima.World.Data {
+ class CustomHousing {
+ static Dictionary m_CustomHouses = new Dictionary();
+
+ public static bool IsHashCurrent(Serial serial, int hash) {
+ if (m_CustomHouses.ContainsKey(serial)) {
+ CustomHouse h = m_CustomHouses[serial];
+ return (h.Hash == hash);
+ }
+ return false;
+ }
+
+ public static CustomHouse GetCustomHouseData(Serial serial) => m_CustomHouses[serial];
+
+ public static void UpdateCustomHouseData(Serial serial, int hash, int planecount, CustomHousePlane[] planes) {
+ CustomHouse house;
+ if (m_CustomHouses.ContainsKey(serial)) {
+ house = m_CustomHouses[serial];
+ }
+ else {
+ house = new CustomHouse(serial);
+ m_CustomHouses.Add(serial, house);
+ }
+ house.Update(hash, planecount, planes);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dev/Ultima/World/Entities/Multis/Multi.cs b/dev/Ultima/World/Entities/Multis/Multi.cs
index 8a1d6a23..1b6c2c61 100644
--- a/dev/Ultima/World/Entities/Multis/Multi.cs
+++ b/dev/Ultima/World/Entities/Multis/Multi.cs
@@ -14,6 +14,7 @@
using UltimaXNA.Ultima.Resources;
using UltimaXNA.Ultima.World.Entities.Items;
using UltimaXNA.Ultima.World.Maps;
+using UltimaXNA.Ultima.World.Data;
#endregion
namespace UltimaXNA.Ultima.World.Entities.Multis
diff --git a/dev/Ultima/World/Input/WorldInput.cs b/dev/Ultima/World/Input/WorldInput.cs
index 1529efa5..01b6462c 100644
--- a/dev/Ultima/World/Input/WorldInput.cs
+++ b/dev/Ultima/World/Input/WorldInput.cs
@@ -21,6 +21,7 @@
using UltimaXNA.Ultima.Data;
using UltimaXNA.Ultima.Input;
using UltimaXNA.Ultima.Network.Client;
+using UltimaXNA.Ultima.Player;
using UltimaXNA.Ultima.UI.Controls;
using UltimaXNA.Ultima.UI.WorldGumps;
using UltimaXNA.Ultima.World.Entities;
@@ -440,6 +441,8 @@ private void onInteractButton(InputEventMouse e, AEntity overEntity, Vector2 ove
}
else if (overEntity is Mobile)
{
+ if (PlayerState.Partying.GetMember(overEntity.Serial) != null)//is he in your party// number of 0x11 packet dont have information about stamina/mana k(IMPORTANT!!!)
+ return;
// request basic stats - gives us the name rename flag
m_Network.Send(new MobileQueryPacket(MobileQueryPacket.StatusType.BasicStatus, overEntity.Serial));
// drag off a status gump for this mobile.
diff --git a/dev/Ultima/World/WorldClient.cs b/dev/Ultima/World/WorldClient.cs
index 09c2766c..014ccdec 100644
--- a/dev/Ultima/World/WorldClient.cs
+++ b/dev/Ultima/World/WorldClient.cs
@@ -23,10 +23,13 @@
using UltimaXNA.Ultima.Data;
using UltimaXNA.Ultima.Network.Client;
using UltimaXNA.Ultima.Network.Server;
+using UltimaXNA.Ultima.Network.Server.GeneralInfo;
using UltimaXNA.Ultima.Player;
+using UltimaXNA.Ultima.Player.Partying;
using UltimaXNA.Ultima.Resources;
using UltimaXNA.Ultima.UI;
using UltimaXNA.Ultima.UI.WorldGumps;
+using UltimaXNA.Ultima.World.Data;
using UltimaXNA.Ultima.World.Entities;
using UltimaXNA.Ultima.World.Entities.Items;
using UltimaXNA.Ultima.World.Entities.Items.Containers;
@@ -39,16 +42,15 @@ namespace UltimaXNA.Ultima.World
{
class WorldClient : IDisposable
{
- private Timer m_KeepAliveTimer;
- private INetworkClient m_Network;
- private UserInterfaceService m_UserInterface;
- private WorldModel m_World;
- private List> m_RegisteredHandlers;
-
+ Timer m_KeepAliveTimer;
+ INetworkClient m_Network;
+ UserInterfaceService m_UserInterface;
+ WorldModel m_World;
+ List> m_RegisteredHandlers;
+
public WorldClient(WorldModel world)
{
m_World = world;
-
m_RegisteredHandlers = new List>();
m_Network = ServiceRegistry.GetService();
m_UserInterface = ServiceRegistry.GetService();
@@ -66,7 +68,6 @@ public void Initialize()
Register(0x22, "Move Acknowledged", 3, new TypedPacketReceiveHandler(ReceiveMoveAck));
Register(0x23, "Drag Effect", 26, new TypedPacketReceiveHandler(ReceiveDragItem));
Register(0x24, "Open Container", 7, new TypedPacketReceiveHandler(ReceiveContainer));
-
if (ClientVersion.HasExtendedAddItemPacket(Settings.UltimaOnline.PatchVersion))
{
Register(0x25, "Container Content Update", 20, new TypedPacketReceiveHandler(ReceiveAddSingleItemToContainer));
@@ -75,7 +76,6 @@ public void Initialize()
{
Register(0x25, "Container Content Update", 21, new TypedPacketReceiveHandler(ReceiveAddSingleItemToContainer));
}
-
Register(0x27, "Lift Rejection", 2, new TypedPacketReceiveHandler(ReceiveRejectMoveItemRequest));
Register(0x2C, "Resurect menu", 2, new TypedPacketReceiveHandler(ReceiveResurrectionMenu));
Register(0x2D, "Mob Attributes", 17, new TypedPacketReceiveHandler(ReceiveMobileAttributes));
@@ -129,7 +129,6 @@ public void Initialize()
Register(0xD8, "Send Custom House", -1, new TypedPacketReceiveHandler(ReceiveSendCustomHouse));
Register(0xDC, "SE Introduced Revision", 9, new TypedPacketReceiveHandler(ReceiveToolTipRevision));
Register(0xDD, "Compressed Gump", -1, new TypedPacketReceiveHandler(ReceiveCompressedGump));
-
/* Deprecated (not used by RunUO) and/or not implmented
* Left them here incase we need to implement in the future
network.Register(0x17, "Health Bar Status Update", 12, OnHealthBarStatusUpdate);
@@ -172,7 +171,7 @@ public void Register(int id, string name, int length, TypedPacketReceiveHandl
m_RegisteredHandlers.Add(new Tuple(id, onReceive));
m_Network.Register(id, name, length, onReceive);
}
-
+
public void SendWorldLoginPackets()
{
GetMySkills();
@@ -199,13 +198,13 @@ public void StartKeepAlivePackets()
TimeSpan.FromSeconds(4));
}
- private void StopKeepAlivePackets()
+ void StopKeepAlivePackets()
{
if (m_KeepAliveTimer != null)
m_KeepAliveTimer.Dispose();
}
- private void SendKeepAlivePacket()
+ void SendKeepAlivePacket()
{
m_Network.Send(new UOSEKeepAlivePacket());
}
@@ -250,19 +249,19 @@ public void GetMyBasicStatus()
m_Network.Send(new MobileQueryPacket(MobileQueryPacket.StatusType.BasicStatus, WorldModel.PlayerSerial));
}
- private void ReceiveTargetCursor(IRecvPacket packet)
+ void ReceiveTargetCursor(IRecvPacket packet)
{
TargetCursorPacket p = (TargetCursorPacket)packet;
m_World.Cursor.SetTargeting((WorldCursor.TargetType)p.CommandType, p.CursorID);
}
- private void ReceiveTargetCursorMulti(IRecvPacket packet)
+ void ReceiveTargetCursorMulti(IRecvPacket packet)
{
TargetCursorMultiPacket p = (TargetCursorMultiPacket)packet;
m_World.Cursor.SetTargetingMulti(p.DeedSerial, p.MultiModel);
}
- private void InternalOnEntity_SendMoveRequestPacket(MoveRequestPacket packet)
+ void InternalOnEntity_SendMoveRequestPacket(MoveRequestPacket packet)
{
m_Network.Send(packet);
}
@@ -271,17 +270,17 @@ private void InternalOnEntity_SendMoveRequestPacket(MoveRequestPacket packet)
// Effect handling
// ======================================================================
- private void ReceiveGraphicEffect(IRecvPacket packet)
+ void ReceiveGraphicEffect(IRecvPacket packet)
{
WorldModel.Effects.Add((GraphicEffectPacket)packet);
}
- private void ReceiveHuedEffect(IRecvPacket packet)
+ void ReceiveHuedEffect(IRecvPacket packet)
{
WorldModel.Effects.Add((GraphicEffectHuedPacket)packet);
}
- private void ReceiveOnParticleEffect(IRecvPacket packet)
+ void ReceiveOnParticleEffect(IRecvPacket packet)
{
WorldModel.Effects.Add((GraphicEffectExtendedPacket)packet);
}
@@ -290,7 +289,7 @@ private void ReceiveOnParticleEffect(IRecvPacket packet)
// Entity handling
// ======================================================================
- private void ReceiveAddMultipleItemsToContainer(IRecvPacket packet)
+ void ReceiveAddMultipleItemsToContainer(IRecvPacket packet)
{
ContainerContentPacket p = (ContainerContentPacket)packet;
if (p.Items.Length == 0)
@@ -319,7 +318,7 @@ private void ReceiveAddMultipleItemsToContainer(IRecvPacket packet)
}
}
- private void ReceiveAddSingleItemToContainer(IRecvPacket packet)
+ void ReceiveAddSingleItemToContainer(IRecvPacket packet)
{
AddSingleItemToContainerPacket p = (AddSingleItemToContainerPacket)packet;
@@ -343,7 +342,7 @@ private void ReceiveAddSingleItemToContainer(IRecvPacket packet)
}
}
- private Item add_Item(Serial serial, int itemID, int nHue, Serial parentSerial, int amount)
+ Item add_Item(Serial serial, int itemID, int nHue, Serial parentSerial, int amount)
{
Item item;
if (itemID == 0x2006)
@@ -376,7 +375,7 @@ private Item add_Item(Serial serial, int itemID, int nHue, Serial parentSerial,
return item;
}
- private void ReceiveContainer(IRecvPacket packet)
+ void ReceiveContainer(IRecvPacket packet)
{
OpenContainerPacket p = (OpenContainerPacket)packet;
@@ -409,7 +408,7 @@ private void ReceiveContainer(IRecvPacket packet)
}
}
- private void ReceiveWorldItem(IRecvPacket packet)
+ void ReceiveWorldItem(IRecvPacket packet)
{
ObjectInfoPacket p = (ObjectInfoPacket)packet;
@@ -430,7 +429,7 @@ private void ReceiveWorldItem(IRecvPacket packet)
}
}
- private void ReceiveWornItem(IRecvPacket packet)
+ void ReceiveWornItem(IRecvPacket packet)
{
WornItemPacket p = (WornItemPacket)packet;
Item item = add_Item(p.Serial, p.ItemId, p.Hue, p.ParentSerial, 0);
@@ -439,13 +438,13 @@ private void ReceiveWornItem(IRecvPacket packet)
m_Network.Send(new QueryPropertiesPacket(item.Serial));
}
- private void ReceiveDeleteObject(IRecvPacket packet)
+ void ReceiveDeleteObject(IRecvPacket packet)
{
RemoveEntityPacket p = (RemoveEntityPacket)packet;
WorldModel.Entities.RemoveEntity(p.Serial);
}
- private void ReceiveMobileIncoming(IRecvPacket packet)
+ void ReceiveMobileIncoming(IRecvPacket packet)
{
MobileIncomingPacket p = (MobileIncomingPacket)packet;
Mobile mobile = WorldModel.Entities.GetObject(p.Serial, true);
@@ -473,7 +472,7 @@ private void ReceiveMobileIncoming(IRecvPacket packet)
m_Network.Send(new SingleClickPacket(p.Serial)); // look at the object so we receive its stats.
}
- private void ReceiveDeathAnimation(IRecvPacket packet)
+ void ReceiveDeathAnimation(IRecvPacket packet)
{
DeathAnimationPacket p = (DeathAnimationPacket)packet;
Mobile m = WorldModel.Entities.GetObject(p.PlayerSerial, false);
@@ -490,7 +489,7 @@ private void ReceiveDeathAnimation(IRecvPacket packet)
}
}
- private void ReceiveDragItem(IRecvPacket packet)
+ void ReceiveDragItem(IRecvPacket packet)
{
DragEffectPacket p = (DragEffectPacket)packet;
// This is sent by the server to display an item being dragged from one place to another.
@@ -510,7 +509,7 @@ private void ReceiveDragItem(IRecvPacket packet)
announce_UnhandledPacket(packet);
}
- private void ReceiveMobileAttributes(IRecvPacket packet)
+ void ReceiveMobileAttributes(IRecvPacket packet)
{
MobileAttributesPacket p = (MobileAttributesPacket)packet;
Mobile mobile = WorldModel.Entities.GetObject(p.Serial, false);
@@ -527,7 +526,7 @@ private void ReceiveMobileAttributes(IRecvPacket packet)
mobile.Stamina.Max = p.MaxStamina;
}
- private void ReceiveMobileAnimation(IRecvPacket packet)
+ void ReceiveMobileAnimation(IRecvPacket packet)
{
MobileAnimationPacket p = (MobileAnimationPacket)packet;
Mobile mobile = WorldModel.Entities.GetObject(p.Serial, false);
@@ -537,7 +536,7 @@ private void ReceiveMobileAnimation(IRecvPacket packet)
mobile.Animate(p.Action, p.FrameCount, p.RepeatCount, p.Reverse, p.Repeat, p.Delay);
}
- private void ReceiveMobileMoving(IRecvPacket packet)
+ void ReceiveMobileMoving(IRecvPacket packet)
{
MobileMovingPacket p = (MobileMovingPacket)packet;
Mobile mobile = WorldModel.Entities.GetObject(p.Serial, true);
@@ -561,7 +560,7 @@ private void ReceiveMobileMoving(IRecvPacket packet)
}
}
- private void ReceiveMobileUpdate(IRecvPacket packet)
+ void ReceiveMobileUpdate(IRecvPacket packet)
{
MobileUpdatePacket p = (MobileUpdatePacket)packet;
Mobile mobile = WorldModel.Entities.GetObject(p.Serial, true);
@@ -580,7 +579,7 @@ private void ReceiveMobileUpdate(IRecvPacket packet)
}
}
- private void ReceiveMoveAck(IRecvPacket packet)
+ void ReceiveMoveAck(IRecvPacket packet)
{
MoveAcknowledgePacket p = (MoveAcknowledgePacket)packet;
Mobile player = (Mobile)WorldModel.Entities.GetPlayerEntity();
@@ -588,20 +587,20 @@ private void ReceiveMoveAck(IRecvPacket packet)
player.Notoriety = p.Notoriety;
}
- private void ReceiveMoveRej(IRecvPacket packet)
+ void ReceiveMoveRej(IRecvPacket packet)
{
MovementRejectPacket p = (MovementRejectPacket)packet;
Mobile player = (Mobile)WorldModel.Entities.GetPlayerEntity();
player.PlayerMobile_MoveEventRej(p.Sequence, p.X, p.Y, p.Z, p.Direction);
}
- private void ReceivePlayerMove(IRecvPacket packet)
+ void ReceivePlayerMove(IRecvPacket packet)
{
PlayerMovePacket p = (PlayerMovePacket)packet;
announce_UnhandledPacket(packet);
}
- private void ReceiveRejectMoveItemRequest(IRecvPacket packet)
+ void ReceiveRejectMoveItemRequest(IRecvPacket packet)
{
LiftRejectionPacket p = (LiftRejectionPacket)packet;
m_World.Interaction.ChatMessage("Could not pick up item: " + p.ErrorMessage);
@@ -612,7 +611,7 @@ private void ReceiveRejectMoveItemRequest(IRecvPacket packet)
// Corpse handling
// ======================================================================
- private void ReceiveCorpseClothing(IRecvPacket packet)
+ void ReceiveCorpseClothing(IRecvPacket packet)
{
CorpseClothingPacket p = (CorpseClothingPacket)packet;
Corpse corpse = WorldModel.Entities.GetObject(p.CorpseSerial, false);
@@ -630,14 +629,14 @@ private void ReceiveCorpseClothing(IRecvPacket packet)
// Combat handling
// ======================================================================
- private void ReceiveChangeCombatant(IRecvPacket packet)
+ void ReceiveChangeCombatant(IRecvPacket packet)
{
ChangeCombatantPacket p = (ChangeCombatantPacket)packet;
if (p.Serial > 0x00000000)
m_World.Interaction.LastTarget = p.Serial;
}
- private void ReceiveDamage(IRecvPacket packet)
+ void ReceiveDamage(IRecvPacket packet)
{
DamagePacket p = (DamagePacket)packet;
Mobile entity = WorldModel.Entities.GetObject(p.Serial, false);
@@ -647,7 +646,7 @@ private void ReceiveDamage(IRecvPacket packet)
m_World.Interaction.ChatMessage(string.Format("{0} takes {1} damage!", entity.Name, p.Damage));
}
- private void ReceiveOnSwing(IRecvPacket packet)
+ void ReceiveOnSwing(IRecvPacket packet)
{
SwingPacket p = (SwingPacket)packet;
// this changes our last target - does this behavior match legacy?
@@ -657,13 +656,13 @@ private void ReceiveOnSwing(IRecvPacket packet)
}
}
- private void ReceiveWarMode(IRecvPacket packet)
+ void ReceiveWarMode(IRecvPacket packet)
{
WarModePacket p = (WarModePacket)packet;
((Mobile)WorldModel.Entities.GetPlayerEntity()).Flags.IsWarMode = p.WarMode;
}
- private void ReceiveUpdateMana(IRecvPacket packet)
+ void ReceiveUpdateMana(IRecvPacket packet)
{
UpdateManaPacket p = (UpdateManaPacket)packet;
Mobile entity = WorldModel.Entities.GetObject(p.Serial, false);
@@ -672,7 +671,7 @@ private void ReceiveUpdateMana(IRecvPacket packet)
entity.Mana.Update(p.Current, p.Max);
}
- private void ReceiveUpdateStamina(IRecvPacket packet)
+ void ReceiveUpdateStamina(IRecvPacket packet)
{
UpdateStaminaPacket p = (UpdateStaminaPacket)packet;
Mobile entity = WorldModel.Entities.GetObject(p.Serial, false);
@@ -681,7 +680,7 @@ private void ReceiveUpdateStamina(IRecvPacket packet)
entity.Stamina.Update(p.Current, p.Max);
}
- private void ReceiveUpdateHealth(IRecvPacket packet)
+ void ReceiveUpdateHealth(IRecvPacket packet)
{
UpdateHealthPacket p = (UpdateHealthPacket)packet;
Mobile entity = WorldModel.Entities.GetObject(p.Serial, false);
@@ -694,7 +693,7 @@ private void ReceiveUpdateHealth(IRecvPacket packet)
// Chat / messaging handling
// ======================================================================
- private void ReceiveCLILOCMessage(IRecvPacket packet)
+ void ReceiveCLILOCMessage(IRecvPacket packet)
{
MessageLocalizedPacket p = (MessageLocalizedPacket)packet;
@@ -704,19 +703,19 @@ private void ReceiveCLILOCMessage(IRecvPacket packet)
ReceiveTextMessage(p.MessageType, strCliLoc, p.Font, p.Hue, p.Serial, p.SpeakerName, true);
}
- private void ReceiveAsciiMessage(IRecvPacket packet)
+ void ReceiveAsciiMessage(IRecvPacket packet)
{
AsciiMessagePacket p = (AsciiMessagePacket)packet;
ReceiveTextMessage(p.MsgType, p.Text, p.Font, p.Hue, p.Serial, p.SpeakerName, false);
}
- private void ReceiveUnicodeMessage(IRecvPacket packet)
+ void ReceiveUnicodeMessage(IRecvPacket packet)
{
UnicodeMessagePacket p = (UnicodeMessagePacket)packet;
ReceiveTextMessage(p.MsgType, p.Text, p.Font, p.Hue, p.Serial, p.SpeakerName, true);
}
- private void ReceiveMessageLocalizedAffix(IRecvPacket packet)
+ void ReceiveMessageLocalizedAffix(IRecvPacket packet)
{
MessageLocalizedAffixPacket p = (MessageLocalizedAffixPacket)packet;
@@ -727,7 +726,7 @@ private void ReceiveMessageLocalizedAffix(IRecvPacket packet)
ReceiveTextMessage(p.MessageType, localizedString, p.Font, p.Hue, p.Serial, p.SpeakerName, true);
}
- private string constructCliLoc(string baseCliloc, string arg = null, bool capitalize = false)
+ string constructCliLoc(string baseCliloc, string arg = null, bool capitalize = false)
{
if (string.IsNullOrEmpty(baseCliloc))
return string.Empty;
@@ -785,7 +784,7 @@ private string constructCliLoc(string baseCliloc, string arg = null, bool capita
}
}
- private void ReceiveTextMessage(MessageTypes msgType, string text, int font, ushort hue, Serial serial, string speakerName, bool asUnicode)
+ void ReceiveTextMessage(MessageTypes msgType, string text, int font, ushort hue, Serial serial, string speakerName, bool asUnicode)
{
// PlayerState.Journaling.AddEntry(text, font, hue, speakerName, asUnicode);
Overhead overhead;
@@ -835,14 +834,17 @@ private void ReceiveTextMessage(MessageTypes msgType, string text, int font, ush
m_World.Interaction.ChatMessage("[SPELL] " + text, font, hue, asUnicode);
break;
case MessageTypes.Guild:
- m_World.Interaction.ChatMessage("[UILD] " + text, font, hue, asUnicode);
+ m_World.Interaction.ChatMessage($"[GUILD] {speakerName}: {text}", font, hue, asUnicode);
break;
case MessageTypes.Alliance:
- m_World.Interaction.ChatMessage("[ALLIANCE] " + text, font, hue, asUnicode);
+ m_World.Interaction.ChatMessage($"[ALLIANCE] {speakerName}: {text}", font, hue, asUnicode);
break;
case MessageTypes.Command:
m_World.Interaction.ChatMessage("[COMMAND] " + text, font, hue, asUnicode);
break;
+ case MessageTypes.PartyDisplayOnly:
+ m_World.Interaction.ChatMessage($"[PARTY] {speakerName}: {text}", font, hue, asUnicode);
+ break;
case MessageTypes.Information:
m_World.Interaction.CreateLabel(msgType, serial, text, font, hue, asUnicode);
break;
@@ -856,7 +858,7 @@ private void ReceiveTextMessage(MessageTypes msgType, string text, int font, ush
// Gump & Menu handling
// ======================================================================
- private void ReceiveResurrectionMenu(IRecvPacket packet)
+ void ReceiveResurrectionMenu(IRecvPacket packet)
{
ResurrectionMenuPacket p = (ResurrectionMenuPacket)packet;
switch (p.ResurrectionAction)
@@ -870,13 +872,13 @@ private void ReceiveResurrectionMenu(IRecvPacket packet)
}
}
- private void ReceivePopupMessage(IRecvPacket packet)
+ void ReceivePopupMessage(IRecvPacket packet)
{
PopupMessagePacket p = (PopupMessagePacket)packet;
MsgBoxGump.Show(p.Message, MsgBoxTypes.OkOnly);
}
- private void ReceiveOpenBuyWindow(IRecvPacket packet)
+ void ReceiveOpenBuyWindow(IRecvPacket packet)
{
VendorBuyListPacket p = (VendorBuyListPacket)packet;
Item entity = WorldModel.Entities.GetObject- (p.VendorPackSerial, false);
@@ -886,20 +888,20 @@ private void ReceiveOpenBuyWindow(IRecvPacket packet)
m_UserInterface.AddControl(new VendorBuyGump(entity, p), 200, 200);
}
- private void ReceiveSellList(IRecvPacket packet)
+ void ReceiveSellList(IRecvPacket packet)
{
VendorSellListPacket p = (VendorSellListPacket)packet;
m_UserInterface.RemoveControl();
m_UserInterface.AddControl(new VendorSellGump(p), 200, 200);
}
- private void ReceiveOpenPaperdoll(IRecvPacket packet)
+ void ReceiveOpenPaperdoll(IRecvPacket packet)
{
OpenPaperdollPacket p = packet as OpenPaperdollPacket;
if (m_UserInterface.GetControl(p.Serial) == null)
m_UserInterface.AddControl(new PaperDollGump(p.Serial, p.MobileTitle), 400, 100);
}
- private void ReceiveCompressedGump(IRecvPacket packet)
+ void ReceiveCompressedGump(IRecvPacket packet)
{
CompressedGumpPacket p = (CompressedGumpPacket)packet;
if (p.HasData)
@@ -913,19 +915,20 @@ private void ReceiveCompressedGump(IRecvPacket packet)
}
}
- private void ReceiveDisplayGumpFast(IRecvPacket packet)
+ void ReceiveDisplayGumpFast(IRecvPacket packet)
{
announce_UnhandledPacket(packet);
}
- private void ReceiveDisplayMenu(IRecvPacket packet)
+ void ReceiveDisplayMenu(IRecvPacket packet)
{
announce_UnhandledPacket(packet);
}
- private bool TryParseGumplings(string gumpData, out string[] pieces)
+ bool TryParseGumplings(string gumpData, out string[] pieces)
{
- List i = new List(); ;
+ List i = new List();
+ ;
int dataIndex = 0;
while (dataIndex < gumpData.Length)
{
@@ -958,18 +961,18 @@ private bool TryParseGumplings(string gumpData, out string[] pieces)
// Other packets
//
- private void ReceiveNewSubserver(IRecvPacket packet)
+ void ReceiveNewSubserver(IRecvPacket packet)
{
SubServerPacket p = (SubServerPacket)packet;
announce_UnhandledPacket(packet);
}
- private void ReceiveObjectHelpResponse(IRecvPacket packet)
+ void ReceiveObjectHelpResponse(IRecvPacket packet)
{
announce_UnhandledPacket(packet);
}
- private void ReceiveObjectPropertyList(IRecvPacket packet)
+ void ReceiveObjectPropertyList(IRecvPacket packet)
{
ObjectPropertyListPacket p = (ObjectPropertyListPacket)packet;
@@ -996,7 +999,7 @@ private void ReceiveObjectPropertyList(IRecvPacket packet)
}
}
- private void ReceiveSendCustomHouse(IRecvPacket packet)
+ void ReceiveSendCustomHouse(IRecvPacket packet)
{
CustomHousePacket p = (CustomHousePacket)packet;
CustomHousing.UpdateCustomHouseData(p.HouseSerial, p.RevisionHash, p.PlaneCount, p.Planes);
@@ -1009,7 +1012,7 @@ private void ReceiveSendCustomHouse(IRecvPacket packet)
}
}
- private void ReceiveSkillsList(IRecvPacket packet)
+ void ReceiveSkillsList(IRecvPacket packet)
{
foreach (SendSkillsPacket_SkillEntry skill in ((SendSkillsPacket)packet).Skills)
{
@@ -1024,7 +1027,7 @@ private void ReceiveSkillsList(IRecvPacket packet)
}
}
- private void ReceiveStatusInfo(IRecvPacket packet)
+ void ReceiveStatusInfo(IRecvPacket packet)
{
StatusInfoPacket p = (StatusInfoPacket)packet;
@@ -1059,18 +1062,18 @@ private void ReceiveStatusInfo(IRecvPacket packet)
mobile.PlayerCanChangeName = p.NameChangeFlag;
}
- private void ReceiveTime(IRecvPacket packet)
+ void ReceiveTime(IRecvPacket packet)
{
TimePacket p = (TimePacket)packet;
m_World.Interaction.ChatMessage(string.Format("The current server time is {0}:{1}:{2}", p.Hour, p.Minute, p.Second));
}
- private void ReceiveTipNotice(IRecvPacket packet)
+ void ReceiveTipNotice(IRecvPacket packet)
{
announce_UnhandledPacket(packet);
}
- private void ReceiveToolTipRevision(IRecvPacket packet)
+ void ReceiveToolTipRevision(IRecvPacket packet)
{
ObjectPropertyListUpdatePacket p = (ObjectPropertyListUpdatePacket)packet;
AEntity entity = WorldModel.Entities.GetObject(p.Serial, false);
@@ -1087,117 +1090,133 @@ private void ReceiveToolTipRevision(IRecvPacket packet)
}
}
- private void announce_UnhandledPacket(IRecvPacket packet)
+ void announce_UnhandledPacket(IRecvPacket packet)
{
Tracer.Warn(string.Format("Client: Unhandled {0} [ID:{1}]", packet.Name, packet.Id));
}
- private void announce_UnhandledPacket(IRecvPacket packet, string addendum)
+ void announce_UnhandledPacket(IRecvPacket packet, string addendum)
{
Tracer.Warn(string.Format("Client: Unhandled {0} [ID:{1}] {2}]", packet.Name, packet.Id, addendum));
}
- private void ReceiveExtended0x78(IRecvPacket packet)
+ void ReceiveExtended0x78(IRecvPacket packet)
{
announce_UnhandledPacket(packet);
}
- private void ReceiveGeneralInfo(IRecvPacket packet)
+ void ReceiveGeneralInfo(IRecvPacket packet)
{
// Documented here: http://docs.polserver.com/packets/index.php?Packet=0xBF
GeneralInfoPacket p = (GeneralInfoPacket)packet;
switch (p.Subcommand)
{
- case 0x04: // Close generic gump
+ case GeneralInfoPacket.CloseGump:
+ CloseGumpInfo closeGumpInfo = p.Info as CloseGumpInfo;
+ AControl control = m_UserInterface.GetControlByTypeID(closeGumpInfo.GumpTypeID);
+ (control as Gump)?.OnButtonClick(closeGumpInfo.GumpButtonID);
+ break;
+ case GeneralInfoPacket.Party:
+ PartyInfo partyInfo = p.Info as PartyInfo;
+ switch (partyInfo.SubsubCommand)
{
- AControl control = m_UserInterface.GetControlByTypeID(p.CloseGumpTypeID);
- if (control is Gump)
- {
- (control as Gump).OnButtonClick(p.CloseGumpButtonID);
- }
+ case PartyInfo.CommandPartyList:
+ PlayerState.Partying.ReceivePartyMemberList(partyInfo.Info as PartyMemberListInfo);
+ break;
+ case PartyInfo.CommandRemoveMember:
+ PlayerState.Partying.ReceiveRemovePartyMember(partyInfo.Info as PartyRemoveMemberInfo);
+ break;
+ case PartyInfo.CommandPrivateMessage:
+ case PartyInfo.CommandPublicMessage:
+ PartyMessageInfo msg = partyInfo.Info as PartyMessageInfo;
+ PartyMember member = PlayerState.Partying.GetMember(msg.Source);
+ // note: msx752 identified hue 50 for "targeted to : " and 34 for "Help me.. I'm stunned !!"
+ ushort hue = (ushort)(msg.IsPrivate ? Settings.UserInterface.PartyPrivateMsgColor : Settings.UserInterface.PartyMsgColor);
+ ReceiveTextMessage(MessageTypes.PartyDisplayOnly, msg.Message, 3, hue, 0xFFFFFFF, member.Name, true);
+ break;
+ case PartyInfo.CommandInvitation:
+ PlayerState.Partying.ReceiveInvitation(partyInfo.Info as PartyInvitationInfo);
+ break;
}
break;
- case 0x06: // party system
- announce_UnhandledPacket(packet, "subcommand " + p.Subcommand);
+ case GeneralInfoPacket.SetMap:
+ MapIndexInfo mapInfo = p.Info as MapIndexInfo;
+ m_World.MapIndex = mapInfo.MapID;
break;
- case 0x08: // set map
- m_World.MapIndex = p.MapID;
+ case GeneralInfoPacket.ContextMenu:
+ ContextMenuInfo menuInfo = p.Info as ContextMenuInfo;
+ InputManager input = ServiceRegistry.GetService();
+ m_UserInterface.AddControl(new ContextMenuGump(menuInfo.Menu), input.MousePosition.X - 10, input.MousePosition.Y - 20);
break;
- case 0x14: // return context menu
- {
- InputManager input = ServiceRegistry.GetService();
- m_UserInterface.AddControl(new ContextMenuGump(p.ContextMenu), input.MousePosition.X - 10, input.MousePosition.Y - 20);
- break;
- }
- case 0x18: // Enable map-diff (files) / number of maps
- // as of 6.0.0.0, this only tells us the number of maps.
- TileMatrixDataPatch.EnableMapDiffs(p.MapDiffs);
+ case GeneralInfoPacket.MapDiff:
+ TileMatrixDataPatch.EnableMapDiffs(p.Info as MapDiffInfo);
m_World.Map.ReloadStatics();
break;
- case 0x19: // Extended stats
- if (p.ExtendedStatsSerial != WorldModel.PlayerSerial)
+ case GeneralInfoPacket.ExtendedStats:
+ ExtendedStatsInfo extendedStats = p.Info as ExtendedStatsInfo;
+ if (extendedStats.Serial != WorldModel.PlayerSerial)
+ {
Tracer.Warn("Extended Stats packet (0xBF subcommand 0x19) received for a mobile not our own.");
+ }
else
{
- PlayerState.StatLocks.StrengthLock = p.ExtendedStatsLocks.Strength;
- PlayerState.StatLocks.DexterityLock = p.ExtendedStatsLocks.Dexterity;
- PlayerState.StatLocks.IntelligenceLock = p.ExtendedStatsLocks.Intelligence;
+ PlayerState.StatLocks.StrengthLock = extendedStats.Locks.Strength;
+ PlayerState.StatLocks.DexterityLock = extendedStats.Locks.Dexterity;
+ PlayerState.StatLocks.IntelligenceLock = extendedStats.Locks.Intelligence;
}
break;
- case 0x1B: // spellbook data
- SpellbookData spellbook = p.Spellbook;
+ case GeneralInfoPacket.SpellBookContents:
+ SpellbookData spellbook = (p.Info as SpellBookContentsInfo).Spellbook;
WorldModel.Entities.GetObject(spellbook.Serial, true).ReceiveSpellData(spellbook.BookType, spellbook.SpellsBitfield);
break;
- case 0x1D: // House revision state
- if (CustomHousing.IsHashCurrent(p.HouseRevisionState.Serial, p.HouseRevisionState.Hash))
+ case GeneralInfoPacket.HouseRevision:
+ HouseRevisionInfo houseInfo = p.Info as HouseRevisionInfo;
+ if (CustomHousing.IsHashCurrent(houseInfo.Revision.Serial, houseInfo.Revision.Hash))
{
- Multi multi = WorldModel.Entities.GetObject(p.HouseRevisionState.Serial, false);
+ Multi multi = WorldModel.Entities.GetObject(houseInfo.Revision.Serial, false);
if (multi == null)
{
// received a house revision for a multi that does not exist.
}
else
{
- if (multi.CustomHouseRevision != p.HouseRevisionState.Hash)
+ if (multi.CustomHouseRevision != houseInfo.Revision.Hash)
{
- CustomHouse house = CustomHousing.GetCustomHouseData(p.HouseRevisionState.Serial);
+ CustomHouse house = CustomHousing.GetCustomHouseData(houseInfo.Revision.Serial);
multi.AddCustomHousingTiles(house);
}
}
}
else
{
- m_Network.Send(new RequestCustomHousePacket(p.HouseRevisionState.Serial));
+ m_Network.Send(new RequestCustomHousePacket(houseInfo.Revision.Serial));
}
break;
- case 0x21: // (AOS) Ability icon confirm.
+ case GeneralInfoPacket.AOSAbilityIconConfirm: // (AOS) Ability icon confirm.
// no data, just (bf 00 05 21)
- // ???
- break;
- default:
- announce_UnhandledPacket(packet, "subcommand " + p.Subcommand);
+ // What do we do with this???
break;
}
}
- private void ReceiveGlobalQueueCount(IRecvPacket packet)
+ void ReceiveGlobalQueueCount(IRecvPacket packet)
{
GlobalQueuePacket p = (GlobalQueuePacket)packet;
m_World.Interaction.ChatMessage("System: There are currently " + p.Count + " available calls in the global queue.");
}
- private void ReceiveInvalidMapEnable(IRecvPacket packet)
+ void ReceiveInvalidMapEnable(IRecvPacket packet)
{
announce_UnhandledPacket(packet);
}
- private void ReceiveOpenWebBrowser(IRecvPacket packet)
+ void ReceiveOpenWebBrowser(IRecvPacket packet)
{
OpenWebBrowserPacket p = (OpenWebBrowserPacket)packet;
Process.Start("iexplore.exe", p.WebsiteUrl);
}
- private void ReceiveOverallLightLevel(IRecvPacket packet)
+ void ReceiveOverallLightLevel(IRecvPacket packet)
{
// byte iLightLevel = reader.ReadByte();
// 0x00 - day
@@ -1210,7 +1229,7 @@ private void ReceiveOverallLightLevel(IRecvPacket packet)
((WorldView)m_World.GetView()).Isometric.Lighting.OverallLightning = p.LightLevel;
}
- private void ReceivePersonalLightLevel(IRecvPacket packet)
+ void ReceivePersonalLightLevel(IRecvPacket packet)
{
// int iCreatureID = reader.ReadInt();
// byte iLightLevel = reader.ReadByte();
@@ -1224,26 +1243,26 @@ private void ReceivePersonalLightLevel(IRecvPacket packet)
((WorldView)m_World.GetView()).Isometric.Lighting.PersonalLightning = p.LightLevel;
}
- private void ReceivePlayMusic(IRecvPacket packet)
+ void ReceivePlayMusic(IRecvPacket packet)
{
PlayMusicPacket p = (PlayMusicPacket)packet;
AudioService service = ServiceRegistry.GetService();
service.PlayMusic(p.MusicID);
}
- private void ReceivePlaySoundEffect(IRecvPacket packet)
+ void ReceivePlaySoundEffect(IRecvPacket packet)
{
PlaySoundEffectPacket p = (PlaySoundEffectPacket)packet;
AudioService service = ServiceRegistry.GetService();
service.PlaySound(p.SoundModel, spamCheck: true);
}
- private void ReceiveQuestArrow(IRecvPacket packet)
+ void ReceiveQuestArrow(IRecvPacket packet)
{
announce_UnhandledPacket(packet);
}
- private void ReceiveRequestNameResponse(IRecvPacket packet)
+ void ReceiveRequestNameResponse(IRecvPacket packet)
{
RequestNameResponsePacket p = (RequestNameResponsePacket)packet;
Mobile mobile = WorldModel.Entities.GetObject(p.Serial, false);
@@ -1256,7 +1275,7 @@ private void ReceiveRequestNameResponse(IRecvPacket packet)
/// Handle a season change packet.
///
/// Should be of type SeasonChangePacket.
- private void ReceiveSeasonalInformation(IRecvPacket packet)
+ void ReceiveSeasonalInformation(IRecvPacket packet)
{
SeasonChangePacket p = (SeasonChangePacket)packet;
if (p.SeasonChanged)
@@ -1265,7 +1284,7 @@ private void ReceiveSeasonalInformation(IRecvPacket packet)
}
}
- private void ReceiveSetWeather(IRecvPacket packet)
+ void ReceiveSetWeather(IRecvPacket packet)
{
announce_UnhandledPacket(packet);
}
diff --git a/dev/Ultima/World/WorldInteraction.cs b/dev/Ultima/World/WorldInteraction.cs
index a42a7dc3..c440124d 100644
--- a/dev/Ultima/World/WorldInteraction.cs
+++ b/dev/Ultima/World/WorldInteraction.cs
@@ -8,6 +8,7 @@
* (at your option) any later version.
*
***************************************************************************/
+
#region usings
using System;
using System.Collections.Generic;
@@ -17,7 +18,6 @@
using UltimaXNA.Ultima.Data;
using UltimaXNA.Ultima.Network.Client;
using UltimaXNA.Ultima.Player;
-using UltimaXNA.Ultima.Resources;
using UltimaXNA.Ultima.UI.WorldGumps;
using UltimaXNA.Ultima.World.Entities;
using UltimaXNA.Ultima.World.Entities.Items;
@@ -26,8 +26,7 @@
using UltimaXNA.Ultima.UI;
#endregion
-namespace UltimaXNA.Ultima.World
-{
+namespace UltimaXNA.Ultima.World {
///
/// Hosts methods for interacting with the world.
///
@@ -56,42 +55,6 @@ public Serial LastTarget
}
}
- public void SendSpeech(string text, ChatMode mode) // used by chatwindow.
- {
- MessageTypes speechType = MessageTypes.Normal;
- int hue = 0;
-
- switch (mode)
- {
- case ChatMode.Default:
- speechType = MessageTypes.Normal;
- hue = Settings.UserInterface.SpeechColor;
- break;
- case ChatMode.Whisper:
- speechType = MessageTypes.Whisper;
- hue = Settings.UserInterface.SpeechColor;
- break;
- case ChatMode.Emote:
- speechType = MessageTypes.Emote;
- hue = Settings.UserInterface.EmoteColor;
- break;
- case ChatMode.Party:
- // not yet implemented
- speechType = MessageTypes.Normal;
- hue = Settings.UserInterface.SpeechColor;
- break;
- case ChatMode.Guild:
- speechType = MessageTypes.Guild;
- hue = Settings.UserInterface.GuildMsgColor;
- break;
- case ChatMode.Alliance:
- speechType = MessageTypes.Alliance;
- hue = Settings.UserInterface.AllianceMsgColor;
- break;
- }
- m_Network.Send(new AsciiSpeechPacket(speechType, 0, hue + 2, "ENU", text));
- }
-
///
/// Informs the server we have single-clicked on an entity. Also requests a context menu.
///
@@ -231,7 +194,7 @@ public void CreateLabel(MessageTypes msgType, Serial serial, string text, int fo
}
else
{
- ChatMessage("[LABEL] " + text, font, hue, asUnicode);
+ ChatMessage(text, font, hue, asUnicode);
}
}
diff --git a/dev/Ultima/World/WorldViews/IsometricLighting.cs b/dev/Ultima/World/WorldViews/IsometricLighting.cs
index d833a4a3..21caaa6e 100644
--- a/dev/Ultima/World/WorldViews/IsometricLighting.cs
+++ b/dev/Ultima/World/WorldViews/IsometricLighting.cs
@@ -20,6 +20,11 @@ public class IsometricLighting
private float m_LightDirection = 4.12f;
private float m_LightHeight = -0.75f;
+ public IsometricLighting()
+ {
+ RecalculateLightningValues();
+ }
+
public int PersonalLightning
{
set { m_LightLevelPersonal = value; RecalculateLightningValues(); }
diff --git a/dev/UltimaXNA.csproj b/dev/UltimaXNA.csproj
index 19a17d29..177ebbed 100644
--- a/dev/UltimaXNA.csproj
+++ b/dev/UltimaXNA.csproj
@@ -130,7 +130,23 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -290,6 +306,15 @@
+
+
+
+
+
+
+
+
+
@@ -323,9 +348,13 @@
+
+
+
+
@@ -535,7 +564,7 @@
-
+
diff --git a/plugins/ExamplePlugin/HueTestGump.cs b/plugins/ExamplePlugin/DebugHuesGumpModule.cs
similarity index 96%
rename from plugins/ExamplePlugin/HueTestGump.cs
rename to plugins/ExamplePlugin/DebugHuesGumpModule.cs
index 528fcdfc..e976624b 100644
--- a/plugins/ExamplePlugin/HueTestGump.cs
+++ b/plugins/ExamplePlugin/DebugHuesGumpModule.cs
@@ -1,18 +1,17 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.IO;
+using UltimaXNA.Core.Graphics;
+using UltimaXNA.Core.Patterns;
using UltimaXNA.Core.Resources;
using UltimaXNA.Core.UI;
using UltimaXNA.Ultima.Resources;
using UltimaXNA.Ultima.UI;
using UltimaXNA.Ultima.UI.Controls;
-using UltimaXNA.Core.Graphics;
-using UltimaXNA.Core.Patterns;
-using System;
using UltimaXNA.Ultima.UI.LoginGumps;
namespace UltimaXNA.Ultima.Login {
- class HueTestGumpModule : IModule {
+ class DebugHuesGumpModule : IModule {
public string Name => "DebugHueTest";
public void Load() {
@@ -24,15 +23,15 @@ public void Unload() {
}
void OnClickDebugGump() {
- ServiceRegistry.GetService().AddControl(new HueTestGump(), 0, 0);
+ ServiceRegistry.GetService().AddControl(new DebugHuesGump(), 0, 0);
}
}
- class HueTestGump : Gump {
+ class DebugHuesGump : Gump {
TextLabel m_Label;
HuedControl m_HueDisplay;
- public HueTestGump()
+ public DebugHuesGump()
: base(0, 0) {
OverHue = -1;
}
diff --git a/plugins/ExamplePlugin/DebugOutputGumpsModule.cs b/plugins/ExamplePlugin/DebugOutputGumpsModule.cs
new file mode 100644
index 00000000..2d751689
--- /dev/null
+++ b/plugins/ExamplePlugin/DebugOutputGumpsModule.cs
@@ -0,0 +1,53 @@
+using Microsoft.Xna.Framework.Graphics;
+using System.Threading;
+using System.Threading.Tasks;
+using UltimaXNA;
+using UltimaXNA.Core.Diagnostics.Tracing;
+using UltimaXNA.Core.Patterns;
+using UltimaXNA.Core.Resources;
+using UltimaXNA.Ultima.UI.LoginGumps;
+
+namespace ExamplePlugin {
+ class DebugOutputGumpsModule : IModule {
+ public string Name => "DebugOutputGumpsModule";
+ Task m_WriteGumps;
+ CancellationTokenSource m_WriteGumpsCancel;
+
+ public void Load() {
+ // LoginGump.AddButton("Debug:Gumps", OnClickDebugGump);
+ }
+
+ public void Unload() {
+ // LoginGump.RemoveButton(OnClickDebugGump);
+ }
+
+ void OnClickDebugGump() {
+ if (m_WriteGumps == null) {
+ Tracer.Info("Writing all gump textures to /Gumps folder. Progress may be slow. Click Debug:Gumps button again to cancel.");
+ // This is ugly! I have no idea how to do parallelism. Someone help me?
+ m_WriteGumpsCancel = new CancellationTokenSource();
+ m_WriteGumps = new Task(
+ () => {
+ System.IO.Directory.CreateDirectory("Gumps");
+ IResourceProvider resources = ServiceRegistry.GetService();
+ for (int i = 0; i < 0x10000; i++) {
+ Texture2D texture = resources.GetUITexture(i);
+ if (texture != null) {
+ texture.SaveAsJpeg(new System.IO.FileStream($"Gumps/{i:D6}.jpg", System.IO.FileMode.Create), texture.Width, texture.Height);
+ }
+ if (m_WriteGumpsCancel.Token.IsCancellationRequested) {
+ break;
+ }
+ }
+ m_WriteGumpsCancel = null;
+ m_WriteGumps = null;
+ }, m_WriteGumpsCancel.Token);
+ m_WriteGumps.Start();
+ }
+ else {
+ Tracer.Info("Canceled writing gump textures.");
+ m_WriteGumpsCancel.Cancel();
+ }
+ }
+ }
+}
diff --git a/plugins/ExamplePlugin/ExamplePlugin.csproj b/plugins/ExamplePlugin/ExamplePlugin.csproj
index 50bd99a6..6f4ac7ca 100644
--- a/plugins/ExamplePlugin/ExamplePlugin.csproj
+++ b/plugins/ExamplePlugin/ExamplePlugin.csproj
@@ -49,8 +49,10 @@
-
+
+
+
diff --git a/plugins/ExamplePlugin/HueTestState.cs b/plugins/ExamplePlugin/HueTestState.cs
deleted file mode 100644
index ada0964b..00000000
--- a/plugins/ExamplePlugin/HueTestState.cs
+++ /dev/null
@@ -1,155 +0,0 @@
-using Microsoft.Xna.Framework;
-using Microsoft.Xna.Framework.Graphics;
-using System.IO;
-using UltimaXNA.Core.Graphics;
-using UltimaXNA.Core.UI;
-using UltimaXNA.Ultima.Resources;
-using UltimaXNA.Ultima.UI.Controls;
-using UltimaXNA.Ultima.UI;
-using UltimaXNA.Core.Resources;
-
-namespace UltimaXNA.Ultima.Login.States
-{
- /*class HueTestState : AState
- {
- Gump m_Gump;
- TextLabel m_Label;
- HuedControl m_HueDisplay;
-
- UserInterfaceService m_UserInterface;
-
- public HueTestState()
- : base()
- {
- OverHue = -1;
-
- m_UserInterface = ServiceRegistry.GetService();
- }
-
- public static string Caption
- {
- get
- {
- if (OverHue <= -1)
- {
- return "Over: None";
- }
- else
- {
- return string.Format("Over: {0} [Hue index {1} 0x{1:x}]", OverHue, OverHue - 2);
- }
- }
- }
-
- public static int OverHue
- {
- get;
- set;
- }
-
- public override void Intitialize()
- {
- base.Intitialize();
-
- m_Gump = (Gump)m_UserInterface.AddControl(new Gump(0, 0), 0, 0);
- m_Gump.Size = new Point(800, 600);
- m_Gump.AddControl(new ResizePic(m_Gump, 5, 5, 3000, 790, 590));
-
- int rowwidth = 60;
-
- // caption string
- m_Label = (TextLabel)m_Gump.AddControl(new TextLabel(m_Gump, 50, 8, 0, null));
-
- // object that is hued based on the current overhue.
- m_HueDisplay = (HuedControl)m_Gump.AddControl(new HuedControl(m_Gump, 8305));
- m_Gump.LastControl.Position = new Point(745, 15);
- ((HuedControl)m_Gump.LastControl).Hue = 0;
-
- // unhued object
- m_Gump.AddControl(new HuedControl(m_Gump));
- m_Gump.LastControl.Position = new Point(-5, 10);
- ((HuedControl)m_Gump.LastControl).Hue = 0;
-
- // hue index 1 (uo hue -1), aka one of the "True Black" hues
- m_Gump.AddControl(new HuedControl(m_Gump));
- m_Gump.LastControl.Position = new Point(3, 10);
- ((HuedControl)m_Gump.LastControl).Hue = 1;
-
- for (int i = 0; i < 3000; i++)
- {
- m_Gump.AddControl(new HuedControl(m_Gump));
- m_Gump.LastControl.Position = new Point((i % rowwidth) * 11 - 5, (i / rowwidth) * 10 + 28);
- ((HuedControl)m_Gump.LastControl).Hue = i + 2;
- }
-
- using (FileStream file = new FileStream("hues0.png", FileMode.Create))
- {
- HueData.HueTexture0.SaveAsPng(file, HueData.HueTexture0.Width, HueData.HueTexture0.Height);
- }
-
- using (FileStream file = new FileStream("hues1.png", FileMode.Create))
- {
- HueData.HueTexture1.SaveAsPng(file, HueData.HueTexture1.Width, HueData.HueTexture1.Height);
- }
- }
-
- public override void Update(double totalTime, double frameTime)
- {
- m_Label.Text = Caption;
- m_HueDisplay.Hue = OverHue <= -1 ? 0 : OverHue;
- base.Update(totalTime, frameTime);
- }
-
- class HuedControl : AControl
- {
- private int m_StaticTextureID = 0;
-
- public HuedControl(AControl parent, int staticID = 0x1bf5)
- : base(parent)
- {
- HandlesMouseInput = true;
- m_StaticTextureID = staticID;
- }
-
- public int Hue = 0;
-
- private Texture2D m_texture;
-
- public override void Draw(SpriteBatchUI spriteBatch, Point position)
- {
- if (m_texture == null)
- {
- IResourceProvider provider = ServiceRegistry.GetService();
- m_texture = provider.GetItemTexture(m_StaticTextureID);
- Size = new Point(m_texture.Width, m_texture.Height);
- }
- spriteBatch.Draw2D(m_texture, new Vector3(position.X, position.Y, 0), Utility.GetHueVector(Hue));
- base.Draw(spriteBatch, position);
- }
-
- protected override void OnMouseOver(int x, int y)
- {
- OverHue = Hue;
- }
-
- protected override void OnMouseOut(int x, int y)
- {
- OverHue = -1;
- }
-
- protected override bool IsPointWithinControl(int x, int y)
- {
- if (m_texture != null)
- {
- uint[] pixel = new uint[m_texture.Width * m_texture.Height];
- m_texture.GetData(pixel);
- if (pixel[y * m_texture.Width + x] != 0x00000000)
- {
- return true;
- }
- }
- return false;
- }
- }
- }*/
-}
diff --git a/plugins/ExamplePlugin/MapRenderer/MapRendererModule.cs b/plugins/ExamplePlugin/MapRenderer/MapRendererModule.cs
new file mode 100644
index 00000000..ad6a2aec
--- /dev/null
+++ b/plugins/ExamplePlugin/MapRenderer/MapRendererModule.cs
@@ -0,0 +1,81 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using System.IO;
+using UltimaXNA;
+using UltimaXNA.Core.Graphics;
+using UltimaXNA.Core.Patterns;
+using UltimaXNA.Ultima.UI.LoginGumps;
+using UltimaXNA.Ultima.World.Entities;
+using UltimaXNA.Ultima.World.EntityViews;
+using UltimaXNA.Ultima.World.Input;
+using UltimaXNA.Ultima.World.Maps;
+using UltimaXNA.Ultima.World.WorldViews;
+
+namespace ExamplePlugin.MapRenderer
+{
+ class MapRendererModule : IModule
+ {
+ public string Name => "Map Renderer";
+
+ public void Load()
+ {
+ // LoginGump.AddButton("Debug:Map", OnClick);
+ }
+
+ public void Unload()
+ {
+ LoginGump.RemoveButton(OnClick);
+ }
+
+ const int Width = 44 * 8;
+ const int Height = 44 * 8;
+ const int WidthExtra = 44 * 4;
+ const int HeightExtra = 44 * 4;
+ const int HeightExtra2 = 256 * 4;
+
+ void OnClick()
+ {
+ Directory.CreateDirectory("Chunks");
+ IsometricLighting lighting = new IsometricLighting();
+ MouseOverList mouseOverNull = new MouseOverList(new MousePicking());
+ SpriteBatch3D spritebatch = ServiceRegistry.GetService();
+ RenderTarget2D render = new RenderTarget2D(spritebatch.GraphicsDevice, Width + WidthExtra * 2, Height + HeightExtra * 2 + HeightExtra2);
+ Map map = new Map(0);
+ for (int chunky = 0; chunky < 10; chunky++)
+ {
+ for (int chunkx = 0; chunkx < 10; chunkx++)
+ {
+ spritebatch.GraphicsDevice.SetRenderTarget(render);
+ spritebatch.Reset();
+ uint cx = (uint)chunkx + 200;
+ uint cy = (uint)chunky + 200;
+ map.CenterPosition = new Point((int)(cx << 3), (int)(cy << 3));
+ MapChunk chunk = map.GetMapChunk(cx, cy);
+ chunk.LoadStatics(map.MapData, map);
+ int z = 0;
+ for (int i = 0; i < 64; i++)
+ {
+ int y = (i / 8);
+ int x = (i % 8);
+ int sy = y * 22 + x * 22 + HeightExtra + HeightExtra2;
+ int sx = 22 * 8 - y * 22 + x * 22 + WidthExtra;
+ MapTile tile = chunk.Tiles[i];
+ tile.ForceSort();
+ for (int j = 0; j < tile.Entities.Count; j++)
+ {
+ AEntity e = tile.Entities[j];
+ AEntityView view = e.GetView();
+ view.Draw(spritebatch, new Vector3(sx, sy, 0), mouseOverNull, map, false);
+ }
+ }
+ spritebatch.SetLightIntensity(lighting.IsometricLightLevel);
+ spritebatch.SetLightDirection(lighting.IsometricLightDirection);
+ spritebatch.GraphicsDevice.Clear(Color.Transparent);
+ spritebatch.FlushSprites(true);
+ spritebatch.GraphicsDevice.SetRenderTarget(null);
+ render.SaveAsPng(new FileStream($"Chunks/{cy}-{cx}.png", FileMode.Create), render.Width, render.Height);
+ }
+ }
+ }
+ }
+}