Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

First, include a mutations.xml in your mod that defines a new mutation.

Code Block
language

...

xml

...

title

...

Mutations.xml sample, adding a simple mod
linenumberstrue
collapsetrue

...

<?xml version="1.0" encoding="utf-8" ?>
<mutations>
  <category Name="Physical">
    <mutation Name="Udder" Cost="1" MaxSelected="1" Class="FreeholdTutorial_Udder" Exclusions="" Code="ea"></mutation>
 </category>
</mutations>

Then add a new .cs file in your mod that implements the class. Here's a skeletal implementation of the entry above. It must be in the XRL.World.Parts.Mutation namespace and must ultimately descend from BaseMutation. (Though not necessarily directly, if you have a very complex mod) 

 

Code Block
languagec#
titleSkeletal mutation example.
linenumberstrue
collapsetrue
using System;
using System.Collections.Generic;
using System.Text;

using XRL.Rules;
using XRL.Messages;
using ConsoleLib.Console;

namespace XRL.World.Parts.Mutation
{
    [Serializable]
    class FreeholdTutorial_Udder : BaseMutation
    {
        public FreeholdTutorial_Udder()
   

...

 

...

 

...

 

...

  {
      

...

      Name = "FreeholdTutorial_Udder";
 

...

 

...

 

...

 

...

 

...

       DisplayName 

...

= "Udder";
        }

        public override void Register(GameObject Object)
        {
        }
     

...

 

...

  
        public override string 

...

GetDescription()
        {
            

...

return "

...

"

...

;
        }

        public override string 

...

GetLevelText(int Level)
        {
            

...

string Ret = "You 

...

have udders.\n";
            return Ret;
        }

        public override 

...

bool 

...

BeforeRender(

...

Event 

...

E)
        {
            

...

if (ParentObject.IsPlayer())
            {
           

...

 

...

 

...

 

...

 

...

 if (ParentObject.pPhysics != null && ParentObject.pPhysics.CurrentCell != null)
    

...

 

...

 

...

 

...

 

...

 

...

 

...

 

...

     {
       

...

 

...

 

...

 

...

 

...

         ParentObject.pPhysics.CurrentCell.ParentZone.AddLight(ParentObject.pPhysics.CurrentCell.X, ParentObject.pPhysics.CurrentCell.Y, Level, LightLevel.Darkvision);

...

 

...

         

...

      }
   

...

 

...

 

...

 

...

 

...

 

...

    }
    

...

        return true;
     

...

 

...

 

...

 

...

}

...


...

        public override bool FireEvent(Event E)
 

...

 

...

 

...

 

...

 

...

 

...

  {
          

...

 

...

 

...

return 

...

base.

...

FireEvent(

...

E);
        }

  

...

  

...

 

...

 

...

 

...

 

...

public override bool ChangeLevel(int NewLevel)
        {
    

...

  

...

 

...

 

...

    return true;
       

...

 }

        public override bool Mutate(GameObject GO, 

...

int Level)
        {
        

...

 

...

 

...

 

...

 

...

return true;
        }

     

...

   public override 

...

bool Unmutate(GameObject GO)
        {
            

...

return true;
        }
    }

...

}

 

Here's a full example of the Flaming Hands mutation from the game's source code.

Code Block
languagec#
themeRDark
titleFlaming Hands - Full Example .cs
linenumberstrue
collapsetrue
using System;
using System.Collections.Generic;
using XRL.UI;
using ConsoleLib.Console;

namespace XRL.World.Parts.Mutation
{
    [Serializable]
    class FlamingHands : BaseMutation
    {
        

...

public FlamingHands(

...

)

...

 

...

 

...

 

...

 

...

 

...

 

...

 

...

 {
            Name = "FlamingHands";
            DisplayName = "Flaming 

...

Hands";

...

 

...

 

...

 

...

 

...

 

...

 

...

 

...

 }

