#W# Reworking Inn rooms.
This commit is contained in:
parent
7104f56ebf
commit
66eafd3451
15 changed files with 918 additions and 576 deletions
|
|
@ -27,7 +27,7 @@
|
||||||
<rect x="843" y="672" width="9" height="13" />
|
<rect x="843" y="672" width="9" height="13" />
|
||||||
<rect x="837" y="679" width="8" height="6" />
|
<rect x="837" y="679" width="8" height="6" />
|
||||||
</region>
|
</region>
|
||||||
<region type="BusinessRegion" priority="70" name="the Bastion Inn">
|
<region type="InnRegion" priority="70" name="the Bastion Inn">
|
||||||
<rect x="830" y="654" width="9" height="20" />
|
<rect x="830" y="654" width="9" height="20" />
|
||||||
<rect x="821" y="666" width="9" height="8" />
|
<rect x="821" y="666" width="9" height="8" />
|
||||||
</region>
|
</region>
|
||||||
|
|
@ -224,5 +224,8 @@
|
||||||
<rect x="4687" y="338" width="383" height="198" />
|
<rect x="4687" y="338" width="383" height="198" />
|
||||||
<rect x="5075" y="335" width="84" height="73" />
|
<rect x="5075" y="335" width="84" height="73" />
|
||||||
</region>
|
</region>
|
||||||
|
<region type="BaseRegion" priority="55" name="the Inn Rooms">
|
||||||
|
<rect x="7120" y="3980" width="47" height="115" />
|
||||||
|
</region>
|
||||||
</Facet>
|
</Facet>
|
||||||
</ServerRegions>
|
</ServerRegions>
|
||||||
|
|
|
||||||
3
Data/Config/innrooms.cfg
Normal file
3
Data/Config/innrooms.cfg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
Small Vaelen 7133 3992 0
|
||||||
|
Medium Vaelen 7135 4027 0
|
||||||
|
Large Vaelen 7137 4066 0
|
||||||
8
Data/Decoration/Towns/InnRoomDoors.cfg
Normal file
8
Data/Decoration/Towns/InnRoomDoors.cfg
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
InnExitDoor 1765
|
||||||
|
7138 4075 0
|
||||||
|
|
||||||
|
InnExitDoor 1765
|
||||||
|
7136 4034 0
|
||||||
|
|
||||||
|
InnExitDoor 1765
|
||||||
|
7134 3997 0
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
#Bastion Inn
|
|
||||||
Vaelen 835 666 0 Vaelen 7138 4066 0 1779
|
|
||||||
49
Scripts/Commands/CheckBalance.cs
Normal file
49
Scripts/Commands/CheckBalance.cs
Normal file
|
|
@ -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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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<ContextMenuEntry> 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(); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
128
Scripts/Engines/InnRooms/InnExitDoor.cs
Normal file
128
Scripts/Engines/InnRooms/InnExitDoor.cs
Normal file
|
|
@ -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<ContextMenuEntry> list)
|
||||||
|
{
|
||||||
|
base.GetContextMenuEntries(from, list);
|
||||||
|
|
||||||
|
if (from.Alive && from is PlayerMobile)
|
||||||
|
{
|
||||||
|
List<BaseHouse> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
115
Scripts/Engines/InnRooms/InnRentalGump.cs
Normal file
115
Scripts/Engines/InnRooms/InnRentalGump.cs
Normal file
|
|
@ -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, "<CENTER><BASEFONT COLOR=#FFFFFF>Inn Room Rentals</BASEFONT></CENTER>", 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, $"<BASEFONT COLOR=#FCCA03>{title}</BASEFONT>", false, false);
|
||||||
|
AddHtml(70, yPos + 20, 200, 20, $"<BASEFONT COLOR=#FFFFFF>Cost: {cost} Gold</BASEFONT>", false, false);
|
||||||
|
|
||||||
|
// Pushed the availability count to X: 320 to sit nicely on the right side
|
||||||
|
AddHtml(320, yPos + 10, 150, 20, $"<BASEFONT COLOR=#FFFFFF>{available} / {total} Available</BASEFONT>", 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, "<BASEFONT COLOR=#FF0000>X</BASEFONT>", 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
287
Scripts/Engines/InnRooms/InnRoomHouse.cs
Normal file
287
Scripts/Engines/InnRooms/InnRoomHouse.cs
Normal file
|
|
@ -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<Mobile> 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(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
82
Scripts/Engines/InnRooms/InnRoomManagementGump.cs
Normal file
82
Scripts/Engines/InnRooms/InnRoomManagementGump.cs
Normal file
|
|
@ -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, "<CENTER><BASEFONT COLOR=#FFFFFF>Room Management</BASEFONT></CENTER>", false, false);
|
||||||
|
|
||||||
|
// --- STORAGE STATS ---
|
||||||
|
AddHtml(40, 60, 150, 20, "<BASEFONT COLOR=#FCCA03>Lockdowns:</BASEFONT>", false, false);
|
||||||
|
AddHtml(150, 60, 200, 20, $"<BASEFONT COLOR=#FFFFFF>{m_House.LockDownCount} / {m_House.MaxLockDowns}</BASEFONT>", false, false);
|
||||||
|
|
||||||
|
AddHtml(40, 90, 150, 20, "<BASEFONT COLOR=#FCCA03>Secures:</BASEFONT>", false, false);
|
||||||
|
AddHtml(150, 90, 200, 20, $"<BASEFONT COLOR=#FFFFFF>{m_House.SecureCount} / {m_House.MaxSecures}</BASEFONT>", false, false);
|
||||||
|
|
||||||
|
// --- RENT INFO ---
|
||||||
|
AddHtml(40, 140, 150, 20, "<BASEFONT COLOR=#FCCA03>Rent Expires:</BASEFONT>", false, false);
|
||||||
|
AddHtml(150, 140, 200, 20, $"<BASEFONT COLOR=#FFFFFF>{m_House.RentExpires.ToString("g")}</BASEFONT>", false, false);
|
||||||
|
|
||||||
|
// --- ACTIONS ---
|
||||||
|
// Auto-Renew Status Placeholder (We will wire this up next!)
|
||||||
|
AddHtml(40, 190, 250, 20, "<BASEFONT COLOR=#777777>Auto-Renew: Disabled</BASEFONT>", false, false);
|
||||||
|
|
||||||
|
// Cancel Button
|
||||||
|
AddHtml(75, 280, 250, 20, "<BASEFONT COLOR=#FF0000>Cancel Rental Contract</BASEFONT>", 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, "<CENTER><BASEFONT COLOR=#FFFFFF>Cancel Room Rental?</BASEFONT></CENTER>", false, false);
|
||||||
|
AddHtml(20, 50, 260, 40, "<BASEFONT COLOR=#FFFFFF>This will instantly pack your items into your bank and evict you.</BASEFONT>", false, false);
|
||||||
|
|
||||||
|
AddButton(40, 100, 4005, 4007, 1, GumpButtonType.Reply, 0);
|
||||||
|
AddHtml(75, 100, 100, 20, "<BASEFONT COLOR=#FFFFFF>Confirm</BASEFONT>", false, false);
|
||||||
|
|
||||||
|
AddButton(160, 100, 4005, 4007, 0, GumpButtonType.Reply, 0);
|
||||||
|
AddHtml(195, 100, 100, 20, "<BASEFONT COLOR=#FFFFFF>Cancel</BASEFONT>", false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnResponse(NetState sender, RelayInfo info)
|
||||||
|
{
|
||||||
|
if (info.ButtonID == 1 && m_House != null && !m_House.Deleted)
|
||||||
|
{
|
||||||
|
m_House.Evict();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
133
Scripts/Engines/InnRooms/InnRoomManager.cs
Normal file
133
Scripts/Engines/InnRooms/InnRoomManager.cs
Normal file
|
|
@ -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<RoomDefinition> Rooms = new List<RoomDefinition>();
|
||||||
|
|
||||||
|
// 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<int> GetOccupiedIndexes()
|
||||||
|
{
|
||||||
|
List<int> occupied = new List<int>();
|
||||||
|
|
||||||
|
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<int> 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<int> 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -201,6 +201,11 @@ namespace Server.Mobiles
|
||||||
private List<Mobile> m_AllFollowers;
|
private List<Mobile> m_AllFollowers;
|
||||||
private List<Mobile> m_RecentlyReported;
|
private List<Mobile> m_RecentlyReported;
|
||||||
|
|
||||||
|
// --- Start Inn Room
|
||||||
|
private Point3D m_LastInnLocation;
|
||||||
|
private Map m_LastInnMap;
|
||||||
|
// -- End Inn Room
|
||||||
|
|
||||||
#region Getters & Setters
|
#region Getters & Setters
|
||||||
|
|
||||||
public List<Mobile> RecentlyReported
|
public List<Mobile> RecentlyReported
|
||||||
|
|
@ -473,6 +478,22 @@ namespace Server.Mobiles
|
||||||
get{ return GetFlag( PlayerFlag.RefuseTrades ); }
|
get{ return GetFlag( PlayerFlag.RefuseTrades ); }
|
||||||
set{ SetFlag( PlayerFlag.RefuseTrades, value ); }
|
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
|
#endregion
|
||||||
|
|
||||||
#region Auto Arrow Recovery
|
#region Auto Arrow Recovery
|
||||||
|
|
@ -3358,6 +3379,12 @@ namespace Server.Mobiles
|
||||||
|
|
||||||
switch ( version )
|
switch ( version )
|
||||||
{
|
{
|
||||||
|
case 30:
|
||||||
|
{
|
||||||
|
m_LastInnLocation = reader.ReadPoint3D();
|
||||||
|
m_LastInnMap = reader.ReadMap();
|
||||||
|
goto case 29;
|
||||||
|
}
|
||||||
case 29:
|
case 29:
|
||||||
{
|
{
|
||||||
if (reader.ReadBool())
|
if (reader.ReadBool())
|
||||||
|
|
@ -3667,7 +3694,12 @@ namespace Server.Mobiles
|
||||||
|
|
||||||
base.Serialize( writer );
|
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)
|
if (m_StuckMenuUses != null)
|
||||||
{
|
{
|
||||||
|
|
@ -5175,4 +5207,4 @@ namespace Server.Mobiles
|
||||||
m_AutoStabled.Clear();
|
m_AutoStabled.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
75
Scripts/Regions/InnRegion.cs
Normal file
75
Scripts/Regions/InnRegion.cs
Normal file
|
|
@ -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<BaseHouse> houses = BaseHouse.GetHouses(m);
|
||||||
|
|
||||||
|
foreach (BaseHouse house in houses)
|
||||||
|
{
|
||||||
|
if (house is InnRoomHouse && !house.Deleted)
|
||||||
|
{
|
||||||
|
return (InnRoomHouse)house;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue