From 4bde9a4c863bf9f8e1d8a900007dbe9675f6f8b9 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sun, 17 Apr 2022 16:51:59 -0700 Subject: [PATCH] fix: Codegens vendor contracts (#1000) --- .../Items/Deeds/VendorRentalContract.cs | 572 +++++++-------- .../Server.Items.VendorRentalContract.v0.json | 30 + .../Server.Mobiles.RentedVendor.v0.json | 62 ++ .../UOContent/Mobiles/Vendors/RentedVendor.cs | 669 +++++++++--------- 4 files changed, 686 insertions(+), 647 deletions(-) create mode 100644 Projects/UOContent/Migrations/Server.Items.VendorRentalContract.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.RentedVendor.v0.json diff --git a/Projects/UOContent/Items/Deeds/VendorRentalContract.cs b/Projects/UOContent/Items/Deeds/VendorRentalContract.cs index 6c37f9f55..ea2dc6cfc 100644 --- a/Projects/UOContent/Items/Deeds/VendorRentalContract.cs +++ b/Projects/UOContent/Items/Deeds/VendorRentalContract.cs @@ -1,369 +1,347 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.ContextMenus; using Server.Gumps; using Server.Mobiles; using Server.Multis; using Server.Targeting; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0)] +public partial class VendorRentalContract : Item { - public class VendorRentalContract : Item + [SerializableField(0, getter: "private", setter: "private")] + private int _rentalDurationId; // TODO: Replace this with something more robust + + private VendorRentalDuration _duration; + + private Mobile _offeree; + private Timer _offerExpireTimer; + + [SerializableField(1)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private int _price; + + [SerializableField(2)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private bool _landlordRenew; + + [Constructible] + public VendorRentalContract() : base(0x14F0) { - private VendorRentalDuration m_Duration; + Weight = 1.0; + Hue = 0x672; - private Mobile m_Offeree; - private Timer m_OfferExpireTimer; + _duration = VendorRentalDuration.Instances[0]; + Price = 1500; + } - [Constructible] - public VendorRentalContract() : base(0x14F0) + public override int LabelNumber => 1062332; // a vendor rental contract + + public VendorRentalDuration Duration + { + get => _duration; + set { - Weight = 1.0; - Hue = 0x672; - - m_Duration = VendorRentalDuration.Instances[0]; - Price = 1500; - } - - public VendorRentalContract(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1062332; // a vendor rental contract - - public VendorRentalDuration Duration - { - get => m_Duration; - set + if (value != null) { - if (value != null) - { - m_Duration = value; - } + _duration = value; + _rentalDurationId = _duration.ID; + } + } + } + + public Mobile Offeree + { + get => _offeree; + set + { + if (_offerExpireTimer != null) + { + _offerExpireTimer.Stop(); + _offerExpireTimer = null; + } + + _offeree = value; + + if (value != null) + { + _offerExpireTimer = new OfferExpireTimer(this); + _offerExpireTimer.Start(); + } + + InvalidateProperties(); + } + } + + public override void GetProperties(ObjectPropertyList list) + { + base.GetProperties(list); + + if (Offeree != null) + { + list.Add(1062368, Offeree.Name); // Being Offered To ~1_NAME~ + } + } + + public bool IsLandlord(Mobile m) + { + if (IsLockedDown) + { + var house = BaseHouse.FindHouseAt(this); + + if (house != null && house.DecayType != DecayType.Condemned) + { + return house.IsOwner(m); } } - [CommandProperty(AccessLevel.GameMaster)] - public int Price { get; set; } + return false; + } - [CommandProperty(AccessLevel.GameMaster)] - public bool LandlordRenew { get; set; } - - public Mobile Offeree + public bool IsUsableBy(Mobile from, bool byLandlord, bool byBackpack, bool noOfferee, bool sendMessage) + { + if (Deleted || !from.CheckAlive(sendMessage)) { - get => m_Offeree; - set - { - if (m_OfferExpireTimer != null) - { - m_OfferExpireTimer.Stop(); - m_OfferExpireTimer = null; - } - - m_Offeree = value; - - if (value != null) - { - m_OfferExpireTimer = new OfferExpireTimer(this); - m_OfferExpireTimer.Start(); - } - - InvalidateProperties(); - } - } - - public override void GetProperties(ObjectPropertyList list) - { - base.GetProperties(list); - - if (Offeree != null) - { - list.Add(1062368, Offeree.Name); // Being Offered To ~1_NAME~ - } - } - - public bool IsLandlord(Mobile m) - { - if (IsLockedDown) - { - var house = BaseHouse.FindHouseAt(this); - - if (house != null && house.DecayType != DecayType.Condemned) - { - return house.IsOwner(m); - } - } - return false; } - public bool IsUsableBy(Mobile from, bool byLandlord, bool byBackpack, bool noOfferee, bool sendMessage) + if (noOfferee && Offeree != null) { - if (Deleted || !from.CheckAlive(sendMessage)) - { - return false; - } - - if (noOfferee && Offeree != null) - { - if (sendMessage) - { - from.SendLocalizedMessage(1062343); // That item is currently in use. - } - - return false; - } - - if (byBackpack && IsChildOf(from.Backpack)) - { - return true; - } - - if (byLandlord && IsLandlord(from)) - { - if (from.Map != Map || !from.InRange(this, 5)) - { - if (sendMessage) - { - from.SendLocalizedMessage(501853); // Target is too far away. - } - - return false; - } - - return true; - } - - return false; - } - - public override void OnDelete() - { - if (IsLockedDown) - { - var house = BaseHouse.FindHouseAt(this); - - house?.VendorRentalContracts.Remove(this); - } - } - - public override void OnDoubleClick(Mobile from) - { - if (Offeree != null) + if (sendMessage) { from.SendLocalizedMessage(1062343); // That item is currently in use. } - else if (!IsLockedDown) - { - if (!IsChildOf(from.Backpack)) - { - from.SendLocalizedMessage(1062334); // This item must be in your backpack to be used. - return; - } - var house = BaseHouse.FindHouseAt(from); + return false; + } - if (house?.IsOwner(from) != true) - { - from.SendLocalizedMessage( - 1062333 - ); // You must be standing inside of a house that you own to make use of this contract. - } - else if (!house.IsAosRules) - { - from.SendMessage("Rental contracts can only be placed in AOS-enabled houses."); - } - else if (!house.Public) - { - from.SendLocalizedMessage(1062335); // Rental contracts can only be placed in public houses. - } - else if (!house.CanPlaceNewVendor()) - { - from.SendLocalizedMessage(1062352); // You do not have enough storage available to place this contract. - } - else - { - from.SendLocalizedMessage(1062337); // Target the exact location you wish to rent out. - from.Target = new RentTarget(this); - } - } - else if (IsLandlord(from)) + if (byBackpack && IsChildOf(from.Backpack)) + { + return true; + } + + if (byLandlord && IsLandlord(from)) + { + if (from.Map != Map || !from.InRange(this, 5)) { - if (from.InRange(this, 5)) - { - from.CloseGump(); - from.SendGump(new VendorRentalContractGump(this, from)); - } - else + if (sendMessage) { from.SendLocalizedMessage(501853); // Target is too far away. } + + return false; } + + return true; } - public override void GetContextMenuEntries(Mobile from, List list) - { - base.GetContextMenuEntries(from, list); + return false; + } - if (IsUsableBy(from, true, true, true, false)) + public override void OnDelete() + { + if (IsLockedDown) + { + var house = BaseHouse.FindHouseAt(this); + + house?.VendorRentalContracts.Remove(this); + } + } + + public override void OnDoubleClick(Mobile from) + { + if (Offeree != null) + { + from.SendLocalizedMessage(1062343); // That item is currently in use. + } + else if (!IsLockedDown) + { + if (!IsChildOf(from.Backpack)) { - list.Add(new ContractOptionEntry(this)); + from.SendLocalizedMessage(1062334); // This item must be in your backpack to be used. + return; } - } - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); + var house = BaseHouse.FindHouseAt(from); - writer.WriteEncodedInt(0); // version - - writer.WriteEncodedInt(m_Duration.ID); - - writer.Write(Price); - writer.Write(LandlordRenew); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - - var durationID = reader.ReadEncodedInt(); - if (durationID < VendorRentalDuration.Instances.Length) + if (house?.IsOwner(from) != true) { - m_Duration = VendorRentalDuration.Instances[durationID]; + from.SendLocalizedMessage( + 1062333 + ); // You must be standing inside of a house that you own to make use of this contract. + } + else if (!house.IsAosRules) + { + from.SendMessage("Rental contracts can only be placed in AOS-enabled houses."); + } + else if (!house.Public) + { + from.SendLocalizedMessage(1062335); // Rental contracts can only be placed in public houses. + } + else if (!house.CanPlaceNewVendor()) + { + from.SendLocalizedMessage(1062352); // You do not have enough storage available to place this contract. } else { - m_Duration = VendorRentalDuration.Instances[0]; - } - - Price = reader.ReadInt(); - LandlordRenew = reader.ReadBool(); - } - - private class ContractOptionEntry : ContextMenuEntry - { - private readonly VendorRentalContract m_Contract; - - public ContractOptionEntry(VendorRentalContract contract) : base(6209) => m_Contract = contract; - - public override void OnClick() - { - var from = Owner.From; - - if (m_Contract.IsUsableBy(from, true, true, true, true)) - { - from.CloseGump(); - from.SendGump(new VendorRentalContractGump(m_Contract, from)); - } + from.SendLocalizedMessage(1062337); // Target the exact location you wish to rent out. + from.Target = new RentTarget(this); } } - - private class RentTarget : Target + else if (IsLandlord(from)) { - private readonly VendorRentalContract m_Contract; - - public RentTarget(VendorRentalContract contract) : base(-1, false, TargetFlags.None) => m_Contract = contract; - - protected override void OnTarget(Mobile from, object targeted) + if (from.InRange(this, 5)) { - if (!m_Contract.IsUsableBy(from, false, true, true, true)) - { - return; - } + from.CloseGump(); + from.SendGump(new VendorRentalContractGump(this, from)); + } + else + { + from.SendLocalizedMessage(501853); // Target is too far away. + } + } + } - if (targeted is not IPoint3D location) - { - return; - } + public override void GetContextMenuEntries(Mobile from, List list) + { + base.GetContextMenuEntries(from, list); - var pLocation = new Point3D(location); - var map = from.Map; + if (IsUsableBy(from, true, true, true, false)) + { + list.Add(new ContractOptionEntry(this)); + } + } - var house = BaseHouse.FindHouseAt(pLocation, map, 0); + [AfterDeserialization] + private void AfterDeserialization() + { + var index = Math.Clamp(_rentalDurationId, 0, VendorRentalDuration.Instances.Length); + _duration = VendorRentalDuration.Instances[index]; + } - if (house?.IsOwner(from) != true) + private class ContractOptionEntry : ContextMenuEntry + { + private readonly VendorRentalContract m_Contract; + + public ContractOptionEntry(VendorRentalContract contract) : base(6209) => m_Contract = contract; + + public override void OnClick() + { + var from = Owner.From; + + if (m_Contract.IsUsableBy(from, true, true, true, true)) + { + from.CloseGump(); + from.SendGump(new VendorRentalContractGump(m_Contract, from)); + } + } + } + + private class RentTarget : Target + { + private readonly VendorRentalContract m_Contract; + + public RentTarget(VendorRentalContract contract) : base(-1, false, TargetFlags.None) => m_Contract = contract; + + protected override void OnTarget(Mobile from, object targeted) + { + if (!m_Contract.IsUsableBy(from, false, true, true, true)) + { + return; + } + + if (targeted is not IPoint3D location) + { + return; + } + + var pLocation = new Point3D(location); + var map = from.Map; + + var house = BaseHouse.FindHouseAt(pLocation, map, 0); + + if (house?.IsOwner(from) != true) + { + from.SendLocalizedMessage(1062338); // The location being rented out must be inside of your house. + } + else if (BaseHouse.FindHouseAt(from) != house) + { + // You must be located inside of the house in which you are trying to place the contract. + from.SendLocalizedMessage(1062339); + } + else if (!house.IsAosRules) + { + from.SendMessage("Rental contracts can only be placed in AOS-enabled houses."); + } + else if (!house.Public) + { + from.SendLocalizedMessage(1062335); // Rental contracts can only be placed in public houses. + } + else if (house.DecayType == DecayType.Condemned) + { + from.SendLocalizedMessage(1062468); // You cannot place a contract in a condemned house. + } + else if (!house.CanPlaceNewVendor()) + { + from.SendLocalizedMessage(1062352); // You do not have enought storage available to place this contract. + } + else if (!map.CanFit(pLocation, 16, false, false)) + { + from.SendLocalizedMessage(1062486); // A vendor cannot exist at that location. Please try again. + } + else + { + BaseHouse.IsThereVendor(pLocation, map, out var vendor, out var contract); + + if (vendor) { - from.SendLocalizedMessage(1062338); // The location being rented out must be inside of your house. + // You may not place a rental contract at this location while other beings occupy it. + from.SendLocalizedMessage(1062342); } - else if (BaseHouse.FindHouseAt(from) != house) + else if (contract) { - // You must be located inside of the house in which you are trying to place the contract. - from.SendLocalizedMessage(1062339); - } - else if (!house.IsAosRules) - { - from.SendMessage("Rental contracts can only be placed in AOS-enabled houses."); - } - else if (!house.Public) - { - from.SendLocalizedMessage(1062335); // Rental contracts can only be placed in public houses. - } - else if (house.DecayType == DecayType.Condemned) - { - from.SendLocalizedMessage(1062468); // You cannot place a contract in a condemned house. - } - else if (!house.CanPlaceNewVendor()) - { - from.SendLocalizedMessage(1062352); // You do not have enought storage available to place this contract. - } - else if (!map.CanFit(pLocation, 16, false, false)) - { - from.SendLocalizedMessage(1062486); // A vendor cannot exist at that location. Please try again. + // That location is cluttered. Please clear out any objects there and try again. + from.SendLocalizedMessage(1062341); } else { - BaseHouse.IsThereVendor(pLocation, map, out var vendor, out var contract); + m_Contract.MoveToWorld(pLocation, map); - if (vendor) + if (!house.LockDown(from, m_Contract)) { - // You may not place a rental contract at this location while other beings occupy it. - from.SendLocalizedMessage(1062342); - } - else if (contract) - { - // That location is cluttered. Please clear out any objects there and try again. - from.SendLocalizedMessage(1062341); - } - else - { - m_Contract.MoveToWorld(pLocation, map); - - if (!house.LockDown(from, m_Contract)) - { - from.AddToBackpack(m_Contract); - } + from.AddToBackpack(m_Contract); } } } - - protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType) - { - from.SendLocalizedMessage(1062336); // You decide not to place the contract at this time. - } } - private class OfferExpireTimer : Timer + protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType) { - private readonly VendorRentalContract m_Contract; + from.SendLocalizedMessage(1062336); // You decide not to place the contract at this time. + } + } - public OfferExpireTimer(VendorRentalContract contract) : base(TimeSpan.FromSeconds(30.0)) + private class OfferExpireTimer : Timer + { + private readonly VendorRentalContract m_Contract; + + public OfferExpireTimer(VendorRentalContract contract) : base(TimeSpan.FromSeconds(30.0)) + { + m_Contract = contract; + } + + protected override void OnTick() + { + var offeree = m_Contract.Offeree; + + if (offeree != null) { - m_Contract = contract; - } + offeree.CloseGump(); - protected override void OnTick() - { - var offeree = m_Contract.Offeree; - - if (offeree != null) - { - offeree.CloseGump(); - - m_Contract.Offeree = null; - } + m_Contract.Offeree = null; } } } diff --git a/Projects/UOContent/Migrations/Server.Items.VendorRentalContract.v0.json b/Projects/UOContent/Migrations/Server.Items.VendorRentalContract.v0.json new file mode 100644 index 000000000..691547581 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.VendorRentalContract.v0.json @@ -0,0 +1,30 @@ +{ + "version": 0, + "type": "Server.Items.VendorRentalContract", + "properties": [ + { + "name": "RentalDurationId", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Price", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "LandlordRenew", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.RentedVendor.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.RentedVendor.v0.json new file mode 100644 index 000000000..54f9c360a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.RentedVendor.v0.json @@ -0,0 +1,62 @@ +{ + "version": 0, + "type": "Server.Mobiles.RentedVendor", + "properties": [ + { + "name": "RentalDurationId", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "RentalPrice", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "LandlordRenew", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "RenterRenew", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "RenewalPrice", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "RentalGold", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "RentalExpireTime", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "DeltaTime" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Mobiles/Vendors/RentedVendor.cs b/Projects/UOContent/Mobiles/Vendors/RentedVendor.cs index 10ede3f19..d2192f64e 100644 --- a/Projects/UOContent/Mobiles/Vendors/RentedVendor.cs +++ b/Projects/UOContent/Mobiles/Vendors/RentedVendor.cs @@ -1,390 +1,359 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.ContextMenus; using Server.Gumps; using Server.Misc; using Server.Multis; using Server.Prompts; -namespace Server.Mobiles -{ - public class VendorRentalDuration - { - public static readonly VendorRentalDuration[] Instances = - { - new(TimeSpan.FromDays(7.0), 1062361), // 1 Week - new(TimeSpan.FromDays(14.0), 1062362), // 2 Weeks - new(TimeSpan.FromDays(21.0), 1062363), // 3 Weeks - new(TimeSpan.FromDays(28.0), 1062364) // 1 Month - }; +namespace Server.Mobiles; - private VendorRentalDuration(TimeSpan duration, int name) +public class VendorRentalDuration +{ + public static readonly VendorRentalDuration[] Instances = + { + new(TimeSpan.FromDays(7.0), 1062361), // 1 Week + new(TimeSpan.FromDays(14.0), 1062362), // 2 Weeks + new(TimeSpan.FromDays(21.0), 1062363), // 3 Weeks + new(TimeSpan.FromDays(28.0), 1062364) // 1 Month + }; + + private VendorRentalDuration(TimeSpan duration, int name) + { + Duration = duration; + Name = name; + } + + public TimeSpan Duration { get; } + + public int Name { get; } + + public int ID + { + get { - Duration = duration; - Name = name; + for (var i = 0; i < Instances.Length; i++) + { + if (Instances[i] == this) + { + return i; + } + } + + return 0; + } + } +} + +[SerializationGenerator(0)] +public partial class RentedVendor : PlayerVendor +{ + private Timer _rentalExpireTimer; + + public RentedVendor( + Mobile owner, BaseHouse house, VendorRentalDuration duration, int rentalPrice, + bool landlordRenew, int rentalGold + ) : base(owner, house) + { + RentalDuration = duration; + RentalPrice = RenewalPrice = rentalPrice; + LandlordRenew = landlordRenew; + RenterRenew = false; + + RentalGold = rentalGold; + + RentalExpireTime = Core.Now + duration.Duration; + _rentalExpireTimer = new RentalExpireTimer(this, duration.Duration); + _rentalExpireTimer.Start(); + } + + public VendorRentalDuration RentalDuration { get; private set; } + + [SerializableField(0, getter: "private", setter: "private")] + private int _rentalDurationId; // TODO: Replace this with something more robust + + [SerializableField(1)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private int _rentalPrice; + + [SerializableField(2)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private bool _landlordRenew; + + [SerializableField(3)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private bool _renterRenew; + + [SerializableField(4)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private int _renewalPrice; + + [SerializableField(5)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private int _rentalGold; + + [DeltaDateTime] + [SerializableField(6)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private DateTime _rentalExpireTime; + + [CommandProperty(AccessLevel.GameMaster)] + public Mobile Landlord => House?.Owner; + + [CommandProperty(AccessLevel.GameMaster)] + public bool Renew => LandlordRenew && RenterRenew && House != null && House.DecayType != DecayType.Condemned; + + public override bool IsOwner(Mobile m) => m == Owner || m.AccessLevel >= AccessLevel.GameMaster || + Core.ML && AccountHandler.CheckAccount(m, Owner); + + public bool IsLandlord(Mobile m) => House?.IsOwner(m) == true; + + public void ComputeRentalExpireDelay(out int days, out int hours) + { + var delay = RentalExpireTime - Core.Now; + + if (delay <= TimeSpan.Zero) + { + days = 0; + hours = 0; + } + else + { + days = delay.Days; + hours = delay.Hours; + } + } + + public void SendRentalExpireMessage(Mobile to) + { + ComputeRentalExpireDelay(out var days, out var hours); + + to.SendLocalizedMessage( + 1062464, + $"{days}\t{hours}" + ); // The rental contract on this vendor will expire in ~1_DAY~ day(s) and ~2_HOUR~ hour(s). + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + _rentalExpireTimer.Stop(); + } + + public override void Destroy(bool toBackpack) + { + if (RentalGold > 0 && House?.IsAosRules == true) + { + House.MovingCrate ??= new MovingCrate(House); + + Banker.Deposit(House.MovingCrate, RentalGold); + RentalGold = 0; } - public TimeSpan Duration { get; } + base.Destroy(toBackpack); + } - public int Name { get; } - - public int ID + public override void GetContextMenuEntries(Mobile from, List list) + { + if (from.Alive) { - get + if (IsOwner(from)) { - for (var i = 0; i < Instances.Length; i++) + list.Add(new ContractOptionsEntry(this)); + } + else if (IsLandlord(from)) + { + if (RentalGold > 0) { - if (Instances[i] == this) - { - return i; - } + list.Add(new CollectRentEntry(this)); } - return 0; + list.Add(new TerminateContractEntry(this)); + list.Add(new ContractOptionsEntry(this)); + } + } + + base.GetContextMenuEntries(from, list); + } + + [AfterDeserialization] + private void AfterDeserialization() + { + var index = Math.Clamp(_rentalDurationId, 0, VendorRentalDuration.Instances.Length - 1); + RentalDuration = VendorRentalDuration.Instances[index]; + + var delay = _rentalExpireTime - Core.Now; + _rentalExpireTimer = new RentalExpireTimer(this, delay > TimeSpan.Zero ? delay : TimeSpan.Zero).Start(); + } + + private class ContractOptionsEntry : ContextMenuEntry + { + private readonly RentedVendor m_Vendor; + + public ContractOptionsEntry(RentedVendor vendor) : base(6209) => m_Vendor = vendor; + + public override void OnClick() + { + var from = Owner.From; + + if (m_Vendor.Deleted || !from.CheckAlive()) + { + return; + } + + if (m_Vendor.IsOwner(from)) + { + from.CloseGump(); + from.SendGump(new RenterVendorRentalGump(m_Vendor)); + + m_Vendor.SendRentalExpireMessage(from); + } + else if (m_Vendor.IsLandlord(from)) + { + from.CloseGump(); + from.SendGump(new LandlordVendorRentalGump(m_Vendor)); + + m_Vendor.SendRentalExpireMessage(from); } } } - public class RentedVendor : PlayerVendor + private class CollectRentEntry : ContextMenuEntry { - private Timer m_RentalExpireTimer; + private readonly RentedVendor m_Vendor; - public RentedVendor( - Mobile owner, BaseHouse house, VendorRentalDuration duration, int rentalPrice, - bool landlordRenew, int rentalGold - ) : base(owner, house) + public CollectRentEntry(RentedVendor vendor) : base(6212) => m_Vendor = vendor; + + public override void OnClick() { - RentalDuration = duration; - RentalPrice = RenewalPrice = rentalPrice; - LandlordRenew = landlordRenew; - RenterRenew = false; + var from = Owner.From; - RentalGold = rentalGold; - - RentalExpireTime = Core.Now + duration.Duration; - m_RentalExpireTimer = new RentalExpireTimer(this, duration.Duration); - m_RentalExpireTimer.Start(); - } - - public RentedVendor(Serial serial) : base(serial) - { - } - - public VendorRentalDuration RentalDuration { get; private set; } - - [CommandProperty(AccessLevel.GameMaster)] - public int RentalPrice { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public bool LandlordRenew { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public bool RenterRenew { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public bool Renew => LandlordRenew && RenterRenew && House != null && House.DecayType != DecayType.Condemned; - - [CommandProperty(AccessLevel.GameMaster)] - public int RenewalPrice { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public int RentalGold { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public DateTime RentalExpireTime { get; private set; } - - [CommandProperty(AccessLevel.GameMaster)] - public Mobile Landlord => House?.Owner; - - public override bool IsOwner(Mobile m) => m == Owner || m.AccessLevel >= AccessLevel.GameMaster || - Core.ML && AccountHandler.CheckAccount(m, Owner); - - public bool IsLandlord(Mobile m) => House?.IsOwner(m) == true; - - public void ComputeRentalExpireDelay(out int days, out int hours) - { - var delay = RentalExpireTime - Core.Now; - - if (delay <= TimeSpan.Zero) + if (m_Vendor.Deleted || !from.CheckAlive() || !m_Vendor.IsLandlord(from)) { - days = 0; - hours = 0; - } - else - { - days = delay.Days; - hours = delay.Hours; - } - } - - public void SendRentalExpireMessage(Mobile to) - { - ComputeRentalExpireDelay(out var days, out var hours); - - to.SendLocalizedMessage( - 1062464, - $"{days}\t{hours}" - ); // The rental contract on this vendor will expire in ~1_DAY~ day(s) and ~2_HOUR~ hour(s). - } - - public override void OnAfterDelete() - { - base.OnAfterDelete(); - - m_RentalExpireTimer.Stop(); - } - - public override void Destroy(bool toBackpack) - { - if (RentalGold > 0 && House?.IsAosRules == true) - { - House.MovingCrate ??= new MovingCrate(House); - - Banker.Deposit(House.MovingCrate, RentalGold); - RentalGold = 0; + return; } - base.Destroy(toBackpack); - } - - public override void GetContextMenuEntries(Mobile from, List list) - { - if (from.Alive) + if (m_Vendor.RentalGold > 0) { - if (IsOwner(from)) + var depositedGold = Banker.DepositUpTo(from, m_Vendor.RentalGold); + m_Vendor.RentalGold -= depositedGold; + + if (depositedGold > 0) { - list.Add(new ContractOptionsEntry(this)); - } - else if (IsLandlord(from)) - { - if (RentalGold > 0) - { - list.Add(new CollectRentEntry(this)); - } - - list.Add(new TerminateContractEntry(this)); - list.Add(new ContractOptionsEntry(this)); - } - } - - base.GetContextMenuEntries(from, list); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - - writer.WriteEncodedInt(RentalDuration.ID); - - writer.Write(RentalPrice); - writer.Write(LandlordRenew); - writer.Write(RenterRenew); - writer.Write(RenewalPrice); - - writer.Write(RentalGold); - - writer.WriteDeltaTime(RentalExpireTime); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - - var durationID = reader.ReadEncodedInt(); - if (durationID < VendorRentalDuration.Instances.Length) - { - RentalDuration = VendorRentalDuration.Instances[durationID]; - } - else - { - RentalDuration = VendorRentalDuration.Instances[0]; - } - - RentalPrice = reader.ReadInt(); - LandlordRenew = reader.ReadBool(); - RenterRenew = reader.ReadBool(); - RenewalPrice = reader.ReadInt(); - - RentalGold = reader.ReadInt(); - - RentalExpireTime = reader.ReadDeltaTime(); - - var delay = RentalExpireTime - Core.Now; - m_RentalExpireTimer = new RentalExpireTimer(this, delay > TimeSpan.Zero ? delay : TimeSpan.Zero); - m_RentalExpireTimer.Start(); - } - - private class ContractOptionsEntry : ContextMenuEntry - { - private readonly RentedVendor m_Vendor; - - public ContractOptionsEntry(RentedVendor vendor) : base(6209) => m_Vendor = vendor; - - public override void OnClick() - { - var from = Owner.From; - - if (m_Vendor.Deleted || !from.CheckAlive()) - { - return; - } - - if (m_Vendor.IsOwner(from)) - { - from.CloseGump(); - from.SendGump(new RenterVendorRentalGump(m_Vendor)); - - m_Vendor.SendRentalExpireMessage(from); - } - else if (m_Vendor.IsLandlord(from)) - { - from.CloseGump(); - from.SendGump(new LandlordVendorRentalGump(m_Vendor)); - - m_Vendor.SendRentalExpireMessage(from); - } - } - } - - private class CollectRentEntry : ContextMenuEntry - { - private readonly RentedVendor m_Vendor; - - public CollectRentEntry(RentedVendor vendor) : base(6212) => m_Vendor = vendor; - - public override void OnClick() - { - var from = Owner.From; - - if (m_Vendor.Deleted || !from.CheckAlive() || !m_Vendor.IsLandlord(from)) - { - return; + from.SendLocalizedMessage( + 1060397, + depositedGold.ToString() + ); // ~1_AMOUNT~ gold has been deposited into your bank box. } if (m_Vendor.RentalGold > 0) { - var depositedGold = Banker.DepositUpTo(from, m_Vendor.RentalGold); - m_Vendor.RentalGold -= depositedGold; - - if (depositedGold > 0) - { - from.SendLocalizedMessage( - 1060397, - depositedGold.ToString() - ); // ~1_AMOUNT~ gold has been deposited into your bank box. - } - - if (m_Vendor.RentalGold > 0) - { - from.SendLocalizedMessage(500390); // Your bank box is full. - } - } - } - } - - private class TerminateContractEntry : ContextMenuEntry - { - private readonly RentedVendor m_Vendor; - - public TerminateContractEntry(RentedVendor vendor) : base(6218) => m_Vendor = vendor; - - public override void OnClick() - { - var from = Owner.From; - - if (m_Vendor.Deleted || !from.CheckAlive() || !m_Vendor.IsLandlord(from)) - { - return; - } - - from.SendLocalizedMessage( - 1062503 - ); // Enter the amount of gold you wish to offer the renter in exchange for immediate termination of this contract? - from.Prompt = new RefundOfferPrompt(m_Vendor); - } - } - - private class RefundOfferPrompt : Prompt - { - private readonly RentedVendor m_Vendor; - - public RefundOfferPrompt(RentedVendor vendor) => m_Vendor = vendor; - - public override void OnResponse(Mobile from, string text) - { - if (!m_Vendor.CanInteractWith(from, false) || !m_Vendor.IsLandlord(from)) - { - return; - } - - text = text.Trim(); - - if (!int.TryParse(text, out var amount)) - { - amount = -1; - } - - var owner = m_Vendor.Owner; - if (owner == null) - { - return; - } - - if (amount < 0) - { - from.SendLocalizedMessage(1062506); // You did not enter a valid amount. Offer canceled. - } - else if (Banker.GetBalance(from) < amount) - { - from.SendLocalizedMessage(1062507); // You do not have that much money in your bank account. - } - else if (owner.Map != m_Vendor.Map || !owner.InRange(m_Vendor, 5)) - { - from.SendLocalizedMessage( - 1062505 - ); // The renter must be closer to the vendor in order for you to make this offer. - } - else - { - from.SendLocalizedMessage(1062504); // Please wait while the renter considers your offer. - - owner.CloseGump(); - owner.SendGump(new VendorRentalRefundGump(m_Vendor, from, amount)); - } - } - } - - private class RentalExpireTimer : Timer - { - private readonly RentedVendor m_Vendor; - - public RentalExpireTimer(RentedVendor vendor, TimeSpan delay) : base(delay, vendor.RentalDuration.Duration) - { - m_Vendor = vendor; - } - - protected override void OnTick() - { - var renewalPrice = m_Vendor.RenewalPrice; - - if (m_Vendor.Renew && m_Vendor.HoldGold >= renewalPrice) - { - m_Vendor.HoldGold -= renewalPrice; - m_Vendor.RentalGold += renewalPrice; - - m_Vendor.RentalPrice = renewalPrice; - - m_Vendor.RentalExpireTime = Core.Now + m_Vendor.RentalDuration.Duration; - } - else - { - m_Vendor.Destroy(false); + from.SendLocalizedMessage(500390); // Your bank box is full. } } } } + + private class TerminateContractEntry : ContextMenuEntry + { + private readonly RentedVendor m_Vendor; + + public TerminateContractEntry(RentedVendor vendor) : base(6218) => m_Vendor = vendor; + + public override void OnClick() + { + var from = Owner.From; + + if (m_Vendor.Deleted || !from.CheckAlive() || !m_Vendor.IsLandlord(from)) + { + return; + } + + from.SendLocalizedMessage( + 1062503 + ); // Enter the amount of gold you wish to offer the renter in exchange for immediate termination of this contract? + from.Prompt = new RefundOfferPrompt(m_Vendor); + } + } + + private class RefundOfferPrompt : Prompt + { + private readonly RentedVendor m_Vendor; + + public RefundOfferPrompt(RentedVendor vendor) => m_Vendor = vendor; + + public override void OnResponse(Mobile from, string text) + { + if (!m_Vendor.CanInteractWith(from, false) || !m_Vendor.IsLandlord(from)) + { + return; + } + + text = text.Trim(); + + if (!int.TryParse(text, out var amount)) + { + amount = -1; + } + + var owner = m_Vendor.Owner; + if (owner == null) + { + return; + } + + if (amount < 0) + { + from.SendLocalizedMessage(1062506); // You did not enter a valid amount. Offer canceled. + } + else if (Banker.GetBalance(from) < amount) + { + from.SendLocalizedMessage(1062507); // You do not have that much money in your bank account. + } + else if (owner.Map != m_Vendor.Map || !owner.InRange(m_Vendor, 5)) + { + from.SendLocalizedMessage( + 1062505 + ); // The renter must be closer to the vendor in order for you to make this offer. + } + else + { + from.SendLocalizedMessage(1062504); // Please wait while the renter considers your offer. + + owner.CloseGump(); + owner.SendGump(new VendorRentalRefundGump(m_Vendor, from, amount)); + } + } + } + + private class RentalExpireTimer : Timer + { + private readonly RentedVendor m_Vendor; + + public RentalExpireTimer(RentedVendor vendor, TimeSpan delay) : base(delay, vendor.RentalDuration.Duration) + { + m_Vendor = vendor; + } + + protected override void OnTick() + { + var renewalPrice = m_Vendor.RenewalPrice; + + if (m_Vendor.Renew && m_Vendor.HoldGold >= renewalPrice) + { + m_Vendor.HoldGold -= renewalPrice; + m_Vendor.RentalGold += renewalPrice; + + m_Vendor.RentalPrice = renewalPrice; + + m_Vendor.RentalExpireTime = Core.Now + m_Vendor.RentalDuration.Duration; + } + else + { + m_Vendor.Destroy(false); + } + } + } }