        public Guid FlamingHandsActivatedAbilityID = Guid.Empty;
        public ActivatedAbilityEntry FlamingHandsActivatedAbility = null;

   

...

 

...

 

...

 

...

 

...

 

...

public 

...

override 

...

void Register(GameObject Object)
        {
            Object.RegisterPartEvent(this, "BeginEquip"); 

...

            Object.RegisterPartEvent(this, "CommandFlamingHands");
   

...

         Object.RegisterPartEvent(this, "AIGetOffensiveMutationList");
       

...

 }

...

 

...

 

...

 

...

     public override string GetDescription()
        {
            return "You emit jets of flame from your hands.";

...

 

...

 

...

 

...

 

...

    }

        public override string GetLevelText(int Level)
  

...

      {
            string Ret = "Emits a 9-square 

...

ray 

...

of 

...

flame 

...

in the direction of your choice\n";
            Ret += "Cooldown: 

...

10 rounds\n";
            Ret += "Damage: " + Level 

...

+ "d6\n";
            Ret += "Cannot wear gloves";
        

...

 

...

 

...

 

...

 return Ret;
        }

        public void Flame(Cell 

...

C, ScreenBuffer Buffer)
        {
            string Damage = Level + 

...

"

...

d6"

...

;

            Body pBody = ParentObject.GetPart("Body") as Body;
     

...

 

...

      int nHandCount = pBody.GetPart("Hands").Count-1;
            if (nHandCount 

...

> 0) Damage += "+" + nHandCount.ToString();

            if (C != null)
        

...

    {
                

...

List<GameObject> Objects = C.GetObjectsInCell();

            

...

    foreach (GameObject GO in Objects)
    

...

            {
 

...

 

...

             

...

 

...

 

...

 

...

  if( GO.PhasedMatches( ParentObject ) )
      

...

   

...

 

...

 

...

 

...

        {
    

...

 

...

 

...

 

...

 

...

 

...

 

...

             

...

 

...

GO.FireEvent(Event.New("TemperatureChange", "Amount", 310 + (30 * Level), "Owner", ParentObject));
            

...

 

...

 

...

 

...

 

...

 

...

 

...

      for (int x = 0; x < 5; x++) GO.ParticleText("&r

...

" + (char)(219 + Rules.Stat.Random(

...

0, 4)), 2.9f, 

...

1);
            

...

 

...

 

...

 

...

 

...

 

...

 

...

      for (int x = 0; x < 

...

5; x++) GO.ParticleText("&R" + (char)(219 + Rules.Stat.Random(0, 4)), 2.9f, 1);
     

...

 

...

 

...

 

...

 

...

 

...

 

...

             for 

...

(int 

...

x =

...

 0; x < 5; x++) GO.ParticleText("&W" + (char)(219 + Rules.Stat.Random(0, 4)), 2.9f, 1);
                  

...

 

...

 }
                }

...


...

 

...

 

...

 

...

 

...

            foreach (GameObject GO in 

...

C.GetObjectsWithPart("Combat"))
                

...

{
             

...

       if( GO.PhasedMatches( 

...

ParentObject ) )
       

...

 

...

 

...

 

...

 

...

         {
            

...

  

...

 

...

 

...

        Damage Dmg = new 

...

Damage(Rules.Stat.Roll(Damage));
                 

...

 

...

 

...

     Dmg.AddAttribute("Fire");
                     

...

 

...

 

...

 

...

Dmg.

...

AddAttribute("

...

Heat")

...

;

                        

...

Event eTakeDamage = 

...

Event.New("TakeDamage");
                 

...

 

...

 

...

 

...

 

...

 

...

 

...

 eTakeDamage.AddParameter("Damage", Dmg);
                

...

 

...

       eTakeDamage.AddParameter("Owner", ParentObject);
   

...

              

...

 

...

 

...

 

...

    eTakeDamage.AddParameter("Attacker", ParentObject);
       

...

                 

...

eTakeDamage.AddParameter("Message", "from %o flames!");

               

...

         

...

GO.FireEvent(eTakeDamage);
      

...

 

...

 

...

 

...

 

...

          }
    

...

 

...

 

...

 

...

 

...

 

...

       }
         

...

 

...

 

...

 

...

}

...


...

