ModernUO/ToAdd/Traps/CurseItemsTrap.cs

301 lines
No EOL
8.7 KiB
C#

/**********************************
* Author: Expa *
* Optimized ModernUO .Net 10/C#14 *
**********************************/
using System;
using System.Collections.Generic;
using ModernUO.Serialization;
using Server;
using Server.Items;
using Server.Mobiles;
using Server.Targeting;
namespace Server.Items;
static class CurseConfig
{
public const int ScrollId = 0x1F2E;
public const int ScrollHue = 0x58;
public const int TrapId = 0x1B7B;
public const int Range = 1;
public const int SoundRemove = 0x1F7;
public const int SoundTrap = 0x1E1;
public const int PartRemove = 0x373A;
public const int PartTrap = 0x374B;
public const string TrapName = "Hidden Curse Trap";
public static readonly TimeSpan Cooldown = TimeSpan.FromMinutes(5);
public const bool ShowWhileCooling = true;
public const string Suffix = " [The Cursed!]";
public const string MsgNotInPack = "Scroll must be in backpack.";
public const string MsgPrompt = "Choose cursed item.";
public const string MsgNotCursed = "Not cursed.";
public const string MsgSelect = "Select an item.";
public const string MsgRemovedPack = "Curse removed (backpack).";
public const string MsgRemovedGround = "Curse removed (ground).";
public const string MsgReload = "Trap reloading!";
public const string MsgCursed = "Item cursed and moved.";
}
[SerializationGenerator(0, false)]
public partial class CurseRemovalScroll : Item
{
[Constructible]
public CurseRemovalScroll() : base(CurseConfig.ScrollId) =>
Name = "Curse Removal Scroll";
public CurseRemovalScroll(Serial s) : base(s) { }
public override void OnDoubleClick(Mobile from)
{
if (from?.Backpack is null || !this.IsChildOf(from.Backpack))
{
from?.SendMessage(CurseConfig.MsgNotInPack);
return;
}
from.Target = new T(this);
from.SendMessage(CurseConfig.MsgPrompt);
}
private sealed class T : Target
{
private readonly CurseRemovalScroll _s;
public T(CurseRemovalScroll s) : base(3, false, TargetFlags.None) => _s = s;
protected override void OnTarget(Mobile from, object target)
{
if (_s?.Deleted != false || from?.Deleted != false)
return;
if (target is not Item it)
{
from.SendMessage(CurseConfig.MsgSelect);
return;
}
var isCursed = it.LootType == LootType.Cursed ||
(it.Name?.EndsWith(CurseConfig.Suffix, StringComparison.Ordinal) == true);
if (!isCursed)
{
from.SendMessage(CurseConfig.MsgNotCursed);
return;
}
it.LootType = LootType.Regular;
it.Movable = true;
if (!string.IsNullOrEmpty(it.Name) && it.Name.EndsWith(CurseConfig.Suffix, StringComparison.Ordinal))
{
var baseName = it.Name[..^CurseConfig.Suffix.Length];
it.Name = string.IsNullOrEmpty(baseName) ? null : baseName;
}
from.PlaySound(CurseConfig.SoundRemove);
from.FixedParticles(CurseConfig.PartRemove, 10, 15, 5018, EffectLayer.Waist);
from.SendMessage(it.IsChildOf(from.Backpack) ? CurseConfig.MsgRemovedPack : CurseConfig.MsgRemovedGround);
_s.Delete();
}
}
}
[SerializationGenerator(0, false)]
public partial class CurseTrap : BaseTrap
{
private DateTime _next = DateTime.Now;
[Constructible]
public CurseTrap() : base(CurseConfig.TrapId) => InitTrap();
public CurseTrap(Serial s) : base(s) => InitTrap();
private void InitTrap()
{
Name = CurseConfig.TrapName;
Movable = false;
Visible = false;
}
public override bool PassivelyTriggered => true;
public override TimeSpan PassiveTriggerDelay => TimeSpan.Zero;
public override int PassiveTriggerRange => CurseConfig.Range;
public override TimeSpan ResetDelay => TimeSpan.Zero;
public override void OnTrigger(Mobile from)
{
if (from?.Alive != true || !from.Player)
return;
if (Core.Now < _next)
{
from.SendMessage(CurseConfig.MsgReload);
return;
}
var item = SelectTarget(from);
if (item == null)
return;
var oldName = item.Name;
var oldLoot = item.LootType;
var oldMovable = item.Movable;
try
{
if (item.Parent == from)
from.RemoveItem(item);
item.LootType = LootType.Regular;
item.Movable = false;
if (!(item.Name?.EndsWith(CurseConfig.Suffix) == true))
{
var baseName = oldName ?? item.ItemData.Name ?? item.GetType().Name;
item.Name = baseName + CurseConfig.Suffix;
}
if (!item.IsChildOf(from.Backpack) && !TryPlaceInBackpack(from, item))
item.MoveToWorld(from.Location, from.Map);
from.PlaySound(CurseConfig.SoundTrap);
from.FixedParticles(CurseConfig.PartTrap, 10, 10, 5044, EffectLayer.Head);
from.SendMessage(CurseConfig.MsgCursed);
_next = Core.Now + CurseConfig.Cooldown;
}
catch
{
if (item?.Deleted == false)
{
item.LootType = oldLoot;
item.Movable = oldMovable;
item.Name = oldName;
}
}
}
private static bool TryPlaceInBackpack(Mobile to, Item it)
{
if (to?.Backpack is null || it.IsChildOf(to.Backpack))
return false;
try
{
return to.Backpack.TryDropItem(to, it, false);
}
catch
{
try
{
to.Backpack.DropItem(it);
return true;
}
catch
{
return false;
}
}
}
private static Item SelectTarget(Mobile m)
{
if (m == null)
return null;
double totalWeight = 0.0;
Item firstCandidate = null;
foreach (var itm in m.Items)
{
if (itm?.Deleted != false)
continue;
var inEquip = itm.Parent == m;
var inPack = m.Backpack != null && itm.IsChildOf(m.Backpack);
if (!inEquip && !inPack)
continue;
if (itm.LootType == LootType.Cursed || !itm.Movable)
continue;
if (itm.Layer is Layer.Backpack or Layer.Hair or Layer.FacialHair)
continue;
var weight = CalculateWeight(itm);
if (weight <= 0.0)
continue;
if (firstCandidate == null)
firstCandidate = itm;
totalWeight += weight;
}
if (totalWeight <= 0.0)
return firstCandidate;
var roll = Utility.RandomDouble() * totalWeight;
var accumulated = 0.0;
foreach (var itm in m.Items)
{
if (itm?.Deleted != false)
continue;
var inEquip = itm.Parent == m;
var inPack = m.Backpack != null && itm.IsChildOf(m.Backpack);
if (!inEquip && !inPack)
continue;
if (itm.LootType == LootType.Cursed || !itm.Movable)
continue;
if (itm.Layer is Layer.Backpack or Layer.Hair or Layer.FacialHair)
continue;
var weight = CalculateWeight(itm);
if (weight <= 0.0)
continue;
accumulated += weight;
if (accumulated >= roll)
return itm;
}
return firstCandidate;
}
private static double CalculateWeight(Item it)
{
var baseWeight = it.Amount > 1 ? Math.Min(it.Amount, 100) : 1.0;
return it switch
{
Gold g => baseWeight + Math.Min(g.Amount, 1000) / 10.0,
Bandage => baseWeight + 50,
Arrow => baseWeight + 50,
Bolt => baseWeight + 50,
SpellScroll => baseWeight + 40,
BasePotion => baseWeight + 40,
BaseReagent => baseWeight + 40,
Spellbook => baseWeight + 60,
Runebook => baseWeight + 80,
_ => GetByTypeName(it, baseWeight)
};
}
private static double GetByTypeName(Item it, double baseWeight)
{
var typeName = it.GetType().Name;
if (typeName.Contains("scroll", StringComparison.OrdinalIgnoreCase))
return baseWeight + 20;
if (typeName.Contains("book", StringComparison.OrdinalIgnoreCase) && it is not Spellbook)
return baseWeight + 15;
return baseWeight;
}
}