JobDrivers, JobGivers and WorkGivers, oh my! [HELP] :)

Started by Lord Fappington, January 07, 2015, 01:27:21 AM

Previous topic - Next topic

Lord Fappington

Hi all making nice progress on my Romance mod; however, I'm getting jammed up at the jobdriver, jobgiver phase.  I really need to make my own custom ones as I don't think I can fully implement my plan using what already exists.

Therefore, could someone quickly answer the following questions, if possible?  Your help is greatly appreciated!!

1.  What is the role of JobDrivers JobGivers and WorkGivers?  Are they interrelated, mutually exclusive?  They appear to be linked to JobDefs and WorkTypeDefs.

2.  In coding JobDrivers and JobGivers, I see a lot of code that I really have a hard time understanding.  For example in JobDriver_Research (see below).  What the fluff are <MakeNewToils> & _Iterator_## and how do I get them to work?
protected override IEnumerable<Toil> MakeNewToils ()
{
JobDriver_Research.<MakeNewToils>c__Iterator37 <MakeNewToils>c__Iterator = new JobDriver_Research.<MakeNewToils>c__Iterator37 ();
<MakeNewToils>c__Iterator.<>f__this = this;
JobDriver_Research.<MakeNewToils>c__Iterator37 expr_0E = <MakeNewToils>c__Iterator;
expr_0E.$PC = -2;
return expr_0E;

Temeez

I had the same problem. I don't know the exact reason why assembly decompilers can't seem to open those pieces of codes. The only way I managed to understand a JobDriver and WorkGiver was to look at other mods that had them.

I used 3 different decompilers when learning, because sometimes I got different results when decompiling something. Xamarin Studio, JetBrains dotPeek and ILSpy. I would suggest you to check other mods where those things are used, my beer mod for example opens at least with the ILSpy. One mod that I remember having Jobs and Works is called Miscellaneous.

Hope this helps.

Haplo

What you see there is the problem that the compiler doesn't understand certain functions and can't rebuild the original correctly. This happens with nearly all the jobs because of how the Toils work.
I have the source code of Miscellaneous out where you can take a look into how the Jobs work. I think I have also a template in there, with a detailed description, but I'm not sure right now as I'm not at home. Try to find TEMPLATE_Job*** or something like that in the files.
You can also look into the sources that RimWorld provides. There you can find a few Jobs in their original code. 

Lord Fappington

Quote from: Haplo on January 07, 2015, 06:45:31 AM
What you see there is the problem that the compiler doesn't understand certain functions and can't rebuild the original correctly. This happens with nearly all the jobs because of how the Toils work.
I have the source code of Miscellaneous out where you can take a look into how the Jobs work. I think I have also a template in there, with a detailed description, but I'm not sure right now as I'm not at home. Try to find TEMPLATE_Job*** or something like that in the files.
You can also look into the sources that RimWorld provides. There you can find a few Jobs in their original code.

Thank you Haplo & Temeez; that makes a lot more sense :)

How about towards my other question, regarding jobdrivers/jobgivers and workgivers?

Haplo

So here is my detailed description, how a jobdriver is called in Miscellaneous:

I actually meant the Template_JobDriver_GotoCellAndDoX.cs from the SourceCode of Miscellaneous.
In there I've descibed how the JobDriver uses the Toils to do its work one step after the other.

So let's start.


*A classroom appears with a blackboard full of unreadable C# code*

Hello class.
My name is Haplo and I'm your tutor for todays crash course in job driving ;)

Please note that this is a crash course, not a detailed tutorial. I won't explain the inner workings, but only show the basics for how it works and works together.
For everything else, do the old fashioned way and try to understand it yourself :P

First an example from my Template:
In the example I use two types of Toils: Predefined and New.

Here I use a predefined Toil. This means, that I use a toil provided by Tynan's game code.

            // Toil 1:
            // Goto Target (TargetPack A is selected (It has the info where the target cell is))
            yield return Toils_Goto.GotoCell(TargetIndex.A, PathMode.OnCell);


And here I use a new Toil with an action. This is a Toil, that can be defined to do whatever you want it to do.
Just note, that it is always only ONE action it can do at the same time. So don't tell a Pawn to go to X and Y in the same Toil. Use two Toils to do that!

            // Toil 2:
            // Set playerController to drafted => The pawn switches to being drafted
            Toil arrivalDraft = new Toil();
            arrivalDraft.initAction = () =>
            {
                // Here you can insert your own code about what should be done
                // At the time when this toil is executed, the pawn is at the goto-cell from the first toil
                pawn.playerController.Drafted = true;
            };
            arrivalDraft.defaultCompleteMode = ToilCompleteMode.Instant;
            yield return arrivalDraft;


The JobDriver uses these Toils to do its work one step after the other.
Do Toil 1 until its finished..
.. then do Toil 2 until that is finished..
.. xxxxx


