diff --git a/Data/Locations/felucca.xml b/Data/Locations/felucca.xml index 890b786..6d4594c 100644 --- a/Data/Locations/felucca.xml +++ b/Data/Locations/felucca.xml @@ -3,19 +3,9 @@ - - - - - - - - + - - - - + @@ -276,4 +266,4 @@ - \ No newline at end of file + diff --git a/Scripts/Commands/BuildWorld.cs b/Scripts/Commands/BuildWorld.cs index c236055..7ad5d98 100644 --- a/Scripts/Commands/BuildWorld.cs +++ b/Scripts/Commands/BuildWorld.cs @@ -79,14 +79,15 @@ namespace Server.Scripts.Commands public static void DoDecorate( Mobile m ) { - m.SendMessage( "Generating World Decorations, Signs & Teleporters..." ); + m.SendMessage( "Generating World Decorations, Signs & Teleporters, Inn Room Doors..." ); Console.WriteLine( "Decorating the world..."); CommandSystem.Handle(m, String.Format("{0}Decorate", CommandSystem.Prefix)); CommandSystem.Handle(m, String.Format("{0}SignGen", CommandSystem.Prefix)); CommandSystem.Handle(m, String.Format("{0}TelGen", CommandSystem.Prefix)); + CommandSystem.Handle(m, String.Format("{0}InnGen", CommandSystem.Prefix)); - m.SendMessage( "Decorations, Signs, and Teleporters generated!" ); + m.SendMessage( "Decorations, Signs, Inn Room Doors, and Teleporters generated!" ); } public static void DoSpawns( Mobile m ) diff --git a/Scripts/Engines/InnRooms/InnDoor.cs b/Scripts/Engines/InnRooms/InnDoor.cs new file mode 100644 index 0000000..38a4d13 --- /dev/null +++ b/Scripts/Engines/InnRooms/InnDoor.cs @@ -0,0 +1,345 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Custom.InnRooms; +using Server.Multis; +using System.Collections.Generic; +using Server.ContextMenus; + +namespace Server.Custom.InnRooms +{ + public class InnDoor : Item + { + private Mobile m_Renter; + private InnRoomHouse m_House; + private DateTime m_RentExpires; + private Point3D m_RoomLocation; + private Map m_RoomMap; + + // --- NEW VARIABLES --- + private bool m_AutoRenew; + private int m_RentCost = 5000; + private TimeSpan m_RentDuration = TimeSpan.FromDays(7); + + // --- PUBLIC PROPERTIES EXPOSED FOR THE GUMP --- + [CommandProperty(AccessLevel.GameMaster)] + public bool AutoRenew { get { return m_AutoRenew; } set { m_AutoRenew = value; } } + + public DateTime RentExpires { get { return m_RentExpires; } set { m_RentExpires = value; } } + public int RentCost { get { return m_RentCost; } } + public TimeSpan RentDuration { get { return m_RentDuration; } } + public InnRoomHouse House { get { return m_House; } } + + [CommandProperty(AccessLevel.GameMaster)] + public bool ForceEviction { get { return false; } set { if (value == true) { EvictTenant(); } } } + + [CommandProperty(AccessLevel.GameMaster)] + public Mobile Renter { get { return m_Renter; } set { m_Renter = value; InvalidateProperties(); } } + + [CommandProperty(AccessLevel.GameMaster)] + public Point3D RoomLocation { get { return m_RoomLocation; } set { m_RoomLocation = value; } } + + [CommandProperty(AccessLevel.GameMaster)] + public Map RoomMap { get { return m_RoomMap; } set { m_RoomMap = value; } } + + [Constructable] + public InnDoor() : this(0x6E5) + { + } + + [Constructable] + public InnDoor(int itemID) : base(itemID) + { + Movable = false; + Name = "A Vacant Inn Room"; + } + + public InnDoor(Serial serial) : base(serial) + { + } + + public override void OnDoubleClick(Mobile from) + { + if (!from.InRange(GetWorldLocation(), 2)) + { + from.LocalOverheadMessage(Server.Network.MessageType.Regular, 0x3B2, 1019045); + return; + } + + // SCENARIO A: The room is VACANT + if (m_Renter == null) + { + if (RoomLocation == Point3D.Zero || RoomMap == null) + { + from.SendMessage(33, "This door is out of order (No Room Linked)."); + return; + } + + // Swapped to Banker.Withdraw to support AccountGold out of the gate + if (Banker.Withdraw(from, m_RentCost)) + { + m_Renter = from; + m_RentExpires = DateTime.UtcNow + m_RentDuration; + m_AutoRenew = true; // Automatically turn it on for new renters! + Name = from.Name + "'s Inn Room"; + + m_House = new InnRoomHouse(from); + Point3D buriedLocation = new Point3D(RoomLocation.X, RoomLocation.Y, RoomLocation.Z - 20); + m_House.MoveToWorld(buriedLocation, RoomMap); + + from.PlaySound(0x249); + from.SendMessage(68, $"You have rented this room for {m_RentDuration.Days} days."); + TeleportToRoom(from); + } + else + { + from.SendMessage(33, $"You need {m_RentCost} gold in your bank to rent this room."); + } + } + // SCENARIO B: The room is RENTED + else + { + // Check if the rent has expired + if (DateTime.UtcNow > m_RentExpires) + { + // MAGIC AUTO-RENEW CHECK + if (m_AutoRenew && m_Renter != null && Banker.Withdraw(m_Renter, m_RentCost)) + { + m_RentExpires += m_RentDuration; + InvalidateProperties(); + + // Let them know it silently renewed behind the scenes + if (from == m_Renter) + from.SendMessage(68, $"Your auto-renew just processed {m_RentCost} gold from your bank!"); + } + else + { + EvictTenant(); + from.SendMessage(33, "The rent expired and the room has been cleared. It is now vacant."); + return; + } + } + + if (m_House != null && (m_House.IsOwner(from) || m_House.IsFriend(from) || m_House.HasAccess(from))) + { + TeleportToRoom(from); + } + else + { + from.SendMessage(33, "The door is locked and you do not have permission to enter."); + } + } + } + + private void TeleportToRoom(Mobile m) + { + m.PlaySound(0x1EA); + m.MoveToWorld(RoomLocation, RoomMap); + m.SendMessage(0x3B2, "You enter the inn room."); + } + + public void EvictTenant() + { + if (m_Renter != null && m_House != null && !m_House.Deleted) + { + BankBox bank = m_Renter.BankBox; + + if (bank != null) + { + InnEvictionCrate crate = new InnEvictionCrate(); + + for (int i = m_House.Secures.Count - 1; i >= 0; --i) + { + SecureInfo info = m_House.Secures[i] as SecureInfo; + if (info != null && info.Item != null) + { + Container secureContainer = info.Item; + secureContainer.IsSecure = false; + secureContainer.IsLockedDown = false; + secureContainer.Movable = true; + crate.DropItem(secureContainer); + } + } + m_House.Secures.Clear(); + + for (int i = m_House.LockDowns.Count - 1; i >= 0; --i) + { + Item item = m_House.LockDowns[i] as Item; + if (item != null) + { + item.IsLockedDown = false; + item.Movable = true; + crate.DropItem(item); + } + } + m_House.LockDowns.Clear(); + + if (crate.Items.Count > 0) + { + bank.DropItem(crate); + m_Renter.SendMessage(33, "Your inn room rent has expired. Your belongings have been packed into a crate and placed in your bank box."); + } + else + { + crate.Delete(); + } + } + + m_House.Delete(); + } + + m_Renter = null; + m_House = null; + m_AutoRenew = false; + Name = "A Vacant Inn Room"; + } + + public override void GetProperties(ObjectPropertyList list) + { + base.GetProperties(list); + + if (m_Renter != null) + { + list.Add(1060658, "Rent Cost\tPaid"); + list.Add(1060659, "Expires\t{0}", m_RentExpires.ToString("g")); + } + else + { + list.Add(1060658, "Rent Cost\t{0} Gold", m_RentCost); + } + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + // Bumped to version 1 to safely save the AutoRenew toggle! + writer.Write((int)1); + + writer.Write(m_AutoRenew); + + writer.Write(m_Renter); + writer.WriteItem(m_House); + writer.Write(m_RentExpires); + writer.Write(m_RoomLocation); + writer.Write(m_RoomMap); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + int version = reader.ReadInt(); + + if (version >= 1) + { + m_AutoRenew = reader.ReadBool(); + } + + m_Renter = reader.ReadMobile(); + m_House = reader.ReadItem() as InnRoomHouse; + m_RentExpires = reader.ReadDateTime(); + m_RoomLocation = reader.ReadPoint3D(); + m_RoomMap = reader.ReadMap(); + } + + // --- NEW CONTEXT MENU FOR THE GUMP --- + public override void GetContextMenuEntries(Mobile from, List list) + { + base.GetContextMenuEntries(from, list); + + if (from.Alive && m_Renter == from) + { + // Cliloc 1063490 is "Manage" + list.Add(new ManageRoomEntry(from, this)); + } + } + + private class ManageRoomEntry : ContextMenuEntry + { + private Mobile m_From; + private InnDoor m_Door; + + public ManageRoomEntry(Mobile from, InnDoor door) : base(1063490, 2) + { + m_From = from; + m_Door = door; + } + + public override void OnClick() + { + if (m_Door.Deleted || m_Door.Renter != m_From) + return; + + m_From.SendGump(new InnRoomManagementGump((PlayerMobile)m_From, m_Door)); + } + } + + // --- BONUS: DRAG AND DROP RENEWAL --- + public override bool OnDragDrop(Mobile from, Item dropped) + { + if (from == m_Renter && dropped is Gold) + { + if (dropped.Amount == m_RentCost) + { + dropped.Delete(); + m_RentExpires += m_RentDuration; + + from.PlaySound(0x249); + from.SendMessage(68, $"You hand the gold to the landlord and extend your rent."); + from.SendMessage(68, $"Your room is now paid until: {m_RentExpires.ToString("g")}"); + InvalidateProperties(); + + return true; + } + else + { + from.SendMessage(33, $"You must drop exactly {m_RentCost} gold on the door to renew it."); + return false; + } + } + + return base.OnDragDrop(from, dropped); + } + } + + public class InnEvictionCrate : WoodenBox + { + public override int DefaultMaxItems { get { return 500; } } + public override int DefaultMaxWeight { get { return 5000; } } + + [Constructable] + public InnEvictionCrate() + { + Name = "Evicted Inn Room Belongings"; + Hue = 33; + } + + public InnEvictionCrate(Serial serial) : base(serial) { } + + public override bool CheckHold(Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight) + { + if (m.AccessLevel < AccessLevel.GameMaster) + { + if (message) + m.SendLocalizedMessage(1061145); + + return false; + } + + return base.CheckHold(m, item, message, checkItems, plusItems, plusWeight); + } + + public override void OnItemRemoved(Item item) + { + base.OnItemRemoved(item); + + if (this.TotalItems == 0) + { + Delete(); + } + } + + public override void Serialize(GenericWriter writer) { base.Serialize(writer); writer.Write((int)0); } + public override void Deserialize(GenericReader reader) { base.Deserialize(reader); int version = reader.ReadInt(); } + } +} diff --git a/Scripts/Engines/InnDoor.cs b/Scripts/Engines/InnRooms/InnDoor.cs.bak similarity index 100% rename from Scripts/Engines/InnDoor.cs rename to Scripts/Engines/InnRooms/InnDoor.cs.bak diff --git a/Scripts/Engines/InnRoomHouse.cs b/Scripts/Engines/InnRooms/InnRoomHouse.cs similarity index 70% rename from Scripts/Engines/InnRoomHouse.cs rename to Scripts/Engines/InnRooms/InnRoomHouse.cs index cd91c9a..bb7851b 100644 --- a/Scripts/Engines/InnRoomHouse.cs +++ b/Scripts/Engines/InnRooms/InnRoomHouse.cs @@ -16,26 +16,38 @@ namespace Server.Custom.InnRooms get { return new Point3D(this.X, this.Y - 5, this.Z + 20); } } - public InnRoomHouse(Mobile owner) : base(0x1DF3, owner, 50, 2) + // --- THE STORAGE FIXES --- + + // 1. Negate the 20% Mondain's Legacy bonus so they get exactly 250 items! + public override double BonusStorageScalar { get { return 1.0; } } + + // 2. Both base values are passed as 250 + public InnRoomHouse(Mobile owner) : base(0xA26, owner, 250, 250) { RestrictDecay = true; + + // Force the initial properties to 250 + MaxLockDowns = 250; + MaxSecures = 250; } public InnRoomHouse(Serial serial) : base(serial) { } - // --- THE LOCKDOWN FIX --- + // The absolute maximum number of floor items public override int GetAosMaxLockdowns() { - return 50; // 50 floor items + return 250; } + // The absolute maximum number of total items (chests + floor items + inside chests) public override int GetAosMaxSecures() { - return 250; // 250 total items in the room + return 250; } - // ------------------------ + + // ------------------------- public override bool IsInside(Point3D p, int height) { diff --git a/Scripts/Engines/InnRooms/InnRoomManagementGump.cs b/Scripts/Engines/InnRooms/InnRoomManagementGump.cs new file mode 100644 index 0000000..d7428cb --- /dev/null +++ b/Scripts/Engines/InnRooms/InnRoomManagementGump.cs @@ -0,0 +1,117 @@ +using System; +using Server; +using Server.Gumps; +using Server.Mobiles; +using Server.Network; + +namespace Server.Custom.InnRooms +{ + public class InnRoomManagementGump : Gump + { + private PlayerMobile m_Player; + private InnDoor m_Door; + + public InnRoomManagementGump(PlayerMobile pm, InnDoor door) : base(150, 150) + { + m_Player = pm; + m_Door = door; + + if (m_Door == null || m_Door.House == null || m_Door.Deleted) + return; + + AddPage(0); + AddBackground(0, 0, 400, 350, 9270); + AddHtml(0, 15, 400, 20, "
Room Management
", false, false); + + // --- STORAGE STATS --- + // Tapping into the core BaseHouse math to get exact counts! + int sec = 0, ven = 0, lck = 0, mc = 0; + int usedStorage = m_Door.House.GetAosCurSecures(out sec, out ven, out lck, out mc); + int maxStorage = m_Door.House.GetAosMaxSecures(); + + AddHtml(40, 60, 150, 20, "Storage Used:", false, false); + AddHtml(150, 60, 200, 20, $"{usedStorage} / {maxStorage} Items", false, false); + + // --- RENT INFO --- + AddHtml(40, 110, 150, 20, "Rent Expires:", false, false); + AddHtml(150, 110, 200, 20, $"{m_Door.RentExpires.ToString("g")}", false, false); + + // --- ACTIONS --- + // 1. Auto-Renew Toggle + AddHtml(75, 170, 250, 20, m_Door.AutoRenew ? "Auto-Renew is ON" : "Auto-Renew is OFF", false, false); + AddButton(40, 170, 4005, 4007, 1, GumpButtonType.Reply, 0); + + // 2. Manual Renew + AddHtml(75, 210, 250, 20, $"Manual Renew ({m_Door.RentCost} Gold)", false, false); + AddButton(40, 210, 4005, 4007, 2, GumpButtonType.Reply, 0); + + // 3. Cancel Button + AddHtml(75, 280, 250, 20, "Cancel Rental Contract", false, false); + AddButton(40, 280, 4005, 4007, 3, GumpButtonType.Reply, 0); + } + + public override void OnResponse(NetState sender, RelayInfo info) + { + if (m_Door == null || m_Door.Deleted || m_Door.Renter != m_Player) + return; + + switch (info.ButtonID) + { + case 1: // Toggle Auto-Renew + m_Door.AutoRenew = !m_Door.AutoRenew; + m_Player.SendMessage(68, m_Door.AutoRenew ? "Auto-Renew enabled." : "Auto-Renew disabled."); + m_Player.SendGump(new InnRoomManagementGump(m_Player, m_Door)); // Refresh Gump + break; + + case 2: // Manual Renew + if (Banker.Withdraw(m_Player, m_Door.RentCost)) + { + m_Door.RentExpires += m_Door.RentDuration; + m_Player.PlaySound(0x249); + m_Player.SendMessage(68, $"You paid {m_Door.RentCost} gold. Room paid until: {m_Door.RentExpires.ToString("g")}"); + m_Door.InvalidateProperties(); + } + else + { + m_Player.SendMessage(33, $"You need {m_Door.RentCost} gold in your bank to renew."); + } + m_Player.SendGump(new InnRoomManagementGump(m_Player, m_Door)); // Refresh Gump + break; + + case 3: // Cancel Contract + m_Player.SendGump(new CancelRentGump(m_Door)); + break; + } + } + } + + // --- CANCELLATION CONFIRMATION GUMP --- + public class CancelRentGump : Gump + { + private InnDoor m_Door; + + public CancelRentGump(InnDoor door) : base(200, 200) + { + m_Door = door; + + AddPage(0); + AddBackground(0, 0, 300, 150, 9270); + AddHtml(0, 15, 300, 20, "
Cancel Room Rental?
", false, false); + AddHtml(20, 50, 260, 40, "This will instantly pack your items into your bank and evict you.", false, false); + + AddButton(40, 100, 4005, 4007, 1, GumpButtonType.Reply, 0); + AddHtml(75, 100, 100, 20, "Confirm", false, false); + + AddButton(160, 100, 4005, 4007, 0, GumpButtonType.Reply, 0); + AddHtml(195, 100, 100, 20, "Cancel", false, false); + } + + public override void OnResponse(NetState sender, RelayInfo info) + { + if (info.ButtonID == 1 && m_Door != null && !m_Door.Deleted) + { + m_Door.EvictTenant(); + } + } + } +} diff --git a/Scripts/Misc/CurrentExpansion.cs b/Scripts/Misc/CurrentExpansion.cs index 27f9a81..9375933 100644 --- a/Scripts/Misc/CurrentExpansion.cs +++ b/Scripts/Misc/CurrentExpansion.cs @@ -7,7 +7,7 @@ namespace Server { public class CurrentExpansion { - private static readonly Expansion Expansion = Expansion.TOL; + private static readonly Expansion Expansion = Expansion.AOS; public static void Configure() { @@ -20,7 +20,7 @@ namespace Server bool Enabled = Core.AOS; - Mobile.InsuranceEnabled = Enabled; + Mobile.InsuranceEnabled = !Enabled; ObjectPropertyList.Enabled = Enabled; Mobile.VisibleDamageType = Enabled ? VisibleDamageType.Related : VisibleDamageType.None; Mobile.GuildClickMessage = !Enabled;