99
edits
(add modding info box) |
(Rm refs to IPart.Name and IPart.DisplayName) |
||
(11 intermediate revisions by 4 users not shown) | |||
Line 1: | Line 1: | ||
[[Category:Modding]]{{Modding Info}} | [[Category:Script Modding]]{{Modding Info}}{{Modding Topic Prerequisites|Modding:C Sharp Scripting}} | ||
== Tutorial == | == 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. | ||
Line 22: | 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 32: | Line 32: | ||
class FreeholdTutorial_Udder : BaseMutation | class FreeholdTutorial_Udder : BaseMutation | ||
{ | { | ||
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 52: | Line 46: | ||
return Ret; | return Ret; | ||
} | } | ||
public override bool | 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 64: | 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 89: | 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>) | ||
<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; | ||
namespace XRL.World.Parts.Mutation | namespace XRL.World.Parts.Mutation | ||
{ | { | ||
[Serializable] | |||
public class FlamingHands : BaseMutation | |||
{ | |||
public override bool GeneratesEquipment() | |||
{ | |||
return true; | |||
} | |||
public override void Register(GameObject Object) | |||
{ | |||
Object.RegisterPartEvent(this, "CommandFlamingHands"); | |||
Object.RegisterPartEvent(this, "AIGetOffensiveMutationList"); | |||
base.Register(Object); | |||
} | |||
public override string GetDescription() | |||
{ | |||
return "You emit jets of flame from your hands."; | |||
} | |||
public override string GetLevelText(int Level) | |||
{ | |||
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 string ComputeDamage(int UseLevel) | |||
{ | |||
string text = UseLevel + "d4"; | |||
if (this.ParentObject != null) | |||
{ | |||
int partCount = this.ParentObject.GetPart<Body>().GetPartCount(this.BodyPartType); | |||
if (partCount > 0) | |||
{ | |||
text = text + "+" + partCount; | |||
} | |||
} | |||
else | |||
{ | |||
text += "+1"; | |||
} | |||
return text; | |||
} | |||
public string ComputeDamage() | |||
{ | |||
return this.ComputeDamage(base.Level); | |||
} | |||
public void Flame(Cell C, ScreenBuffer Buffer, bool doEffect = true) | |||
{ | |||
string dice = this.ComputeDamage(); | |||
if (C != null) | |||
{ | |||
foreach (GameObject gameObject in C.GetObjectsInCell()) | |||
{ | |||
if (gameObject.PhaseMatches(this.ParentObject)) | |||
{ | |||
gameObject.TemperatureChange(310 + 25 * base.Level, this.ParentObject, false, false, false, 0, null, null); | |||
if (doEffect) | |||
{ | |||
for (int i = 0; i < 5; i++) | |||
{ | |||
gameObject.ParticleText("&r" + ((char)(219 + Stat.Random(0, 4))).ToString(), 2.9f, 1); | |||
} | |||
for (int j = 0; j < 5; j++) | |||
{ | |||
gameObject.ParticleText("&R" + ((char)(219 + Stat.Random(0, 4))).ToString(), 2.9f, 1); | |||
} | |||
for (int k = 0; k < 5; k++) | |||
{ | |||
gameObject.ParticleText("&W" + ((char)(219 + Stat.Random(0, 4))).ToString(), 2.9f, 1); | |||
} | |||
} | |||
} | |||
} | |||
DieRoll cachedDieRoll = dice.GetCachedDieRoll(); | |||
foreach (GameObject gameObject2 in C.GetObjectsWithPartReadonly("Combat")) | |||
{ | |||
if (gameObject2.PhaseMatches(this.ParentObject)) | |||
{ | |||
Damage damage = new Damage(cachedDieRoll.Resolve()); | |||
damage.AddAttribute("Fire"); | |||
damage.AddAttribute("Heat"); | |||
Event @event = Event.New("TakeDamage", 0, 0, 0); | |||
@event.SetParameter("Damage", damage); | |||
@event.SetParameter("Owner", this.ParentObject); | |||
@event.SetParameter("Attacker", this.ParentObject); | |||
@event.SetParameter("Message", "from %o flames!"); | |||
gameObject2.FireEvent(@event); | |||
} | |||
} | |||
} | |||
if (doEffect) | |||
{ | |||
Buffer.Goto(C.X, C.Y); | |||
string str = "&C"; | |||
int num = Stat.Random(1, 3); | |||
if (num == 1) | |||
{ | |||
str = "&R"; | |||
} | |||
if (num == 2) | |||
{ | |||
str = "&r"; | |||
} | |||
if (num == 3) | |||
{ | |||
str = "&W"; | |||
} | |||
int num2 = Stat.Random(1, 3); | |||
if (num2 == 1) | |||
{ | |||
str += "^R"; | |||
} | |||
if (num2 == 2) | |||
{ | |||
str += "^r"; | |||
} | |||
if (num2 == 3) | |||
{ | |||
str += "^W"; | |||
} | |||
if (C.ParentZone == XRLCore.Core.Game.ZoneManager.ActiveZone) | |||
{ | |||
Stat.Random(1, 3); | |||
Buffer.Write(str + ((char)(219 + Stat.Random(0, 4))).ToString(), true); | |||
Popup._TextConsole.DrawBuffer(Buffer, null, false); | |||
Thread.Sleep(10); | |||
} | |||
} | |||
} | |||
public static bool Cast(FlamingHands mutation = null, string level = "5-6") | |||
{ | |||
if (mutation == null) | |||
{ | |||
mutation = new FlamingHands(); | |||
mutation.Level = Stat.Roll(level, null); | |||
mutation.ParentObject = XRLCore.Core.Game.Player.Body; | |||
} | |||
ScreenBuffer scrapBuffer = ScreenBuffer.GetScrapBuffer1(true); | |||
XRLCore.Core.RenderMapToBuffer(scrapBuffer); | |||
List<Cell> list = mutation.PickLine(9, AllowVis.Any, null, false, null); | |||
if (list == null) | |||
{ | |||
return true; | |||
} | |||
if (list.Count <= 0) | |||
{ | |||
return true; | |||
} | |||
if (list != null) | |||
{ | |||
if (list.Count == 1 && mutation.ParentObject.IsPlayer() && Popup.ShowYesNoCancel("Are you sure you want to target yourself?") != DialogResult.Yes) | |||
{ | |||
return true; | |||
} | |||
mutation.CooldownMyActivatedAbility(mutation.FlamingHandsActivatedAbilityID, 10, null); | |||
mutation.UseEnergy(1000); | |||
mutation.PlayWorldSound(mutation.Sound, 0.5f, 0f, true, null); | |||
int num = 0; | |||
while (num < 9 && num < list.Count) | |||
{ | |||
if (list.Count == 1 || list[num] != mutation.ParentObject.pPhysics.CurrentCell) | |||
{ | |||
mutation.Flame(list[num], scrapBuffer, true); | |||
} | |||
foreach (GameObject gameObject in list[num].LoopObjectsWithPart("Physics")) | |||
{ | |||
if (gameObject.pPhysics.Solid && gameObject.GetIntProperty("AllowMissiles", 0) == 0) | |||
{ | |||
Forcefield part = gameObject.GetPart<Forcefield>(); | |||
if (part == null || !part.CanMissilePassFrom(mutation.ParentObject, null)) | |||
{ | |||
num = 999; | |||
break; | |||
} | |||
} | |||
} | |||
num++; | |||
} | |||
} | |||
return true; | |||
} | |||
public override bool FireEvent(Event E) | |||
{ | |||
if (E.ID == "AIGetOffensiveMutationList") | |||
{ | |||
if (E.GetIntParameter("Distance", 0) <= 9 && base.IsMyActivatedAbilityAIUsable(this.FlamingHandsActivatedAbilityID, null) && this.ParentObject.HasLOSTo(E.GetGameObjectParameter("Target"), true, true, null)) | |||
{ | |||
E.AddAICommand("CommandFlamingHands", 1, null, false); | |||
} | |||
} | |||
else if (E.ID == "CommandFlamingHands") | |||
{ | |||
return FlamingHands.Cast(this, "5-6"); | |||
} | |||
return true; | |||
} | |||
public override bool ChangeLevel(int NewLevel) | |||
{ | |||
if (GameObject.validate(ref this.FlamesObject)) | |||
{ | |||
this.FlamesObject.GetPart<TemperatureOnHit>().Amount = base.Level * 2 + "d8"; | |||
} | |||
return base.ChangeLevel(NewLevel); | |||
} | |||
private void AddAbility() | |||
{ | |||
this.FlamingHandsActivatedAbilityID = base.AddMyActivatedAbility("Flaming Hands", "CommandFlamingHands", "Physical Mutation", -1, null, "\a", false, false, false, false, false, false, null); | |||
} | |||
public override bool Mutate(GameObject GO, int Level) | |||
{ | |||
this.Unmutate(GO); | |||
if (this.CreateObject) | |||
{ | |||
Body part = GO.GetPart<Body>(); | |||
if (part != null) | |||
{ | |||
BodyPart firstPart = part.GetFirstPart(this.BodyPartType); | |||
if (firstPart != null) | |||
{ | |||
GO.FireEvent(Event.New("CommandForceUnequipObject", "BodyPart", firstPart)); | |||
this.FlamesObject = GameObject.create("Ghostly Flames"); | |||
this.FlamesObject.GetPart<Armor>().WornOn = firstPart.Type; | |||
Event @event = Event.New("CommandForceEquipObject", 0, 0, 0); | |||
@event.SetParameter("Object", this.FlamesObject); | |||
@event.SetParameter("BodyPart", firstPart); | |||
@event.SetSilent(true); | |||
GO.FireEvent(@event); | |||
this.AddAbility(); | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
this.AddAbility(); | |||
} | |||
this.ChangeLevel(Level); | |||
return base.Mutate(GO, Level); | |||
} | |||
public override bool Unmutate(GameObject GO) | |||
{ | |||
base.CleanUpMutationEquipment(GO, ref this.FlamesObject); | |||
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 360: | 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; | ||
namespace XRL.World.Parts.Mutation | namespace XRL.World.Parts.Mutation | ||
{ | { | ||
[Serializable] | |||
public class FlamingHands : BaseMutation | |||
{ | |||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 373: | 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() | |||
{ | |||
return "You emit jets of flame from your hands."; | |||
} | |||
public override string GetLevelText(int Level) | |||
{ | |||
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"; | |||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 393: | Line 403: | ||
public override bool ChangeLevel(int NewLevel) | public override bool ChangeLevel(int NewLevel) | ||
{ | { | ||
if (GameObject.validate(ref this.FlamesObject)) | |||
{ | |||
this.FlamesObject.GetPart<TemperatureOnHit>().Amount = base.Level * 2 + "d8"; | |||
} | |||
return base.ChangeLevel(NewLevel); | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 494: | Line 503: | ||
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. | 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}} |