diff --git a/Scripts/Engines/Craft/Core/Repair.cs b/Scripts/Engines/Craft/Core/Repair.cs
index cbc4d62c6..a98fe650e 100644
--- a/Scripts/Engines/Craft/Core/Repair.cs
+++ b/Scripts/Engines/Craft/Core/Repair.cs
@@ -428,6 +428,7 @@ namespace Server.Engines.Craft
}
else if( toDelete )
{
+ from.SendLocalizedMessage( number );
m_Deed.Delete();
}
}
diff --git a/Scripts/Items/Misc/Corpses/Corpse.cs b/Scripts/Items/Misc/Corpses/Corpse.cs
index 71d1c39f1..15f049ac2 100644
--- a/Scripts/Items/Misc/Corpses/Corpse.cs
+++ b/Scripts/Items/Misc/Corpses/Corpse.cs
@@ -14,32 +14,69 @@ using Server.Network;
namespace Server.Items
{
+ public interface IDevourable
+ {
+ bool Devour( Corpse corpse );
+ }
+
+ [Flags]
+ public enum CorpseFlag
+ {
+ None = 0x00000000,
+
+ ///
+ /// Has this corpse been carved?
+ ///
+ Carved = 0x00000001,
+
+ ///
+ /// If true, this corpse will not turn into bones
+ ///
+ NoBones = 0x00000002,
+
+ ///
+ /// If true, the corpse has turned into bones
+ ///
+ IsBones = 0x00000004,
+
+ ///
+ /// Has this corpse yet been visited by a taxidermist?
+ ///
+ VisitedByTaxidermist = 0x00000008,
+
+ ///
+ /// Has this corpse yet been used to channel spiritual energy? (AOS Spirit Speak)
+ ///
+ Channeled = 0x00000010,
+
+ ///
+ /// Was the owner criminal when he died?
+ ///
+ Criminal = 0x00000020,
+ }
+
public class Corpse : Container, ICarvable
{
- private Mobile m_Owner; // Whos corpse is this?
- private Mobile m_Killer; // Who killed the owner?
- private bool m_Carved; // Has this corpse been carved?
+ private Mobile m_Owner; // Whos corpse is this?
+ private Mobile m_Killer; // Who killed the owner?
+ private CorpseFlag m_Flags; // @see CorpseFlag
- private List m_Looters; // Who's looted this corpse?
- private List- m_EquipItems; // List of items equiped when the owner died. Ingame, these items display /on/ the corpse, not just inside
- private List m_Aggressors; // Anyone from this list will be able to loot this corpse; we attacked them, or they attacked us when we were freely attackable
+ private List m_Looters; // Who's looted this corpse?
+ private List
- m_EquipItems; // List of items equiped when the owner died. Ingame, these items display /on/ the corpse, not just inside
+ private List m_Aggressors; // Anyone from this list will be able to loot this corpse; we attacked them, or they attacked us when we were freely attackable
- private string m_CorpseName; // Value of the CorpseNameAttribute attached to the owner when he died -or- null if the owner had no CorpseNameAttribute; use "the remains of ~name~"
- private bool m_NoBones; // If true, this corpse will not turn into bones
-
- private bool m_VisitedByTaxidermist; // Has this corpse yet been visited by a taxidermist?
- private bool m_Channeled; // Has this corpse yet been used to channel spiritual energy? (AOS Spirit Speak)
+ private string m_CorpseName; // Value of the CorpseNameAttribute attached to the owner when he died -or- null if the owner had no CorpseNameAttribute; use "the remains of ~name~"
+ private IDevourable m_Devourer; // The creature that devoured this corpse
// For notoriety:
- private AccessLevel m_AccessLevel; // Which AccessLevel the owner had when he died
- private Guild m_Guild; // Which Guild the owner was in when he died
- private int m_Kills; // How many kills the owner had when he died
- private bool m_Criminal; // Was the owner criminal when he died?
+ private AccessLevel m_AccessLevel; // Which AccessLevel the owner had when he died
+ private Guild m_Guild; // Which Guild the owner was in when he died
+ private int m_Kills; // How many kills the owner had when he died
- private DateTime m_TimeOfDeath; // What time was this corpse created?
+ private DateTime m_TimeOfDeath; // What time was this corpse created?
- private HairInfo m_Hair; // This contains the hair of the owner
- private FacialHairInfo m_FacialHair; // This contains the facial hair of the owner
+ private HairInfo m_Hair; // This contains the hair of the owner
+ private FacialHairInfo m_FacialHair; // This contains the facial hair of the owner
public static readonly TimeSpan MonsterLootRightSacrifice = TimeSpan.FromMinutes( 2.0 );
@@ -76,25 +113,37 @@ namespace Server.Items
public HairInfo Hair { get { return m_Hair; } }
public FacialHairInfo FacialHair { get { return m_FacialHair; } }
+ [CommandProperty( AccessLevel.GameMaster )]
+ public bool IsBones
+ {
+ get { return GetFlag( CorpseFlag.IsBones ); }
+ }
+
+ [CommandProperty( AccessLevel.GameMaster )]
+ public bool Devoured
+ {
+ get { return (m_Devourer != null); }
+ }
+
[CommandProperty( AccessLevel.GameMaster )]
public bool Carved
{
- get{ return m_Carved; }
- set{ m_Carved = value; }
+ get{ return GetFlag( CorpseFlag.Carved ); }
+ set { SetFlag( CorpseFlag.Carved, value ); }
}
[CommandProperty( AccessLevel.GameMaster )]
public bool VisitedByTaxidermist
{
- get{ return m_VisitedByTaxidermist; }
- set{ m_VisitedByTaxidermist = value; }
+ get { return GetFlag( CorpseFlag.VisitedByTaxidermist ); }
+ set { SetFlag( CorpseFlag.VisitedByTaxidermist, value ); }
}
[CommandProperty( AccessLevel.GameMaster )]
public bool Channeled
{
- get{ return m_Channeled; }
- set{ m_Channeled = value; }
+ get { return GetFlag( CorpseFlag.Channeled ); }
+ set { SetFlag( CorpseFlag.Channeled, value ); }
}
[CommandProperty( AccessLevel.GameMaster )]
@@ -139,8 +188,8 @@ namespace Server.Items
[CommandProperty( AccessLevel.GameMaster )]
public bool Criminal
{
- get{ return m_Criminal; }
- set{ m_Criminal = value; }
+ get { return GetFlag( CorpseFlag.Criminal ); }
+ set { SetFlag( CorpseFlag.Criminal, value ); }
}
[CommandProperty( AccessLevel.GameMaster )]
@@ -160,7 +209,9 @@ namespace Server.Items
Hue = 0;
ProcessDelta();
- m_NoBones = true;
+ SetFlag( CorpseFlag.NoBones, true );
+ SetFlag( CorpseFlag.IsBones, true );
+
BeginDecay( m_BoneDecayTime );
}
@@ -201,7 +252,7 @@ namespace Server.Items
protected override void OnTick()
{
- if ( !m_Corpse.m_NoBones )
+ if ( !m_Corpse.GetFlag( CorpseFlag.NoBones ) )
m_Corpse.TurnToBones();
else
m_Corpse.Delete();
@@ -307,14 +358,14 @@ namespace Server.Items
m_AccessLevel = owner.AccessLevel;
m_Guild = owner.Guild as Guild;
m_Kills = owner.Kills;
- m_Criminal = owner.Criminal;
+ SetFlag( CorpseFlag.Criminal, owner.Criminal );
m_Hair = hair;
m_FacialHair = facialhair;
// This corpse does not turn to bones if: the owner is not a player
- m_NoBones = !owner.Player;
+ SetFlag( CorpseFlag.NoBones, !owner.Player );
m_Looters = new List();
m_EquipItems = equipItems;
@@ -371,6 +422,8 @@ namespace Server.Items
}
BeginDecay( m_DefaultDecayTime );
+
+ DevourCorpse();
}
private void AssignInstancedLoot()
@@ -383,11 +436,23 @@ namespace Server.Items
{
}
+ protected bool GetFlag( CorpseFlag flag )
+ {
+ return ((m_Flags & flag) != 0);
+ }
+
+ protected void SetFlag( CorpseFlag flag, bool on )
+ {
+ m_Flags = (on ? m_Flags | flag : m_Flags & ~flag);
+ }
+
public override void Serialize( GenericWriter writer )
{
base.Serialize( writer );
- writer.Write( (int) 10 ); // version
+ writer.Write( (int) 11 ); // version
+
+ writer.Write( (int)m_Flags );
writer.WriteDeltaTime( m_TimeOfDeath );
@@ -415,8 +480,6 @@ namespace Server.Items
}
}
- writer.Write( m_VisitedByTaxidermist );
-
writer.Write( m_DecayTimer != null );
if ( m_DecayTimer != null )
@@ -425,20 +488,15 @@ namespace Server.Items
writer.Write( m_Looters );
writer.Write( m_Killer );
- writer.Write( (bool) m_Carved );
-
writer.Write( m_Aggressors );
writer.Write( m_Owner );
- writer.Write( m_NoBones );
-
writer.Write( (string) m_CorpseName );
writer.Write( (int) m_AccessLevel );
writer.Write( (Guild) m_Guild );
writer.Write( (int) m_Kills );
- writer.Write( (bool) m_Criminal );
writer.Write( m_EquipItems );
}
@@ -451,6 +509,43 @@ namespace Server.Items
switch ( version )
{
+ case 11:
+ {
+ // Version 11, we move all bools to a CorpseFlag
+ m_Flags = (CorpseFlag)reader.ReadInt();
+
+ m_TimeOfDeath = reader.ReadDeltaTime();
+
+ int count = reader.ReadInt();
+
+ for( int i = 0; i < count; ++i )
+ {
+ Item item = reader.ReadItem();
+
+ if( reader.ReadBool() )
+ SetRestoreInfo( item, reader.ReadPoint3D() );
+ else if( item != null )
+ SetRestoreInfo( item, item.Location );
+ }
+
+ if( reader.ReadBool() )
+ BeginDecay( reader.ReadDeltaTime() - DateTime.Now );
+
+ m_Looters = reader.ReadStrongMobileList();
+ m_Killer = reader.ReadMobile();
+
+ m_Aggressors = reader.ReadStrongMobileList();
+ m_Owner = reader.ReadMobile();
+
+ m_CorpseName = reader.ReadString();
+
+ m_AccessLevel = (AccessLevel)reader.ReadInt();
+ reader.ReadInt(); // guild reserve
+ m_Kills = reader.ReadInt();
+
+ m_EquipItems = reader.ReadStrongItemList();
+ break;
+ }
case 10:
{
m_TimeOfDeath = reader.ReadDeltaTime();
@@ -475,7 +570,7 @@ namespace Server.Items
}
case 8:
{
- m_VisitedByTaxidermist = reader.ReadBool();
+ SetFlag( CorpseFlag.VisitedByTaxidermist, reader.ReadBool() );
goto case 7;
}
@@ -495,7 +590,7 @@ namespace Server.Items
}
case 5:
{
- m_Carved = reader.ReadBool();
+ SetFlag( CorpseFlag.Carved, reader.ReadBool() );
goto case 4;
}
@@ -513,7 +608,7 @@ namespace Server.Items
}
case 2:
{
- m_NoBones = reader.ReadBool();
+ SetFlag( CorpseFlag.NoBones, reader.ReadBool() );
goto case 1;
}
@@ -540,7 +635,7 @@ namespace Server.Items
m_AccessLevel = (AccessLevel)reader.ReadInt();
reader.ReadInt(); // guild reserve
m_Kills = reader.ReadInt();
- m_Criminal = reader.ReadBool();
+ SetFlag( CorpseFlag.Criminal, reader.ReadBool() );
m_EquipItems = reader.ReadStrongItemList();
@@ -549,6 +644,15 @@ namespace Server.Items
}
}
+ public bool DevourCorpse()
+ {
+ if( Devoured || Deleted || m_Killer == null || m_Killer.Deleted || !m_Killer.Alive || !(m_Killer is IDevourable) || m_Owner == null || m_Owner.Deleted )
+ return false;
+
+ m_Devourer = (IDevourable)m_Killer; // Set the devourer the killer
+ return m_Devourer.Devour( this ); // Devour the corpse if it hasn't
+ }
+
public override void SendInfoTo( NetState state, bool sendOplPacket )
{
base.SendInfoTo( state, sendOplPacket );
@@ -780,7 +884,7 @@ namespace Server.Items
if ( gathered && !didntFit )
{
- m_Carved = true;
+ SetFlag( CorpseFlag.Carved, true );
if ( ItemID == 0x2006 )
{
@@ -941,7 +1045,7 @@ namespace Server.Items
Mobile dead = m_Owner;
- if ( m_Carved || dead == null )
+ if ( GetFlag( CorpseFlag.Carved ) || dead == null )
{
from.SendLocalizedMessage( 500485 ); // You see nothing useful to carve from the corpse.
}
@@ -956,7 +1060,7 @@ namespace Server.Items
new RightArm().MoveToWorld( Location, Map );
new Head( dead.Name ).MoveToWorld( Location, Map );
- m_Carved = true;
+ SetFlag( CorpseFlag.Carved, true );
ProcessDelta();
SendRemovePacket();
diff --git a/Scripts/Mobiles/AI/Creature/BaseCreature.cs b/Scripts/Mobiles/AI/Creature/BaseCreature.cs
index 71f01506c..98d315877 100644
--- a/Scripts/Mobiles/AI/Creature/BaseCreature.cs
+++ b/Scripts/Mobiles/AI/Creature/BaseCreature.cs
@@ -955,6 +955,22 @@ namespace Server.Mobiles
base.OnBeforeSpawn( location, m );
}
+ public virtual void OnBeforeParagonConvert()
+ {
+ }
+
+ public virtual void OnAfterParagonConvert()
+ {
+ }
+
+ public virtual void OnBeforeParagonUnConvert()
+ {
+ }
+
+ public virtual void OnAfterParagonUnConvert()
+ {
+ }
+
public override ApplyPoisonResult ApplyPoison( Mobile from, Poison poison )
{
if ( !Alive || IsDeadPet )
diff --git a/Scripts/Mobiles/AI/Creature/Paragon.cs b/Scripts/Mobiles/AI/Creature/Paragon.cs
index a699c6f89..ccb1a8de3 100644
--- a/Scripts/Mobiles/AI/Creature/Paragon.cs
+++ b/Scripts/Mobiles/AI/Creature/Paragon.cs
@@ -46,6 +46,8 @@ namespace Server.Mobiles
if ( bc.IsParagon )
return;
+ bc.OnBeforeParagonConvert();
+
bc.Hue = Hue;
if ( bc.HitsMaxSeed >= 0 )
@@ -88,6 +90,8 @@ namespace Server.Mobiles
if( Math.Abs( bc.Karma ) > 32000 )
bc.Karma = 32000 * Math.Sign( bc.Karma );
}
+
+ bc.OnAfterParagonConvert();
}
public static void UnConvert( BaseCreature bc )
@@ -95,6 +99,7 @@ namespace Server.Mobiles
if ( !bc.IsParagon )
return;
+ bc.OnBeforeParagonUnConvert();
bc.Hue = 0;
if ( bc.HitsMaxSeed >= 0 )
@@ -126,6 +131,8 @@ namespace Server.Mobiles
bc.Fame = (int)( bc.Fame / FameBuff );
if ( bc.Karma != 0 )
bc.Karma = (int)( bc.Karma / KarmaBuff );
+
+ bc.OnAfterParagonUnConvert();
}
public static bool CheckConvert( BaseCreature bc )
diff --git a/Scripts/Mobiles/Monsters/Misc/Melee/PlagueBeast.cs b/Scripts/Mobiles/Monsters/Misc/Melee/PlagueBeast.cs
index 18ee2b94f..d32fd01ad 100644
--- a/Scripts/Mobiles/Monsters/Misc/Melee/PlagueBeast.cs
+++ b/Scripts/Mobiles/Monsters/Misc/Melee/PlagueBeast.cs
@@ -2,12 +2,37 @@ using System;
using System.Collections;
using Server.Items;
using Server.Targeting;
+using Server.Network;
namespace Server.Mobiles
{
[CorpseName( "a plague beast corpse" )]
- public class PlagueBeast : BaseCreature
+ public class PlagueBeast : BaseCreature, IDevourable
{
+ private int m_DevourTotal;
+ private int m_DevourGoal;
+ private bool m_HasMetalChest = false;
+
+ [CommandProperty( AccessLevel.GameMaster )]
+ public int TotalDevoured
+ {
+ get { return m_DevourTotal; }
+ set { m_DevourTotal = value; }
+ }
+
+ [CommandProperty( AccessLevel.GameMaster )]
+ public int DevourGoal
+ {
+ get { return m_DevourGoal; }
+ set { m_DevourGoal = value; }
+ }
+
+ [CommandProperty( AccessLevel.GameMaster )]
+ public bool HasMetalChest
+ {
+ get { return m_HasMetalChest; }
+ }
+
[Constructable]
public PlagueBeast() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 )
{
@@ -43,9 +68,11 @@ namespace Server.Mobiles
if ( Utility.RandomDouble() < 0.80 )
PackItem( new PlagueBeastGland() );
- if ( Core.ML && Utility.RandomDouble() < .33 )
+ if ( Core.ML && Utility.RandomDouble() < 0.33 )
PackItem( Engines.Plants.Seed.RandomPeculiarSeed(4) );
+ m_DevourTotal = 0;
+ m_DevourGoal = Utility.RandomMinMax( 15, 25 ); // The goal amount of how many corpses must devour before metal chest is rewarded
}
public override void GenerateLoot()
@@ -55,7 +82,24 @@ namespace Server.Mobiles
// TODO: dungeon chest, healthy gland
}
- // TODO: Poison attack
+ public override void OnAfterParagonConvert()
+ {
+ m_DevourGoal += 25;
+ }
+
+ public override void OnAfterParagonUnConvert()
+ {
+ m_DevourGoal -= 25;
+ }
+
+ public override void OnGaveMeleeAttack( Mobile defender )
+ {
+ base.OnGaveMeleeAttack( defender );
+
+ defender.ApplyPoison( this, IsParagon ? Poison.Lethal : Poison.Deadly );
+ defender.FixedParticles( 0x374A, 10, 15, 5021, EffectLayer.Waist );
+ defender.PlaySound( 0x1CB );
+ }
public override void OnDamagedBySpell( Mobile caster )
{
@@ -119,13 +163,103 @@ namespace Server.Mobiles
public override void Serialize( GenericWriter writer )
{
base.Serialize( writer );
- writer.Write( (int) 0 );
+ writer.Write( (int) 1 );
+
+ writer.Write( m_HasMetalChest );
+ writer.Write( m_DevourTotal );
+ writer.Write( m_DevourGoal );
}
public override void Deserialize( GenericReader reader )
{
base.Deserialize( reader );
int version = reader.ReadInt();
+
+ switch( version )
+ {
+ case 1:
+ {
+ m_HasMetalChest = reader.ReadBool();
+ m_DevourTotal = reader.ReadInt();
+ m_DevourGoal = reader.ReadInt();
+ break;
+ }
+ }
+ }
+
+ public override void OnThink()
+ {
+ base.OnThink();
+
+ // Check to see if we need to devour any corpses
+ IPooledEnumerable eable = GetItemsInRange( 3 ); // Get all corpses in range
+
+ foreach( Item item in eable )
+ {
+ if( item is Corpse ) // For each Corpse
+ {
+ Corpse corpse = item as Corpse;
+
+ // Ensure that the corpse was killed by us
+ if( corpse != null && corpse.Killer == this && corpse.Owner != null )
+ {
+ if( !corpse.DevourCorpse() && !corpse.Devoured )
+ PublicOverheadMessage( MessageType.Emote, 0x3B2, 1053032 ); // * The plague beast attempts to absorb the remains, but cannot! *
+ }
+ }
+ }
+ eable.Free();
+ }
+
+ #region IDevourable Members
+
+ public bool Devour( Corpse corpse )
+ {
+ if( corpse == null || corpse.Owner == null ) // sorry we can't devour because the corpse's owner is null
+ return false;
+
+ if( corpse.Owner.Body.IsHuman )
+ corpse.TurnToBones(); // Not bones yet, and we are a human body therefore we turn to bones.
+
+ IncreaseHits( (int)Math.Ceiling( (double)corpse.Owner.HitsMax * 0.75 ) );
+ m_DevourTotal++;
+
+ PublicOverheadMessage( MessageType.Emote, 0x3B2, 1053033 ); // * The plague beast absorbs the fleshy remains of the corpse *
+
+ if( !m_HasMetalChest && m_DevourTotal >= m_DevourGoal )
+ {
+ PackItem( new MetalChest() );
+ m_HasMetalChest = true;
+ }
+
+ return true;
+ }
+
+ #endregion
+
+ private void IncreaseHits( int hp )
+ {
+ int maxhits = (int)(2000 * (IsParagon ? Paragon.HitsBuff : 1));
+
+ if( hp < 1000 && !Core.AOS )
+ hp = (hp * 100) / 60;
+
+ if( HitsMaxSeed >= maxhits )
+ {
+ HitsMaxSeed = maxhits;
+ Hits += hp + Utility.RandomMinMax( 10, 20 ); // increase the hp until it hits if it goes over it'll max at 2000
+ // Also provide heal for each devour on top of the hp increase
+ }
+ else
+ {
+ int min = (hp / 2) + 10;
+ int max = hp + 20;
+ int hpToIncrease = Utility.RandomMinMax( min, max );
+
+ HitsMaxSeed += hpToIncrease;
+ Hits += hpToIncrease;
+ // Also provide heal for each devour
+ }
}
}
}
\ No newline at end of file