Ludeon Forums

RimWorld => Mods => Help => Topic started by: Argain on May 30, 2014, 02:17:25 AM

Title: [Solved] Targeting Electronics
Post by: Argain on May 30, 2014, 02:17:25 AM
I'm working on a mod to make a custom animal target electronics during a mental break, but when the Incident arrives the animal doesn't seem to do anything... not even sure if it's possible to target electronics? Here's the behavior code so far:


public class JobGiver_Curiosity : ThinkNode_JobGiver
    {
        public float searchRadius = 99999f;
        private const float WaitChance = 0.5f;
        private const int WaitTicks = 90;
        protected override Job TryGiveTerminalJob(Pawn pawn)
        {
            int num = 0;
            List<Building> allBuildingsColonistElec = Find.ListerBuildings.allBuildingsColonistElecFire.ToList(); //hopefully pulling a list of electronic buildings to target?
            int count = allBuildingsColonistElec.Count;
            float num2 = this.searchRadius * this.searchRadius;
            Building building;
            while (true)
            {
                int index = UnityEngine.Random.Range(0, count);
                building = allBuildingsColonistElec[index];
                num++;
                if (num > 75)
                {
                    break;
                }
                if (BuildingTrashUtility.IsGoodTrashTargetFor(building, pawn))
                {
                    if (this.searchRadius >= 9999f || (building.Position - pawn.Position).LengthHorizontalSquared <= num2)
                    {
                        goto AL_D8;
                    }
                }
            }
            return null;
        AL_D8:
            return BuildingTrashUtility.AttackJobOnFor(building, pawn);
        }
    }
Title: Re: Targeting Electronics
Post by: Justin C on May 30, 2014, 04:55:01 AM
You're going to need to tell the animal to do that job in its think tree, in the XML. So you'll need a ThinkNode_Conditional to check to see if the Pawn should be doing that job, and then you will need to make sure it checks that conditional in the XML and then does your custom job if the conditional returns true. Just look at any of the existing think trees for examples.

Also, that code is just going to get the first random building it sees within its search radius. So it could be right next to a valid target and instead travel all the way across the map for a different one it picked at random. Just figured I'd point that out in case it wasn't the behavior you are intending.
Title: Re: Targeting Electronics
Post by: Argain on May 30, 2014, 05:12:30 PM
The XML files have already been setup, it's just the behavior code that's being a pain...

Rimrat.xml

<?xml version="1.0" encoding="utf-8" ?>
<ThinkTrees>

<ThinkTreeDef>
<defName>RimratStandard</defName>
<thinkRoot Class="AI.ThinkNode_Priority">
<subNodes>
<li Class="AI.ThinkNode_Subtree">
<treeDef>Curiosity</treeDef>
</li>
<li Class="AI.ThinkNode_Subtree">
<treeDef>BurningResponse</treeDef>
</li>
<li Class="AI.ThinkNode_Subtree">
<treeDef>Psychotic</treeDef>
</li>
<li Class="AI.ThinkNode_Subtree">
<treeDef>SelfDefense</treeDef>
</li>
<li Class="AI.ThinkNode_SatisfyNeeds"/>
<li Class="AI.JobGiver_WanderCurrentRoom"/>
<li Class="AI.JobGiver_IdleError"/>
</subNodes>
</thinkRoot>
</ThinkTreeDef>

</ThinkTrees>


SubThinkTree_Rimrat.xml

<?xml version="1.0" encoding="utf-8" ?>
<ThinkTrees>

<ThinkTreeDef>
<defName>Curiosity</defName>
<thinkRoot Class="AI.ThinkNode_ConditionalRimrat, RimratMod">
<brokenState>Curious</brokenState>
<subNodes>
<li Class="AI.ThinkNode_Priority">
<subNodes>
<li Class="AI.JobGiver_Curiosity, RimratMod" />
<li Class="AI.JobGiver_WanderAnywhere" />
</subNodes>
</li>
</subNodes>
</thinkRoot>
</ThinkTreeDef>

</ThinkTrees>


Incident_Rimrat.xml

<?xml version="1.0" encoding="utf-8" ?>
<IncidentDefs>

<IncidentDef>
<defName>AnimalCuriositySingle</defName>
<workerClass>IncidentWorker_AnimalCuriositySingle</workerClass>
<chance>15</chance>
<global>true</global>
<favorability>Bad</favorability>
<threatLevel>SmallThreat</threatLevel>
</IncidentDef>

</IncidentDefs>


Rimrat.cs

using System;
using System.Linq;
using System.Collections.Generic;

using UnityEngine;
using AI;
using UI;

public static class AnimalCuriosityUtility
{
    public static float PointsPerAnimal(ThingDef animalDef)
    {
        float num = 10f;
        num += (float)animalDef.race.meleeDamage * 1.8f + (float)animalDef.maxHealth * 0.23f;
        if (animalDef.label == "Rimrat")
        {
            num += 30f;
        }
        return num;
    }
}

public class IncidentWorker_AnimalCuriositySingle : IncidentWorker
{
private const int FixedPoints = 30;
public override bool TryExecute(IncidentParms parms)
{
int maxPoints = 150;
if (Find.TickManager.tickCount < 200000)
{
maxPoints = 40;
}
List<Pawn> list = (
from p in Find.ListerPawns.AllPawns
            where !p.RaceDef.humanoid && AnimalCuriosityUtility.PointsPerAnimal(p.def) <= (float)maxPoints
select p).ToList<Pawn>();
if (list.Count == 0)
{
return false;
}
Pawn pawn = list.RandomListElement<Pawn>();
        AnimalPsychologyUtility.DoMentalBreak(pawn, AnimalMindBrokenState.Curious);
        string text = "A rimrat has become curious about your electronics...".Translate(new object[]
{
pawn.Label.ToLower()
});
Find.LetterStack.ReceiveLetter(new Letter(text, LetterUrgency.High, pawn));
return true;
}
}

namespace AI
{
    public enum AnimalMindBrokenState
    {
        Unbroken,
        Psychotic,
        Curious
    }

    public class JobGiver_Curiosity : ThinkNode_JobGiver
    {
        public float searchRadius = 99999f;
        private const float WaitChance = 0.5f;
        private const int WaitTicks = 90;
        protected override Job TryGiveTerminalJob(Pawn pawn)
        {
            int num = 0;

            //Updated to hopefully retrieve a list of all buildings on the map that can conduct electricity
            List<Building> allBuildingsColonistElect = new List<Building>();
            for (int i = 0; i < Find.ListerBuildings.allBuildingsColonist.Count; i++)
            {
                if (Find.ListerBuildings.allBuildingsColonist[i].TransmitsPower)
                {
                    allBuildingsColonistElect.Add(Find.ListerBuildings.allBuildingsColonist[i]);
                }
            }
            int count = allBuildingsColonistElect.Count;
            float num2 = this.searchRadius * this.searchRadius;
            Building building;
            while (true)
            {
                int index = UnityEngine.Random.Range(0, count);
                building = allBuildingsColonistElect[index];
                num++;
                if (num > 75)
                {
                    break;
                }
                if (BuildingTrashUtility.IsGoodTrashTargetFor(building, pawn))
                {
                    if (this.searchRadius >= 9999f || (building.Position - pawn.Position).LengthHorizontalSquared <= num2)
                    {
                        goto AL_D8;
                    }
                }
            }
            return null;
        AL_D8:
            return BuildingTrashUtility.AttackJobOnFor(building, pawn);
        }
    }

    public class ThinkNode_ConditionalRimrat : ThinkNode_Priority
    {
        private AnimalMindBrokenState brokenState;
        public override JobPackage TryIssueJobPackage(Pawn pawn)
        {
            if (!pawn.MindState.BrokenState.Equals(this.brokenState))
            {
                return null;
            }
            return base.TryIssueJobPackage(pawn);
        }
    }

    public static class AnimalPsychologyUtility
    {
        public static void DoMentalBreak(Pawn pawn)
        {
            if (!pawn.thinker.mindState.BrokenState.Equals(AnimalMindBrokenState.Unbroken))
            {
                return;
            }
            int num = UnityEngine.Random.Range(0, 3);
            if (pawn.JailerFaction != null)
            {
                num = 0;
            }
            switch (num)
            {
                case 0:
                    AnimalPsychologyUtility.DoMentalBreak(pawn, AnimalMindBrokenState.Curious);
                    return;
                case 1:
                    AnimalPsychologyUtility.DoMentalBreak(pawn, AnimalMindBrokenState.Psychotic);
                    return;
                default:
                    return;
            }
        }

        public static void DoMentalBreak(Pawn pawn, AnimalMindBrokenState state)
        {
            if (pawn.IsColonist || pawn.IsPrisonerOfColony)
            {
                string text = string.Empty;
                switch (state)
                {
                    case AnimalMindBrokenState.Curious:
                        text = "MentalBreakMischievous".Translate(new object[]
{
pawn
});
                        break;
                    case AnimalMindBrokenState.Psychotic:
                        text = "MentalBreakRampage".Translate(new object[]
{
pawn
});
                        break;
                }
                if (text != string.Empty)
                {
                    Find.LetterStack.ReceiveLetter(new Letter(text, LetterUrgency.High, pawn));
                }
            }
            if (pawn.playerController != null && pawn.playerController.Drafted)
            {
                pawn.playerController.Drafted = false;
            }
            pawn.MindState.BrokenState = (MindBrokenState)state;
            pawn.jobs.EndCurrentJob(JobCondition.ForcedInterrupt);
        }
    }
}
Title: Re: Targeting Electronics
Post by: Argain on May 30, 2014, 05:17:24 PM
I think probably the biggest hurdle I'm having with this is not being able to debug the code while it's running... is there a way to print text to the log window? Was thinking maybe then I could at least print out the list of buildings being pulled to make sure they're the right things to be targeting.
Title: Re: Targeting Electronics
Post by: Argain on May 30, 2014, 07:53:22 PM
Not sure how or why, but I had a Rimrat randomly seek out the Solar building and start attacking it... but then all the rats started slowly losing hp until they died. So, I'm close... just some odd bugs to work out  ???

Updated Rimrat.cs

using System;
using System.Linq;
using System.Collections.Generic;

using UnityEngine;
using AI;
using UI;

public class IncidentWorker_AnimalCuriositySingle : IncidentWorker
{
public override bool TryExecute(IncidentParms parms)
{
List<Pawn> list = (
from p in Find.ListerPawns.AllPawns
            where p.def.label == "Rimrat"
select p).ToList<Pawn>();
if (list.Count == 0)
{
return false;
}
Pawn pawn = list.RandomListElement<Pawn>();
        AnimalPsychologyUtility.DoMentalBreak(pawn, AnimalMindBrokenState.Curious);
        string text = "A Rimrat has become curious about your electrical wires.".Translate(new object[]
{
pawn.Label.ToLower()
});
Find.LetterStack.ReceiveLetter(new Letter(text, LetterUrgency.High, pawn));
return true;
}
}

namespace AI
{
    public enum AnimalMindBrokenState
    {
        Unbroken,
        Curious
    }

    public class JobGiver_Curiosity : ThinkNode_JobGiver
    {
        public float searchRadius = 99999f;
        private const float WaitChance = 0.5f;
        private const int WaitTicks = 90;
        protected override Job TryGiveTerminalJob(Pawn pawn)
        {
            int num = 0;
            List<Building> allBuildingsColonistElect = new List<Building>();
            for (int i = 0; i < Find.ListerBuildings.allBuildingsColonist.Count; i++)
            {
                if (Find.ListerBuildings.allBuildingsColonist[i].ConnectedToNet.hasPowerSource)
                {
                    allBuildingsColonistElect.Add(Find.ListerBuildings.allBuildingsColonist[i]);
                }
            }
           
            int count = allBuildingsColonistElect.Count;
            float num2 = this.searchRadius * this.searchRadius;
            Building building;
            while (true)
            {
                int index = UnityEngine.Random.Range(0, count);
                building = allBuildingsColonistElect[index];
                num++;
                if (num > 75)
                {
                    break;
                }
                if (RimratBuildingTrashUtility.IsGoodTrashTargetFor(building, pawn))
                {
                    if (this.searchRadius >= 9999f || (building.Position - pawn.Position).LengthHorizontalSquared <= num2)
                    {
                        goto AL_D8;
                    }
                }
            }
            return null;
        AL_D8:
            return RimratBuildingTrashUtility.AttackJobOnFor(building, pawn);
        }
    }

