108
edits
m (add cleanup note) |
(Rm refs to IPart.Name and IPart.DisplayName) |
||
(7 intermediate revisions by 2 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> |