Modding:Mutations: Difference between revisions

Jump to navigation Jump to search
928 bytes removed ,  23:49, 16 April 2021
Rm refs to IPart.Name and IPart.DisplayName
(Add info about Construction and Exclusions)
(Rm refs to IPart.Name and IPart.DisplayName)
 
(16 intermediate revisions by 4 users not shown)
Line 1: Line 1:
[[Category:Modding]]
[[Category:Script Modding]]{{Modding Info}}{{Modding Topic Prerequisites|Modding:C Sharp Scripting}}
== Tutorial ==
First, include a mutations.xml in your mod that defines a new mutation.
First, include a mutations.xml in your mod that defines a new mutation.
Mutations.xml sample, adding a simple mod
Mutations.xml sample, adding a simple mod
Line 21: Line 22:
using System.Collections.Generic;
using System.Collections.Generic;
using System.Text;
using System.Text;
 
using XRL.Rules;
using XRL.Rules;
using XRL.Messages;
using XRL.Messages;
using ConsoleLib.Console;
using ConsoleLib.Console;
 
namespace XRL.World.Parts.Mutation
namespace XRL.World.Parts.Mutation
{
{
Line 31: Line 32:
     class FreeholdTutorial_Udder : BaseMutation
     class FreeholdTutorial_Udder : BaseMutation
     {
     {
        public FreeholdTutorial_Udder()
        {
            Name = "FreeholdTutorial_Udder";
            DisplayName = "Udder";
        }
         public override void Register(GameObject Object)
         public override void Register(GameObject Object)
         {
         {
         }
         }
       
 
         public override string GetDescription()
         public override string GetDescription()
         {
         {
             return "";
             return "";
         }
         }
 
         public override string GetLevelText(int Level)
         public override string GetLevelText(int Level)
         {
         {
Line 51: Line 46:
             return Ret;
             return Ret;
         }
         }
 
         public override bool BeforeRender(Event E)
         public override bool WantEvent(int ID, int cascade)
        {
            return base.WantEvent(ID, cascade) || ID == BeforeRenderEvent.ID;
        }
 
        public override bool HandleEvent(BeforeRenderEvent e)
         {
         {
             if (ParentObject.IsPlayer())
             if (ParentObject.IsPlayer())
Line 63: Line 63:
             return true;
             return true;
         }
         }
 
         public override bool FireEvent(Event E)
         public override bool FireEvent(Event E)
         {
         {
             return base.FireEvent(E);
             return base.FireEvent(E);
         }
         }
 
         public override bool ChangeLevel(int NewLevel)
         public override bool ChangeLevel(int NewLevel)
         {
         {
             return true;
             return true;
         }
         }
 
         public override bool Mutate(GameObject GO, int Level)
         public override bool Mutate(GameObject GO, int Level)
         {
         {
             return true;
             return true;
         }
         }
 
         public override bool Unmutate(GameObject GO)
         public override bool Unmutate(GameObject GO)
         {
         {
Line 88: Line 88:
   
   


Here's a full example of the Flaming Hands mutation from the game's source code.
Here's a full decompiled example of the [[Flaming Hands]] mutation from the game's source code. (Note that, because it's decompiled, some of the variables have very generic names, like <code>gameObject</code> instead of something more descriptive, like <code>target</code>)
Flaming Hands - Full Example .cs
<syntaxhighlight lang="csharp">
<syntaxhighlight lang="csharp">
using System;
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Threading;
using ConsoleLib.Console;
using XRL.Core;
using XRL.Rules;
using XRL.UI;
using XRL.UI;
using ConsoleLib.Console;
 
namespace XRL.World.Parts.Mutation
namespace XRL.World.Parts.Mutation
{
{
    [Serializable]
[Serializable]
    class FlamingHands : BaseMutation
public class FlamingHands : BaseMutation
    {
{
        public FlamingHands()
public override bool GeneratesEquipment()
        {
{
            Name = "FlamingHands";
return true;
            DisplayName = "Flaming Hands";
}
        }
 
public override void Register(GameObject Object)
        public Guid FlamingHandsActivatedAbilityID = Guid.Empty;
{
        public ActivatedAbilityEntry FlamingHandsActivatedAbility = null;
Object.RegisterPartEvent(this, "CommandFlamingHands");
Object.RegisterPartEvent(this, "AIGetOffensiveMutationList");
        public override void Register(GameObject Object)
base.Register(Object);
        {
}
            Object.RegisterPartEvent(this, "BeginEquip");
 
            Object.RegisterPartEvent(this, "CommandFlamingHands");
public override string GetDescription()
            Object.RegisterPartEvent(this, "AIGetOffensiveMutationList");
{
        }
return "You emit jets of flame from your hands.";
}
        public override string GetDescription()
 
        {
public override string GetLevelText(int Level)
            return "You emit jets of flame from your hands.";
{
        }
return "Emits a 9-square ray of flame in the direction of your choice\n" + "Cooldown: 10 rounds\n" + "Damage: " + this.ComputeDamage(Level) + "\n" + "Cannot wear gloves";
}
        public override string GetLevelText(int Level)
 
        {
public string ComputeDamage(int UseLevel)
            string Ret = "Emits a 9-square ray of flame in the direction of your choice\n";
{
            Ret += "Cooldown: 10 rounds\n";
string text = UseLevel + "d4";
            Ret += "Damage: " + Level + "d6\n";
if (this.ParentObject != null)
            Ret += "Cannot wear gloves";
{
            return Ret;
int partCount = this.ParentObject.GetPart<Body>().GetPartCount(this.BodyPartType);
        }
if (partCount > 0)
{
        public void Flame(Cell C, ScreenBuffer Buffer)
text = text + "+" + partCount;
        {
}
            string Damage = Level + "d6";
}
else
            Body pBody = ParentObject.GetPart("Body") as Body;
{
            int nHandCount = pBody.GetPart("Hands").Count-1;
text += "+1";
            if (nHandCount > 0) Damage += "+" + nHandCount.ToString();
}
return text;
            if (C != null)
}
            {
 
                List<GameObject> Objects = C.GetObjectsInCell();
public string ComputeDamage()
{
                foreach (GameObject GO in Objects)
return this.ComputeDamage(base.Level);
                {
}
                    if( GO.PhasedMatches( ParentObject ) )
 
                    {
public void Flame(Cell C, ScreenBuffer Buffer, bool doEffect = true)
                        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);
string dice = this.ComputeDamage();
                        for (int x = 0; x < 5; x++) GO.ParticleText("&R" + (char)(219 + Rules.Stat.Random(0, 4)), 2.9f, 1);
if (C != null)
                        for (int x = 0; x < 5; x++) GO.ParticleText("&W" + (char)(219 + Rules.Stat.Random(0, 4)), 2.9f, 1);
{
                    }
foreach (GameObject gameObject in C.GetObjectsInCell())
                }
{
if (gameObject.PhaseMatches(this.ParentObject))
                foreach (GameObject GO in C.GetObjectsWithPart("Combat"))
{
                {
gameObject.TemperatureChange(310 + 25 * base.Level, this.ParentObject, false, false, false, 0, null, null);
                    if( GO.PhasedMatches( ParentObject ) )
if (doEffect)
                    {
{
                        Damage Dmg = new Damage(Rules.Stat.Roll(Damage));
for (int i = 0; i < 5; i++)
                        Dmg.AddAttribute("Fire");
{
                        Dmg.AddAttribute("Heat");
gameObject.ParticleText("&r" + ((char)(219 + Stat.Random(0, 4))).ToString(), 2.9f, 1);
}
                        Event eTakeDamage = Event.New("TakeDamage");
for (int j = 0; j < 5; j++)
                        eTakeDamage.AddParameter("Damage", Dmg);
{
                        eTakeDamage.AddParameter("Owner", ParentObject);
gameObject.ParticleText("&R" + ((char)(219 + Stat.Random(0, 4))).ToString(), 2.9f, 1);
                        eTakeDamage.AddParameter("Attacker", ParentObject);
}
                        eTakeDamage.AddParameter("Message", "from %o flames!");
for (int k = 0; k < 5; k++)
{
                        GO.FireEvent(eTakeDamage);
gameObject.ParticleText("&W" + ((char)(219 + Stat.Random(0, 4))).ToString(), 2.9f, 1);
                    }
}
                }
}
            }
}
}
            Buffer.Goto(C.X, C.Y);
DieRoll cachedDieRoll = dice.GetCachedDieRoll();
            string sColor = "&C";
foreach (GameObject gameObject2 in C.GetObjectsWithPartReadonly("Combat"))
            int r = Rules.Stat.Random(1, 3);
{
            if (r == 1) sColor = "&R";
if (gameObject2.PhaseMatches(this.ParentObject))
            if (r == 2) sColor = "&r";
{
            if (r == 3) sColor = "&W";
Damage damage = new Damage(cachedDieRoll.Resolve());
damage.AddAttribute("Fire");
            r = Rules.Stat.Random(1, 3);
damage.AddAttribute("Heat");
            if (r == 1) sColor += "^R";
Event @event = Event.New("TakeDamage", 0, 0, 0);
            if (r == 2) sColor += "^r";
@event.SetParameter("Damage", damage);
            if (r == 3) sColor += "^W";
@event.SetParameter("Owner", this.ParentObject);
@event.SetParameter("Attacker", this.ParentObject);
            if( C.ParentZone == XRL.Core.XRLCore.Core.Game.ZoneManager.ActiveZone )
@event.SetParameter("Message", "from %o flames!");
            {
gameObject2.FireEvent(@event);
                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);
if (doEffect)
            }
{
        }
Buffer.Goto(C.X, C.Y);
string str = "&C";
        public override bool FireEvent(Event E)
int num = Stat.Random(1, 3);
        {
if (num == 1)
            if (E.ID == "AIGetOffensiveMutationList")
{
            {
str = "&R";
                int Distance = (int)E.GetParameter("Distance");
}
                GameObject Target = E.GetParameter("Target") as GameObject;
if (num == 2)
                List<XRL.World.AI.GoalHandlers.AICommandList> CommandList = (List<XRL.World.AI.GoalHandlers.AICommandList>)E.GetParameter("List");
{
str = "&r";
                if (FlamingHandsActivatedAbility != null && FlamingHandsActivatedAbility.Cooldown <= 0 && Distance <= 9 && ParentObject.HasLOSTo(Target) ) CommandList.Add(new XRL.World.AI.GoalHandlers.AICommandList("CommandFlamingHands", 1));
}
                return true;
if (num == 3)
            }
{
str = "&W";
            if (E.ID == "CommandFlamingHands")
}
            {
int num2 = Stat.Random(1, 3);
                ScreenBuffer Buffer = new ScreenBuffer(80, 25);
if (num2 == 1)
                Core.XRLCore.Core.RenderMapToBuffer(Buffer);
{
str += "^R";
                List<Cell> TargetCell = PickLine(9, AllowVis.Any);
}
                if (TargetCell == null) return true;
if (num2 == 2)
                if (TargetCell.Count <= 0) return true;
{
str += "^r";
                if (TargetCell != null)
}
                {
if (num2 == 3)
                    if (TargetCell.Count == 1)
{
                    {
str += "^W";
                        if (ParentObject.IsPlayer())
}
                            if (UI.Popup.ShowYesNoCancel("Are you sure you want to target yourself?") != DialogResult.Yes)
if (C.ParentZone == XRLCore.Core.Game.ZoneManager.ActiveZone)
                            {
{
                                return true;
Stat.Random(1, 3);
                            }
Buffer.Write(str + ((char)(219 + Stat.Random(0, 4))).ToString(), true);
                    }
Popup._TextConsole.DrawBuffer(Buffer, null, false);
Thread.Sleep(10);
                    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++)
 
                    {
public static bool Cast(FlamingHands mutation = null, string level = "5-6")
                        if (TargetCell.Count == 1 || TargetCell[x] != ParentObject.pPhysics.CurrentCell)
{
                        Flame(TargetCell[x],Buffer);
if (mutation == null)
{
                        foreach( GameObject GO in TargetCell[x].GetObjectsWithPart("Physics") )
mutation = new FlamingHands();
                        {
mutation.Level = Stat.Roll(level, null);
                            if (GO.pPhysics.Solid)
mutation.ParentObject = XRLCore.Core.Game.Player.Body;
                            {
}
                                x = 999;
ScreenBuffer scrapBuffer = ScreenBuffer.GetScrapBuffer1(true);
                                break;
XRLCore.Core.RenderMapToBuffer(scrapBuffer);
                            }
List<Cell> list = mutation.PickLine(9, AllowVis.Any, null, false, null);
                        }
if (list == null)
                    }
{
                }
return true;
            }
}
if (list.Count <= 0)
            if (E.ID == "BeginEquip")
{
            {
return true;
                GameObject Equipment = E.GetParameter("Object") as GameObject;
}
                string BodyPartName = E.GetParameter("BodyPartName") as string;
if (list != null)
{
                if (BodyPartName == "Hands")
if (list.Count == 1 && mutation.ParentObject.IsPlayer() && Popup.ShowYesNoCancel("Are you sure you want to target yourself?") != DialogResult.Yes)
                {
{
                    if (IsPlayer())
return true;
                    {
}
                        UI.Popup.Show("Your flaming hands prevents you from equipping " + Equipment.DisplayName + "!");
mutation.CooldownMyActivatedAbility(mutation.FlamingHandsActivatedAbilityID, 10, null);
                    }
mutation.UseEnergy(1000);
mutation.PlayWorldSound(mutation.Sound, 0.5f, 0f, true, null);
                    E.bCancelled = true;
int num = 0;
                    return false;
while (num < 9 && num < list.Count)
                }
{
            }
if (list.Count == 1 || list[num] != mutation.ParentObject.pPhysics.CurrentCell)
{
            return true;
mutation.Flame(list[num], scrapBuffer, true);
        }
}
foreach (GameObject gameObject in list[num].LoopObjectsWithPart("Physics"))
{
        int OldFlame = -1;
if (gameObject.pPhysics.Solid && gameObject.GetIntProperty("AllowMissiles", 0) == 0)
        int OldVapor = -1;
{
Forcefield part = gameObject.GetPart<Forcefield>();
        public override bool ChangeLevel(int NewLevel)
if (part == null || !part.CanMissilePassFrom(mutation.ParentObject, null))
        {
{
            Physics pPhysics = ParentObject.GetPart("Physics") as Physics;
num = 999;
break;
            TemperatureOnHit pTemp = FlamesObject.GetPart("TemperatureOnHit") as TemperatureOnHit;
}
            pTemp.Amount = (Level*2) + "d8";
}
}
            return base.ChangeLevel(NewLevel);
num++;
        }
}
}
        public override bool Mutate(GameObject GO, int Level)
return true;
        {
}
            Unmutate(GO);
 
public override bool FireEvent(Event E)
            ActivatedAbilities pAA = GO.GetPart("ActivatedAbilities") as ActivatedAbilities;
{
            Physics pPhysics = GO.GetPart("Physics") as Physics;
if (E.ID == "AIGetOffensiveMutationList")
{
            if (pPhysics != null)
if (E.GetIntParameter("Distance", 0) <= 9 && base.IsMyActivatedAbilityAIUsable(this.FlamingHandsActivatedAbilityID, null) && this.ParentObject.HasLOSTo(E.GetGameObjectParameter("Target"), true, true, null))
            {
{
                OldFlame = pPhysics.FlameTemperature;
E.AddAICommand("CommandFlamingHands", 1, null, false);
                OldVapor = pPhysics.VaporTemperature;
}
            }
}
           
else if (E.ID == "CommandFlamingHands")
            Body pBody = GO.GetPart("Body") as Body;
{
            if (pBody != null)
return FlamingHands.Cast(this, "5-6");
            {
}
                GO.FireEvent(Event.New("CommandForceUnequipObject", "BodyPartName", "Hands"));
return true;
                FlamesObject = GameObjectFactory.Factory.CreateObject("Ghostly Flames");
}
                Event eCommandEquipObject = Event.New("CommandEquipObject");
 
                eCommandEquipObject.AddParameter("Object", FlamesObject);
public override bool ChangeLevel(int NewLevel)
                eCommandEquipObject.AddParameter("BodyPartName", "Hands");
{
                GO.FireEvent(eCommandEquipObject);
if (GameObject.validate(ref this.FlamesObject))
            }
{
this.FlamesObject.GetPart<TemperatureOnHit>().Amount = base.Level * 2 + "d8";
            FlamingHandsActivatedAbilityID = pAA.AddAbility("Flaming Hands", "CommandFlamingHands", "Physical Mutation");
}
            FlamingHandsActivatedAbility = pAA.AbilityByGuid[FlamingHandsActivatedAbilityID];
return base.ChangeLevel(NewLevel);
            return true;
}
        }
 
private void AddAbility()
        public GameObject FlamesObject = null;
{
this.FlamingHandsActivatedAbilityID = base.AddMyActivatedAbility("Flaming Hands", "CommandFlamingHands", "Physical Mutation", -1, null, "\a", false, false, false, false, false, false, null);
        public override bool Unmutate(GameObject GO)
}
        {
 
            Physics pPhysics = GO.GetPart("Physics") as Physics;
public override bool Mutate(GameObject GO, int Level)
{
            if (pPhysics != null)
this.Unmutate(GO);
            {
if (this.CreateObject)
                if (OldFlame != -1) pPhysics.FlameTemperature = OldFlame;
{
                if (OldVapor != -1) pPhysics.BrittleTemperature = OldVapor;
Body part = GO.GetPart<Body>();
                OldFlame = -1;
if (part != null)
                OldVapor = -1;
{
BodyPart firstPart = part.GetFirstPart(this.BodyPartType);
                pPhysics.Temperature = 25;
if (firstPart != null)
            }
{
           
GO.FireEvent(Event.New("CommandForceUnequipObject", "BodyPart", firstPart));
            Body pBody = GO.GetPart("Body") as Body;
this.FlamesObject = GameObject.create("Ghostly Flames");
            if (pBody != null)
this.FlamesObject.GetPart<Armor>().WornOn = firstPart.Type;
            {
Event @event = Event.New("CommandForceEquipObject", 0, 0, 0);
                BodyPart pMainBody = pBody.GetPartByName("Hands");
@event.SetParameter("Object", this.FlamesObject);
                if( pMainBody != null )
@event.SetParameter("BodyPart", firstPart);
                if (pMainBody.Equipped != null)
@event.SetSilent(true);
                {
GO.FireEvent(@event);
                    if (pMainBody.Equipped.Blueprint == "Ghostly Flames")
this.AddAbility();
                    {
}
                        pMainBody.Equipped.FireEvent(Event.New("Unequipped", "UnequippingObject", ParentObject, "BodyPart", pMainBody));
}
                        pMainBody.Unequip();
}
                    }
else
                }
{
            }
this.AddAbility();
}
            if (FlamingHandsActivatedAbilityID != Guid.Empty)
this.ChangeLevel(Level);
            {
return base.Mutate(GO, Level);
                ActivatedAbilities pAA = GO.GetPart("ActivatedAbilities") as ActivatedAbilities;
}
                pAA.RemoveAbility(FlamingHandsActivatedAbilityID);
 
                FlamingHandsActivatedAbilityID = Guid.Empty;
public override bool Unmutate(GameObject GO)
            }
{
base.CleanUpMutationEquipment(GO, ref this.FlamesObject);
            return true;
base.RemoveMyActivatedAbility(ref this.FlamingHandsActivatedAbilityID, null);
        }
return base.Unmutate(GO);
    }
}
 
public string BodyPartType = "Hands";
 
public bool CreateObject = true;
 
public string Sound = "burn_crackling";
 
public GameObject FlamesObject;
 
public Guid FlamingHandsActivatedAbilityID = Guid.Empty;
}
}
}
</syntaxhighlight>
</syntaxhighlight>
Line 359: Line 371:
using System;
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Threading;
using ConsoleLib.Console;
using XRL.Core;
using XRL.Rules;
using XRL.UI;
using XRL.UI;
using ConsoleLib.Console;
 
namespace XRL.World.Parts.Mutation
namespace XRL.World.Parts.Mutation
{
{
    [Serializable]
[Serializable]
    class FlamingHands : BaseMutation
public class FlamingHands : BaseMutation
    {
{
</syntaxhighlight>
</syntaxhighlight>


Line 372: Line 387:
GetDescription and GetLevelText are called to generate the descriptive for a given level of the mutation.
GetDescription and GetLevelText are called to generate the descriptive for a given level of the mutation.
<syntaxhighlight lang="csharp">
<syntaxhighlight lang="csharp">
        public override string GetDescription()
public override string GetDescription()
        {
{
            return "You emit jets of flame from your hands.";
return "You emit jets of flame from your hands.";
        }
}
 
 
        public override string GetLevelText(int Level)
public override string GetLevelText(int Level)
        {
{
            string Ret = "Emits a 9-square ray of flame in the direction of your choice\n";
return "Emits a 9-square ray of flame in the direction of your choice\n" + "Cooldown: 10 rounds\n" + "Damage: " + this.ComputeDamage(Level) + "\n" + "Cannot wear gloves";
            Ret += "Cooldown: 10 rounds\n";
}
            Ret += "Damage: " + Level + "d6\n";
            Ret += "Cannot wear gloves";
            return Ret;
        }
</syntaxhighlight>
</syntaxhighlight>
   
   
Line 392: Line 403:
public override bool ChangeLevel(int NewLevel)
public override bool ChangeLevel(int NewLevel)
{
{
    Physics pPhysics = ParentObject.GetPart("Physics") as Physics;
if (GameObject.validate(ref this.FlamesObject))
{
    TemperatureOnHit pTemp = FlamesObject.GetPart("TemperatureOnHit") as TemperatureOnHit;
this.FlamesObject.GetPart<TemperatureOnHit>().Amount = base.Level * 2 + "d8";
    pTemp.Amount = (Level*2) + "d8";
}
return base.ChangeLevel(NewLevel);
    return base.ChangeLevel(NewLevel);
}
}
</syntaxhighlight>
</syntaxhighlight>
Line 417: Line 427:
</syntaxhighlight>
</syntaxhighlight>


== Additional Details ==
== XML Parameters and Additional Details ==


You may add the following optional elements to a <mutation> tag in Mutations.xml
You may add the following elements to a <mutation> tag in Mutations.xml
* Constructor
* Name
* Exclusions
* Class
* Cost
* MaxSelected
* Constructor ''(optional)''
* Exclusions ''(optional)''
* BearerDescription
* Code
 
=== Name ===
Name of the mutation as it appears in the character creation screen.
 
=== Class ===
The name of the .cs Class object that is used to instantiate your mutation object.
 
=== Cost ===
The mutation costs this many mutation points when a character selects it during the character creation process.
 
=== MaxSelected ===
The maximum number of copies of this mutation that can be selected during character creation.
 
Currently this is only used for Unstable Mutation and there is some hard-coded special handling of that mutation when it comes to details such as constructing a Build Library code for builds that include Unstable Mutation. It is not clear if this value can be set to more than 1 for a modded mutation without causing some problems.


=== Constructor ===
=== Constructor ===
Line 458: Line 488:
</syntaxhighlight>
</syntaxhighlight>


== Exclusions ==
=== Exclusions ===
The Exclusions parameter defines mutations that should be considered mutually exclusive with this mutation. For example, you can only have one type of back-slot mutation, so the game defines the other three types of back-slot mutations as Exclusions in the Mutations.xml file.
The Exclusions parameter defines mutations that should be considered mutually exclusive with this mutation. For example, you can only have one type of back-slot mutation, so the game defines the other three types of back-slot mutations as Exclusions in the Mutations.xml file.


Line 465: Line 495:
<mutation Name="Stinger (Confusing Venom)" Cost="3" MaxSelected="1" Class="Stinger" Constructor="Confuse" Exclusions="Stinger (Paralyzing Venom),Stinger (Poisoning Venom),Wings" BearerDescription="those with stingers tipped with confusing venom" Code="bx"></mutation>
<mutation Name="Stinger (Confusing Venom)" Cost="3" MaxSelected="1" Class="Stinger" Constructor="Confuse" Exclusions="Stinger (Paralyzing Venom),Stinger (Poisoning Venom),Wings" BearerDescription="those with stingers tipped with confusing venom" Code="bx"></mutation>
</syntaxhighlight>
</syntaxhighlight>
=== BearerDescription ===
It appears that this description is used in some of the random generation algorithms for villages and history in the game. For example, if a village reveres mutants with the Multiple Arms mutation, they might use the string defined in Mutations.xml ("the many-armed") to describe them in their praises or monuments.
=== Code ===
The Code element value is used when constructing a Build Library code for characters you create.
It is unclear if there is really a "best practice" for codes. Probably one should avoid using the codes used by base game mutations, but conflict with other mods may be inevitable. It would appear that this code can be longer than 2 characters, but that is an untested hypothesis.
{{Modding Navbox}}

Navigation menu