diff --git a/Data/Config/Regions.xml b/Data/Config/Regions.xml
index d91cfb0..ed56857 100644
--- a/Data/Config/Regions.xml
+++ b/Data/Config/Regions.xml
@@ -27,7 +27,7 @@
-
+
@@ -224,5 +224,8 @@
+
+
+
diff --git a/Data/Config/innrooms.cfg b/Data/Config/innrooms.cfg
new file mode 100644
index 0000000..354d4d9
--- /dev/null
+++ b/Data/Config/innrooms.cfg
@@ -0,0 +1,3 @@
+Small Vaelen 7133 3992 0
+Medium Vaelen 7135 4027 0
+Large Vaelen 7137 4066 0
diff --git a/Data/Decoration/Towns/InnRoomDoors.cfg b/Data/Decoration/Towns/InnRoomDoors.cfg
new file mode 100644
index 0000000..a230e33
--- /dev/null
+++ b/Data/Decoration/Towns/InnRoomDoors.cfg
@@ -0,0 +1,8 @@
+InnExitDoor 1765
+7138 4075 0
+
+InnExitDoor 1765
+7136 4034 0
+
+InnExitDoor 1765
+7134 3997 0
diff --git a/Data/Decoration/inndoors.cfg b/Data/Decoration/inndoors.cfg
deleted file mode 100644
index 22a24ed..0000000
--- a/Data/Decoration/inndoors.cfg
+++ /dev/null
@@ -1,2 +0,0 @@
-#Bastion Inn
-Vaelen 835 666 0 Vaelen 7138 4066 0 1779
diff --git a/Scripts/Commands/CheckBalance.cs b/Scripts/Commands/CheckBalance.cs
new file mode 100644
index 0000000..e541120
--- /dev/null
+++ b/Scripts/Commands/CheckBalance.cs
@@ -0,0 +1,49 @@
+using System;
+using Server;
+using Server.Commands;
+using Server.Mobiles;
+using Server.Targeting;
+
+namespace Server.Custom.Commands
+{
+ public class CheckBalance
+ {
+ public static void Initialize()
+ {
+ CommandSystem.Register("CheckBalance", AccessLevel.GameMaster, new CommandEventHandler(CheckBalance_OnCommand));
+ }
+
+ [Usage("CheckBalance")]
+ [Description("Checks the true Banker balance (including AccountGold) of a targeted player.")]
+ public static void CheckBalance_OnCommand(CommandEventArgs e)
+ {
+ e.Mobile.Target = new BalanceTarget();
+ e.Mobile.SendMessage(68, "Target a player to check their total bank balance.");
+ }
+
+ private class BalanceTarget : Target
+ {
+ // The '-1' means standard range, 'false' means it doesn't allow ground targeting
+ public BalanceTarget() : base(-1, false, TargetFlags.None)
+ {
+ }
+
+ protected override void OnTarget(Mobile from, object targeted)
+ {
+ if (targeted is PlayerMobile)
+ {
+ PlayerMobile pm = (PlayerMobile)targeted;
+
+ // Banker.GetBalance checks both AccountGold AND physical bank gold
+ long balance = Banker.GetBalance(pm);
+
+ from.SendMessage(68, $"{pm.Name} has a true bank balance of: {balance} gold.");
+ }
+ else
+ {
+ from.SendMessage(33, "You must target a player character.");
+ }
+ }
+ }
+ }
+}
diff --git a/Scripts/Commands/GenInnDoors.cs b/Scripts/Commands/GenInnDoors.cs
deleted file mode 100644
index 8a47ebe..0000000
--- a/Scripts/Commands/GenInnDoors.cs
+++ /dev/null
@@ -1,140 +0,0 @@
-using System;
-using System.IO;
-using System.Collections;
-using Server;
-using Server.Items;
-using Server.Custom.InnRooms; // Ensure it can find our InnDoor!
-
-namespace Server.Commands
-{
- public class GenInnDoors
- {
- public static void Initialize()
- {
- CommandSystem.Register("InnGen", AccessLevel.Administrator, new CommandEventHandler(GenInnDoors_OnCommand));
- }
-
- [Usage("InnGen")]
- [Description("Generates Inn Doors from a configuration file.")]
- public static void GenInnDoors_OnCommand(CommandEventArgs e)
- {
- e.Mobile.SendMessage("Generating Inn Doors from config, please wait.");
-
- int count = new InnDoorCreator().CreateDoors();
-
- e.Mobile.SendMessage("Inn Door generation complete. {0} doors were generated.", count);
- }
-
- public class InnDoorCreator
- {
- private int m_Count;
- private static Queue m_Queue = new Queue();
-
- // Finds and deletes existing InnDoors at the target location to prevent stacking
- public static bool FindExistingDoor(Map map, Point3D p)
- {
- IPooledEnumerable eable = map.GetItemsInRange(p, 0);
-
- foreach (Item item in eable)
- {
- if (item is InnDoor)
- {
- int delta = item.Z - p.Z;
-
- if (delta >= -12 && delta <= 12) // Uses your existing Z-delta logic
- m_Queue.Enqueue(item);
- }
- }
-
- eable.Free();
-
- while (m_Queue.Count > 0)
- ((Item)m_Queue.Dequeue()).Delete();
-
- return false;
- }
-
- public void CreateDoor(Point3D loc, Map mapLoc, Point3D roomLoc, Map roomMap, int itemID)
- {
- if (!FindExistingDoor(mapLoc, loc))
- {
- m_Count++;
-
- // Uses our new parameterized constructor for custom facings!
- InnDoor door = new InnDoor(itemID);
-
- // Wire up the void room coordinates automatically
- door.RoomLocation = roomLoc;
- door.RoomMap = roomMap;
-
- door.MoveToWorld(loc, mapLoc);
- }
- }
-
- public int CreateDoors()
- {
- // Reads from the same Decoration directory[cite: 3]
- string filePath = Path.Combine(Core.BaseDirectory, "Data", "Decoration", "inndoors.cfg");
-
- if (!File.Exists(filePath))
- {
- Console.WriteLine("Warning: {0} not found. No Inn Doors generated.", filePath);
- return 0;
- }
-
- using (StreamReader ip = new StreamReader(filePath))
- {
- string line;
- while ((line = ip.ReadLine()) != null)
- {
- line = line.Trim();
-
- if (line.Length == 0 || line.StartsWith("#"))
- continue;
-
- string[] split = line.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
-
- // We need at least 8 arguments (Door Map/X/Y/Z and Room Map/X/Y/Z)
- if (split.Length >= 8)
- {
- try
- {
- Map mapLoc = Map.Parse(split[0]);
- int xLoc = int.Parse(split[1]);
- int yLoc = int.Parse(split[2]);
- int zLoc = int.Parse(split[3]);
-
- Map mapRoom = Map.Parse(split[4]);
- int xRoom = int.Parse(split[5]);
- int yRoom = int.Parse(split[6]);
- int zRoom = int.Parse(split[7]);
-
- // Optional 9th argument: ItemID for custom facings! Defaults to 0x6E5 (1765) if missing.
- int itemID = 1765;
- if (split.Length >= 9)
- {
- // Handles both hex (0x6F3) and integer (1779) formats
- if (split[8].StartsWith("0x", StringComparison.OrdinalIgnoreCase))
- itemID = Convert.ToInt32(split[8], 16);
- else
- itemID = int.Parse(split[8]);
- }
-
- if (mapLoc != null && mapRoom != null)
- {
- CreateDoor(new Point3D(xLoc, yLoc, zLoc), mapLoc, new Point3D(xRoom, yRoom, zRoom), mapRoom, itemID);
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine("Error parsing Inn Door config line: {0}\n{1}", line, ex.Message);
- }
- }
- }
- }
-
- return m_Count;
- }
- }
- }
-}
diff --git a/Scripts/Engines/InnDoor.cs b/Scripts/Engines/InnDoor.cs
deleted file mode 100644
index 5599e33..0000000
--- a/Scripts/Engines/InnDoor.cs
+++ /dev/null
@@ -1,352 +0,0 @@
-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;
-
- // Where is the void room located?
- private Point3D m_RoomLocation;
- private Map m_RoomMap;
-
- [CommandProperty(AccessLevel.GameMaster)]
- public bool ForceEviction
- { get { return false; } set { if (value == true) { EvictTenant(); } } }
-
- // Configuration
- private int m_RentCost = 5000; // 5,000 gold per week
- private TimeSpan m_RentDuration = TimeSpan.FromDays(7); // 1 week
-
- [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) // Standard wooden door graphic
- {
- 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); // I can't reach that.
- 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;
- }
-
- if (from.Backpack != null && from.Backpack.ConsumeTotal(typeof(Gold), m_RentCost))
- {
- // Rent the room!
- m_Renter = from;
- m_RentExpires = DateTime.UtcNow + m_RentDuration;
- Name = from.Name + "'s Inn Room";
-
- // Spawn the invisible house controller in the void room
- m_House = new InnRoomHouse(from);
- //m_House.MoveToWorld(RoomLocation, RoomMap);
- // BURY THE STAIRCASE! We spawn the house 20 Z-levels beneath the actual floor.
- 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 backpack to rent this room.");
- }
- }
- // SCENARIO B: The room is RENTED
- else
- {
- // Check if the rent has expired
- if (DateTime.UtcNow > m_RentExpires)
- {
- EvictTenant();
- from.SendMessage(33, "The rent expired and the room has been cleared. It is now vacant.");
- return;
- }
-
- // Check Access using your BaseHouse logic!
- 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)
- {
- // 1. Locate the player's bank box
- BankBox bank = m_Renter.BankBox;
-
- if (bank != null)
- {
- // 2. Spawn our heavy-duty moving crate
- InnEvictionCrate crate = new InnEvictionCrate();
-
- // 3. Pack up all Secured Containers
- // We must iterate backward (Count - 1 to 0) when removing things from lists!
- 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;
-
- // Drop the entire secured bag/chest into the crate
- crate.DropItem(secureContainer);
- }
- }
- m_House.Secures.Clear();
-
- // 4. Pack up all individual Locked Down Items
- 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;
-
- // Drop the loose item into the crate
- crate.DropItem(item);
- }
- }
- m_House.LockDowns.Clear();
-
- // 5. Deliver the crate to the bank!
- if (crate.Items.Count > 0)
- {
- // Using DropItem forces it into the bank, bypassing standard bank limits
- 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
- {
- // If they didn't have any items in the room, just delete the empty crate
- crate.Delete();
- }
- }
-
- // 6. Demolish the invisible house controller
- m_House.Delete();
- }
-
- // 7. Reset the door so a new player can rent it
- m_Renter = null;
- m_House = null;
- Name = "A Vacant Inn Room";
- }
-
- public override void GetProperties(ObjectPropertyList list)
- {
- base.GetProperties(list);
-
- if (m_Renter != null)
- {
- list.Add(1060658, "Rent Cost\tPaid"); // ~1_val~: ~2_val~
- list.Add(1060659, "Expires\t{0}", m_RentExpires.ToString("g")); // ~1_val~: ~2_val~
- }
- else
- {
- list.Add(1060658, "Rent Cost\t{0} Gold", m_RentCost); // ~1_val~: ~2_val~
- }
- }
-
- public override void Serialize(GenericWriter writer)
- {
- base.Serialize(writer);
- writer.Write((int)0); // version
-
- 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();
-
- m_Renter = reader.ReadMobile();
- m_House = reader.ReadItem() as InnRoomHouse;
- m_RentExpires = reader.ReadDateTime();
- m_RoomLocation = reader.ReadPoint3D();
- m_RoomMap = reader.ReadMap();
- }
- // --- 1. THE CONTEXT MENU MENU OVERRIDE ---
- public override void GetContextMenuEntries(Mobile from, List list)
- {
- base.GetContextMenuEntries(from, list);
-
- // Only the active renter gets the option to renew
- if (from.Alive && m_Renter == from)
- {
- // 1062400 is the UO Client Cliloc for "Renew Contract"
- list.Add(new RenewRentEntry(from, this));
- }
- }
-
- // --- 2. THE CONTEXT MENU ACTION LOGIC ---
- private class RenewRentEntry : ContextMenuEntry
- {
- private Mobile m_From;
- private InnDoor m_Door;
-
- // The '2' is the interaction range
- public RenewRentEntry(Mobile from, InnDoor door) : base(1034309, 2)
- {
- m_From = from;
- m_Door = door;
- }
-
- public override void OnClick()
- {
- if (m_Door.Deleted || m_Door.Renter != m_From)
- return;
-
- // Check their backpack for the gold
- if (m_From.Backpack != null && m_From.Backpack.ConsumeTotal(typeof(Gold), m_Door.m_RentCost))
- {
- // Add 7 days to their current expiration time
- m_Door.m_RentExpires += m_Door.m_RentDuration;
-
- m_From.PlaySound(0x249); // Coin jingle sound
- m_From.SendMessage(68, $"You have paid {m_Door.m_RentCost} gold to extend your rent.");
- m_From.SendMessage(68, $"Your room is now paid until: {m_Door.m_RentExpires.ToString("g")}");
- m_Door.InvalidateProperties(); // Updates the door's mouse-over text
- }
- else
- {
- m_From.SendMessage(33, $"You need {m_Door.m_RentCost} gold in your backpack to renew this room.");
- }
- }
- }
-
- // --- 3. BONUS: DRAG AND DROP RENEWAL ---
- public override bool OnDragDrop(Mobile from, Item dropped)
- {
- // If the renter drops gold directly onto the door...
- if (from == m_Renter && dropped is Gold)
- {
- if (dropped.Amount == m_RentCost)
- {
- dropped.Delete(); // Consume the gold
- 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; // Rejects the drop and bounces the gold back to their cursor
- }
- }
-
- 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; // Bright red
- }
-
- public InnEvictionCrate(Serial serial) : base(serial) { }
-
- // 1. This prevents players from putting anything INTO the crate
- 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); // "You cannot place items into a house moving crate."
-
- return false;
- }
-
- return base.CheckHold(m, item, message, checkItems, plusItems, plusWeight);
- }
-
- // 2. This makes the crate automatically vanish once the player empties it
- 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/InnRoomHouse.cs b/Scripts/Engines/InnRoomHouse.cs
deleted file mode 100644
index cd91c9a..0000000
--- a/Scripts/Engines/InnRoomHouse.cs
+++ /dev/null
@@ -1,79 +0,0 @@
-using System;
-using Server;
-using Server.Multis;
-
-namespace Server.Custom.InnRooms
-{
- public class InnRoomHouse : BaseHouse
- {
- public override Rectangle2D[] Area
- {
- get { return new Rectangle2D[] { new Rectangle2D(-5, -5, 10, 10) }; }
- }
-
- public override Point3D BaseBanLocation
- {
- get { return new Point3D(this.X, this.Y - 5, this.Z + 20); }
- }
-
- public InnRoomHouse(Mobile owner) : base(0x1DF3, owner, 50, 2)
- {
- RestrictDecay = true;
- }
-
- public InnRoomHouse(Serial serial) : base(serial)
- {
- }
-
- // --- THE LOCKDOWN FIX ---
- public override int GetAosMaxLockdowns()
- {
- return 50; // 50 floor items
- }
-
- public override int GetAosMaxSecures()
- {
- return 250; // 250 total items in the room
- }
- // ------------------------
-
- public override bool IsInside(Point3D p, int height)
- {
- if (Deleted)
- return false;
-
- int rx = p.X - this.X;
- int ry = p.Y - this.Y;
-
- bool inArea = false;
- foreach (Rectangle2D rect in Area)
- {
- if (rect.Contains(new Point2D(rx, ry)))
- {
- inArea = true;
- break;
- }
- }
-
- if (!inArea)
- return false;
-
- if (p.Z >= this.Z && (p.Z + height) <= this.Z + 40)
- return true;
-
- return false;
- }
-
- 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/InnRooms/InnExitDoor.cs b/Scripts/Engines/InnRooms/InnExitDoor.cs
new file mode 100644
index 0000000..4ae47b8
--- /dev/null
+++ b/Scripts/Engines/InnRooms/InnExitDoor.cs
@@ -0,0 +1,128 @@
+using System;
+using System.Collections.Generic;
+using Server;
+using Server.Mobiles;
+using Server.Multis;
+using Server.ContextMenus;
+
+namespace Server.Custom.InnRooms
+{
+ public class InnExitDoor : Item
+ {
+ [Constructable]
+ public InnExitDoor() : this(0x6E5)
+ {
+ }
+
+ [Constructable]
+ public InnExitDoor(int itemID) : base(itemID)
+ {
+ Movable = false;
+ Name = "Room Exit";
+ }
+
+ public InnExitDoor(Serial serial) : base(serial)
+ {
+ }
+
+ [CommandProperty(AccessLevel.GameMaster)]
+ public bool ForceEviction
+ {
+ get { return false; }
+ set
+ {
+ if (value)
+ {
+ foreach (Item item in World.Items.Values)
+ {
+ if (item is InnRoomHouse)
+ {
+ InnRoomHouse house = (InnRoomHouse)item;
+ if (house.Map == this.Map && house.IsInside(this.Location, 16))
+ {
+ house.Evict();
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public override void OnDoubleClick(Mobile from)
+ {
+ if (!from.InRange(GetWorldLocation(), 2))
+ {
+ from.LocalOverheadMessage(Server.Network.MessageType.Regular, 0x3B2, 1019045);
+ return;
+ }
+
+ if (from is PlayerMobile)
+ {
+ PlayerMobile pm = (PlayerMobile)from;
+
+ if (pm.LastInnMap != null && pm.LastInnMap != Map.Internal && pm.LastInnLocation != Point3D.Zero)
+ {
+ pm.PlaySound(0x1EA);
+ pm.MoveToWorld(pm.LastInnLocation, pm.LastInnMap);
+ pm.SendMessage(68, "You step out of your room and back into the inn.");
+
+ pm.LastInnLocation = Point3D.Zero;
+ pm.LastInnMap = null;
+ }
+ else
+ {
+ from.SendMessage(33, "Your return location was lost. Please use a runebook or page a GameMaster.");
+ }
+ }
+ }
+
+ public override void GetContextMenuEntries(Mobile from, List list)
+ {
+ base.GetContextMenuEntries(from, list);
+
+ if (from.Alive && from is PlayerMobile)
+ {
+ List houses = BaseHouse.GetHouses(from);
+ foreach (BaseHouse h in houses)
+ {
+ if (h is InnRoomHouse && h.IsInside(this.Location, 16))
+ {
+ // Cliloc 6171 displays as "Open House Menu"
+ list.Add(new ManageRoomEntry(from, (InnRoomHouse)h));
+ break;
+ }
+ }
+ }
+ }
+
+ private class ManageRoomEntry : ContextMenuEntry
+ {
+ private Mobile m_From;
+ private InnRoomHouse m_House;
+
+ public ManageRoomEntry(Mobile from, InnRoomHouse house) : base(1063490, 2)
+ {
+ m_From = from;
+ m_House = house;
+ }
+
+ public override void OnClick()
+ {
+ m_From.SendGump(new InnRoomManagementGump((PlayerMobile)m_From, m_House));
+ }
+ }
+
+ 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/InnRooms/InnRentalGump.cs b/Scripts/Engines/InnRooms/InnRentalGump.cs
new file mode 100644
index 0000000..632d56d
--- /dev/null
+++ b/Scripts/Engines/InnRooms/InnRentalGump.cs
@@ -0,0 +1,115 @@
+using System;
+using Server;
+using Server.Gumps;
+using Server.Mobiles;
+using Server.Items;
+using Server.Network;
+
+namespace Server.Custom.InnRooms
+{
+ public class InnRentalGump : Gump
+ {
+ private PlayerMobile m_Player;
+
+ public InnRentalGump(PlayerMobile pm) : base(150, 150)
+ {
+ m_Player = pm;
+
+ AddPage(0);
+
+ // Widened the background from 400 to 500
+ AddBackground(0, 0, 500, 300, 9270);
+
+ // Centered the header to match the new 500 width
+ AddHtml(0, 15, 500, 20, "Inn Room Rentals", false, false);
+
+ DrawRoomOption(1, "Small Room", InnRoomSize.Small, 60);
+ DrawRoomOption(2, "Medium Room", InnRoomSize.Medium, 120);
+ DrawRoomOption(3, "Large Room", InnRoomSize.Large, 180);
+ }
+
+ private void DrawRoomOption(int buttonId, string title, InnRoomSize size, int yPos)
+ {
+ int available = InnRoomManager.GetAvailableCount(size);
+ int cost = InnRoomManager.GetCost(size);
+
+ int total = 0;
+ foreach (RoomDefinition def in InnRoomManager.Rooms)
+ {
+ if (def.Size == size)
+ total++;
+ }
+
+ // Pushed the text to X: 70 to clear the button
+ AddHtml(70, yPos, 150, 20, $"{title}", false, false);
+ AddHtml(70, yPos + 20, 200, 20, $"Cost: {cost} Gold", false, false);
+
+ // Pushed the availability count to X: 320 to sit nicely on the right side
+ AddHtml(320, yPos + 10, 150, 20, $"{available} / {total} Available", false, false);
+
+ if (available > 0)
+ {
+ // Kept the button firmly at X: 25
+ AddButton(25, yPos + 5, 4005, 4007, buttonId, GumpButtonType.Reply, 0);
+ }
+ else
+ {
+ AddHtml(25, yPos + 5, 20, 20, "X", false, false);
+ }
+ }
+
+ public override void OnResponse(NetState sender, RelayInfo info)
+ {
+ if (info.ButtonID == 0 || m_Player == null) return;
+
+ InnRoomSize selectedSize = InnRoomSize.Small;
+ if (info.ButtonID == 2) selectedSize = InnRoomSize.Medium;
+ if (info.ButtonID == 3) selectedSize = InnRoomSize.Large;
+
+ int slot = InnRoomManager.FindFirstOpenSlot(selectedSize);
+
+ if (slot == -1)
+ {
+ m_Player.SendMessage(33, "Those rooms are currently sold out.");
+ return;
+ }
+
+ int cost = InnRoomManager.GetCost(selectedSize);
+
+ if (Banker.Withdraw(m_Player, cost))
+ {
+ RentRoom(selectedSize, slot);
+ }
+ else if (m_Player.Backpack != null && m_Player.Backpack.ConsumeTotal(typeof(Gold), cost))
+ {
+ RentRoom(selectedSize, slot);
+ }
+ else
+ {
+ m_Player.SendMessage(33, $"You need {cost} gold in your account or backpack to rent that room.");
+ }
+ }
+
+ private void RentRoom(InnRoomSize size, int slot)
+ {
+ m_Player.LastInnLocation = m_Player.Location;
+ m_Player.LastInnMap = m_Player.Map;
+
+ RoomDefinition roomDef = InnRoomManager.Rooms[slot];
+ Point3D voidRoomLoc = roomDef.Location;
+ Map voidRoomMap = roomDef.RoomMap;
+
+ InnRoomHouse newHouse = new InnRoomHouse(m_Player);
+ newHouse.RoomTier = size;
+ newHouse.RoomIndex = slot;
+ newHouse.RentExpires = DateTime.UtcNow + TimeSpan.FromDays(7);
+
+ newHouse.MoveToWorld(new Point3D(voidRoomLoc.X, voidRoomLoc.Y, voidRoomLoc.Z - 20), voidRoomMap);
+
+ m_Player.PlaySound(0x249);
+ m_Player.SendMessage(68, $"You have rented a {size} room.");
+
+ m_Player.MoveToWorld(voidRoomLoc, voidRoomMap);
+ }
+ }
+}
diff --git a/Scripts/Engines/InnRooms/InnRoomHouse.cs b/Scripts/Engines/InnRooms/InnRoomHouse.cs
new file mode 100644
index 0000000..cd76e4f
--- /dev/null
+++ b/Scripts/Engines/InnRooms/InnRoomHouse.cs
@@ -0,0 +1,287 @@
+using System;
+using System.Collections.Generic;
+using Server;
+using Server.Items;
+using Server.Mobiles;
+using Server.Multis;
+
+namespace Server.Custom.InnRooms
+{
+ public class InnRoomHouse : BaseHouse
+ {
+ public int RoomIndex;
+ private InnRoomSize m_RoomTier;
+
+ [CommandProperty(AccessLevel.GameMaster)]
+ public InnRoomSize RoomTier
+ {
+ get { return m_RoomTier; }
+ set
+ {
+ m_RoomTier = value;
+ MaxLockDowns = GetAosMaxLockdowns();
+ MaxSecures = GetAosMaxSecures();
+ }
+ }
+
+ // --- NEW EVICTION VARIABLES ---
+ public Mobile Renter;
+ public DateTime RentExpires;
+ private RentTimer m_Timer;
+
+ public override Rectangle2D[] Area
+ {
+ get { return new Rectangle2D[] { new Rectangle2D(-15, -15, 30, 30) }; }
+ }
+
+ public override Point3D BaseBanLocation
+ {
+ get { return new Point3D(this.X, this.Y - 5, this.Z + 20); }
+ }
+
+ public InnRoomHouse(Mobile owner) : base(0xA28, owner, 50, 2)
+ {
+ RestrictDecay = true;
+ Renter = owner;
+
+ // Start the internal clock!
+ m_Timer = new RentTimer(this);
+ m_Timer.Start();
+ }
+
+ public InnRoomHouse(Serial serial) : base(serial)
+ {
+ }
+
+ public override int GetAosMaxLockdowns()
+ {
+ switch (RoomTier)
+ {
+ case InnRoomSize.Small: return 350; // 125 Total Items
+ case InnRoomSize.Medium: return 375; // 250 Total Items
+ case InnRoomSize.Large: return 500; // 500 Total Items
+ default: return 125;
+ }
+ }
+
+ public override int GetAosMaxSecures()
+ {
+ switch (RoomTier)
+ {
+ case InnRoomSize.Small: return 2; // 2 Secure Chests
+ case InnRoomSize.Medium: return 3; // 3 Secure Chests
+ case InnRoomSize.Large: return 4; // 4 Secure Chests
+ default: return 2;
+ }
+ }
+
+ public override bool IsInside(Point3D p, int height)
+ {
+ if (Deleted) return false;
+
+ int rx = p.X - this.X;
+ int ry = p.Y - this.Y;
+
+ bool inArea = false;
+ foreach (Rectangle2D rect in Area)
+ {
+ if (rect.Contains(new Point2D(rx, ry)))
+ {
+ inArea = true;
+ break;
+ }
+ }
+
+ if (!inArea) return false;
+ if (p.Z >= this.Z && (p.Z + height) <= this.Z + 40) return true;
+
+ return false;
+ }
+
+ // --- EVICTION LOGIC ---
+ public void Evict()
+ {
+ if (Deleted) return;
+
+ // 1. Pack up items and send to bank
+ if (Renter != null && Renter.BankBox != null)
+ {
+ InnEvictionCrate crate = new InnEvictionCrate();
+
+ for (int i = Secures.Count - 1; i >= 0; --i)
+ {
+ SecureInfo info = Secures[i] as SecureInfo;
+ if (info != null && info.Item != null)
+ {
+ Container sec = info.Item;
+ sec.IsSecure = false;
+ sec.IsLockedDown = false;
+ sec.Movable = true;
+ crate.DropItem(sec);
+ }
+ }
+ Secures.Clear();
+
+ for (int i = LockDowns.Count - 1; i >= 0; --i)
+ {
+ Item item = LockDowns[i] as Item;
+ if (item != null)
+ {
+ item.IsLockedDown = false;
+ item.Movable = true;
+ crate.DropItem(item);
+ }
+ }
+ LockDowns.Clear();
+
+ if (crate.Items.Count > 0)
+ {
+ Renter.BankBox.DropItem(crate);
+ Renter.SendMessage(33, "Your inn room rent has expired. Your belongings were moved to your bank.");
+ }
+ else
+ {
+ crate.Delete();
+ }
+ }
+
+ // 2. Kick anyone currently inside the room back to town
+ List insideMobiles = GetMobiles();
+ foreach (Mobile m in insideMobiles)
+ {
+ if (m is PlayerMobile)
+ {
+ PlayerMobile pm = (PlayerMobile)m;
+ if (pm.LastInnMap != null && pm.LastInnLocation != Point3D.Zero)
+ {
+ pm.MoveToWorld(pm.LastInnLocation, pm.LastInnMap);
+ pm.LastInnLocation = Point3D.Zero;
+ pm.LastInnMap = null;
+ }
+ else
+ {
+ // Safe fallback if they don't have an anchor
+ pm.MoveToWorld(new Point3D(865, 605, 0), Map.Trammel); // Britain Bank
+ }
+ pm.SendMessage(33, "The rent has expired and you have been evicted from the room.");
+ }
+ }
+
+ // 3. Destroy the house (Freeing the slot for the Manager!)
+ Delete();
+ }
+
+ public override void OnDelete()
+ {
+ if (m_Timer != null)
+ m_Timer.Stop();
+
+ base.OnDelete();
+ }
+
+ public override void Serialize(GenericWriter writer)
+ {
+ base.Serialize(writer);
+ writer.Write((int)2); // Version bumped to 2!
+
+ writer.Write(Renter);
+ writer.Write(RentExpires);
+
+ writer.Write((int)RoomTier);
+ writer.Write(RoomIndex);
+ }
+
+ public override void Deserialize(GenericReader reader)
+ {
+ base.Deserialize(reader);
+ int version = reader.ReadInt();
+
+ switch (version)
+ {
+ case 2:
+ {
+ Renter = reader.ReadMobile();
+ RentExpires = reader.ReadDateTime();
+ goto case 1;
+ }
+ case 1:
+ {
+ RoomTier = (InnRoomSize)reader.ReadInt();
+ RoomIndex = reader.ReadInt();
+ goto case 0;
+ }
+ case 0:
+ {
+ break;
+ }
+ }
+
+ // Restart the internal clock on server boot
+ m_Timer = new RentTimer(this);
+ m_Timer.Start();
+ }
+
+ // --- INTERNAL CLOCK ---
+ private class RentTimer : Timer
+ {
+ private InnRoomHouse m_House;
+
+ // Checks the expiration date every 1 minute
+ public RentTimer(InnRoomHouse house) : base(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.0))
+ {
+ m_House = house;
+ Priority = TimerPriority.FiveSeconds;
+ }
+
+ protected override void OnTick()
+ {
+ if (m_House == null || m_House.Deleted)
+ {
+ Stop();
+ return;
+ }
+
+ if (DateTime.UtcNow > m_House.RentExpires)
+ {
+ m_House.Evict();
+ Stop();
+ }
+ }
+ }
+ }
+
+ // --- READ-ONLY EVICTION CRATE ---
+ 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/InnRooms/InnRoomManagementGump.cs b/Scripts/Engines/InnRooms/InnRoomManagementGump.cs
new file mode 100644
index 0000000..fadf6c5
--- /dev/null
+++ b/Scripts/Engines/InnRooms/InnRoomManagementGump.cs
@@ -0,0 +1,82 @@
+using System;
+using Server;
+using Server.Gumps;
+using Server.Mobiles;
+using Server.Network;
+using Server.Multis;
+
+namespace Server.Custom.InnRooms
+{
+ public class InnRoomManagementGump : Gump
+ {
+ private PlayerMobile m_Player;
+ private InnRoomHouse m_House;
+
+ public InnRoomManagementGump(PlayerMobile pm, InnRoomHouse house) : base(150, 150)
+ {
+ m_Player = pm;
+ m_House = house;
+
+ AddPage(0);
+ AddBackground(0, 0, 400, 350, 9270);
+ AddHtml(0, 15, 400, 20, "Room Management", false, false);
+
+ // --- STORAGE STATS ---
+ AddHtml(40, 60, 150, 20, "Lockdowns:", false, false);
+ AddHtml(150, 60, 200, 20, $"{m_House.LockDownCount} / {m_House.MaxLockDowns}", false, false);
+
+ AddHtml(40, 90, 150, 20, "Secures:", false, false);
+ AddHtml(150, 90, 200, 20, $"{m_House.SecureCount} / {m_House.MaxSecures}", false, false);
+
+ // --- RENT INFO ---
+ AddHtml(40, 140, 150, 20, "Rent Expires:", false, false);
+ AddHtml(150, 140, 200, 20, $"{m_House.RentExpires.ToString("g")}", false, false);
+
+ // --- ACTIONS ---
+ // Auto-Renew Status Placeholder (We will wire this up next!)
+ AddHtml(40, 190, 250, 20, "Auto-Renew: Disabled", false, false);
+
+ // Cancel Button
+ AddHtml(75, 280, 250, 20, "Cancel Rental Contract", false, false);
+ AddButton(40, 280, 4005, 4007, 1, GumpButtonType.Reply, 0);
+ }
+
+ public override void OnResponse(NetState sender, RelayInfo info)
+ {
+ if (info.ButtonID == 1 && m_House != null && !m_House.Deleted)
+ {
+ m_Player.SendGump(new CancelRentGump(m_House));
+ }
+ }
+ }
+
+ // --- CANCELLATION CONFIRMATION GUMP ---
+ public class CancelRentGump : Gump
+ {
+ private InnRoomHouse m_House;
+
+ public CancelRentGump(InnRoomHouse house) : base(200, 200)
+ {
+ m_House = house;
+
+ 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_House != null && !m_House.Deleted)
+ {
+ m_House.Evict();
+ }
+ }
+ }
+}
diff --git a/Scripts/Engines/InnRooms/InnRoomManager.cs b/Scripts/Engines/InnRooms/InnRoomManager.cs
new file mode 100644
index 0000000..008fb47
--- /dev/null
+++ b/Scripts/Engines/InnRooms/InnRoomManager.cs
@@ -0,0 +1,133 @@
+using System;
+using System.IO;
+using System.Collections.Generic;
+using Server;
+using Server.Items;
+using Server.Custom.InnRooms;
+
+namespace Server.Custom.InnRooms
+{
+ public enum InnRoomSize { Small, Medium, Large }
+
+ // This object holds the coordinates for a single room from your config file
+ public class RoomDefinition
+ {
+ public InnRoomSize Size;
+ public Map RoomMap;
+ public Point3D Location;
+ }
+
+ public class InnRoomManager
+ {
+ // The master list of all rooms loaded from the config file
+ public static List Rooms = new List();
+
+ // This runs automatically when the server starts
+ public static void Initialize()
+ {
+ LoadRooms();
+ }
+
+ public static int GetCost(InnRoomSize size)
+ {
+ switch (size)
+ {
+ case InnRoomSize.Small: return 5000;
+ case InnRoomSize.Medium: return 10000;
+ case InnRoomSize.Large: return 20000;
+ default: return 5000;
+ }
+ }
+
+ public static void LoadRooms()
+ {
+ Rooms.Clear();
+ string filePath = Path.Combine(Core.BaseDirectory, "Data", "Config", "innrooms.cfg");
+
+ if (!File.Exists(filePath))
+ {
+ Console.WriteLine("InnRoomManager: No innrooms.cfg found!");
+ return;
+ }
+
+ using (StreamReader ip = new StreamReader(filePath))
+ {
+ string line;
+ while ((line = ip.ReadLine()) != null)
+ {
+ line = line.Trim();
+ if (line.Length == 0 || line.StartsWith("#")) continue;
+
+ string[] split = line.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
+
+ // Expected Format: Size Map X Y Z
+ // Example: Small Felucca 6000 6000 0
+ if (split.Length >= 5)
+ {
+ try
+ {
+ RoomDefinition def = new RoomDefinition();
+ def.Size = (InnRoomSize)Enum.Parse(typeof(InnRoomSize), split[0], true);
+ def.RoomMap = Map.Parse(split[1]);
+ def.Location = new Point3D(int.Parse(split[2]), int.Parse(split[3]), int.Parse(split[4]));
+
+ Rooms.Add(def);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"InnRoomManager Error parsing line: {line}\n{ex.Message}");
+ }
+ }
+ }
+ }
+ Console.WriteLine($"InnRoomManager: Loaded {Rooms.Count} total rooms from config.");
+ }
+
+ // Checks all active rentals on the server to see which list indexes are currently taken
+ public static List GetOccupiedIndexes()
+ {
+ List occupied = new List();
+
+ foreach (Item item in World.Items.Values)
+ {
+ if (item is InnRoomHouse)
+ {
+ InnRoomHouse house = (InnRoomHouse)item;
+ occupied.Add(house.RoomIndex);
+ }
+ }
+ return occupied;
+ }
+
+ // Counts how many rooms of a specific size are NOT currently rented
+ public static int GetAvailableCount(InnRoomSize size)
+ {
+ List occupied = GetOccupiedIndexes();
+ int count = 0;
+
+ for (int i = 0; i < Rooms.Count; i++)
+ {
+ if (Rooms[i].Size == size && !occupied.Contains(i))
+ {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ // Finds the exact list index of the first available room of the requested size
+ public static int FindFirstOpenSlot(InnRoomSize size)
+ {
+ List occupied = GetOccupiedIndexes();
+
+ for (int i = 0; i < Rooms.Count; i++)
+ {
+ if (Rooms[i].Size == size && !occupied.Contains(i))
+ {
+ return i; // Returns the exact index in the Rooms list
+ }
+ }
+ return -1; // -1 means sold out
+ }
+ }
+}
diff --git a/Scripts/Mobiles/PlayerMobile.cs b/Scripts/Mobiles/PlayerMobile.cs
index d37da4f..4e73159 100644
--- a/Scripts/Mobiles/PlayerMobile.cs
+++ b/Scripts/Mobiles/PlayerMobile.cs
@@ -201,6 +201,11 @@ namespace Server.Mobiles
private List m_AllFollowers;
private List m_RecentlyReported;
+ // --- Start Inn Room
+ private Point3D m_LastInnLocation;
+ private Map m_LastInnMap;
+ // -- End Inn Room
+
#region Getters & Setters
public List RecentlyReported
@@ -473,6 +478,22 @@ namespace Server.Mobiles
get{ return GetFlag( PlayerFlag.RefuseTrades ); }
set{ SetFlag( PlayerFlag.RefuseTrades, value ); }
}
+
+ // -- Start Inn Room
+ [CommandProperty(AccessLevel.GameMaster)]
+ public Point3D LastInnLocation
+ {
+ get { return m_LastInnLocation; }
+ set { m_LastInnLocation = value; }
+ }
+
+ [CommandProperty(AccessLevel.GameMaster)]
+ public Map LastInnMap
+ {
+ get { return m_LastInnMap; }
+ set { m_LastInnMap = value; }
+ }
+ // -- End Inn Room
#endregion
#region Auto Arrow Recovery
@@ -3358,6 +3379,12 @@ namespace Server.Mobiles
switch ( version )
{
+ case 30:
+ {
+ m_LastInnLocation = reader.ReadPoint3D();
+ m_LastInnMap = reader.ReadMap();
+ goto case 29;
+ }
case 29:
{
if (reader.ReadBool())
@@ -3667,7 +3694,12 @@ namespace Server.Mobiles
base.Serialize( writer );
- writer.Write( (int) 29 ); // version
+ writer.Write( (int) 30 ); // version
+
+ // -- Start Inn Room
+ writer.Write( m_LastInnLocation );
+ writer.Write( m_LastInnMap );
+ // -- End Inn Room
if (m_StuckMenuUses != null)
{
@@ -5175,4 +5207,4 @@ namespace Server.Mobiles
m_AutoStabled.Clear();
}
}
-}
\ No newline at end of file
+}
diff --git a/Scripts/Regions/InnRegion.cs b/Scripts/Regions/InnRegion.cs
new file mode 100644
index 0000000..93fbc34
--- /dev/null
+++ b/Scripts/Regions/InnRegion.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Xml; // Required for loading from Regions.xml
+using System.Collections.Generic;
+using Server;
+using Server.Mobiles;
+using Server.Regions;
+using Server.Multis;
+
+namespace Server.Custom.InnRooms
+{
+ public class InnRegion : BaseRegion
+ {
+ // Constructor 1: Used if manually generated in a script
+ public InnRegion(string name, Map map, int priority, params Rectangle2D[] area)
+ : base(name, map, priority, area)
+ {
+ Register();
+ }
+
+ // Constructor 2: Used when loading from Regions.xml (This fixes your crash!)
+ public InnRegion(XmlElement xml, Map map, Region parent)
+ : base(xml, map, parent)
+ {
+ // Note: We don't call Register() here because the XML loader handles it automatically!
+ }
+
+ public override void OnSpeech(SpeechEventArgs e)
+ {
+ base.OnSpeech(e);
+
+ if (!e.Handled && e.Mobile is PlayerMobile && e.Mobile.Alive)
+ {
+ PlayerMobile pm = (PlayerMobile)e.Mobile;
+
+ if (e.Speech.ToLower().Contains("inn room"))
+ {
+ e.Handled = true;
+
+ InnRoomHouse rentedRoom = GetPlayerRoom(pm);
+
+ if (rentedRoom != null)
+ {
+ pm.LastInnLocation = pm.Location;
+ pm.LastInnMap = pm.Map;
+
+ Point3D roomLoc = new Point3D(rentedRoom.X, rentedRoom.Y, rentedRoom.Z + 20);
+
+ pm.PlaySound(0x1EA);
+ pm.MoveToWorld(roomLoc, rentedRoom.Map);
+ pm.SendMessage(68, "You are whisked away to your rented room.");
+ }
+ else
+ {
+ pm.CloseGump(typeof(InnRentalGump));
+ pm.SendGump(new InnRentalGump(pm));
+ }
+ }
+ }
+ }
+
+ private InnRoomHouse GetPlayerRoom(Mobile m)
+ {
+ List houses = BaseHouse.GetHouses(m);
+
+ foreach (BaseHouse house in houses)
+ {
+ if (house is InnRoomHouse && !house.Deleted)
+ {
+ return (InnRoomHouse)house;
+ }
+ }
+ return null;
+ }
+ }
+}