#W# Bosses: Working on Dungeon Bosses. Added some reward drops to increase stat/skill/totalskill cap.

This commit is contained in:
WarrentyExpired 2026-10-31 12:44:45 -04:00
parent 00c25cd506
commit 69a2b8772b
11 changed files with 406 additions and 3 deletions

View file

@ -0,0 +1,68 @@
using System;
using Server;
using Server.Items;
using Server.Mobiles;
using Server.Network;
using ModernUO.Serialization;
namespace Server.Custom.Dungeons
{
[SerializationGenerator(0)]
public abstract partial class BossAlter : Item
{
private Mobile _ActiveBoss;
[CommandProperty(AccessLevel.GameMaster)]
public Mobile ActiveBoss { get => _ActiveBoss; set => _ActiveBoss = value; }
public abstract Type SummonItem { get; }
public abstract Type BossType { get; }
public BossAlter(int itemID) : base(itemID)
{
Movable = false;
}
public override bool OnDragDrop(Mobile from, Item dropped)
{
if (dropped.GetType() != SummonItem)
{
from.SendMessage(33, "The altar rejects your offering. It requires a different catalyst.");
return false;
}
if (_ActiveBoss != null && _ActiveBoss.Alive && !_ActiveBoss.Deleted)
{
from.SendMessage(33, "The guardian of this domain is already present!");
return false;
}
dropped.Delete();
Effects.PlaySound(Location, Map, 0x20F);
Effects.SendLocationEffect(Location, Map, 0x3728, 10, 10);
Effects.SendLocationEffect(Location, Map, 0x3709, 10, 30, 1153, 0);
try
{
Mobile boss = (Mobile)Activator.CreateInstance(BossType);
boss.MoveToWorld(new Point3D(X + 1, Y + 1, Z), Map);
boss.PlaySound(boss.GetAngerSound());
_ActiveBoss = boss;
from.SendMessage(89, "The altar consumes your offering, and a terrible presence enters the room!");
}
catch (Exception e)
{
Console.WriteLine($"Error spawning dungeon boss: {e.Message}");
from.SendMessage(33, "The altar flickers, but nothing happens.");
}
return true;
}
public BossAlter(Serial serial) : base(serial)
{
}
}
}

View file

@ -0,0 +1,27 @@
using System;
using Server;
using Server.Items;
using Server.Mobiles;
using ModernUO.Serialization;
namespace Server.Custom.Dungeons
{
[SerializationGenerator(0)]
public partial class ShameAlter : BossAlter
{
public override Type SummonItem => typeof(PulsingBloodClot);
public override Type BossType => typeof(CrystallineTitan);
[Constructible]
public ShameAlter() : base(0xFFBD)
{
Name = "Altar of the Bloodstone Titan";
Hue = 1157;
}
public ShameAlter(Serial serial) : base(serial)
{
}
}
}

View file

@ -0,0 +1,23 @@
using System;
using Server;
using Server.Items;
using ModernUO.Serialization;
namespace Server.Custom.Dungeons
{
[SerializationGenerator(0)] // ModernUO Auto-Serialization
public partial class PulsingBloodClot : Item
{
[Constructible] // Spelled with an 'i' for ModernUO
public PulsingBloodClot() : base(0x122A)
{
Name = "Pulsing Blood Clot";
Weight = 1.0;
Hue = 1157;
}
public PulsingBloodClot(Serial serial) : base(serial)
{
}
}
}

View file

@ -0,0 +1,57 @@
using ModernUO.Serialization;
namespace Server.Mobiles
{
[SerializationGenerator(0, false)]
public partial class BloodstoneTitan : BaseCreature
{
[Constructible]
public BloodstoneTitan() : base(AIType.AI_Mage)
{
Body = 829;
Hue = 1157;
BaseSoundID = 278;
SetStr(1026, 1115);
SetDex(80, 100);
SetInt(550, 750);
SetHits(1200, 1300);
SetDamage(27, 37);
SetDamageType(ResistanceType.Physical, 40);
SetDamageType(ResistanceType.Poison, 30);
SetDamageType(ResistanceType.Energy, 30);
SetResistance(ResistanceType.Physical, 55, 65);
SetResistance(ResistanceType.Fire, 20, 30);
SetResistance(ResistanceType.Cold, 40, 50);
SetResistance(ResistanceType.Poison, 50, 60);
SetResistance(ResistanceType.Energy, 30, 40);
SetSkill(SkillName.EvalInt, 99.1, 110.0);
SetSkill(SkillName.Magery, 99.1, 110.0);
SetSkill(SkillName.Meditation, 80.4, 100.0);
SetSkill(SkillName.MagicResist, 80.1, 95.0);
SetSkill(SkillName.Tactics, 99.1, 100.0);
SetSkill(SkillName.Wrestling, 99.1, 100.0);
Fame = 32000;
Karma = -32000;
VirtualArmor = 60;
}
public override string CorpseName => "a bloodstone corpse";
public override string DefaultName => "a bloodstone titan";
public override int TreasureMapLevel => 5;
public override void GenerateLoot()
{
AddLoot(LootPack.SuperBoss, 2);
AddLoot(LootPack.SuperBoss, 2);
}
}
}

