Full Version : MirrorImage Spell
xmlspawner >>Scripting Support >>MirrorImage Spell


<< Prev | Next >>

Haazen- 07-06-2006
I have had MirrorImage spell on our shard for near 6 months. The Clone acted very much like a summoned creature that magery casts. I am not real happy with the new release of MirrorImage. It is not a fighting companion, but rather just a distraction. I have finally got the MirrorImage Spell working more to what the players on our shard have been used to. And I am please with how it works now. There is just one issue I figure out.

It is meant to use 2 control slots. When you cast the first MirrorImage, it does use 2 slots. But each one after only uses 1. If you look at properties on the Clone, it shows 2 ControlSlots.

Any idea what I can do to make all Clones use 2 slots. Here is the code:

CODE
using System;
using System.Reflection;
using System.Collections.Generic;
using Server;
using Server.Items;
using Server.Mobiles;
using Server.Spells;
using Server.Spells.Necromancy;
using Server.Spells.Ninjitsu;

namespace Server.Spells.Ninjitsu
{
public class MirrorImage : NinjaSpell
{
 private static Dictionary<Mobile, Int32> m_CloneCount = new Dictionary<Mobile, Int32>();

 public static bool HasClone( Mobile m )
 {
  return m_CloneCount.ContainsKey( m );
 }

 public static void AddClone( Mobile m )
 {
  if ( m == null )
   return;

  if ( m_CloneCount.ContainsKey( m ) )
   m_CloneCount[m]++;
  else
   m_CloneCount[m] = 1;
 }

 public static void RemoveClone( Mobile m )
 {
  if ( m == null )
   return;

  if ( m_CloneCount.ContainsKey( m ) )
  {
   m_CloneCount[m]--;

   if ( m_CloneCount[m] == 0 )
    m_CloneCount.Remove( m );
  }
 }

 private static SpellInfo m_Info = new SpellInfo(
  "Mirror Image", null,
  SpellCircle.Seventh,
  -1,
  9002
  );

 public override double RequiredSkill{ get{ return 40.0; } }
 public override int RequiredMana{ get{ return 10; } }

 public override bool BlockedByAnimalForm{ get{ return false; } }

 public MirrorImage( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
 {
 }

 public override bool CheckCast()
 {
  if ( Caster.Mounted )
  {
   Caster.SendLocalizedMessage( 1063132 ); // You cannot use this ability while mounted.
   return false;
  }
  else if ( (Caster.Followers + 2) > Caster.FollowersMax )
  {
   Caster.SendLocalizedMessage( 1063133 ); // You cannot summon a mirror image because you have too many followers.
   return false;
  }
  else if ( Necromancy.TransformationSpell.UnderTransformation( Caster, typeof( HorrificBeastSpell ) ) )
  {
   Caster.SendLocalizedMessage( 1061091 ); // You cannot cast that spell in this form.
   return false;
  }

  return base.CheckCast();
 }

 public override bool CheckDisturb( DisturbType type, bool firstCircle, bool resistable )
 {
  return false;
 }

 public override void OnBeginCast()
 {
  base.OnBeginCast();

  Caster.SendLocalizedMessage( 1063134 ); // You begin to summon a mirror image of yourself.
 }

 public override void OnCast()
 {
  if ( Caster.Mounted )
  {
   Caster.SendLocalizedMessage( 1063132 ); // You cannot use this ability while mounted.
  }
  else if ( (Caster.Followers + 2) > Caster.FollowersMax )
  {
   Caster.SendLocalizedMessage( 1063133 ); // You cannot summon a mirror image because you have too many followers.
  }
  else if ( Necromancy.TransformationSpell.UnderTransformation( Caster, typeof( HorrificBeastSpell ) ) )
  {
   Caster.SendLocalizedMessage( 1061091 ); // You cannot cast that spell in this form.
  }
  else if ( CheckSequence() )
  {
   Caster.FixedParticles( 0x376A, 1, 14, 0x13B5, EffectLayer.Waist );
   Caster.PlaySound( 0x511 );

   new Clone( Caster ).MoveToWorld( Caster.Location, Caster.Map );
   
  }

  FinishSequence();
 }
}
}

namespace Server.Mobiles
{
public class Clone : BaseCreature
{
 private Mobile m_Caster;

 public Clone( Mobile caster ) : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 )
 {
  m_Caster = caster;

  Body = caster.Body;

  Hue = caster.Hue;
  Female = caster.Female;

  Name = caster.Name;
  NameHue = caster.NameHue;

  Title = caster.Title;
  Kills = caster.Kills;

  HairItemID = caster.HairItemID;
  HairHue = caster.HairHue;

  FacialHairItemID = caster.FacialHairItemID;
  FacialHairHue = caster.FacialHairHue;

  for ( int i = 0; i < caster.Skills.Length; ++i )
  {
   Skills[i].Base = caster.Skills[i].Base;
   Skills[i].Cap = caster.Skills[i].Cap;
  }

  for( int i = 0; i < caster.Items.Count; i++ )
  {
   if ( !(caster.Items[i] is Backpack) && !(caster.Items[i] is BankBox ) )
    AddItem( CloneItem( caster.Items[i] ) );
  }

  double scaler = 2.0;

  Str = (int)(caster.Str / scaler);
  Dex = (int)(caster.Dex / scaler);
  Int = (int)(caster.Int / scaler);
  RawStr = (int)(caster.RawStr / scaler);
  RawDex = (int)(caster.RawDex / scaler);
  RawInt = (int)(caster.RawInt / scaler);
  StatCap = (int)(caster.StatCap / scaler);
  Hits = (int)(caster.Hits / scaler);
  Mana = (int)(caster.Mana / scaler);
  Stam = (int)(caster.Stam / scaler);

  Summoned = true;
  SummonMaster = caster;
  ControlSlots = 2;
  Controlled = true;
  ControlMaster = caster;
  ControlOrder = OrderType.Guard;
  ControlTarget = caster;

  TimeSpan duration = TimeSpan.FromSeconds( 30 + caster.Skills.Ninjitsu.Fixed / 40 );

  new UnsummonTimer( caster, this, duration ).Start();
  SummonEnd = DateTime.Now + duration;

  MirrorImage.AddClone( m_Caster );
 }

 public override bool IsHumanInTown() { return false; }

 private Item CloneItem( Item item )
 {
  Type t = item.GetType();
  ConstructorInfo c = t.GetConstructor( Type.EmptyTypes );
  object o = c.Invoke( null );

  Item newItem = (Item)o;
  CopyProps( newItem, item );
  item.OnAfterDuped( newItem );
  newItem.Layer = item.Layer;

  return newItem;
 }

 public override bool DeleteCorpseOnDeath { get { return true; } }

 public override void OnDelete()
 {
  Effects.SendLocationParticles( EffectItem.Create( Location, Map, EffectItem.DefaultDuration ), 0x3728, 10, 15, 5042 );

  base.OnDelete();
 }

 public override void OnAfterDelete()
 {
  MirrorImage.RemoveClone( m_Caster );
  base.OnAfterDelete();
 }

 public override bool IsDispellable { get { return false; } }
 public override bool Commandable { get { return true; } }

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

 public override void Serialize( GenericWriter writer )
 {
  base.Serialize( writer );

  writer.WriteEncodedInt( 0 ); // version

  writer.Write( m_Caster );
 }

 public override void Deserialize( GenericReader reader )
 {
  base.Deserialize( reader );

  int version = reader.ReadEncodedInt();

  m_Caster = reader.ReadMobile();

  MirrorImage.AddClone( m_Caster );
 }

 private static void CopyProps( Item dest, Item src )
 {
  PropertyInfo[] props = src.GetType().GetProperties();
  for ( int i = 0; i < props.Length; i++ )
  {
   try
   {
    if ( props[i].CanRead && props[i].CanWrite )
    {
     props[i].SetValue( dest, props[i].GetValue( src, null ), null );
    }
   }
   catch
   {
   }
  }
 }
}
}


Thank you all very much.

nim- 07-07-2006
Hello

Here is the code of what happen when you set a value for the summonmaster
CODE

 public Mobile SummonMaster
 {
  get
  {
   return m_SummonMaster;
  }
  set
  {
   if ( m_SummonMaster == value )
    return;

   RemoveFollowers();
   m_SummonMaster = value;
   AddFollowers();

   Delta( MobileDelta.Noto );
  }
 }


In the Addfollower the master will have the number of creature he controle increase.

This is what you do in the clone creation.
CODE

 Summoned = true;
 SummonMaster = caster;
 ControlSlots = 2;


You set the summonmaster before setting the controleslot for the clone so at the time you set the summonmaster the ControlSlots = 1

change that part like this and it will cost 2 controlSlots

CODE

 ControlSlots = 2;
 Summoned = true;
 SummonMaster = caster;


Nim

Haazen- 07-07-2006
Thank you so very much Nim. I am so pleased.

Haazen- 07-09-2006
A new issue has come up in this script.
Here is the line that debug points to:
System.NullReferenceException: Object reference not set to an instance of an object.
line 199 object o = c.Invoke( null );

I took this line from the Dupe.cs script and don't really know what it is doing.

The items that are causing the crash are, Glove of Mining, Ancient Smithy Hammer, HuntersHeadress. The gloves and hammer have scripted skill bonus that can go above skill max. But the headress just has skill property set. I am sure there are other items we have not discovered yet that can also cause the crash.

We have tested some other items that have skill bonuses and don't cause the crash.

CODE
 private Item CloneItem( Item item )
 {
try{
  Type t = item.GetType();
  ConstructorInfo c = t.GetConstructor( Type.EmptyTypes );
  object o = c.Invoke( null );

  Item newItem = (Item)o;
  CopyProps( newItem, item );
  item.OnAfterDuped( newItem );
  newItem.Layer = item.Layer;

  return newItem;
}
catch{}
 }


I tried to put the error catch but It won't compile : All path do not return a value. I see that is true. I just don't know where or how to catch this error. Returning an item without properties set in most cases is not a problem. I just did all this in the first place so the weapons have some power. A kryss that is just art doesn't hit very hard. hehe

If you can just point me in a proper direction, I would greatly appreciate it.

Thank you.

nim- 07-09-2006
This is because that item does not have a constructor with no parameter.

co c is equal to null and that cause your error.

ex for the ancient smith hammer here are the constructor you have.


[Constructable]
public AncientSmithyHammer( int bonus ) : this( bonus, 600 )
....
[Constructable]
public AncientSmithyHammer( int bonus, int uses ) : base( uses, 0x13E4 )
....


In the catch or after it set a return null;

or prevent the error by checking if you find a constructor without parameters.

Nim

Haazen- 07-10-2006
Again, thank you Nim. Works great now. It puzzles me that the Dupe command can dupe these items but me using similar code will not dupe in this script. I am sure I am missing some other little thing. But, it is not an issue that these items do not dupe on MirrorImage. The important stuff like weapons and armor do.

Thanks again for your help.

nim- 07-10-2006
In the file dupe.cs it try to find a constructor without parameters.

and if they didn t find you you have that message.

CODE

   if ( !done )
   {
    from.SendMessage( "Unable to dupe.  Item must have a 0 parameter constructor." );
   }


Nim

Haazen- 07-10-2006
I added the check

ConstructorInfo c = t.GetConstructor( Type.EmptyTypes );

if ( c != null )

and did a return null in the catch

I am getting smarter. Do I look any smarter? hehe No comment required.

Thanks Nim