I actually have two questions. Both scripts are included for reference.

1. I am trying to create a custom monster that, upon death or nearing death (either is fine) the creature will blow up, using vita nex core effects. I tried to set it to happen if the creature's health was at or below 25, but it did not work. It compiles but does not function. Here is the code in question:

Code:
 public void DoSpecialAbilityBlowUp(Mobile target)
        {
           

            if (Hits <=25 )
            {
                this.Say(string.Format("Malfunction...detonation imminent!", target.Name));
                //Timer.DelayCall(TimeSpan.FromSeconds(4), new TimerCallback(BlowUpEffect)).Start();
                OnCoupDeGras(target);

            }
                       
        }

       public void BlowUpEffect(Mobile target)
        {
            new FireExplodeEffect(this, this.Map, 5, effectHandler: ExplosionDamage).Send();
           
        }

        public void OnCoupDeGras(Mobile from)
        {
           
            DoSpecialAbilityBlowUp(from);
        }

        public virtual void ExplosionDamage(EffectInfo info)
        {
            ArrayList list = new ArrayList();
            Effects.PlaySound(info.Source.Location, info.Map, 0x027);

            foreach (Mobile m in
                info.Source.Location.GetMobilesInRange(info.Map, 0)
                    .Where(m => m != null && !m.Deleted && m.CanBeHarmful(m, false, true)))
            {
                if (m == this || !CanBeHarmful(m))
                    continue;

                if (m.Player)
                    list.Add(m);
            }
            foreach (Mobile m in list)
            {
                DoHarmful(m);
                int toExplode = Utility.RandomMinMax(7, 15);
                m.Damage(toExplode, this);
            }
        }

2. Another effect I've created is for the creature to spew oil when struck. This effect works as desired, however the item I'm using isn't quite right. It is basically a blood puddle set to convert to a flame after a few seconds. The problem is if I set the puddle image to Hue=1; I also get a black flame. Is there a way to have the puddle hue black but the flame hue as default?
 

Attachments

  • X1N30HP.cs
    8.4 KB · Views: 2
  • OilSlick.cs
    2.9 KB · Views: 5
I've not messed with it, but have you tried creating the explosion in the OnBeforeDeath() function? Override it in the creature's file and add in your explosion effect, and I think that should go off whenever the creature dies. You should still have the 'return base.OnbeforeDeath();' at the end of course.
 
I did this with a mob years ago. It was immune to both ranged & magic attacks, so only melee worked on it. When it died, it tried to take you with it. Maybe something like this will help.
Code:
        public void Explode()
        {
            List<Mobile> list = new List<Mobile>();

            foreach (Mobile m in this.GetMobilesInRange(5))
            {
                if (!CanBeHarmful(m))
                    continue;

                if (m is BaseCreature && (((BaseCreature)m).Controlled || ((BaseCreature)m).Summoned))
                    list.Add(m);
                else if (m.Player)
                    list.Add(m);
            }

            foreach (Mobile m in list)
            {
                DoHarmful(m);

                m.FixedParticles(0x36BD, 20, 10, 5044, EffectLayer.Head);
                m.PlaySound(0x207);

                m.SendMessage("You take damage heavy from the exploding GolemMark2!");

                int toDamage = Utility.RandomMinMax(10, 90);
                m.Damage(toDamage, this);

            }
        }
        public override void OnGaveMeleeAttack(Mobile defender)
        {
            base.OnGaveMeleeAttack(defender);
            this.Kill();
            this.FixedParticles(0x36BD, 20, 10, 5044, EffectLayer.Head);
            this.PlaySound(0x207);
            Explode();
        }

        public override bool OnBeforeDeath()
        {
            Explode();
            this.FixedParticles(0x36BD, 20, 10, 5044, EffectLayer.Head);
            this.PlaySound(0x207);
            return base.OnBeforeDeath();
        }
 
