#W# Added random dungeon traps. Added DungeonTrapRegion.
This commit is contained in:
parent
5c92efc0e0
commit
646d9d777d
7 changed files with 452 additions and 2 deletions
301
Scripts/Engines/DungeonTraps.cs
Normal file
301
Scripts/Engines/DungeonTraps.cs
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Regions
|
||||
{
|
||||
public static class CustomDungeonTraps
|
||||
{
|
||||
private static Dictionary<PlayerMobile, Timer> m_KarmaTimers = new Dictionary<PlayerMobile, Timer>();
|
||||
|
||||
public static bool SavingThrow(PlayerMobile pm, string saveType)
|
||||
{
|
||||
int saveThrow = 0;
|
||||
string area = "";
|
||||
|
||||
if (saveType == "Magic")
|
||||
{
|
||||
area = "magical resistance";
|
||||
saveThrow = (int)((pm.Int + pm.Skills[SkillName.MagicResist].Value + pm.EnergyResistance) / 4);
|
||||
}
|
||||
else if (saveType == "Physical")
|
||||
{
|
||||
area = "physical endurance";
|
||||
saveThrow = (int)((pm.Str + pm.PhysicalResistance) / 3);
|
||||
}
|
||||
else if (saveType == "Agility")
|
||||
{
|
||||
area = "quick reflexes";
|
||||
saveThrow = pm.Dex;
|
||||
}
|
||||
else if (saveType == "Poison")
|
||||
{
|
||||
area = "poison resistance";
|
||||
saveThrow = (int)((pm.Str + pm.Skills[SkillName.Poisoning].Value + pm.PoisonResistance) / 4);
|
||||
}
|
||||
|
||||
// Cap the maximum possible save chance at 60%
|
||||
if (saveThrow > 60)
|
||||
{
|
||||
saveThrow = 60;
|
||||
}
|
||||
|
||||
// If their save score is higher than a random 1-100 roll, they succeed!
|
||||
if (saveThrow >= Utility.RandomMinMax(1, 100))
|
||||
{
|
||||
pm.LocalOverheadMessage(Server.Network.MessageType.Emote, 0x3B2, false, "You triggered a trap, but with your " + area + "...you avoid the brunt of it.");
|
||||
pm.PlaySound(pm.Female ? 778 : 1049);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void TriggerRandomTrap(PlayerMobile pm)
|
||||
{
|
||||
// Increase this number as you add more cases below!
|
||||
int roll = Utility.Random(6);
|
||||
|
||||
switch (roll)
|
||||
{
|
||||
case 0: ApplyPotionBreakTrap(pm); break;
|
||||
case 1: ApplySpikeTrap(pm); break;
|
||||
case 2: ApplyGasTrap(pm); break;
|
||||
case 3: ApplyTripWireTrap(pm); break;
|
||||
case 4: ApplyTransmutationTrap(pm); break;
|
||||
case 5: ApplyKarmaTrap(pm); break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void ApplyPotionBreakTrap(PlayerMobile pm)
|
||||
{
|
||||
if (SavingThrow(pm, "Agility"))
|
||||
return;
|
||||
|
||||
pm.PlaySound(0x38D);
|
||||
|
||||
if (pm.Backpack != null)
|
||||
{
|
||||
List<BasePotion> potions = pm.Backpack.FindItemsByType<BasePotion>();
|
||||
|
||||
if (potions.Count > 0)
|
||||
{
|
||||
BasePotion targetPotion = potions[Utility.Random(potions.Count)];
|
||||
targetPotion.Consume(1);
|
||||
|
||||
pm.FixedParticles(0x374A, 10, 15, 5021, EffectLayer.Waist);
|
||||
pm.SendMessage(33, "A concealed tripwire snaps! A flask in your backpack breaks!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pm.Damage(Utility.RandomMinMax(10, 20));
|
||||
pm.SendMessage(33, "You trip over a hidden wire and take a nasty spill!");
|
||||
}
|
||||
|
||||
public static void ApplySpikeTrap(PlayerMobile pm)
|
||||
{
|
||||
if (SavingThrow(pm, "Physical"))
|
||||
return;
|
||||
pm.PlaySound(0x22C);
|
||||
pm.FixedParticles(0x3728, 10, 10, 5021, EffectLayer.Waist);
|
||||
pm.Damage(Utility.RandomMinMax(15, 30));
|
||||
pm.SendMessage(33, "Spikes spring from the floor!");
|
||||
}
|
||||
|
||||
public static void ApplyGasTrap(PlayerMobile pm)
|
||||
{
|
||||
if (SavingThrow(pm, "Poison"))
|
||||
return;
|
||||
pm.PlaySound(0x231);
|
||||
pm.FixedParticles(0x3709, 10, 30, 5052, EffectLayer.Waist);
|
||||
pm.ApplyPoison(pm, Poison.Regular);
|
||||
pm.SendMessage(33, "You trigger a noxious gas vent!");
|
||||
}
|
||||
public static void ApplyTripWireTrap(PlayerMobile pm)
|
||||
{
|
||||
if (SavingThrow(pm, "Agility"))
|
||||
return;
|
||||
|
||||
pm.PlaySound(pm.Female ? 812 : 1086);
|
||||
|
||||
Item dropped = GetRandomEquippedItem(pm);
|
||||
|
||||
if (dropped != null && dropped.LootType != LootType.Blessed)
|
||||
{
|
||||
// Drops it right at their feet
|
||||
dropped.MoveToWorld(pm.Location, pm.Map);
|
||||
pm.LocalOverheadMessage(Server.Network.MessageType.Emote, 33, true, "You tripped over a wire and dropped one of your equipped items!");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback if they are completely naked or only have blessed gear
|
||||
pm.Damage(Utility.RandomMinMax(5, 10));
|
||||
pm.SendMessage(33, "You trip over a hidden wire and take a nasty spill!");
|
||||
}
|
||||
}
|
||||
|
||||
private static Item GetRandomEquippedItem(PlayerMobile pm)
|
||||
{
|
||||
List<Item> wornItems = new List<Item>();
|
||||
|
||||
Layer[] validLayers = { Layer.OneHanded, Layer.TwoHanded, Layer.Helm, Layer.Neck, Layer.Arms, Layer.Gloves, Layer.Ring, Layer.Bracelet, Layer.Shoes };
|
||||
|
||||
foreach (Layer layer in validLayers)
|
||||
{
|
||||
Item item = pm.FindItemOnLayer(layer);
|
||||
|
||||
if (item != null && item.LootType != LootType.Blessed)
|
||||
{
|
||||
wornItems.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (wornItems.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return wornItems[Utility.Random(wornItems.Count)];
|
||||
}
|
||||
|
||||
public static void ApplyTransmutationTrap(PlayerMobile pm)
|
||||
{
|
||||
if (SavingThrow(pm, "Magic"))
|
||||
return;
|
||||
|
||||
if (pm.Backpack != null)
|
||||
{
|
||||
int goldAmount = pm.Backpack.GetAmount(typeof(Gold));
|
||||
|
||||
if (goldAmount > 0)
|
||||
{
|
||||
// Calculate a 10% to 25% tax on their gold
|
||||
int goldToDestroy = (int)(goldAmount * Utility.RandomMinMax(10, 25) / 100.0);
|
||||
|
||||
if (goldToDestroy > 0 && pm.Backpack.ConsumeTotal(typeof(Gold), goldToDestroy))
|
||||
{
|
||||
pm.PlaySound(0x1E1);
|
||||
pm.FixedParticles(0x374A, 10, 15, 5028, EffectLayer.Waist);
|
||||
pm.LocalOverheadMessage(Server.Network.MessageType.Emote, 33, true, "A magical trap triggers, turning a chunk of your gold into useless dust!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback if they have zero gold
|
||||
pm.PlaySound(0x1DF);
|
||||
pm.Mana -= 20;
|
||||
pm.SendMessage(33, "A magical trap drains your mana!");
|
||||
}
|
||||
|
||||
public static void ApplyKarmaTrap(PlayerMobile pm)
|
||||
{
|
||||
if (SavingThrow(pm, "Magic"))
|
||||
return;
|
||||
|
||||
if (pm.Karma == 0)
|
||||
{
|
||||
pm.PlaySound(0x1DF);
|
||||
pm.Mana -= 20;
|
||||
pm.SendMessage(33, "A dark energy washes over you, draining your mana!");
|
||||
return;
|
||||
}
|
||||
|
||||
KarmaCurseToken token = pm.Backpack.FindItemByType<KarmaCurseToken>();
|
||||
|
||||
if (token == null)
|
||||
{
|
||||
pm.Karma *= -1;
|
||||
pm.PlaySound(0x1E1);
|
||||
pm.FixedParticles(0x374A, 10, 15, 5028, EffectLayer.Waist);
|
||||
pm.LocalOverheadMessage(Server.Network.MessageType.Emote, 33, true, "A mind-warping trap triggers, completely inverting your morality!");
|
||||
|
||||
token = new KarmaCurseToken(pm, TimeSpan.FromMinutes(3));
|
||||
pm.Backpack.DropItem(token);
|
||||
}
|
||||
else
|
||||
{
|
||||
token.ExtendCurse(TimeSpan.FromMinutes(5));
|
||||
pm.SendMessage(33, "The mind-warping curse intensifies, extending its duration!");
|
||||
}
|
||||
}
|
||||
}
|
||||
public class KarmaCurseToken : Item
|
||||
{
|
||||
private Mobile m_Owner;
|
||||
private DateTime m_Expires;
|
||||
private Timer m_Timer;
|
||||
|
||||
public KarmaCurseToken(Mobile owner, TimeSpan duration) : base(0x1EA7)
|
||||
{
|
||||
Movable = false;
|
||||
Visible = false;
|
||||
Weight = 0.0;
|
||||
m_Owner = owner;
|
||||
m_Expires = DateTime.UtcNow + duration;
|
||||
|
||||
StartTimer();
|
||||
}
|
||||
|
||||
public KarmaCurseToken(Serial serial) : base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
private void StartTimer()
|
||||
{
|
||||
if (m_Timer != null)
|
||||
m_Timer.Stop();
|
||||
|
||||
TimeSpan delay = m_Expires - DateTime.UtcNow;
|
||||
|
||||
if (delay <= TimeSpan.Zero)
|
||||
{
|
||||
RemoveCurse();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Timer = Timer.DelayCall(delay, RemoveCurse);
|
||||
}
|
||||
}
|
||||
|
||||
public void ExtendCurse(TimeSpan duration)
|
||||
{
|
||||
m_Expires = DateTime.UtcNow + duration;
|
||||
StartTimer();
|
||||
}
|
||||
|
||||
private void RemoveCurse()
|
||||
{
|
||||
if (m_Owner != null && !m_Owner.Deleted)
|
||||
{
|
||||
m_Owner.Karma *= -1;
|
||||
m_Owner.SendMessage(0x3B2, "The morality-warping curse fades, and your true nature returns.");
|
||||
}
|
||||
|
||||
if (m_Timer != null)
|
||||
m_Timer.Stop();
|
||||
|
||||
Delete();
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
writer.Write((int)0);
|
||||
writer.Write(m_Owner);
|
||||
writer.Write(m_Expires);
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
int version = reader.ReadInt();
|
||||
m_Owner = reader.ReadMobile();
|
||||
m_Expires = reader.ReadDateTime();
|
||||
|
||||
StartTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
66
Scripts/Regions/DungeonTrapRegion.cs
Normal file
66
Scripts/Regions/DungeonTrapRegion.cs
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
using System;
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
using System.Xml;
|
||||
|
||||
namespace Server.Regions
|
||||
{
|
||||
public class DungeonTrapRegion : DungeonRegion
|
||||
{
|
||||
public DungeonTrapRegion(XmlElement xml, Map map, Region parent) : base(xml, map, parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnLocationChanged(Mobile m, Point3D oldLocation)
|
||||
{
|
||||
base.OnLocationChanged(m, oldLocation);
|
||||
|
||||
if (m is PlayerMobile pm && pm.Alive && pm.AccessLevel == AccessLevel.Player && m.Location != oldLocation)
|
||||
{
|
||||
// We are using 2% (0.02) base chance per step.
|
||||
if (Utility.RandomDouble() < 0.02)
|
||||
{
|
||||
EvaluateTrapSequence(pm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EvaluateTrapSequence(PlayerMobile pm)
|
||||
{
|
||||
// --- TIER 1: PASSIVE SEARCHING (Detect Hidden) ---
|
||||
if (pm.Skills[SkillName.DetectHidden].Value >= 5.0)
|
||||
{
|
||||
if (pm.CheckSkill(SkillName.DetectHidden, 0.0, 125.0))
|
||||
{
|
||||
pm.PlaySound(pm.Female ? 778 : 1049); // Plays a female/male gasp or sigh
|
||||
//pm.LocalOverheadMessage(Server.Network.MessageType.Emote, 0x3B2, false, "You notice a hidden floor mechanism and carefully step around it.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// --- TIER 2: TRAP AVOIDANCE (Remove Trap) ---
|
||||
if (pm.Skills[SkillName.RemoveTrap].Value >= 5.0)
|
||||
{
|
||||
if (pm.CheckSkill(SkillName.RemoveTrap, 0.0, 125.0))
|
||||
{
|
||||
pm.PlaySound(0x241);
|
||||
//pm.LocalOverheadMessage(Server.Network.MessageType.Emote, 0x3B2, false, "Your experience with traps allows you to safely bypass a hidden trigger.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// --- TIER 3: LUCK ---
|
||||
// A simple scale: 1000 Luck = 10% chance to avoid. 2000 Luck = 20% chance.
|
||||
double luckChance = pm.Luck / 10000.0;
|
||||
if (Utility.RandomDouble() < luckChance)
|
||||
{
|
||||
pm.PlaySound(0x241);
|
||||
//pm.LocalOverheadMessage(Server.Network.MessageType.Emote, 0x3B2, false, "With luck on your side, the trap mechanism fails to trigger.");
|
||||
return;
|
||||
}
|
||||
|
||||
// If they failed all three avoidance checks the trap is sprung.
|
||||
CustomDungeonTraps.TriggerRandomTrap(pm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ namespace Server
|
|||
public static int S_MaxGold = 1000; // maximum is 10,000
|
||||
public static bool S_CanStealWhileHoldingThings = true;
|
||||
public static bool S_MonstersSurprise = true;
|
||||
public static bool S_DungeonTraps = true;
|
||||
public static bool S_DungeonTraps = false;
|
||||
public static double S_FarmSpawnTimer = 15.0;
|
||||
public static int S_HarvestRange = 1;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue