Game Development Community

dev|Pro Game Development Curriculum

Using scripts to make ArcaneFX datablocks

by Dave Young · 09/22/2008 (11:46 am) · 2 comments

It occurred to me this could be useful for some people, I've used this same process to make item effectrons, combat spells, animations, etc. I prefer to use ArcaneFX for animation choreography as it allows me to control things in a very precise and particular manner.

This example uses animations on pets, but it could just as easily be anything you want it to be.

Step 1: Prototype your AFX effect.

First you manually prototype and get working your effect, test it, etc. At that point you have created your base effect and just need to parameterize it for a batch writing technique.

In the example below, I basically have two unique parameters, the word Cat and the name of the anim clip I want to play. This might seem wasteful for animations only, but I add much more to these than a simple anim clip later on. I add sounds and visuals.

datablock afxAnimClipData(Cat_ActionClip_aggressive_CE)
{
  clipname = "aggressive";
  rate = 1.0;
};

datablock afxEffectWrapperData(Cat_ActionClip_aggressive_EW)
{
  effect = Cat_ActionClip_aggressive_CE;
  constraint = "caster";
};

datablock afxMagicSpellData(Cat_ActionClip_aggressive)
{
  allowMovementInterrupts = true;
  castingDur = 5.0;
  addCastingEffect = Cat_ActionClip_aggressive_EW;
  rpgSpellData = Cat_ActionClip_aggressive_RPG;
};

datablock afxRPGMagicSpellData(Cat_ActionClip_aggressive_RPG)
{
  allowMovementInterrupts = true;
  castingDur = 5.0;
  addCastingEffect = Cat_ActionClip_aggressive_EW;
  spellFXData = Cat_ActionClip_aggressive;
};

Step 2: Create a loop to write the datablocks

This is pretty easy, I write a couple of loops to run through some data, in this case it's the names of a bunch of animations, and for each one I write out a complete set of datablocks for each. This really only needs to get done once, after which time the blocks are written, though you could choose to do it every server restart if you really wanted to. Immediately after creating the spell it is exec'd. Notice that in the spell definitions I do the spellFXData and RPGSpellData associations as these animation spells are meant to live outside the normal AFX autoloading path. I also don't need any button or spellbook functionality on these spells, they are called by code.

$WritePetAnimations = false;

$Petsequence[Puppy_1] = "barking";
$Petsequence[Puppy_2] = "laydown";
$Petsequence[Puppy_3] = "layingidle";
$Petsequence[Puppy_4] = "standup";
$Petsequence[Puppy_5] = "jump";
$Petsequence[Puppy_6] = "pickup";
$Petsequence[Puppy_7] = "lowjump";
$Petsequence[Puppy_8] = "standtwoleg";
$Petsequence[Puppy_9] = "sniffing";
$Petsequence[Puppy_10] = "eatdrink";
$Petsequence[Puppy_11] = "poop";
$Petsequence[Puppy_12] = "pee";
$Petsequence[Puppy_13] = "lick";
$Petsequence[Puppy_14] = "scratch";
$Petsequence[Puppy_15] = "scratchbackloop";
$Petsequence[Puppy_16] = "chasetail";
$Petsequence[Puppy_17] = "begging";
$Petsequence[Puppy_18] = "fighting";
$Petsequence[Puppy_19] = "growling";
$Petsequence[Puppy_20] = "sleeping";
$Petsequence[Puppy_21] = "sleeponback";
$Petsequence[Puppy_22] = "sitting";
$Petsequence[Puppy_23] = "excited";
$Petsequence[Puppy_24] = "stalking";
$Petsequence[Puppy_25] = "limping";
$Petsequence[Puppy_26] = "injured";
$Petsequence[Puppy_27] = "nesting";
$NumPetSequences[Puppy] = 27;

$Petsequence[Cat_1] = "aggressive";
$Petsequence[Cat_2] = "laydown";
$Petsequence[Cat_3] = "layingidle";
$Petsequence[Cat_4] = "standup";
$Petsequence[Cat_5] = "jump";
$Petsequence[Cat_6] = "claw";
$Petsequence[Cat_7] = "lowjump";
$Petsequence[Cat_8] = "standtwoleg";
$Petsequence[Cat_9] = "sniffing";
$Petsequence[Cat_10] = "eatdrink";
$Petsequence[Cat_11] = "death";
$Petsequence[Cat_12] = "pee";
$Petsequence[Cat_13] = "lick";
$Petsequence[Cat_14] = "scratch";
$Petsequence[Cat_15] = "scratchground";
$Petsequence[Cat_16] = "chasetail";
$Petsequence[Cat_17] = "begging";
$Petsequence[Cat_18] = "jumphigh";
$Petsequence[Cat_19] = "puke";
$Petsequence[Cat_20] = "sleeping";
$Petsequence[Cat_21] = "startsleep";
$Petsequence[Cat_22] = "sitlook";
$Petsequence[Cat_23] = "nervous";
$Petsequence[Cat_24] = "stalk";
$Petsequence[Cat_25] = "rollover";
$Petsequence[Cat_26] = "rollonfloor";
$Petsequence[Cat_27] = "sitpurr";
$Petsequence[Cat_28] = "stretch";
$NumPetSequences[Cat] = 28;

function WritePetAnimations()
{
   $PetType[1] = "Puppy";
   $PetType[2] = "Cat";
   $NumPetTypes = 2;
   for(%pet=1; %pet<=$NumPetTypes; %pet++)
   {
      %pettypename = $PetType[%pet];
      for(%x=1; %x<$NumPetSequences[$PetType[%pet]]; %x++)
      {
         %animindex = %pettypename @ "_" @ %x;
         %petanimname = $Petsequence[%animindex];
         %spellname = %pettypename @ "_ActionClip_" @ %petanimname;
         if($WritePetAnimations)
            WritePetanimation(%pettypename,%petanimname);
         exec("./Animations/petAnim_" @ %spellname @ ".cs");
      }
   }
}


function WritePetanimation(%pettype,%animname)
{

   %spellname = %pettype @ "_ActionClip_" @ %animname;
   %itemFile = new FileObject();
   if( !%itemFile.openForWrite(expandFilename("./Animations/petAnim_" @ %spellname @ ".cs")) )
    {
      echo("Unable to open " @ %spellname @ " for output.");
      return false;
   }

   %sf="";
   %sf=%sf NL "//Autogenerated datablock for pet animation" @ %pettype
   NL "datablock afxAnimClipData(" @ %spellname @ "_CE)"
   NL "{"
   NL "  clipname = \"" @ %animname @ "\";"
   NL "  rate = 1.0;"
   NL "};";

   %sf = %sf NL
   "datablock afxEffectWrapperData(" @ %pettype @ "_ActionClip_" @ %animname @ "_EW)"
   NL "{"
   NL "  effect = " @ %spellname @ "_CE;"
   NL "  constraint = \"caster\";"
   NL "};";

   %sf = %sf NL
   "datablock afxMagicSpellData(" @ %spellname @ ")"
   NL "{"
   NL "  allowMovementInterrupts = true;"
   NL "  castingDur = 5.0;"
   NL "  addCastingEffect = " @ %spellname @ "_EW;"
   NL "  rpgSpellData = " @ %spellname @ "_RPG;"
   NL "};";

   %sf = %sf NL
   "datablock afxRPGMagicSpellData(" @ %spellname @ "_RPG)"
   NL "{"
   NL "  allowMovementInterrupts = true;"
   NL "  castingDur = 5.0;"
   NL "  addCastingEffect = " @ %spellname @ "_EW;"
   NL "  spellFXData = " @ %spellname @ ";"
   NL "};";

   %itemFile.writeLine(%sf);
   %itemFile.close();
}

To use this, I call WritePetAnimations when the server is started. I set the global $WritePetAnimations depending on whether or not I want them created, or simply exec'd.

I hope this is useful! You can expand upon this example to make weapon effects, effectrons, spells out of a database, etc. All you need to do is the three steps:

1) Model your working effect and test it
2) Parameterize it in a function which accepts inputs (replace " with \") and don't forget the end of line semicolons!
3) Run a loop to generate and/or exec them, feeding your own parameters.

This technique will also stop you from making typos. It's a design based approach to coding functionality and as such, needs to be thought about (ie designed) before you implement. You can get very complex inside the write function, and add lots of variables and logic branches for different cases you require.

#1
09/22/2008 (6:45 pm)
Thanks a lot , it is another implementation of afx launch time parameter. very useful
#2
09/24/2008 (8:54 am)
When AFX includes launch time parameters, this method would be redundant for effects mostly, unless you're building your effects out of a data-driven source like I am. It's also very useful for many other datablock definitions, like those you may use for mountable weapons or inventory items.