View file

@ -0,0 +1,153 @@
using System;
using Server;
using Server.Items;
using Server.Network;
using ModernUO.Serialization;
namespace Server.Mobiles
{
[SerializationGenerator(0, false)]
public partial class CrystallineTitan : BaseCreature
{
private DateTime _NextStanceChange;
private TitanStance _CurrentStance;
private enum TitanStance
{
Normal,
DiamondCore,
Shattered
}
[Constructible]
public CrystallineTitan() : base(AIType.AI_Melee, FightMode.Closest)
{
Name = "The Crystalline Titan";
Body = 1109;
BaseSoundID = 0x127;
SetStr(800, 1000);
SetDex(120, 150);
SetInt(100, 150);
SetHits(15000, 20000);
SetSkill(SkillName.Tactics, 100.0, 120.0);
SetSkill(SkillName.Wrestling, 100.0, 120.0);
SetSkill(SkillName.MagicResist, 120.0, 150.0);
SetSkill(SkillName.Anatomy, 100.0, 120.0);
Fame = 25000;
Karma = -25000;
VirtualArmor = 80;
// Start in the normal stance to show off the raw graphic
EnterNormal();
// Give players 30 seconds of normal combat before the first special phase triggers
_NextStanceChange = DateTime.UtcNow + TimeSpan.FromSeconds(30);
}
public override void OnThink()
{
base.OnThink();
if (Deleted || !Alive || Combatant == null)
return;
// Trigger the swap every 30 seconds while fighting
if (DateTime.UtcNow > _NextStanceChange)
{
// Reset the clock immediately so OnThink doesn't loop
_NextStanceChange = DateTime.UtcNow + TimeSpan.FromSeconds(30);
if (_CurrentStance == TitanStance.DiamondCore || _CurrentStance == TitanStance.Shattered)
{
SpeechHue = 0x3B2;
Say("*The Titan's crystals begin to stabilize...*");
// Wait 2 seconds, then execute the stat/hue change
Timer.DelayCall(TimeSpan.FromSeconds(2.0), () => { if (Alive && !Deleted) EnterNormal(); });
}
else
{
if (Utility.RandomBool())
{
SpeechHue = 0x47E;
Say("*The Titan braces itself as its shell begins to harden!*");
Timer.DelayCall(TimeSpan.FromSeconds(2.0), () => { if (Alive && !Deleted) EnterDiamondCore(); });
}
else
{
SpeechHue = 0x22;
Say("*The Titan's crystals throb with unstable, violent energy!*");
Timer.DelayCall(TimeSpan.FromSeconds(2.0), () => { if (Alive && !Deleted) EnterShattered(); });
}
}
}
}
private void EnterNormal()
{
_CurrentStance = TitanStance.Normal;
Hue = 0;
SetResistance(ResistanceType.Physical, 50, 60);
SetResistance(ResistanceType.Energy, 50, 60);
SetResistance(ResistanceType.Fire, 50, 60);
SetResistance(ResistanceType.Cold, 50, 60);
SetResistance(ResistanceType.Poison, 60, 70);
SetDamage(30, 45);
PlaySound(0x11F); // Deep rumble sound as the hue shifts
}
private void EnterDiamondCore()
{
_CurrentStance = TitanStance.DiamondCore;
Hue = 0x47E;
SetResistance(ResistanceType.Physical, 80, 90);
SetResistance(ResistanceType.Energy, 70, 80);
SetResistance(ResistanceType.Fire, 60, 70);
SetResistance(ResistanceType.Cold, 60, 70);
SetResistance(ResistanceType.Poison, 100, 100);
SetDamage(15, 25);
PlaySound(0x1E5); // Hardening/magic sound as the hue shifts
}
private void EnterShattered()
{
_CurrentStance = TitanStance.Shattered;
Hue = 0x86;
SetResistance(ResistanceType.Physical, 30, 40);
SetResistance(ResistanceType.Energy, 30, 40);
SetResistance(ResistanceType.Fire, 30, 40);
SetResistance(ResistanceType.Cold, 30, 40);
SetDamage(45, 65);
PlaySound(0x1F8); // Shattering explosion sound as the hue shifts
}
// --- Boss Immunities ---
public override bool AutoDispel => true;
public override bool BardImmune => true;
public override bool Uncalmable => true;
public override Poison PoisonImmune => Poison.Lethal;
public override void GenerateLoot()
{
AddLoot(LootPack.SuperBoss, 2);
}
public CrystallineTitan(Serial serial) : base(serial)
{
}
}
}