    public class ThinkNode_ConditionalRimrat : ThinkNode_Priority
    {
        private AnimalMindBrokenState brokenState;
        public override JobPackage TryIssueJobPackage(Pawn pawn)
        {
            if (!pawn.MindState.BrokenState.Equals(this.brokenState))
            {
                return null;
            }
            return base.TryIssueJobPackage(pawn);
        }
    }

    public static class AnimalPsychologyUtility
    {
        public static void DoMentalBreak(Pawn pawn)
        {
            if (!pawn.thinker.mindState.BrokenState.Equals(AnimalMindBrokenState.Unbroken))
            {
                AnimalPsychologyUtility.DoMentalBreak(pawn, AnimalMindBrokenState.Curious);
                return;
            }
        }

        public static void DoMentalBreak(Pawn pawn, AnimalMindBrokenState state)
        {
            pawn.MindState.BrokenState = (MindBrokenState)state;
            pawn.jobs.EndCurrentJob(JobCondition.ForcedInterrupt);
        }
    }

    public static class RimratBuildingTrashUtility
    {
        private const int AttackJobTimeLimit = 1000;
        public static bool IsGoodTrashTargetFor(Building b, Pawn pawn)
        {
            return b.def.eType != EntityType.BuildingInert && b.def.useStandardHealth && pawn.CanReach(b, PathMode.Touch) && !b.IsBurningImmobile() && pawn.HostileTo(b);
        }
        public static Job AttackJobOnFor(Building b, Pawn pawn)
        {
            Job job = new Job(JobDefOf.AttackMelee, b);
            job.TimeLimit = 1000;
            return job;
        }
    }
}
Title: Re: Targeting Electronics
Post by: Justin C on May 30, 2014, 09:42:14 PM
Quote from: Argain on May 30, 2014, 05:17:24 PM
I think probably the biggest hurdle I'm having with this is not being able to debug the code while it's running... is there a way to print text to the log window? Was thinking maybe then I could at least print out the list of buildings being pulled to make sure they're the right things to be targeting.
Log.Warning("Text");

This will print to the console. Press the '~' key to open and close the console.
Title: Re: Targeting Electronics
Post by: Argain on May 30, 2014, 11:34:18 PM
Awesome, thank you much!
Title: Re: Targeting Electronics
Post by: Tynan on June 01, 2014, 08:48:38 PM
Log.Message and Log.Error also work. Error will pop the console up if you're in dev mode.
Title: Re: Targeting Electronics
Post by: Argain on June 02, 2014, 10:25:37 PM
I started 'debugging' using Log messages and noticed somewhere in this ForLoop things are breaking:

List<Building> allBuildings = Find.ListerBuildings.allBuildingsColonist;
List<Building> allBuildingsColonistElect = new List<Building>();

Log.Message("New building list instantiated...");

Log.Message("Adding buildings to list...");
for (int i = 0; i < allBuildings.Count - 1; i++)
{
    Log.Message("Checking building " + allBuildings[i].Label + "... ");
    if (allBuildings[i].ConnectedToNet.hasPowerSource)
    {
        allBuildingsColonistElect.Add(allBuildings[i]);
        Log.Message(allBuildings[i].Label + " added to list...");
    }
     else
    {
        Log.Message(allBuildings[i].Label + " did not have a power source.");
    }
}


The code reaches the first message and returns "Checking building Metal Wall...", but neither of the other messages fire... so it seems the IF statement is breaking somehow? 'hasPowerSource' is Bool, so even if it returned false the other message should still fire and nothing is happening. Any other programmers out there have an idea about this?
Title: Re: Targeting Electronics
Post by: Argain on June 02, 2014, 10:37:51 PM
Nevermind...
if(allBuildings[i].TransmitsPower) did the trick  ;D