fix(core): Converts some packets & misc fixes/cleanup (#414)

- [X] Adds a stop music packet
- [X] Converts chat packets
- [X] Breaks out chat code a little bit more
- [X] Converts character statue animation packet
- [X] Renames some folders to have spaces
- [X] Organizes and consolidates incoming/outgoing packets for other content
- [X] Fixes virtual checks
This commit is contained in:
Kamron Batman 2021-01-16 21:01:54 -08:00 • committed by GitHub
parent a146955f6c
commit fbafd7210d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 814 additions and 662 deletions

View file

@ -0,0 +1,715 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Server.Accounting;
using Server.ContextMenus;
using Server.Engines.VeteranRewards;
using Server.Gumps;
using Server.Items;
using Server.Multis;
using Server.Network;
using Server.Spells;
using Server.Targeting;
namespace Server.Mobiles
{
public enum StatueType
{
Marble,
Jade,
Bronze
}
public enum StatuePose
{
Ready,
Casting,
Salute,
AllPraiseMe,
Fighting,
HandsOnHips
}
public enum StatueMaterial
{
Antique,
Dark,
Medium,
Light
}
public class CharacterStatue : Mobile, IRewardItem
{
private int m_Animation;
private int m_Frames;
private StatueMaterial m_Material;
private StatuePose m_Pose;
private Mobile m_SculptedBy;
private StatueType m_Type;
public CharacterStatue(Mobile from, StatueType type)
{
m_Type = type;
m_Pose = StatuePose.Ready;
m_Material = StatueMaterial.Antique;
Direction = Direction.South;
AccessLevel = AccessLevel.Counselor;
Hits = HitsMax;
Blessed = true;
Frozen = true;
CloneBody(from);
CloneClothes(from);
InvalidateHues();
}
public CharacterStatue(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public StatueType StatueType
{
get => m_Type;
set
{
m_Type = value;
InvalidateHues();
InvalidatePose();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public StatuePose Pose
{
get => m_Pose;
set
{
m_Pose = value;
InvalidatePose();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public StatueMaterial Material
{
get => m_Material;
set
{
m_Material = value;
InvalidateHues();
InvalidatePose();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public Mobile SculptedBy
{
get => m_SculptedBy;
set
{
m_SculptedBy = value;
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public DateTime SculptedOn { get; set; }
public CharacterStatuePlinth Plinth { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public bool IsRewardItem { get; set; }
public override void OnDoubleClick(Mobile from)
{
DisplayPaperdollTo(from);
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
if (m_SculptedBy != null)
{
if (m_SculptedBy.ShowFameTitle && (m_SculptedBy.Player || m_SculptedBy.Body.IsHuman) &&
m_SculptedBy.Fame >= 10000)
{
list.Add(
1076202,
$"{(m_SculptedBy.Female ? "Lady" : "Lord")} {m_SculptedBy.Name}"
); // Sculpted by ~1_Name~
}
else
{
list.Add(1076202, m_SculptedBy.Name); // Sculpted by ~1_Name~
}
}
}
public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list)
{
base.GetContextMenuEntries(from, list);
if (from.Alive && m_SculptedBy != null)
{
var house = BaseHouse.FindHouseAt(this);
if (house?.IsCoOwner(from) == true || from.AccessLevel > AccessLevel.Counselor)
{
list.Add(new DemolishEntry(this));
}
}
}
public override void OnAfterDelete()
{
base.OnAfterDelete();
if (Plinth?.Deleted == false)
{
Plinth.Delete();
}
}
protected override void OnMapChange(Map oldMap)
{
InvalidatePose();
if (Plinth != null)
{
Plinth.Map = Map;
}
}
protected override void OnLocationChange(Point3D oldLocation)
{
InvalidatePose();
if (Plinth != null)
{
Plinth.Location = new Point3D(X, Y, Z - 5);
}
}
public override bool CanBeRenamedBy(Mobile from) => false;
public override bool CanBeDamaged() => false;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void OnRequestedAnimation(Mobile from)
{
from.NetState.SendStatueAnimation(Serial, 1, m_Animation, m_Frames);
}
public override void OnAosSingleClick(Mobile from)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
writer.Write((int)m_Type);
writer.Write((int)m_Pose);
writer.Write((int)m_Material);
writer.Write(m_SculptedBy);
writer.Write(SculptedOn);
writer.Write(Plinth);
writer.Write(IsRewardItem);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadEncodedInt();
m_Type = (StatueType)reader.ReadInt();
m_Pose = (StatuePose)reader.ReadInt();
m_Material = (StatueMaterial)reader.ReadInt();
m_SculptedBy = reader.ReadEntity<Mobile>();
SculptedOn = reader.ReadDateTime();
Plinth = reader.ReadEntity<CharacterStatuePlinth>();
IsRewardItem = reader.ReadBool();
InvalidatePose();
Frozen = true;
if (m_SculptedBy == null || Map == Map.Internal) // Remove preview statues
{
Timer.DelayCall(Delete);
}
}
public void Sculpt(Mobile by)
{
m_SculptedBy = by;
SculptedOn = DateTime.UtcNow;
InvalidateProperties();
}
public bool Demolish(Mobile by)
{
var deed = new CharacterStatueDeed(null);
if (by.PlaceInBackpack(deed))
{
Delete();
deed.Statue = this;
deed.StatueType = m_Type;
deed.IsRewardItem = IsRewardItem;
Plinth?.Delete();
return true;
}
by.SendLocalizedMessage(500720); // You don't have enough room in your backpack!
deed.Delete();
return false;
}
public void Restore(CharacterStatue from)
{
m_Material = from.Material;
m_Pose = from.Pose;
Direction = from.Direction;
CloneBody(from);
CloneClothes(from);
InvalidateHues();
InvalidatePose();
}
public void CloneBody(Mobile from)
{
Name = from.Name;
BodyValue = from.BodyValue;
Female = from.Female;
HairItemID = from.HairItemID;
FacialHairItemID = from.FacialHairItemID;
}
public void CloneClothes(Mobile from)
{
for (var i = Items.Count - 1; i >= 0; i--)
{
Items[i].Delete();
}
for (var i = from.Items.Count - 1; i >= 0; i--)
{
var item = from.Items[i];
if (item.Layer != Layer.Backpack && item.Layer != Layer.Mount && item.Layer != Layer.Bank)
{
AddItem(CloneItem(item));
}
}
}
public Item CloneItem(Item item)
{
var cloned = new Item(item.ItemID)
{
Layer = item.Layer,
Name = item.Name,
Hue = item.Hue,
Weight = item.Weight,
Movable = false
};
return cloned;
}
public void InvalidateHues()
{
Hue = 0xB8F + (int)m_Type * 4 + (int)m_Material;
HairHue = Hue;
if (FacialHairItemID > 0)
{
FacialHairHue = Hue;
}
for (var i = Items.Count - 1; i >= 0; i--)
{
Items[i].Hue = Hue;
}
Plinth?.InvalidateHue();
}
public void InvalidatePose()
{
switch (m_Pose)
{
case StatuePose.Ready:
m_Animation = 4;
m_Frames = 0;
break;
case StatuePose.Casting:
m_Animation = 16;
m_Frames = 2;
break;
case StatuePose.Salute:
m_Animation = 33;
m_Frames = 1;
break;
case StatuePose.AllPraiseMe:
m_Animation = 17;
m_Frames = 4;
break;
case StatuePose.Fighting:
m_Animation = 31;
m_Frames = 5;
break;
case StatuePose.HandsOnHips:
m_Animation = 6;
m_Frames = 1;
break;
}
if (Map == null)
{
return;
}
ProcessDelta();
var eable = Map.GetClientsInRange(Location);
Span<byte> animPacket = stackalloc byte[CharacterStatuePackets.StatueAnimationPacketLength].InitializePacket();
foreach (var state in eable)
{
state.Mobile.ProcessDelta();
CharacterStatuePackets.CreateStatueAnimation(animPacket, Serial, 1, m_Animation, m_Frames);
state.Send(animPacket);
}
eable.Free();
}
private class DemolishEntry : ContextMenuEntry
{
private readonly CharacterStatue m_Statue;
public DemolishEntry(CharacterStatue statue) : base(6275, 2) => m_Statue = statue;
public override void OnClick()
{
if (!m_Statue.Deleted)
{
m_Statue.Demolish(Owner.From);
}
}
}
}
public class CharacterStatueDeed : Item, IRewardItem
{
private bool m_IsRewardItem;
private StatueType m_Type;
public CharacterStatueDeed(CharacterStatue statue) : base(0x14F0)
{
Statue = statue;
if (statue != null)
{
m_Type = statue.StatueType;
m_IsRewardItem = statue.IsRewardItem;
}
LootType = LootType.Blessed;
Weight = 1.0;
}
public CharacterStatueDeed(Serial serial) : base(serial)
{
}
public override int LabelNumber
{
get
{
var t = m_Type;
if (Statue != null)
{
t = Statue.StatueType;
}
return t switch
{
StatueType.Marble => 1076189,
StatueType.Jade => 1076188,
StatueType.Bronze => 1076190,
_ => 1076173
};
}
}
[CommandProperty(AccessLevel.GameMaster)]
public CharacterStatue Statue { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public StatueType StatueType
{
get
{
if (Statue != null)
{
return Statue.StatueType;
}
return m_Type;
}
set => m_Type = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public bool IsRewardItem
{
get => m_IsRewardItem;
set
{
m_IsRewardItem = value;
InvalidateProperties();
}
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
if (m_IsRewardItem)
{
list.Add(1076222); // 6th Year Veteran Reward
}
if (Statue != null)
{
list.Add(1076231, Statue.Name); // Statue of ~1_Name~
}
}
public override void OnDoubleClick(Mobile from)
{
if (from.Account is Account acct && from.AccessLevel == AccessLevel.Player)
{
var time = TimeSpan.FromDays(RewardSystem.RewardInterval.TotalDays * 6) -
(DateTime.UtcNow - acct.Created);
if (time > TimeSpan.Zero)
{
from.SendLocalizedMessage(
1008126,
true,
Math.Ceiling(time.TotalDays / RewardSystem.RewardInterval.TotalDays)
.ToString()
); // Your account is not old enough to use this item. Months until you can use this item :
return;
}
}
if (IsChildOf(from.Backpack))
{
if (!from.IsBodyMod)
{
from.SendLocalizedMessage(1076194); // Select a place where you would like to put your statue.
from.Target = new CharacterStatueTarget(this, StatueType);
}
else
{
from.SendLocalizedMessage(1073648); // You may only proceed while in your original state...
}
}
else
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
}
}
public override void OnDelete()
{
base.OnDelete();
Statue?.Delete();
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(1); // version
writer.Write((int)m_Type);
writer.Write(Statue);
writer.Write(m_IsRewardItem);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadEncodedInt();
if (version >= 1)
{
m_Type = (StatueType)reader.ReadInt();
}
Statue = reader.ReadEntity<CharacterStatue>();
m_IsRewardItem = reader.ReadBool();
}
}
public class CharacterStatueTarget : Target
{
private readonly Item m_Maker;
private readonly StatueType m_Type;
public CharacterStatueTarget(Item maker, StatueType type) : base(-1, true, TargetFlags.None)
{
m_Maker = maker;
m_Type = type;
}
protected override void OnTarget(Mobile from, object targeted)
{
var p = targeted as IPoint3D;
var map = from.Map;
if (p == null || map == null || m_Maker?.Deleted != false)
{
return;
}
if (!m_Maker.IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
return;
}
SpellHelper.GetSurfaceTop(ref p);
BaseHouse house = null;
var loc = new Point3D(p);
if (targeted is Item item && !item.IsLockedDown && !item.IsSecure && !(item is AddonComponent))
{
from.SendLocalizedMessage(1076191); // Statues can only be placed in houses.
return;
}
if (from.IsBodyMod)
{
from.SendLocalizedMessage(1073648); // You may only proceed while in your original state...
return;
}
var result = CouldFit(loc, map, from, ref house);
if (result == AddonFitResult.Valid)
{
var statue = new CharacterStatue(from, m_Type);
var plinth = new CharacterStatuePlinth(statue);
house.Addons.Add(plinth);
if (m_Maker is IRewardItem rewardItem)
{
statue.IsRewardItem = rewardItem.IsRewardItem;
}
statue.Plinth = plinth;
plinth.MoveToWorld(loc, map);
statue.InvalidatePose();
/*
* TODO: Previously the maker wasn't deleted until after statue
* customization, leading to redeeding issues. Exact OSI behavior
* needs looking into.
*/
m_Maker.Delete();
statue.Sculpt(from);
from.CloseGump<CharacterStatueGump>();
from.SendGump(new CharacterStatueGump(m_Maker, statue, from));
return;
}
if (result == AddonFitResult.Blocked)
{
from.SendLocalizedMessage(500269); // You cannot build that there.
return;
}
if (result == AddonFitResult.NotInHouse)
{
// Statues can only be placed in houses where you are the owner or co-owner.
from.SendLocalizedMessage(1076192);
return;
}
if (result == AddonFitResult.DoorTooClose)
{
from.SendLocalizedMessage(500271); // You cannot build near the door.
}
}
public static AddonFitResult CouldFit(Point3D p, Map map, Mobile from, ref BaseHouse house)
{
if (!map.CanFit(p.X, p.Y, p.Z, 20, true))
{
return AddonFitResult.Blocked;
}
if (!BaseAddon.CheckHouse(from, p, map, 20, ref house))
{
return AddonFitResult.NotInHouse;
}
return CheckDoors(p, 20, house);
}
public static AddonFitResult CheckDoors(Point3D p, int height, BaseHouse house)
{
var doors = house.Doors;
for (var i = 0; i < doors.Count; i++)
{
var door = doors[i];
var doorLoc = door.GetWorldLocation();
var doorHeight = door.ItemData.CalcHeight;
if (Utility.InRange(doorLoc, p, 1) &&
(p.Z == doorLoc.Z || p.Z + height > doorLoc.Z && doorLoc.Z + doorHeight > p.Z))
{
return AddonFitResult.DoorTooClose;
}
}
return AddonFitResult.Valid;
}
}
}

View file

@ -0,0 +1,187 @@
using Server.Engines.VeteranRewards;
using Server.Mobiles;
namespace Server.Items
{
public class CharacterStatueMaker : Item, IRewardItem
{
private bool m_IsRewardItem;
private StatueType m_Type;
public CharacterStatueMaker(StatueType type) : base(0x32F0)
{
m_Type = type;
InvalidateHue();
LootType = LootType.Blessed;
Weight = 5.0;
}
public CharacterStatueMaker(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1076173; // Character Statue Maker
[CommandProperty(AccessLevel.GameMaster)]
public StatueType StatueType
{
get => m_Type;
set
{
m_Type = value;
InvalidateHue();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public bool IsRewardItem
{
get => m_IsRewardItem;
set
{
m_IsRewardItem = value;
InvalidateProperties();
}
}
public override void OnDoubleClick(Mobile from)
{
if (m_IsRewardItem && !RewardSystem.CheckIsUsableBy(from, this, new object[] { m_Type }))
{
return;
}
if (IsChildOf(from.Backpack))
{
if (!from.IsBodyMod)
{
from.SendLocalizedMessage(1076194); // Select a place where you would like to put your statue.
from.Target = new CharacterStatueTarget(this, m_Type);
}
else
{
from.SendLocalizedMessage(1073648); // You may only proceed while in your original state...
}
}
else
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
}
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
if (m_IsRewardItem)
{
list.Add(1076222); // 6th Year Veteran Reward
}
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
writer.Write(m_IsRewardItem);
writer.Write((int)m_Type);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadEncodedInt();
m_IsRewardItem = reader.ReadBool();
m_Type = (StatueType)reader.ReadInt();
}
public void InvalidateHue()
{
Hue = 0xB8F + (int)m_Type * 4;
}
}
public class MarbleStatueMaker : CharacterStatueMaker
{
[Constructible]
public MarbleStatueMaker() : base(StatueType.Marble)
{
}
public MarbleStatueMaker(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadEncodedInt();
}
}
public class JadeStatueMaker : CharacterStatueMaker
{
[Constructible]
public JadeStatueMaker() : base(StatueType.Jade)
{
}
public JadeStatueMaker(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadEncodedInt();
}
}
public class BronzeStatueMaker : CharacterStatueMaker
{
[Constructible]
public BronzeStatueMaker() : base(StatueType.Bronze)
{
}
public BronzeStatueMaker(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadEncodedInt();
}
}
}

View file

@ -0,0 +1,60 @@
/*************************************************************************
* ModernUO *
* Copyright (C) 2019-2021 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: CharacterStatuePackets.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Buffers;
using Server.Network;
namespace Server.Engines.VeteranRewards
{
public static class CharacterStatuePackets
{
public const int StatueAnimationPacketLength = 17;
public static void CreateStatueAnimation(Span<byte> buffer, Serial serial, int status, int anim, int frame)
{
if (buffer[0] != 0)
{
return;
}
var writer = new SpanWriter(buffer);
writer.Write((byte)0xBF); // Packet ID
writer.Write((ushort)17);
writer.Write((short)0x19);
writer.Write((byte)0x5);
writer.Write(serial);
writer.Write((byte)0);
writer.Write((byte)0xFF);
writer.Write((byte)status);
writer.Write((byte)0);
writer.Write((byte)anim);
writer.Write((byte)0);
writer.Write((byte)frame);
}
public static void SendStatueAnimation(this NetState ns, Serial serial, int status, int anim, int frame)
{
if (ns == null)
{
return;
}
Span<byte> buffer = stackalloc byte[StatueAnimationPacketLength].InitializePacket();
CreateStatueAnimation(buffer, serial, status, anim, frame);
ns.Send(buffer);
}
}
}

View file

@ -0,0 +1,144 @@
using Server.Gumps;
using Server.Mobiles;
using Server.Multis;
namespace Server.Items
{
public class CharacterStatuePlinth : Static, IAddon
{
private CharacterStatue m_Statue;
public CharacterStatuePlinth(CharacterStatue statue) : base(0x32F2)
{
m_Statue = statue;
InvalidateHue();
}
public CharacterStatuePlinth(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1076201; // Character Statue
public Item Deed => new CharacterStatueDeed(m_Statue);
public virtual bool CouldFit(IPoint3D p, Map map)
{
var point = new Point3D(p.X, p.Y, p.Z);
if (map?.CanFit(point, 20) != true)
{
return false;
}
var house = BaseHouse.FindHouseAt(point, map, 20);
if (house == null)
{
return false;
}
var result = CharacterStatueTarget.CheckDoors(point, 20, house);
if (result == AddonFitResult.Valid)
{
return true;
}
return false;
}
public override void OnAfterDelete()
{
base.OnAfterDelete();
if (m_Statue?.Deleted == false)
{
m_Statue.Delete();
}
}
public override void OnMapChange()
{
if (m_Statue != null)
{
m_Statue.Map = Map;
}
}
public override void OnLocationChange(Point3D oldLocation)
{
if (m_Statue != null)
{
m_Statue.Location = new Point3D(X, Y, Z + 5);
}
}
public override void OnDoubleClick(Mobile from)
{
if (m_Statue != null)
{
from.SendGump(new CharacterPlinthGump(m_Statue));
}
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
writer.Write(m_Statue);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadEncodedInt();
m_Statue = reader.ReadEntity<CharacterStatue>();
if (m_Statue?.SculptedBy == null || Map == Map.Internal)
{
Timer.DelayCall(Delete);
}
}
public void InvalidateHue()
{
if (m_Statue != null)
{
Hue = 0xB8F + (int)m_Statue.StatueType * 4 + (int)m_Statue.Material;
}
}
private class CharacterPlinthGump : Gump
{
public CharacterPlinthGump(CharacterStatue statue) : base(60, 30)
{
Closable = true;
Disposable = true;
Draggable = true;
Resizable = false;
AddPage(0);
AddImage(0, 0, 0x24F4);
AddHtml(55, 50, 150, 20, statue.Name);
AddHtml(55, 75, 150, 20, statue.SculptedOn.ToString());
AddHtmlLocalized(55, 100, 150, 20, GetTypeNumber(statue.StatueType), 0);
}
public int GetTypeNumber(StatueType type)
{
return type switch
{
StatueType.Marble => 1076181,
StatueType.Jade => 1076180,
StatueType.Bronze => 1076230,
_ => 1076181
};
}
}
}
}

View file

@ -0,0 +1,207 @@
using Server.Mobiles;
using Server.Network;
namespace Server.Gumps
{
public class CharacterStatueGump : Gump
{
private readonly Item m_Maker;
private readonly Mobile m_Owner;
private readonly CharacterStatue m_Statue;
public CharacterStatueGump(Item maker, CharacterStatue statue, Mobile owner) : base(60, 36)
{
m_Maker = maker;
m_Statue = statue;
m_Owner = owner;
if (m_Statue == null)
{
return;
}
Closable = true;
Disposable = true;
Draggable = true;
Resizable = false;
AddPage(0);
AddBackground(0, 0, 327, 324, 0x13BE);
AddImageTiled(10, 10, 307, 20, 0xA40);
AddImageTiled(10, 40, 307, 244, 0xA40);
AddImageTiled(10, 294, 307, 20, 0xA40);
AddAlphaRegion(10, 10, 307, 304);
AddHtmlLocalized(14, 12, 327, 20, 1076156, 0x7FFF); // Character Statue Maker
// pose
AddHtmlLocalized(133, 41, 120, 20, 1076168, 0x7FFF); // Choose Pose
AddHtmlLocalized(133, 61, 120, 20, 1076208 + (int)m_Statue.Pose, 0x77E);
AddButton(163, 81, 0xFA5, 0xFA7, (int)Buttons.PoseNext);
AddButton(133, 81, 0xFAE, 0xFB0, (int)Buttons.PosePrev);
// direction
AddHtmlLocalized(133, 126, 120, 20, 1076170, 0x7FFF); // Choose Direction
AddHtmlLocalized(133, 146, 120, 20, GetDirectionNumber(m_Statue.Direction), 0x77E);
AddButton(163, 167, 0xFA5, 0xFA7, (int)Buttons.DirNext);
AddButton(133, 167, 0xFAE, 0xFB0, (int)Buttons.DirPrev);
// material
AddHtmlLocalized(133, 211, 120, 20, 1076171, 0x7FFF); // Choose Material
AddHtmlLocalized(133, 231, 120, 20, GetMaterialNumber(m_Statue.StatueType, m_Statue.Material), 0x77E);
AddButton(163, 253, 0xFA5, 0xFA7, (int)Buttons.MatNext);
AddButton(133, 253, 0xFAE, 0xFB0, (int)Buttons.MatPrev);
// cancel
AddButton(10, 294, 0xFB1, 0xFB2, (int)Buttons.Close);
AddHtmlLocalized(45, 294, 80, 20, 1006045, 0x7FFF); // Cancel
// sculpt
AddButton(234, 294, 0xFB7, 0xFB9, (int)Buttons.Sculpt);
AddHtmlLocalized(269, 294, 80, 20, 1076174, 0x7FFF); // Sculpt
// restore
if (m_Maker is CharacterStatueDeed)
{
AddButton(107, 294, 0xFAB, 0xFAD, (int)Buttons.Restore);
AddHtmlLocalized(142, 294, 80, 20, 1076193, 0x7FFF); // Restore
}
}
private int GetMaterialNumber(StatueType type, StatueMaterial material)
{
switch (material)
{
case StatueMaterial.Antique:
return type switch
{
StatueType.Bronze => 1076187,
StatueType.Jade => 1076186,
StatueType.Marble => 1076182,
_ => 1076187
};
case StatueMaterial.Dark:
if (type == StatueType.Marble)
{
return 1076183;
}
return 1076182;
case StatueMaterial.Medium: return 1076184;
case StatueMaterial.Light: return 1076185;
default: return 1076187;
}
}
private int GetDirectionNumber(Direction direction)
{
return direction switch
{
Direction.North => 1075389,
Direction.Right => 1075388,
Direction.East => 1075387,
Direction.Down => 1076204,
Direction.South => 1075386,
Direction.Left => 1075391,
Direction.West => 1075390,
Direction.Up => 1076205,
_ => 1075386
};
}
public override void OnResponse(NetState state, RelayInfo info)
{
if (m_Statue?.Deleted != false)
{
return;
}
var sendGump = false;
if (info.ButtonID == (int)Buttons.Sculpt)
{
if (m_Maker is CharacterStatueDeed deed)
{
var backup = deed.Statue;
backup?.Delete();
}
m_Maker?.Delete();
m_Statue.Sculpt(state.Mobile);
}
else if (info.ButtonID == (int)Buttons.PosePrev)
{
m_Statue.Pose = (StatuePose)(((int)m_Statue.Pose + 5) % 6);
sendGump = true;
}
else if (info.ButtonID == (int)Buttons.PoseNext)
{
m_Statue.Pose = (StatuePose)(((int)m_Statue.Pose + 1) % 6);
sendGump = true;
}
else if (info.ButtonID == (int)Buttons.DirPrev)
{
m_Statue.Direction = (Direction)(((int)m_Statue.Direction + 7) % 8);
m_Statue.InvalidatePose();
sendGump = true;
}
else if (info.ButtonID == (int)Buttons.DirNext)
{
m_Statue.Direction = (Direction)(((int)m_Statue.Direction + 1) % 8);
m_Statue.InvalidatePose();
sendGump = true;
}
else if (info.ButtonID == (int)Buttons.MatPrev)
{
m_Statue.Material = (StatueMaterial)(((int)m_Statue.Material + 3) % 4);
sendGump = true;
}
else if (info.ButtonID == (int)Buttons.MatNext)
{
m_Statue.Material = (StatueMaterial)(((int)m_Statue.Material + 1) % 4);
sendGump = true;
}
else if (info.ButtonID == (int)Buttons.Restore)
{
if (m_Maker is CharacterStatueDeed deed)
{
var backup = deed.Statue;
if (backup != null)
{
m_Statue.Restore(backup);
}
}
sendGump = true;
}
else // Close
{
sendGump = !m_Statue.Demolish(state.Mobile);
}
if (sendGump)
{
state.Mobile.SendGump(new CharacterStatueGump(m_Maker, m_Statue, m_Owner));
}
}
private enum Buttons
{
Close,
Sculpt,
PosePrev,
PoseNext,
DirPrev,
DirNext,
MatPrev,
MatNext,
Restore
}
}
}

View file

@ -0,0 +1,25 @@
using System.Collections.Generic;
namespace Server.Engines.VeteranRewards
{
public class RewardCategory
{
public RewardCategory(int name)
{
Name = name;
Entries = new List<RewardEntry>();
}
public RewardCategory(string name)
{
NameString = name;
Entries = new List<RewardEntry>();
}
public int Name { get; }
public string NameString { get; }
public List<RewardEntry> Entries { get; }
}
}

View file

@ -0,0 +1,220 @@
using System;
using Server.Gumps;
using Server.Network;
namespace Server.Engines.VeteranRewards
{
public class RewardChoiceGump : Gump
{
private readonly Mobile m_From;
public RewardChoiceGump(Mobile from) : base(0, 0)
{
m_From = from;
from.CloseGump<RewardChoiceGump>();
RenderBackground();
RenderCategories();
}
private void RenderBackground()
{
AddPage(0);
AddBackground(10, 10, 600, 450, 2600);
AddButton(530, 415, 4017, 4019, 0);
AddButton(60, 415, 4014, 4016, 0, GumpButtonType.Page, 1);
AddHtmlLocalized(95, 415, 200, 20, 1049755); // Main Menu
}
private void RenderCategories()
{
var rewardInterval = RewardSystem.RewardInterval;
string intervalAsString;
if (rewardInterval == TimeSpan.FromDays(30.0))
{
intervalAsString = "month";
}
else if (rewardInterval == TimeSpan.FromDays(60.0))
{
intervalAsString = "two months";
}
else if (rewardInterval == TimeSpan.FromDays(90.0))
{
intervalAsString = "three months";
}
else if (rewardInterval == TimeSpan.FromDays(365.0))
{
intervalAsString = "year";
}
else
{
intervalAsString = $"{rewardInterval.TotalDays} day{(rewardInterval.TotalDays == 1 ? "" : "s")}";
}
AddPage(1);
AddHtml(
60,
35,
500,
70,
$"<B>Ultima Online Rewards Program</B><BR>Thank you for being a part of the Ultima Online community for a full {intervalAsString}. As a token of our appreciation, you may select from the following in-game reward items listed below. The gift items will be attributed to the character you have logged-in with on the shard you are on when you chose the item(s). The number of rewards you are entitled to are listed below and are for your entire account. To read more about these rewards before making a selection, feel free to visit the uo.com site at <A HREF=\"http://www.uo.com/rewards\">http://www.uo.com/rewards</A>.",
true,
true
);
RewardSystem.ComputeRewardInfo(m_From, out var cur, out var max);
AddHtmlLocalized(60, 105, 300, 35, 1006006); // Your current total of rewards to choose:
AddLabel(370, 107, 50, (max - cur).ToString());
AddHtmlLocalized(60, 140, 300, 35, 1006007); // You have already chosen:
AddLabel(370, 142, 50, cur.ToString());
var categories = RewardSystem.Categories;
var page = 2;
for (var i = 0; i < categories.Length; ++i)
{
if (!RewardSystem.HasAccess(m_From, categories[i]))
{
page += 1;
continue;
}
AddButton(100, 180 + i * 40, 4005, 4005, 0, GumpButtonType.Page, page);
page += PagesPerCategory(categories[i]);
if (categories[i].NameString != null)
{
AddHtml(135, 180 + i * 40, 300, 20, categories[i].NameString);
}
else
{
AddHtmlLocalized(135, 180 + i * 40, 300, 20, categories[i].Name);
}
}
page = 2;
for (var i = 0; i < categories.Length; ++i)
{
RenderCategory(categories[i], i, ref page);
}
}
private int PagesPerCategory(RewardCategory category)
{
var entries = category.Entries;
var i = 0;
for (var j = 0; j < entries.Count; j++)
{
if (RewardSystem.HasAccess(m_From, entries[j]))
{
i++;
}
}
return (int)Math.Ceiling(i / 24.0);
}
private int GetButtonID(int type, int index) => 2 + index * 20 + type;
private void RenderCategory(RewardCategory category, int index, ref int page)
{
AddPage(page);
var entries = category.Entries;
var i = 0;
for (var j = 0; j < entries.Count; ++j)
{
var entry = entries[j];
if (!RewardSystem.HasAccess(m_From, entry))
{
continue;
}
if (i == 24)
{
AddButton(305, 415, 0xFA5, 0xFA7, 0, GumpButtonType.Page, ++page);
AddHtmlLocalized(340, 415, 200, 20, 1011066); // Next page
AddPage(page);
AddButton(270, 415, 0xFAE, 0xFB0, 0, GumpButtonType.Page, page - 1);
AddHtmlLocalized(185, 415, 200, 20, 1011067); // Previous page
i = 0;
}
AddButton(55 + i / 12 * 250, 80 + i % 12 * 25, 5540, 5541, GetButtonID(index, j));
if (entry.NameString != null)
{
AddHtml(80 + i / 12 * 250, 80 + i % 12 * 25, 250, 20, entry.NameString);
}
else
{
AddHtmlLocalized(80 + i / 12 * 250, 80 + i % 12 * 25, 250, 20, entry.Name);
}
++i;
}
page += 1;
}
public override void OnResponse(NetState sender, RelayInfo info)
{
var buttonID = info.ButtonID - 1;
if (buttonID == 0)
{
RewardSystem.ComputeRewardInfo(m_From, out var cur, out var max);
if (cur < max)
{
m_From.SendGump(new RewardNoticeGump(m_From));
}
}
else
{
--buttonID;
var type = buttonID % 20;
var index = buttonID / 20;
var categories = RewardSystem.Categories;
if (type >= 0 && type < categories.Length)
{
var category = categories[type];
if (index >= 0 && index < category.Entries.Count)
{
var entry = category.Entries[index];
if (!RewardSystem.HasAccess(m_From, entry))
{
return;
}
m_From.SendGump(new RewardConfirmGump(m_From, entry));
}
}
}
}
}
}

View file

@ -0,0 +1,91 @@
using Server.Gumps;
using Server.Items;
using Server.Network;
namespace Server.Engines.VeteranRewards
{
public class RewardConfirmGump : Gump
{
private readonly RewardEntry m_Entry;
private readonly Mobile m_From;
public RewardConfirmGump(Mobile from, RewardEntry entry) : base(0, 0)
{
m_From = from;
m_Entry = entry;
from.CloseGump<RewardConfirmGump>();
AddPage(0);
AddBackground(10, 10, 500, 300, 2600);
AddHtmlLocalized(30, 55, 300, 35, 1006000); // You have selected:
if (entry.NameString != null)
{
AddHtml(335, 55, 150, 35, entry.NameString);
}
else
{
AddHtmlLocalized(335, 55, 150, 35, entry.Name);
}
AddHtmlLocalized(30, 95, 300, 35, 1006001); // This will be assigned to this character:
AddLabel(335, 95, 0, from.Name);
AddHtmlLocalized(
35,
160,
450,
90,
1006002,
true,
true
); // Are you sure you wish to select this reward for this character? You will not be able to transfer this reward to another character on another shard. Click 'ok' below to confirm your selection or 'cancel' to go back to the selection screen.
AddButton(60, 265, 4005, 4007, 1);
AddHtmlLocalized(95, 266, 150, 35, 1006044); // Ok
AddButton(295, 265, 4017, 4019, 0);
AddHtmlLocalized(330, 266, 150, 35, 1006045); // Cancel
}
public override void OnResponse(NetState sender, RelayInfo info)
{
if (info.ButtonID == 1)
{
if (!RewardSystem.HasAccess(m_From, m_Entry))
{
return;
}
var item = m_Entry.Construct();
if (item != null)
{
if (item is RedSoulstone soulstone)
{
soulstone.Account = m_From.Account.Username;
}
if (RewardSystem.ConsumeRewardPoint(m_From))
{
m_From.AddToBackpack(item);
}
else
{
item.Delete();
}
}
}
RewardSystem.ComputeRewardInfo(m_From, out var cur, out var max);
if (cur < max)
{
m_From.SendGump(new RewardNoticeGump(m_From));
}
}
}
}

View file

@ -0,0 +1,77 @@
using Server.Items;
using Server.Multis;
using Server.Network;
namespace Server.Gumps
{
public class RewardDemolitionGump : Gump
{
private readonly IAddon m_Addon;
public RewardDemolitionGump(IAddon addon, int question) : base(150, 50)
{
m_Addon = addon;
Closable = true;
Disposable = true;
Draggable = true;
Resizable = false;
AddBackground(0, 0, 220, 170, 0x13BE);
AddBackground(10, 10, 200, 150, 0xBB8);
AddHtmlLocalized(20, 30, 180, 60, question); // Do you wish to re-deed this decoration?
AddHtmlLocalized(55, 100, 150, 25, 1011011); // CONTINUE
AddButton(20, 100, 0xFA5, 0xFA7, (int)Buttons.Confirm);
AddHtmlLocalized(55, 125, 150, 25, 1011012); // CANCEL
AddButton(20, 125, 0xFA5, 0xFA7, (int)Buttons.Cancel);
}
public override void OnResponse(NetState sender, RelayInfo info)
{
if (!(m_Addon is Item item) || item.Deleted)
{
return;
}
if (info.ButtonID == (int)Buttons.Confirm)
{
var m = sender.Mobile;
var house = BaseHouse.FindHouseAt(m);
if (house?.IsOwner(m) == true)
{
if (m.InRange(item.Location, 2))
{
var deed = m_Addon.Deed;
if (deed != null)
{
m.AddToBackpack(deed);
house.Addons.Remove(item);
item.Delete();
}
}
else
{
m.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that.
}
}
else
{
m.SendLocalizedMessage(
1049784
); // You can only re-deed this decoration if you are the house owner or originally placed the decoration.
}
}
}
private enum Buttons
{
Cancel,
Confirm
}
}
}

View file

@ -0,0 +1,89 @@
using System;
using Server.Utilities;
namespace Server.Engines.VeteranRewards
{
public class RewardEntry
{
public RewardEntry(RewardCategory category, int name, Type itemType, params object[] args)
{
Category = category;
ItemType = itemType;
RequiredExpansion = Expansion.None;
Name = name;
Args = args;
category.Entries.Add(this);
}
public RewardEntry(RewardCategory category, string name, Type itemType, params object[] args)
{
Category = category;
ItemType = itemType;
RequiredExpansion = Expansion.None;
NameString = name;
Args = args;
category.Entries.Add(this);
}
public RewardEntry(
RewardCategory category, int name, Type itemType, Expansion requiredExpansion,
params object[] args
)
{
Category = category;
ItemType = itemType;
RequiredExpansion = requiredExpansion;
Name = name;
Args = args;
category.Entries.Add(this);
}
public RewardEntry(
RewardCategory category, string name, Type itemType, Expansion requiredExpansion,
params object[] args
)
{
Category = category;
ItemType = itemType;
RequiredExpansion = requiredExpansion;
NameString = name;
Args = args;
category.Entries.Add(this);
}
public RewardList List { get; set; }
public RewardCategory Category { get; }
public Type ItemType { get; }
public Expansion RequiredExpansion { get; }
public int Name { get; }
public string NameString { get; }
public object[] Args { get; }
public Item Construct()
{
try
{
var item = ItemType.CreateInstance<Item>(Args);
if (item is IRewardItem rewardItem)
{
rewardItem.IsRewardItem = true;
}
return item;
}
catch
{
// ignored
}
return null;
}
}
}

View file

@ -0,0 +1,22 @@
using System;
namespace Server.Engines.VeteranRewards
{
public class RewardList
{
public RewardList(TimeSpan interval, int index, RewardEntry[] entries)
{
Age = TimeSpan.FromDays(interval.TotalDays * index);
Entries = entries;
for (var i = 0; i < entries.Length; ++i)
{
entries[i].List = this;
}
}
public TimeSpan Age { get; }
public RewardEntry[] Entries { get; }
}
}

View file

@ -0,0 +1,40 @@
using Server.Gumps;
using Server.Network;
namespace Server.Engines.VeteranRewards
{
public class RewardNoticeGump : Gump
{
private readonly Mobile m_From;
public RewardNoticeGump(Mobile from) : base(0, 0)
{
m_From = from;
from.CloseGump<RewardNoticeGump>();
AddPage(0);
AddBackground(10, 10, 500, 135, 2600);
/* You have reward items available.
* Click 'ok' below to get the selection menu or 'cancel' to be prompted upon your next login.
*/
AddHtmlLocalized(52, 35, 420, 55, 1006046, true, true);
AddButton(60, 95, 4005, 4007, 1);
AddHtmlLocalized(95, 96, 150, 35, 1006044); // Ok
AddButton(285, 95, 4017, 4019, 0);
AddHtmlLocalized(320, 96, 150, 35, 1006045); // Cancel
}
public override void OnResponse(NetState sender, RelayInfo info)
{
if (info.ButtonID == 1)
{
m_From.SendGump(new RewardChoiceGump(m_From));
}
}
}
}

View file

@ -0,0 +1,99 @@
using System.Collections.Generic;
using Server.Network;
namespace Server.Gumps
{
public interface IRewardOption
{
void GetOptions(RewardOptionList list);
void OnOptionSelected(Mobile from, int choice);
}
public class RewardOptionGump : Gump
{
private readonly IRewardOption m_Option;
private readonly RewardOptionList m_Options = new();
public RewardOptionGump(IRewardOption option, int title = 0) : base(60, 36)
{
m_Option = option;
m_Option?.GetOptions(m_Options);
AddPage(0);
AddBackground(0, 0, 273, 324, 0x13BE);
AddImageTiled(10, 10, 253, 20, 0xA40);
AddImageTiled(10, 40, 253, 244, 0xA40);
AddImageTiled(10, 294, 253, 20, 0xA40);
AddAlphaRegion(10, 10, 253, 304);
AddButton(10, 294, 0xFB1, 0xFB2, 0);
AddHtmlLocalized(45, 296, 450, 20, 1060051, 0x7FFF); // CANCEL
if (title > 0)
{
AddHtmlLocalized(14, 12, 273, 20, title, 0x7FFF);
}
else
{
AddHtmlLocalized(14, 12, 273, 20, 1080392, 0x7FFF); // Select your choice from the menu below.
}
AddPage(1);
for (var i = 0; i < m_Options.Count; i++)
{
AddButton(19, 49 + i * 24, 0x845, 0x846, m_Options[i].ID);
AddHtmlLocalized(44, 47 + i * 24, 213, 20, m_Options[i].Cliloc, 0x7FFF);
}
}
public override void OnResponse(NetState sender, RelayInfo info)
{
if (m_Option != null && Contains(info.ButtonID))
{
m_Option.OnOptionSelected(sender.Mobile, info.ButtonID);
}
}
private bool Contains(int chosen)
{
if (m_Options == null)
{
return false;
}
foreach (var option in m_Options)
{
if (option.ID == chosen)
{
return true;
}
}
return false;
}
}
public class RewardOption
{
public RewardOption(int id, int cliloc)
{
ID = id;
Cliloc = cliloc;
}
public int ID { get; }
public int Cliloc { get; }
}
public class RewardOptionList : List<RewardOption>
{
public void Add(int id, int cliloc)
{
Add(new RewardOption(id, cliloc));
}
}
}

View file

@ -0,0 +1,638 @@
using System;
using Server.Accounting;
using Server.Items;
using Server.Mobiles;
namespace Server.Engines.VeteranRewards
{
public static class RewardSystem
{
private static RewardCategory[] m_Categories;
private static RewardList[] m_Lists;
public static bool Enabled { get; private set; }
// assuming vet rewards are enabled, should total skill cap bonuses be awarded? (720 skills total at 4th level)
public static bool SkillCapRewards { get; private set; }
public static TimeSpan RewardInterval { get; private set; }
public static RewardCategory[] Categories
{
get
{
if (m_Categories == null)
{
SetupRewardTables();
}
return m_Categories;
}
}
public static RewardList[] Lists
{
get
{
if (m_Lists == null)
{
SetupRewardTables();
}
return m_Lists;
}
}
public static bool HasAccess(Mobile mob, RewardCategory category)
{
var entries = category.Entries;
for (var j = 0; j < entries.Count; ++j)
// RewardEntry entry = entries[j];
{
if (HasAccess(mob, entries[j]))
{
return true;
}
}
return false;
}
public static bool HasAccess(Mobile mob, RewardEntry entry)
{
if (Core.Expansion < entry.RequiredExpansion)
{
return false;
}
return HasAccess(mob, entry.List, out var _);
}
public static bool HasAccess(Mobile mob, RewardList list, out TimeSpan ts)
{
if (list == null)
{
ts = TimeSpan.Zero;
return false;
}
if (!(mob.Account is Account acct))
{
ts = TimeSpan.Zero;
return false;
}
var totalTime = DateTime.UtcNow - acct.Created;
ts = list.Age - totalTime;
if (ts <= TimeSpan.Zero)
{
return true;
}
return false;
}
public static int GetRewardLevel(Mobile mob)
{
if (!(mob.Account is Account acct))
{
return 0;
}
return GetRewardLevel(acct);
}
public static int GetRewardLevel(Account acct)
{
var totalTime = DateTime.UtcNow - acct.Created;
return Math.Max((int)(totalTime.TotalDays / RewardInterval.TotalDays), 0);
}
public static bool HasHalfLevel(Mobile mob)
{
if (!(mob.Account is Account acct))
{
return false;
}
return HasHalfLevel(acct);
}
public static bool HasHalfLevel(Account acct)
{
var totalTime = DateTime.UtcNow - acct.Created;
var level = totalTime.TotalDays / RewardInterval.TotalDays;
return level >= 0.5;
}
public static bool ConsumeRewardPoint(Mobile mob)
{
ComputeRewardInfo(mob, out var cur, out var max);
if (cur >= max)
{
return false;
}
if (!(mob.Account is Account acct))
{
return false;
}
// if (mob.AccessLevel < AccessLevel.GameMaster)
acct.SetTag("numRewardsChosen", (cur + 1).ToString());
return true;
}
public static void ComputeRewardInfo(Mobile mob, out int cur, out int max)
{
ComputeRewardInfo(mob, out cur, out max, out _);
}
public static void ComputeRewardInfo(Mobile mob, out int cur, out int max, out int level)
{
if (!(mob.Account is Account acct))
{
cur = max = level = 0;
return;
}
level = GetRewardLevel(acct);
if (level == 0)
{
cur = max = 0;
return;
}
var tag = acct.GetTag("numRewardsChosen");
if (string.IsNullOrEmpty(tag))
{
cur = 0;
}
else
{
cur = Utility.ToInt32(tag);
}
if (level >= 6)
{
max = 9 + (level - 6) * 2;
}
else
{
max = 2 + level;
}
}
public static bool CheckIsUsableBy(Mobile from, Item item, object[] args = null)
{
var isRelaxedRules = item is DyeTub || item is MonsterStatuette;
var type = item.GetType();
for (var i = 0; i < Lists.Length; ++i)
{
var list = Lists[i];
var entries = list.Entries;
for (var j = 0; j < entries.Length; ++j)
{
if (entries[j].ItemType != type)
{
continue;
}
if (args == null && entries[j].Args.Length == 0)
{
if (isRelaxedRules && i <= 0 || HasAccess(from, list, out var ts))
{
return true;
}
from.SendLocalizedMessage(
1008126,
true,
Math.Ceiling(ts.TotalDays / 30.0)
.ToString()
); // Your account is not old enough to use this item. Months until you can use this item :
return false;
}
if (args?.Length != entries[j].Args.Length)
{
continue;
}
var match = true;
for (var k = 0; match && k < args.Length; ++k)
{
match = args[k].Equals(entries[j].Args[k]);
}
if (match)
{
if (isRelaxedRules && i <= 0 || HasAccess(from, list, out var ts))
{
return true;
}
from.SendLocalizedMessage(
1008126,
true,
Math.Ceiling(ts.TotalDays / 30.0)
.ToString()
); // Your account is not old enough to use this item. Months until you can use this item :
return false;
}
}
}
// no entry?
return true;
}
public static int GetRewardYearLabel(Item item, object[] args)
{
var level = GetRewardYear(item, args);
return 1076216 + (level < 10 ? level :
level < 12 ? level - 9 + 4240 : level - 11 + 37585);
}
public static int GetRewardYear(Item item, object[] args)
{
var type = item.GetType();
for (var i = 0; i < Lists.Length; ++i)
{
var list = Lists[i];
var entries = list.Entries;
for (var j = 0; j < entries.Length; ++j)
{
if (entries[j].ItemType == type)
{
if (args == null && entries[j].Args.Length == 0)
{
return i + 1;
}
if (args?.Length == entries[j].Args.Length)
{
var match = true;
for (var k = 0; match && k < args.Length; ++k)
{
match = args[k].Equals(entries[j].Args[k]);
}
if (match)
{
return i + 1;
}
}
}
}
}
// no entry?
return 0;
}
public static void SetupRewardTables()
{
var monsterStatues = new RewardCategory(1049750);
var cloaksAndRobes = new RewardCategory(1049752);
var etherealSteeds = new RewardCategory(1049751);
var specialDyeTubs = new RewardCategory(1049753);
var houseAddOns = new RewardCategory(1049754);
var miscellaneous = new RewardCategory(1078596);
m_Categories = new[]
{
monsterStatues,
cloaksAndRobes,
etherealSteeds,
specialDyeTubs,
houseAddOns,
miscellaneous
};
const int Bronze = 0x972;
const int Copper = 0x96D;
const int Golden = 0x8A5;
const int Agapite = 0x979;
const int Verite = 0x89F;
const int Valorite = 0x8AB;
const int IceGreen = 0x47F;
const int IceBlue = 0x482;
const int DarkGray = 0x497;
const int Fire = 0x489;
const int IceWhite = 0x47E;
const int JetBlack = 0x001;
const int Pink = 0x490;
const int Crimson = 0x485;
m_Lists = new[]
{
new RewardList(
RewardInterval,
1,
new[]
{
new RewardEntry(specialDyeTubs, 1006008, typeof(RewardBlackDyeTub)),
new RewardEntry(specialDyeTubs, 1006013, typeof(FurnitureDyeTub)),
new RewardEntry(specialDyeTubs, 1006047, typeof(SpecialDyeTub)),
new RewardEntry(cloaksAndRobes, 1006009, typeof(RewardCloak), Bronze, 1041286),
new RewardEntry(cloaksAndRobes, 1006010, typeof(RewardRobe), Bronze, 1041287),
new RewardEntry(cloaksAndRobes, 1080366, typeof(RewardDress), Expansion.ML, Bronze, 1080366),
new RewardEntry(cloaksAndRobes, 1006011, typeof(RewardCloak), Copper, 1041288),
new RewardEntry(cloaksAndRobes, 1006012, typeof(RewardRobe), Copper, 1041289),
new RewardEntry(cloaksAndRobes, 1080367, typeof(RewardDress), Expansion.ML, Copper, 1080367),
new RewardEntry(monsterStatues, 1006024, typeof(MonsterStatuette), MonsterStatuetteType.Crocodile),
new RewardEntry(monsterStatues, 1006025, typeof(MonsterStatuette), MonsterStatuetteType.Daemon),
new RewardEntry(monsterStatues, 1006026, typeof(MonsterStatuette), MonsterStatuetteType.Dragon),
new RewardEntry(
monsterStatues,
1006027,
typeof(MonsterStatuette),
MonsterStatuetteType.EarthElemental
),
new RewardEntry(monsterStatues, 1006028, typeof(MonsterStatuette), MonsterStatuetteType.Ettin),
new RewardEntry(monsterStatues, 1006029, typeof(MonsterStatuette), MonsterStatuetteType.Gargoyle),
new RewardEntry(monsterStatues, 1006030, typeof(MonsterStatuette), MonsterStatuetteType.Gorilla),
new RewardEntry(monsterStatues, 1006031, typeof(MonsterStatuette), MonsterStatuetteType.Lich),
new RewardEntry(monsterStatues, 1006032, typeof(MonsterStatuette), MonsterStatuetteType.Lizardman),
new RewardEntry(monsterStatues, 1006033, typeof(MonsterStatuette), MonsterStatuetteType.Ogre),
new RewardEntry(monsterStatues, 1006034, typeof(MonsterStatuette), MonsterStatuetteType.Orc),
new RewardEntry(monsterStatues, 1006035, typeof(MonsterStatuette), MonsterStatuetteType.Ratman),
new RewardEntry(monsterStatues, 1006036, typeof(MonsterStatuette), MonsterStatuetteType.Skeleton),
new RewardEntry(monsterStatues, 1006037, typeof(MonsterStatuette), MonsterStatuetteType.Troll),
new RewardEntry(
houseAddOns,
1062692,
typeof(ContestMiniHouseDeed),
Expansion.AOS,
MiniHouseType.MalasMountainPass
),
new RewardEntry(
houseAddOns,
1072216,
typeof(ContestMiniHouseDeed),
Expansion.SE,
MiniHouseType.ChurchAtNight
),
new RewardEntry(miscellaneous, 1076155, typeof(RedSoulstone), Expansion.ML),
new RewardEntry(miscellaneous, 1080523, typeof(CommodityDeedBox), Expansion.ML)
}
),
new RewardList(
RewardInterval,
2,
new[]
{
new RewardEntry(specialDyeTubs, 1006052, typeof(LeatherDyeTub)),
new RewardEntry(cloaksAndRobes, 1006014, typeof(RewardCloak), Agapite, 1041290),
new RewardEntry(cloaksAndRobes, 1006015, typeof(RewardRobe), Agapite, 1041291),
new RewardEntry(cloaksAndRobes, 1080369, typeof(RewardDress), Expansion.ML, Agapite, 1080369),
new RewardEntry(cloaksAndRobes, 1006016, typeof(RewardCloak), Golden, 1041292),
new RewardEntry(cloaksAndRobes, 1006017, typeof(RewardRobe), Golden, 1041293),
new RewardEntry(cloaksAndRobes, 1080368, typeof(RewardDress), Expansion.ML, Golden, 1080368),
new RewardEntry(houseAddOns, 1006048, typeof(BannerDeed)),
new RewardEntry(houseAddOns, 1006049, typeof(FlamingHeadDeed)),
new RewardEntry(houseAddOns, 1080409, typeof(MinotaurStatueDeed), Expansion.ML)
}
),
new RewardList(
RewardInterval,
3,
new[]
{
new RewardEntry(cloaksAndRobes, 1006020, typeof(RewardCloak), Verite, 1041294),
new RewardEntry(cloaksAndRobes, 1006021, typeof(RewardRobe), Verite, 1041295),
new RewardEntry(cloaksAndRobes, 1080370, typeof(RewardDress), Expansion.ML, Verite, 1080370),
new RewardEntry(cloaksAndRobes, 1006022, typeof(RewardCloak), Valorite, 1041296),
new RewardEntry(cloaksAndRobes, 1006023, typeof(RewardRobe), Valorite, 1041297),
new RewardEntry(cloaksAndRobes, 1080371, typeof(RewardDress), Expansion.ML, Valorite, 1080371),
new RewardEntry(monsterStatues, 1006038, typeof(MonsterStatuette), MonsterStatuetteType.Cow),
new RewardEntry(monsterStatues, 1006039, typeof(MonsterStatuette), MonsterStatuetteType.Zombie),
new RewardEntry(monsterStatues, 1006040, typeof(MonsterStatuette), MonsterStatuetteType.Llama),
new RewardEntry(etherealSteeds, 1006019, typeof(EtherealHorse)),
new RewardEntry(etherealSteeds, 1006050, typeof(EtherealOstard)),
new RewardEntry(etherealSteeds, 1006051, typeof(EtherealLlama)),
new RewardEntry(houseAddOns, 1080407, typeof(PottedCactusDeed), Expansion.ML)
}
),
new RewardList(
RewardInterval,
4,
new[]
{
new RewardEntry(specialDyeTubs, 1049740, typeof(RunebookDyeTub)),
new RewardEntry(cloaksAndRobes, 1049725, typeof(RewardCloak), DarkGray, 1049757),
new RewardEntry(cloaksAndRobes, 1049726, typeof(RewardRobe), DarkGray, 1049756),
new RewardEntry(cloaksAndRobes, 1080374, typeof(RewardDress), Expansion.ML, DarkGray, 1080374),
new RewardEntry(cloaksAndRobes, 1049727, typeof(RewardCloak), IceGreen, 1049759),
new RewardEntry(cloaksAndRobes, 1049728, typeof(RewardRobe), IceGreen, 1049758),
new RewardEntry(cloaksAndRobes, 1080372, typeof(RewardDress), Expansion.ML, IceGreen, 1080372),
new RewardEntry(cloaksAndRobes, 1049729, typeof(RewardCloak), IceBlue, 1049761),
new RewardEntry(cloaksAndRobes, 1049730, typeof(RewardRobe), IceBlue, 1049760),
new RewardEntry(cloaksAndRobes, 1080373, typeof(RewardDress), Expansion.ML, IceBlue, 1080373),
new RewardEntry(monsterStatues, 1049742, typeof(MonsterStatuette), MonsterStatuetteType.Ophidian),
new RewardEntry(monsterStatues, 1049743, typeof(MonsterStatuette), MonsterStatuetteType.Reaper),
new RewardEntry(monsterStatues, 1049744, typeof(MonsterStatuette), MonsterStatuetteType.Mongbat),
new RewardEntry(etherealSteeds, 1049746, typeof(EtherealKirin)),
new RewardEntry(etherealSteeds, 1049745, typeof(EtherealUnicorn)),
new RewardEntry(etherealSteeds, 1049747, typeof(EtherealRidgeback)),
new RewardEntry(houseAddOns, 1049737, typeof(DecorativeShieldDeed)),
new RewardEntry(houseAddOns, 1049738, typeof(HangingSkeletonDeed))
}
),
new RewardList(
RewardInterval,
5,
new[]
{
new RewardEntry(specialDyeTubs, 1049741, typeof(StatuetteDyeTub)),
new RewardEntry(cloaksAndRobes, 1049731, typeof(RewardCloak), JetBlack, 1049763),
new RewardEntry(cloaksAndRobes, 1049732, typeof(RewardRobe), JetBlack, 1049762),
new RewardEntry(cloaksAndRobes, 1080377, typeof(RewardDress), Expansion.ML, JetBlack, 1080377),
new RewardEntry(cloaksAndRobes, 1049733, typeof(RewardCloak), IceWhite, 1049765),
new RewardEntry(cloaksAndRobes, 1049734, typeof(RewardRobe), IceWhite, 1049764),
new RewardEntry(cloaksAndRobes, 1080376, typeof(RewardDress), Expansion.ML, IceWhite, 1080376),
new RewardEntry(cloaksAndRobes, 1049735, typeof(RewardCloak), Fire, 1049767),
new RewardEntry(cloaksAndRobes, 1049736, typeof(RewardRobe), Fire, 1049766),
new RewardEntry(cloaksAndRobes, 1080375, typeof(RewardDress), Expansion.ML, Fire, 1080375),
new RewardEntry(monsterStatues, 1049768, typeof(MonsterStatuette), MonsterStatuetteType.Gazer),
new RewardEntry(
monsterStatues,
1049769,
typeof(MonsterStatuette),
MonsterStatuetteType.FireElemental
),
new RewardEntry(monsterStatues, 1049770, typeof(MonsterStatuette), MonsterStatuetteType.Wolf),
new RewardEntry(etherealSteeds, 1049749, typeof(EtherealSwampDragon)),
new RewardEntry(etherealSteeds, 1049748, typeof(EtherealBeetle)),
new RewardEntry(houseAddOns, 1049739, typeof(StoneAnkhDeed)),
new RewardEntry(houseAddOns, 1080384, typeof(BloodyPentagramDeed), Expansion.ML)
}
),
new RewardList(
RewardInterval,
6,
new[]
{
new RewardEntry(houseAddOns, 1076188, typeof(CharacterStatueMaker), Expansion.ML, StatueType.Jade),
new RewardEntry(houseAddOns, 1076189, typeof(CharacterStatueMaker), Expansion.ML, StatueType.Marble),
new RewardEntry(houseAddOns, 1076190, typeof(CharacterStatueMaker), Expansion.ML, StatueType.Bronze),
new RewardEntry(houseAddOns, 1080527, typeof(RewardBrazierDeed), Expansion.ML)
}
),
new RewardList(
RewardInterval,
7,
new[]
{
new RewardEntry(houseAddOns, 1076157, typeof(CannonDeed), Expansion.ML),
new RewardEntry(houseAddOns, 1080550, typeof(TreeStumpDeed), Expansion.ML)
}
),
new RewardList(
RewardInterval,
8,
new[]
{
new RewardEntry(miscellaneous, 1076158, typeof(WeaponEngravingTool), Expansion.ML)
}
),
new RewardList(
RewardInterval,
9,
new[]
{
new RewardEntry(etherealSteeds, 1076159, typeof(RideablePolarBear), Expansion.ML),
new RewardEntry(houseAddOns, 1080549, typeof(WallBannerDeed), Expansion.ML)
}
),
new RewardList(
RewardInterval,
10,
new[]
{
new RewardEntry(
monsterStatues,
1080520,
typeof(MonsterStatuette),
Expansion.ML,
MonsterStatuetteType.Harrower
),
new RewardEntry(
monsterStatues,
1080521,
typeof(MonsterStatuette),
Expansion.ML,
MonsterStatuetteType.Efreet
),
new RewardEntry(cloaksAndRobes, 1080382, typeof(RewardCloak), Expansion.ML, Pink, 1080382),
new RewardEntry(cloaksAndRobes, 1080380, typeof(RewardRobe), Expansion.ML, Pink, 1080380),
new RewardEntry(cloaksAndRobes, 1080378, typeof(RewardDress), Expansion.ML, Pink, 1080378),
new RewardEntry(cloaksAndRobes, 1080383, typeof(RewardCloak), Expansion.ML, Crimson, 1080383),
new RewardEntry(cloaksAndRobes, 1080381, typeof(RewardRobe), Expansion.ML, Crimson, 1080381),
new RewardEntry(cloaksAndRobes, 1080379, typeof(RewardDress), Expansion.ML, Crimson, 1080379),
new RewardEntry(etherealSteeds, 1080386, typeof(EtherealCuSidhe), Expansion.ML),
new RewardEntry(houseAddOns, 1080548, typeof(MiningCartDeed), Expansion.ML),
new RewardEntry(houseAddOns, 1080397, typeof(AnkhOfSacrificeDeed), Expansion.ML)
}
),
new RewardList(
RewardInterval,
11,
new[]
{
new RewardEntry(etherealSteeds, 1113908, typeof(EtherealReptalon), Expansion.ML)
}
),
new RewardList(
RewardInterval,
12,
new[]
{
new RewardEntry(etherealSteeds, 1113813, typeof(EtherealHiryu), Expansion.ML)
}
)
};
}
public static void Configure()
{
Enabled = ServerConfiguration.GetOrUpdateSetting("vetRewards.enable", true);
SkillCapRewards = ServerConfiguration.GetOrUpdateSetting("vetRewards.skillCapRewards", true);
RewardInterval = ServerConfiguration.GetOrUpdateSetting("vetRewards.rewardInterval", TimeSpan.FromDays(30.0));
}
public static void Initialize()
{
if (Enabled)
{
EventSink.Login += EventSink_Login;
}
}
private static void EventSink_Login(Mobile m)
{
if (!m.Alive)
{
return;
}
ComputeRewardInfo(m, out var cur, out var max, out var level);
if (m.SkillsCap == 7000 || m.SkillsCap == 7050 || m.SkillsCap == 7100 ||
m.SkillsCap == 7150 || m.SkillsCap == 7200)
{
level = Math.Clamp(level, 0, 4);
if (SkillCapRewards)
{
m.SkillsCap = 7000 + level * 50;
}
else
{
m.SkillsCap = 7000;
}
}
if (Core.ML && m is PlayerMobile pm && !pm.HasStatReward && HasHalfLevel(pm))
{
pm.HasStatReward = true;
pm.StatCap += 5;
}
if (cur < max)
{
m.SendGump(new RewardNoticeGump(m));
}
}
}
public interface IRewardItem
{
bool IsRewardItem { get; set; }
}
}