The calling I use most often is via a FloatMenu (right-click-menu on buildings or pawns). But other ways are also possible without much problems.
This is how I call the JobDef (defined via XML) to call colonists to the tactical computer and draft them:

                Job job = new Job(DefDatabase<JobDef>.GetNamed("GotoCellAndDraft"), thing);
                workPawn.playerController.TakeOrderedJob(job);


And this is the JobDef part, where it all comes together:

    <JobDef>
<defName>GotoCellAndDraft</defName>
<driverClass>Miscellaneous.JobDriver_GotoCellAndDraft</driverClass>
<reportString>Going to gathering spot.</reportString>
</JobDef>


Oh I forgot the actual JobDriver: the following code shows the actual Toils from the JobDriver used here: Goto target cell, spread out, draft.
A simple and straight forward three parts job.

            // Goto Target
            yield return Toils_Goto.GotoCell(TargetIndex.A, pathMode);

            // Spread out
            Toil setPosition = new Toil()
            {
                initAction = () =>
                {
                    IntVec3 freeCell = GenCellFinder.RandomStandableClosewalkCellNear(pawn.Position, spreadOut);
                    TargetPack tp = new TargetPack(freeCell);
                    pawn.pather.StartPath(tp, PathMode.OnCell);
                },
                defaultCompleteMode = ToilCompleteMode.PatherArrival
            };
            yield return setPosition;

            // Set playerController to drafted
            Toil arrivalDraft = new Toil();
            arrivalDraft.initAction = () =>
            {
                pawn.playerController.Drafted = true;
                Find.Selector.Select(pawn);
            };
            arrivalDraft.defaultCompleteMode = ToilCompleteMode.Instant;
            yield return arrivalDraft;


So that is all for todays lesson.

If you have questions I recommend the source of the miscellaneous mod or you can ask me.

Thank you all for your attention and have fun driving your jobs. 8)

*Walks off stage and vanishes in a puff of smoke*

Rikiki

So nice to get a new tutorial to play with! :D
So how can I improve the fishing pier with this now?... *go away mumbling in my beard* ;D

Haplo

I would suggest:
-Toil: Goto Pier
-Toil: Stand around for x ticks while showing fishing rod
-Toil: Random success: Fish OR no fish, that is the question ;)

Lord Fappington

Quote from: Haplo on January 07, 2015, 04:00:18 PM

The calling I use most often is via a FloatMenu (right-click-menu on buildings or pawns). But other ways are also possible without much problems.
This is how I call the JobDef (defined via XML) to call colonists to the tactical computer and draft them:

                Job job = new Job(DefDatabase<JobDef>.GetNamed("GotoCellAndDraft"), thing);
                workPawn.playerController.TakeOrderedJob(job);



This is amazing, danke shane Haplo.  I tried utilizing your float menu approach in my workgiver (is this the proper way to do it?)  However, I cannot find the right context (e.g., Verse.AI, etc.) with the workPawn command.  Under which reference is it?  I did a manual search through Assembly-CSharp and did not find it.

Best,
LF

Rikiki

Have you put the "using Verse.AI;" line at the top of your class file? (near "using RimWorld;")

Haplo

The float menus are something that is only available for things and their derivates (like Building, Pawn,...), not for WorkGivers.
A WorkGiver needs a job, so you only need the first part:
So your WorkGiver needs this (and most likely a bit work on other override functions that I overlooked right now?):

        public override Job JobOnThing(Pawn pawn, Thing t)
        {
            Job job = new Job(DefDatabase<JobDef>.GetNamed("GotoCellAndDraft"), thing);
            return job;
        }

LWM

Quote from: Temeez on January 07, 2015, 02:29:54 AM
I had the same problem. I don't know the exact reason why assembly decompilers can't seem to open those pieces of codes. The only way I managed to understand a JobDriver and WorkGiver was to look at other mods that had them.

I used 3 different decompilers when learning, because sometimes I got different results when decompiling something. Xamarin Studio, JetBrains dotPeek and ILSpy...

I cannot recommend dnSpy enough.  I dual boot Windows and Linux and the only time I ever boot into Windows is so I can use dnSpy when a new version of RimWorld comes out.  I then save(export?) the project and then go back to Linux to work on mods.

https://github.com/0xd4d/dnSpy

--LWM

Mehni

I don't know why we're necro-ing threads that were lasted posted in three years ago, but since we're posting links have a link to a guide that explains jobs from top to bottom:

https://github.com/Mehni/ExampleJob/wiki

mangalores

Just as a small and simplistic explanation. The reason the toils cannot be decompiled by the programs is because they are built as implicit iterator sequences via yield return aka they are executed at runtime and don't exist within the source code without execution, it only knows of code references about an iterator that could have a dynamic number of items in its set to be executed there.

It's a way to write dynamic code sequences (e.g. the same Job could create one time 3 toils and another 6 based on code inside the toil creating classes).

Thought this might give some more insight, but I'll cease my necromancy here