I did this with a mob years ago. It was immune to both ranged & magic attacks, so only melee worked on it. When it died, it tried to take you with it. Maybe something like this will help.
Code:
        public void Explode()
        {
            List<Mobile> list = new List<Mobile>();

            foreach (Mobile m in this.GetMobilesInRange(5))
            {
                if (!CanBeHarmful(m))
                    continue;

                if (m is BaseCreature && (((BaseCreature)m).Controlled || ((BaseCreature)m).Summoned))
                    list.Add(m);
                else if (m.Player)
                    list.Add(m);
            }

            foreach (Mobile m in list)
            {
                DoHarmful(m);

                m.FixedParticles(0x36BD, 20, 10, 5044, EffectLayer.Head);
                m.PlaySound(0x207);

                m.SendMessage("You take damage heavy from the exploding GolemMark2!");

                int toDamage = Utility.RandomMinMax(10, 90);
                m.Damage(toDamage, this);

            }
        }
        public override void OnGaveMeleeAttack(Mobile defender)
        {
            base.OnGaveMeleeAttack(defender);
            this.Kill();
            this.FixedParticles(0x36BD, 20, 10, 5044, EffectLayer.Head);
            this.PlaySound(0x207);
            Explode();
        }

        public override bool OnBeforeDeath()
        {
            Explode();
            this.FixedParticles(0x36BD, 20, 10, 5044, EffectLayer.Head);
            this.PlaySound(0x207);
            return base.OnBeforeDeath();
        }


Thanks for this - looks like it'd work but I'm already using OnGaveMeleeAttack for the oil spill. Is there an alternative?
 
Thanks for this - looks like it'd work but I'm already using OnGaveMeleeAttack for the oil spill. Is there an alternative?

...I'm pretty sure his creature was just a suicide bomber that blows up whenever it attacks OR you kill it.


Also, be sure not to confuse OnGaveMeleeAttack() and OnGotMeleeAttack().
 
@GriffonSpade - I tried to do what I think you were suggesting, but that didn't seem to work. Take a look at what I did here and tell me if I misunderstood

Code:
public void OnBeforeDeath(Mobile target)
        {
       

           
            {
                this.Say(string.Format("Malfunction...detonation imminent!", target.Name));
                //Timer.DelayCall(TimeSpan.FromSeconds(4), new TimerCallback(BlowUpEffect)).Start();
                OnCoupDeGras(target);

            }

        }

        public void BlowUpEffect(Mobile target)
        {
            new FireExplodeEffect(this, this.Map, 5, effectHandler: ExplosionDamage).Send();

        }

        public void OnCoupDeGras(Mobile from)
        {

            OnBeforeDeath(from);
        }

        public virtual void ExplosionDamage(EffectInfo info)
        {
            ArrayList list = new ArrayList();
            Effects.PlaySound(info.Source.Location, info.Map, 0x027);

            foreach (Mobile m in
                info.Source.Location.GetMobilesInRange(info.Map, 0)
                    .Where(m => m != null && !m.Deleted && m.CanBeHarmful(m, false, true)))
            {
                if (m == this || !CanBeHarmful(m))
                    continue;

                if (m.Player)
                    list.Add(m);
            }
            foreach (Mobile m in list)
            {
                DoHarmful(m);
                int toExplode = Utility.RandomMinMax(7, 15);
                m.Damage(toExplode, this);
            }
        }
 
You should not be making up a new OnBeforeDeath(), you should be using the one that already exists and overriding it for one.
 
Here's an example: The SavageRider's override for OnBeforeDeath().

Code:
  public override bool OnBeforeDeath()
  {
  IMount mount = Mount;

  if (mount != null)
  mount.Rider = null;

  if (mount is Mobile)
  ((Mobile)mount).Delete();

  return base.OnBeforeDeath();
  }
 
That method does not pass a variable, and I think I need it to in order to use the effect. If I leave my variable in place, it says "no suitable method to override". If I take it out, it says "not all code paths return a value" and several other errors appear elsewhere because it's looking for that variable. So I guess I don't see how it can be done with a override bool method.
 
In the OnBeforeDeath() override, you can cast this.FindMostRecentDamager to a PlayerMobile, and if it's not null, do damage to it. Let me know if you need more code than that.
 
But if the player is a caster or archer wouldn't the effect happen at their location? I'm trying to get the creature to appear as if it has exploded. I think @Hammerhand is the closest with what I'm trying to do, I just need an alternative to OnGotMeleeAttack since I'm already using that.
 
Here's a 99% functioning sample:

Here's the non-functioning line. It's supposed to make a fireball-explosion effect appear on all those hit by the explosion. I can make it work with explosion-explosion effect easily enough, but I don't know what the second, third, and fourth numbers represent well enough to make them work. (Their variables are 'speed', 'duration', and 'effect'. And the duration does not appear to be related to the number of frames that the animation referenced in the first number has.)
m.FixedParticles(0x36CA, 20, 10, 4019, EffectLayer.Head);