            Buffer.Goto(C.X, C.Y);
    

...

 

...

 

...

 

...

     string sColor = "&C";
        

...

    int r = Rules.Stat.Random(1, 3);
            if (

...

r == 1) sColor = "&R";
            if (r == 2) sColor = "&r";

...

            if (r == 3) sColor = "&W";

     

...

 

...

      r = Rules.Stat.Random(1, 3);
            if (r == 1) sColor 

...

+= "^R";
            if (r == 2) sColor += "^r";
            if (r == 3) sColor 

...

+= "^W";

            if( C.ParentZone == XRL.Core.XRLCore.Core.Game.ZoneManager.ActiveZone )
            {
 

...

 

...

              r = Rules.Stat.Random(1, 3);
           

...

     Buffer.Write(sColor + (char)(219 + Rules.Stat.Random(0, 4)));
          

...

      Popup._TextConsole.DrawBuffer(Buffer);
               

...

 System.Threading.Thread.Sleep(10);
            }
        }

        public override bool 

...

FireEvent(Event

...

 E)
        {
            if (E.ID == "AIGetOffensiveMutationList")
            {
        

...

 

...

 

...

 

...

 

...

 

...

 

...

 

...

 

...

int 

...

Distance 

...

= (int)E.GetParameter("Distance");
                GameObject Target = E.GetParameter("Target") as GameObject;

...

                List<XRL.World.AI.GoalHandlers.AICommandList> CommandList = (List<XRL.World.AI.GoalHandlers.AICommandList>)E.GetParameter("List");

    

...

 

...

 

...

 

...

 

...

 

...

       if (FlamingHandsActivatedAbility != 

...

null && FlamingHandsActivatedAbility.Cooldown <= 0 && Distance <= 9 && ParentObject.HasLOSTo(Target) ) CommandList.Add(new XRL.World.AI.GoalHandlers.AICommandList("CommandFlamingHands", 1));
             

...

   return true;
            }

       

...

 

...

 

...

 

...

  if (E.ID == "CommandFlamingHands")

...

            

...

{
           

...

     ScreenBuffer Buffer = new ScreenBuffer(80, 25);
                

...

Core.XRLCore.Core.RenderMapToBuffer(Buffer);

                List<Cell> TargetCell = PickLine(9, AllowVis.Any);
       

...

         if (TargetCell == null) return true;
                if 

...

(TargetCell.Count <= 

...

0) return true;

                if (TargetCell != null)
         

...

       {
                    if (TargetCell.Count == 1)

...

                    {
    

...

                    

...

if (ParentObject.IsPlayer())
                

...

            

...

if (UI.Popup.ShowYesNoCancel("Are you sure you want to target yourself?") != DialogResult.Yes)
    

...

 

...

 

...

 

...

             

...

        {
        

...

 

...

 

...

 

...

 

...

 

...

                 

...

 

...

 

...

return true;
                 

...

 

...

 

...

 

...

        }
        

...

            

...

}

  

...

 

...

                 if( FlamingHandsActivatedAbility != null 

...

) FlamingHandsActivatedAbility.Cooldown = 110;
                    

...

ParentObject.FireEvent(Event.New("UseEnergy", "Amount", 1000, "Type", "Physical Mutation"));
                    

...


                    

...

for (int x = 

...

0; x < 9 && x < TargetCell.Count; x++)
            

...

 

...

       {
         

...

             

...

  if (TargetCell.Count == 1 || TargetCell[x] != ParentObject.pPhysics.CurrentCell)
    

...

 

...

         

...

          

...

Flame(TargetCell[x],Buffer);

            

...

 

...

 

...

 

...

         foreach( 

...

GameObject 

...

GO 

...

in TargetCell[x].GetObjectsWithPart("Physics") )
        

...

             

...

 

...

 

...

 {
               

...

 

...

 

...

 

...

 

...

 

...

        if (GO.pPhysics.Solid) 
  

...

 

...

  

...

  

...

 

...

              

...

 

...

     {
   

...

          

...

 

...

 

...

 

...

 

...

 

...

 

...

         

...

    x = 999;
      

...

              

...

 

...

 

...

 

...

 

...

 

...

       break;
     

...

 

...

 

...

 

...

 

...

 

...

              

...

 

...

 

...

 

...

 }
           

...

             }
   

...

 

...

 

...

               }
 

...

 

...

 

...

             }
            }

           

...

 if (E.ID == "BeginEquip")

...

 

...

           {
 

...

 

...

 

...

 

...

            GameObject 

...

Equipment 

...

= E.GetParameter("Object") as GameObject;
                

...

string BodyPartName = 

...

E.

...

GetParameter("

...

BodyPartName") as string;

               

...

 if 

...

(BodyPartName == 

...

"

...

Hands")

...


                {
  

...

                  if 

...

(IsPlayer())
                

...

    {
        

...

              

...

 

...

 

...

UI.Popup.

...

Show("

...

Your 

...

flaming hands prevents you from equipping " + Equipment.DisplayName + "!");
     

...

 

...

 

...

             

...

}

...


        

...

          

...

 

...

 

...

E.bCancelled = 

...

true;
         

...

 

...

 

...

 

...

 

...

       return false;

...

             

...

 

...

 

...

 }
            } 

...

 

...

 

...

 

...

         return true;
  

...

      }


        

...

int 

...

OldFlame 

...

= -1

...

;
        

...

int OldVapor 

...

= -1

...

;

...


...

 

...

       public override bool ChangeLevel(int NewLevel)
     

...

 

...

 

...

 {
            Physics pPhysics 

...

= ParentObject.GetPart("Physics") as Physics;

            TemperatureOnHit pTemp = 

...

FlamesObject.GetPart("TemperatureOnHit") as TemperatureOnHit;
            

...

pTemp.Amount =  (Level*2) + "d8";

            return base.ChangeLevel(NewLevel);
        }

...


...

 

...

 

...

 

...

 

...

    public override bool Mutate(GameObject GO, int Level)
  

...

 

...

 

...

 

...

   {
         

...

   Unmutate(GO);

            

...

ActivatedAbilities 

...

pAA = 

...

GO.

...

GetPart("

...

ActivatedAbilities")

...

 as ActivatedAbilities;
            Physics pPhysics 

...

= GO.GetPart("Physics") as Physics;

            

...

if (

...

pPhysics != null)
            {
   

...

             OldFlame = pPhysics.FlameTemperature;
     

...

           OldVapor = pPhysics.VaporTemperature;
            }
   

...

         
            Body pBody = 

...

GO.

...

GetPart("Body") as Body;
            if (pBody != null)
          

...

  {
                GO.FireEvent(Event.New("CommandForceUnequipObject", 

...

"BodyPartName", "Hands"));
                

...

FlamesObject = GameObjectFactory.Factory.CreateObject("Ghostly Flames");
         

...

       Event eCommandEquipObject = Event.New("CommandEquipObject");
   

...

 

...

 

...

 

...

          eCommandEquipObject.AddParameter("Object", FlamesObject);
 

...

               

...

eCommandEquipObject.AddParameter("BodyPartName", "Hands");
                

...

GO.

...

FireEvent(

...

eCommandEquipObject);
            }

   

...

 

...

 

...

       FlamingHandsActivatedAbilityID = pAA.AddAbility("Flaming Hands", "CommandFlamingHands", 

...

"Physical Mutation");
            FlamingHandsActivatedAbility 

...

= pAA.AbilityByGuid[FlamingHandsActivatedAbilityID];
        

...

    return 

...

 

...

Code Block
using System;
using System.Collections.Generic;
using XRL.UI;
using ConsoleLib.Console;

namespace XRL.World.Parts.Mutation
{
    [Serializable]
    class FlamingHands : BaseMutation
    {

 

GetDescription and GetLevelText are called to generate the descriptive for a given level of the mutation.

...

true;
        }

        public GameObject FlamesObject = null;

        public override bool Unmutate(GameObject GO)
        {
            

...

Physics 

...

pPhysics 

...

= GO.GetPart("Physics") as Physics;

     

...

 

...

      if (pPhysics != 

...

null)

...

 

...

 

...

 

...

         {
            

...

 

...

 

...

  if (OldFlame != -1) pPhysics.FlameTemperature = OldFlame;
                if (OldVapor 

...

!= -1) pPhysics.BrittleTemperature = OldVapor;
           

...

 

...

 

...

 

...

 

...

 

...

OldFlame 

...

= -1;
            

...

 

...

 

...

 

...

 OldVapor = -1;

           

...

   

...

  pPhysics.Temperature = 25;
    

...

 

Change level is called any time the mutation changes level.

...

        

...

}

...

 

...

 

...

 

...

         

...


            

...

Body 

...

pBody = 

...

GO.GetPart("

...

Body") as 

...

Body;
            if 

...

(pBody 

...

!= 

...

null)

...

 

...

           {
 

...

 

...

  

...

            BodyPart pMainBody = pBody.GetPartByName("Hands");
              

...

  

...

if( pMainBody != null )
      

...

 

Mutate and Unmutate are called on an object when it gains or loses the mutation.

...

 

...

 

...

 

...

 

...

 

...

 

...

 

...

 

...

 

...

 

...

if 

...

(pMainBody.Equipped != null)
                {
                    if (pMainBody.Equipped.Blueprint == "Ghostly Flames")
                    {
                        pMainBody.Equipped.FireEvent(Event.New("Unequipped", "UnequippingObject", ParentObject, "BodyPart", pMainBody));
                        pMainBody.Unequip();
                    }
                }
            }

            if (FlamingHandsActivatedAbilityID != Guid.Empty)
            {
                ActivatedAbilities pAA = GO.GetPart("ActivatedAbilities") as ActivatedAbilities;
                pAA.RemoveAbility(FlamingHandsActivatedAbilityID);
                FlamingHandsActivatedAbilityID = Guid.Empty;
            }

            return true;
        }
    }
}

 

Your namespace should be XRL.World.Parts.Mutations, the class should be marked serializeable and derived from BaseMutation.

Code Block
using System;
using System.Collections.Generic;
using XRL.UI;
using ConsoleLib.Console;

namespace XRL.World.Parts.Mutation
{
    [Serializable]
    class FlamingHands : BaseMutation
    {

 

GetDescription and GetLevelText are called to generate the descriptive for a given level of the mutation.

Code Block
        public override string GetDescription()
        {
            return "You emit jets of flame from your hands.";
        }
 
        public override string GetLevelText(int Level)
        {
            string Ret = "Emits a 9-square ray of flame in the direction of your choice\n";
            Ret += "Cooldown: 10 rounds\n";
            Ret += "Damage: " + Level + "d6\n";
            Ret += "Cannot wear gloves";
            return Ret;
        }

 

Change level is called any time the mutation changes level.

Code Block
        public override bool ChangeLevel(int NewLevel)
        {
            Physics pPhysics = ParentObject.GetPart("Physics") as Physics;

            TemperatureOnHit pTemp = FlamesObject.GetPart("TemperatureOnHit") as TemperatureOnHit;
            pTemp.Amount =  (Level*2) + "d8";

            return base.ChangeLevel(NewLevel);
        }

 

Mutate and Unmutate are called on an object when it gains or loses the mutation.

Code Block
public override bool Mutate(GameObject GO, int Level)
 
public override bool Unmutate(GameObject GO)

 

BaseMutation derives from Part, so the typical event registration and handling functions are available

 

Code Block
public override void Register(GameObject Object)


public override bool FireEvent(Event E)
Info

You can remove mutations by including a tag with just the Name of the mutation you want with a - in front. For example:

Code Block
<mutation Name="-Flaming Hands"></mutation>
Info

Some steam threads where I discuss mutation modding.

http://steamcommunity.com/app/333640/discussions/3/366298942101209876/?tscn=1468516504

http://steamcommunity.com/app/333640/discussions/3/366298942101209876/?tscn=1468516504

http://steamcommunity.com/app/333640/discussions/3/365172408532130177/