25
edits
mNo edit summary |
|||
(9 intermediate revisions by 2 users not shown) | |||
Line 7: | Line 7: | ||
<mutations> | <mutations> | ||
<category Name="[CATEGORY]"> | <category Name="[CATEGORY]"> | ||
<mutation Name="[NAME]" Cost="[NUMBER]" MaxSelected="[NUMBER]" Class="[ | <mutation Name="[NAME]" Cost="[NUMBER]" MaxSelected="[NUMBER]" Class="[CS CLASS]" Tile="Mutations/YOUR_IMAGE_HERE" Foreground="COLOR" Background="COLOR" </mutation> | ||
</category> | </category> | ||
</mutations> | </mutations> | ||
Line 39: | Line 39: | ||
The tile you will be using for said mutation in-game. | The tile you will be using for said mutation in-game. | ||
By default most mutations use gold/yellow for | By default most mutations use gold/yellow for background and brown for foreground but this can be customized for any color of your choice, see [[Modding:Tiles]] and [[Modding:Colors_%26_Object_Rendering]] for further info on this. | ||
=== 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. | |||
=== 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. | |||
=== Constructor === | === Constructor === | ||
This is used by very few mutations, and its use case is as a string argument (or a comma-delimited string of arguments, if there are more than one) to pass to the mutation's class constructor. All such arguments are received as string parameters in the mutation class constructor. | |||
It should be noted that this parameter is both advance and old, and it shouldn't be necessary and potentially avoided. | |||
== C# Scripting, and Making The Mutation == | |||
Here is an example of a mutation that will add udders to your character, which will edit the title of your character to add udders, and make you moo occasionally. | |||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
using XRL.Rules; | |||
using XRL.Messages; | |||
using ConsoleLib.Console; | |||
// Namespace is a necessity here, and allows the game to find your mutation and add it to the game | |||
namespace XRL.World.Parts.Mutation | namespace XRL.World.Parts.Mutation | ||
{ | { | ||
// This is also necessary as it allows the game to save info related to your mutation, for further info on this, check out the guide on serialization at [[Modding:Serialization_(Saving/Loading)]] | |||
[Serializable] | |||
// This defines the class that you will call in Mutations.XML | |||
class QudWiki_Udders : BaseMutation | |||
{ | |||
// This sets the description for your mutation | |||
public override string GetDescription() | |||
{ | |||
return "You have udders!"; | |||
} | |||
// This sets the description for what exactly your mutation does. | |||
// It is good idea to make helper functions like the "ChanceToMoo" to make dynamic descriptions for changing rules for a mutation. | |||
public override string GetLevelText(int Level) | |||
{ | |||
return "{{rules|You have a " + ChanceToMoo(Level) + "% chance to moo per turn}}"; | |||
} | |||
public int ChanceToMoo(int Level) | |||
{ | |||
return Level; | |||
} | |||
// This is called every time the mutation changes level, and can be used to change things like damage. | |||
// We don't use "ChanceToMoo" in this example so we can allow GetLevelText to use it. | |||
public override bool ChangeLevel(int NewLevel) | |||
{ | |||
return true; | |||
} | |||
// These two are called upon when an object gains said mutation and what happens, and is used to add or remove things as necessary | |||
public override bool Mutate(GameObject MUTANT, int Level) | |||
{ | |||
return base.Mutate(MUTANT, Level); | |||
} | |||
public override bool Unmutate(GameObject MUTANT) | |||
{ | |||
return Base.Unmutate(MUTANT); | |||
} | |||
// Caves of Qud uses two different event systems so here is an example of each | |||
// This is how you tell the "Minimal events" system we want to handle a specific type of event. | |||
// We will listen for GetDisplayNameEvent. | |||
public override bool WantEvent(int ID, int cascade) | |||
{ | |||
return base.WantEvent(ID, cascade) || ID == GetDisplayNameEvent.ID; | |||
} | |||
// Handle the GetDisplayNameEvent | |||
public override bool HandleEvent(GetDisplayNameEvent e) | |||
{ | |||
var DescriptionBuilder = e.DB; | |||
// should show up when you look at anything with the mutation. | |||
DescriptionBuilder.AddWithClause("udders"); | |||
return true; | |||
} | |||
// This is how you handle the other style of qud events | |||
// This is a much more effecient registration for this type of event and should be enabled. | |||
// Tells the event system that all your registrations are handled in your Register(GameObject) method. | |||
public override bool AllowStaticRegistration() | |||
{ | |||
return true; | |||
} | |||
// First we must register the event | |||
public override void Register(GameObject obj) { | |||
obj.RegisterPartEvent(this, "EndTurn"); | |||
// Call the base Register method that we overrode. | |||
base.Register(obj); | |||
} | |||
== | // Then we can handle the EndTurn type events here. | ||
public override bool FireEvent(Event E) | |||
{ | |||
if (E.ID == "EndTurn") | |||
{ | |||
// Reusing the same method we used in GetLevelText means that both of them will remain accurate. | |||
if (ChanceToMoo(Level).in100()) DidX("moo"); | |||
} | |||
return base.FireEvent(E); | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
== Bigger Example == | |||
TODO: | TODO: Re-work the huge file of flaminghands.cs to be have comments and explain it , possibly with Gnarf's help if they are okay/free to do so | ||
This is the source code from <code>FlamingHands.cs</code> | This is the source code from <code>FlamingHands.cs</code> as an example of a more complex mutation. | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
using ConsoleLib.Console; | using ConsoleLib.Console; | ||
Line 90: | Line 173: | ||
{ | { | ||
/// <summary> | |||
/// FlamingHands powers the "Flaming Ray" mutation. You can now choose a variant from hands, feet or face. | |||
/// </summary> | |||
[Serializable] | [Serializable] | ||
public class FlamingHands : BaseDefaultEquipmentMutation | public class FlamingHands : BaseDefaultEquipmentMutation | ||
{ | { | ||
public FlamingHands() | |||
{ | |||
DisplayName = "Flaming Ray"; | |||
} | |||
/// <summary>The <see cref="BodyPart.Type" /> we replace (chosen by variant selection.)</summary> | |||
public string BodyPartType = "Hands"; | public string BodyPartType = "Hands"; | ||
/// <summary>Do we still need to create the object? Setup as a public for serialization purposes.</summary> | |||
public bool CreateObject = true; | public bool CreateObject = true; | ||
/// <summary>Sound file to play when attacking.</summary> | |||
public string Sound = "Abilities/sfx_ability_mutation_flamingRay_attack"; | public string Sound = "Abilities/sfx_ability_mutation_flamingRay_attack"; | ||
[NonSerialized] private static GameObject _Projectile; | |||
/// <summary>Create or retrive the already created Projectile game object.</summary> | |||
private static GameObject Projectile | private static GameObject Projectile | ||
{ | { | ||
Line 112: | Line 205: | ||
} | } | ||
} | } | ||
/// <summary>We are request to be re-mutated automatically when our body is rebuilt. Thanks slog.</summary> | |||
public override bool GeneratesEquipment() | public override bool GeneratesEquipment() | ||
{ | { | ||
return true; | return true; | ||
} | } | ||
Line 131: | Line 219: | ||
} | } | ||
/// <summary>Show selected variant in character creation.</summary> | |||
public override string GetCreateCharacterDisplayName() | public override string GetCreateCharacterDisplayName() | ||
{ | { | ||
Line 149: | Line 238: | ||
} | } | ||
public override string GetLevelText(int | public override string GetLevelText(int level) | ||
{ | { | ||
string Ret = "Emits a 9-square ray of flame in the direction of your choice.\n"; | string Ret = "Emits a 9-square ray of flame in the direction of your choice.\n"; | ||
Ret += "Damage: {{rules|" + ComputeDamage( | Ret += "Damage: {{rules|" + ComputeDamage(level) + "}}\n"; | ||
Ret += "Cooldown: 10 rounds\n"; | Ret += "Cooldown: 10 rounds\n"; | ||
Ret += "Melee attacks heat opponents by {{rules|" + GetHeatOnHitAmount( | Ret += "Melee attacks heat opponents by {{rules|" + GetHeatOnHitAmount(level) + "}} degrees"; | ||
return Ret; | return Ret; | ||
} | } | ||
public string GetHeatOnHitAmount(int | public string GetHeatOnHitAmount(int level) | ||
{ | { | ||
return ( | return (level * 2) + "d8"; | ||
} | } | ||
public string ComputeDamage(int | public string ComputeDamage(int level) | ||
{ | { | ||
string Result = | string Result = level + "d4"; | ||
if (ParentObject != null) | if (ParentObject != null) | ||
{ | { | ||
Line 181: | Line 270: | ||
} | } | ||
public string ComputeDamage() | public string ComputeDamage() => ComputeDamage(Level); | ||
public void Flame(Cell C, ScreenBuffer Buffer, bool doEffect = true) | public void Flame(Cell C, ScreenBuffer Buffer, bool doEffect = true) | ||
Line 271: | Line 357: | ||
} | } | ||
} | } | ||
mutation.CooldownMyActivatedAbility(mutation. | mutation.CooldownMyActivatedAbility(mutation.ActivatedAbilityID, Turns: 10); | ||
mutation.UseEnergy(1000, "Physical Mutation Flaming Hands"); | mutation.UseEnergy(1000, "Physical Mutation Flaming Hands"); | ||
mutation.PlayWorldSound(mutation.Sound, combat: true); | mutation.PlayWorldSound(mutation.Sound, combat: true); | ||
Line 294: | Line 380: | ||
{ | { | ||
if( !CreateObject) return true; | if( !CreateObject) return true; | ||
return HasRegisteredSlot(BodyPartType) && GetRegisteredSlot(BodyPartType,false) != null; | return HasRegisteredSlot(BodyPartType) && GetRegisteredSlot(BodyPartType, false) != null; | ||
} | } | ||
Line 324: | Line 410: | ||
CheckObjectProperlyEquipped() | CheckObjectProperlyEquipped() | ||
&& E.GetIntParameter("Distance") <= 9 | && E.GetIntParameter("Distance") <= 9 | ||
&& IsMyActivatedAbilityAIUsable( | && IsMyActivatedAbilityAIUsable(ActivatedAbilityID) | ||
&& ParentObject.HasLOSTo(E.GetGameObjectParameter("Target"), UseTargetability: true) | && ParentObject.HasLOSTo(E.GetGameObjectParameter("Target"), UseTargetability: true) | ||
) | ) | ||
Line 348: | Line 434: | ||
} | } | ||
return base.FireEvent(E); | return base.FireEvent(E); | ||
} | } | ||
private void AddAbility() | private void AddAbility() | ||
{ | { | ||
ActivatedAbilityID = AddMyActivatedAbility( | |||
Name: "Flaming Ray", | Name: "Flaming Ray", | ||
Command: "CommandFlamingHands", | Command: "CommandFlamingHands", | ||
Class: "Physical Mutation", | Class: "Physical Mutation", | ||
Icon: "" + (char) 168 | Icon: "" + (char) 168, | ||
Description: GetLevelText(Level) | |||
); | ); | ||
} | |||
public override bool ChangeLevel(int NewLevel) | |||
{ | |||
var result = base.ChangeLevel(NewLevel); | |||
// Update the ability description | |||
if (MyActivatedAbility(ActivatedAbilityID) is ActivatedAbilityEntry ability) ability.Description = GetLevelText(Level); | |||
return result; | |||
} | } | ||
Line 371: | Line 461: | ||
return variants; | return variants; | ||
} | } | ||
public override void SetVariant(int n) | public override void SetVariant(int n) | ||
Line 384: | Line 473: | ||
} | } | ||
base.SetVariant(n); | base.SetVariant(n); | ||
} | } | ||
Line 460: | Line 544: | ||
public override bool Unmutate(GameObject GO) | public override bool Unmutate(GameObject GO) | ||
{ | { | ||
RemoveMyActivatedAbility(ref | RemoveMyActivatedAbility(ref ActivatedAbilityID); | ||
return base.Unmutate(GO); | return base.Unmutate(GO); | ||
} | } | ||
Line 467: | Line 551: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
{{Modding Navbox}} | {{Modding Navbox}} |