Code:
    public override bool OnBeforeDeath()
     {
       Explode();
       return base.OnBeforeDeath();
     }

     public void Explode()
     {
       foreach ( Mobile m in GetMobilesInRange( 2 ) )
       {
         if ( m == this || !CanBeHarmful( m ) )
         {
           continue;
         }

         if ( !IsEnemy(m) )
         {
           continue;
         }

         int damage = (int)( 200 / ( 2 + GetDistanceToSqrt(m) ) );

         DoHarmful( m );
         AOS.Damage( m, this, damage, 0, 100, 0, 0, 0 );
         m.FixedParticles(0x36CA, 20, 10, 4019, EffectLayer.Head);
       }

         Effects.PlaySound(Location, Map, 0x307);
         Effects.SendLocationParticles( EffectItem.Create( Location, Map, EffectItem.DefaultDuration ), 0x36BD, 20, 10, 5044 );
     }
 
Last edited:
Yep I think you're right, that's about 99% where we want to be. Now I want to use the vita nex system for the visual effect. Here's what I had before:

new EnergyExplodeEffect(target, target.Map, 5, effectHandler: ExplosionDamage).Send();

(IPoint3D start, Map map, [int range = 5], [int repeat = 0], [TimeSpan? interval = null], [Action <EffectInfo> effectHandler = null], [Action callback = null])

I really only understand how to make it work using the method I had before, so I would need some help making this work with the method you've provided there.
 
A few questions...
Is this supposed to be centered on the exploder? (I'm assuming so)
Should this blast hit enemies through walls? (I'm assuming not)
Should this blast hit non-player enemies? (I'm assuming so)

So, try this:

Code:
    public override void OnThink()
     {
       if (Hits <= 25 )
       {
         this.Say(string.Format("Malfunction...detonation imminent!", target.Name));
         Timer.DelayCall(TimeSpan.FromSeconds(4), new TimerCallback(Kill)).Start();
       }
       base.OnThink();
     }

     public override bool OnBeforeDeath()
     {
       new FireExplodeEffect(this, this.Map, 5, effectHandler: ExplosionDamage).Send();
       return base.OnBeforeDeath();
     }

     public virtual void ExplosionDamage(EffectInfo info)
     {
       ArrayList list = new ArrayList();
       Effects.PlaySound(info.Source.Location, info.Map, 0x027);
       foreach (Mobile m in info.Map.GetMobilesInRange(info.Source.Location, 5))
       {
         if (m == this || !CanBeHarmful(m) || !m.InLOS(info.Source.Location))
           continue;
         if (!IsEnemy(m))
           continue;
/*
         // Try this below if the above check spits out an error due to the creature no longer existing
         if (!m.Player && !(m is BaseCreature && ((BaseCreature)m).ControlMaster.Player))
           continue;
*/
         DoHarmful(m);
         int toExplode = Utility.RandomMinMax(7, 15);
         m.Damage(toExplode, this);
       }
     }
[/quote]

Also, it's notable that the '5' in the getmobilesinrange should probably be inherited through info.Source.???, but I don't know what the designation is.
 
Last edited:
Ok yes this is exactly what I was going for, thank you! Sorry if I wasn't clear to begin with. Any thoughts on the 2nd question? I suppose it could be hydraulic fluid instead of oil heh
 
Any time you set combust to true, set hue to 0, and any time when it's false, set hue to 1?

Like so:
Oil Slick:
Code:
using System;
using Server;
using Server.Mobiles;
using Server.Spells;
using System.Collections;

namespace Server.Items
{
   public class OilSlick : Item
   {
     private TimeSpan m_Duration;

     private int m_MinDamage;
     private int m_MaxDamage;

     private DateTime m_Created;

     private bool m_Combust;

     private Timer m_Timer;

     [CommandProperty( AccessLevel.GameMaster )]
     public bool Combust
     {
       get
       {
         return m_Combust;
       }
       set
       {
         if (m_Combust == value)
           return;

         m_Combust = value;

         if (m_Combust)
         {
           Name = "a burning oil slick";
           ItemID = 0x398C;
           Hue = 0;
         }
         else
         {
           Name = DefaultName;
           ItemID = 0x122A;
           Hue = 1;
         }
       }
     }

     [CommandProperty( AccessLevel.GameMaster )]
     public TimeSpan Duration{ get{ return m_Duration; } set{ m_Duration = value; } }

     [CommandProperty( AccessLevel.GameMaster )]
     public int MinDamage
     {
       get
       {
         return m_MinDamage;
       }
       set
       {
         if ( value < 1 )
           value = 1;

         m_MinDamage = value;
       }
     }

     [CommandProperty( AccessLevel.GameMaster )]
     public int MaxDamage
     {
       get
       {
         return m_MaxDamage;
       }
       set
       {
         if ( value < 1 )
           value = 1;

         if ( value < MinDamage )
           value = MinDamage;

         m_MaxDamage = value;
       }
     }

     [Constructable]
     public OilSlick() : this( TimeSpan.FromSeconds (Utility.Random(10) ), 5, 10 )
     {
     }

     public override string DefaultName { get { return "an oil slick"; } }

     [Constructable]
     public OilSlick( TimeSpan duration, int minDamage, int maxDamage )
       : base( 0x122A )
     {

       Movable = false;

       m_MinDamage = minDamage;
       m_MaxDamage = maxDamage;
       m_Created = DateTime.Now;
       m_Duration = duration;
       Hue = 1;

       m_Timer = Timer.DelayCall( TimeSpan.Zero, TimeSpan.FromSeconds( 5 ), new TimerCallback( OnTick ) );
     }

     public override void OnAfterDelete()
     {
       if( m_Timer != null )
       m_Timer.Stop();
     }

     private void OnTick()
     {
       DateTime now = DateTime.Now;
       TimeSpan age = now - m_Created;

       if( age > m_Duration )
       {
         Delete();
       }
       else
       {
         if( !Combust && age > (m_Duration - age) )
         {
           Combust = true;
         }

         if (Combust)
         {
           foreach( Mobile m in GetMobilesInRange( 0 ) )
           Damage( m );
         }
       }
     }


     public override bool OnMoveOver( Mobile m )
     {
       Damage( m );
       return true;
     }

     public void Damage( Mobile m )
     {
       m.Damage( Utility.RandomMinMax( MinDamage, MaxDamage ) );
     }

     public OilSlick( Serial serial ) : base( serial )
     {
     }

     public override void Serialize( GenericWriter writer )
     {
       //Don't serialize these
     }

     public override void Deserialize( GenericReader reader )
     {
     }
   }
}
Creature:
Code:
using System;
using Server;
using Server.Misc;
using Server.Mobiles;
using Server.Items;
using Server.Network;
using System.Linq;
using VitaNex.FX;

namespace Server.Mobiles
{
   [CorpseName( "a ruined machine" )]
   public class X1N30HP : BaseCreature
   {
     private bool m_Stunning;

     [Constructable]
     public X1N30HP() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.3, 0.6 )
     {
       Name = "X1 N30 HP";
       Body = 768;

       SetStr(851, 950);
       SetDex(100, 125);
       SetInt(61, 90);

       SetHits(7500, 8500);
       SetStam(100, 125);
       SetMana(61, 90);

       SetDamage(40, 55);

       SetSkill(SkillName.MagicResist, 100.1, 115.0);
       SetSkill(SkillName.Tactics, 90.1, 105.0);
       SetSkill(SkillName.Wrestling, 90.1, 105.0);

       SetResistance(ResistanceType.Physical, 60, 70);
       SetResistance(ResistanceType.Fire, 40, 50);
       SetResistance(ResistanceType.Cold, 15, 25);
       SetResistance(ResistanceType.Poison, 15, 25);
       SetResistance(ResistanceType.Energy, 15, 25);

       Fame = 30000;
       Karma = -30000;
       VirtualArmor = 75;

       if ( 0.1 > Utility.RandomDouble() )
         PackItem( new PowerCrystal() );

       if ( 0.4 > Utility.RandomDouble() )
         PackItem( new ClockworkAssembly() );
     }

     public override void OnDeath( Container c )
     {
       base.OnDeath( c );

       if ( 0.05 > Utility.RandomDouble() )
       {
         if ( !IsParagon )
         {
           if ( 0.75 > Utility.RandomDouble() )
             c.DropItem( DawnsMusicGear.RandomCommon );
           else
             c.DropItem( DawnsMusicGear.RandomUncommon );
         }
         else
         {
           c.DropItem( DawnsMusicGear.RandomRare );
         }
       }
     }

     public override void GenerateLoot()
     {
       AddLoot( LootPack.Rich );
       AddLoot( LootPack.Gems, 1 );
     }

     public override bool BardImmune{ get{ return !Core.AOS; } }
     public override bool BleedImmune{ get{ return true; } }
     public override Poison PoisonImmune{ get{ return Poison.Lethal; } }
     public override int Meat{ get{ return 1; } }
     public override int TreasureMapLevel{ get{ return 5; } }
     public override bool AutoDispel { get { return true; } }
     public override double AutoDispelChance { get { return 1.0; } }
     public override bool Unprovokable { get { return Core.LBR; } }
     public override bool Uncalmable { get { return Core.LBR; } }
     public override bool CanMoveOverObstacles { get { return true; } }
     public override bool CanDestroyObstacles { get { return true; } }

     public override int GetDeathSound() { return 0x423; }
     public override int GetAttackSound() { return 0x23B; }
     public override int GetHurtSound() { return 0x140; }

     public override void OnGaveMeleeAttack( Mobile defender )
     {
       base.OnGaveMeleeAttack( defender );

       if ( !m_Stunning && 0.3 > Utility.RandomDouble() )
       {
         m_Stunning = true;

         defender.Animate( 21, 6, 1, true, false, 0 );
         PlaySound( 0xEE );
         defender.LocalOverheadMessage( MessageType.Regular, 0x3B2, false, "You have been stunned by a colossal blow!" );

         BaseWeapon weapon = Weapon as BaseWeapon;

         if ( weapon != null )
         weapon.OnHit( this, defender );

         if ( defender.Alive )
         {
           defender.Frozen = true;
           Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), new TimerStateCallback( Recover_Callback ), defender );
         }
       }
     }

     private void Recover_Callback( object state )
     {
       Mobile defender = state as Mobile;

       if ( defender != null )
       {
         defender.Frozen = false;
         defender.Combatant = null;
         defender.LocalOverheadMessage( MessageType.Regular, 0x3B2, false, "You recover your senses." );
       }

       m_Stunning = false;
     }

     public void SpillOil()
     {
       for (int i = 0; i < 10; ++i)
       {
         int x1 = X + Utility.Random(-8,8);
         int y1 = Y + Utility.Random(-8,8);
         int z1 = Map.GetAverageZ(x1, y1);

         Point3D loc = new Point3D(x1, y1, z1);
         bool validLocation = Map.CanFit(loc, 16, false, false) && InLOS(loc);

         if ( validLocation )
           new OilSlick().MoveToWorld(loc, Map);
       }
     }

     public override void OnDamagedBySpell(Mobile from)
     {
       if (0.15 >= Utility.RandomDouble())
         SpillOil();

       base.OnDamagedBySpell(from);
     }

     public override void OnGotMeleeAttack(Mobile from)
     {
       if (0.15 >= Utility.RandomDouble())
        SpillOil();

       base.OnGotMeleeAttack(from);
     }

     public override void OnThink()
     {
       if (Hits <= 25 )
       {
         Say("Malfunction...detonation imminent!");
         Timer.DelayCall(TimeSpan.FromSeconds(4), new TimerCallback(Kill)).Start();
       }

       base.OnThink();
     }

     public override bool OnBeforeDeath()
     {
       new FireExplodeEffect(this, Map, 5, effectHandler: ExplosionDamage).Send();

       return base.OnBeforeDeath();
     }

     public virtual void ExplosionDamage(EffectInfo info)
     {
       Effects.PlaySound(info.Source.Location, info.Map, 0x027);

       foreach (Mobile m in info.Map.GetMobilesInRange(info.Source.Location, 5))
       {
         if (m == this || !CanBeHarmful(m) || !m.InLOS(info.Source.Location))
           continue;

         if (!IsEnemy(m))
           continue;

         // Try this below if the above check spits out an error due to the creature no longer existing
         if (!m.Player && !(m is BaseCreature && ((BaseCreature)m).ControlMaster.Player))
           continue;

         DoHarmful(m);
         int toExplode = Utility.RandomMinMax(7, 15);
         m.Damage(toExplode, this);
       }
     }

     public X1N30HP( Serial serial ) : base( serial )
     {
     }

     public override void Serialize(GenericWriter writer)
     {
       base.Serialize(writer);
       writer.Write((int)0);
     }

     public override void Deserialize(GenericReader reader)
     {
       base.Deserialize(reader);
       int version = reader.ReadInt();
     }
   }
}
 
Last edited:

Active Shards

Donations

Total amount
$0.00
Goal
$1,000.00
Back