Troubles with Job_Drivers

Started by Wivex, July 22, 2015, 07:17:00 AM

Previous topic - Next topic

Wivex

So, I'm making mod for auto using tools from RTFTJ for the corresponding jobs (auto picking them up, using and getting hauled to storage by pawns automatically). And stuck with writing my own Job_driver to do that.
To ease the coding i decided to call for vanilla jobs in my Job_Driver instead or writing my own toils from scratch. The scheme (not full code) for wood cutting with axes:

namespace ToolsForJobs
{
    public class JobDriver_PlantCut_WithTool : JobDriver
    {
        public JobDriver_PlantCut_WithTool() : base() { }

        protected override IEnumerable<Toil> MakeNewToils()
        {
            //Pick up Tool
            {
                Toil toilPickupTool = new Toil();
                toilPickupTool.initAction = () =>
                {
                    ...
                    pawn.jobs.jobQueue.EnqueueLast(new Job(JobDefOf.Equip, closestAvailableTool))
                }
                yield return toilPickupTool;
            }

            //Cut Tree
            {
                Toil toilCutTree = new Toil();
                toilCutTree.initAction = () =>
                {
                    ...
                    pawn.jobs.jobQueue.EnqueueLast(new Job(JobDefOf.CutPlant, closestAvailableTree))
                }
                yield return toilCutTree;
            }

            //Return Tool
            {
                Toil toilReturnTool = new Toil();
                toilReturnTool.initAction = () =>
                {
                    ...
                    pawn.jobs.jobQueue.EnqueueLast(HaulAIUtility.HaulToStorageJob(pawn, pawn.equipment.Primary as Thing))
                }
                yield return toilCutTree;
            }
      }
}


The problem here is that all toils initialize at the same time and checking acting pawn hands (to pick up an axe if pawn doesn't have it equipped and then check hands again to drop it) doesn't work because i can't trace pawn's status and equpment change while toils are executed. Meaning i will get the same pawn.equipment.Primary status in toilPickupTool and toilReturnTool even if it's really changed while Job_driver executes. How can i do that?

1000101

Make sure you have the xml defs for the JobDriver and WorkGiver setup for your job (and possibly designators depending on what you're doing).
(2*b)||!(2*b) - That is the question.
There are 10 kinds of people in this world - those that understand binary and those that don't.

Powered By

Wivex

I sure have and they are working properly, just non important here.

1000101

Ok, just making sure it's not an issue of the job priority :)

Checking pawns equipment, that can partly be done as part of setting up the toils (if pawn has tool, don't add fetch toil(s)).

It can also be done in the toil itself.  For this part you will probably need to write the toil from scratch as the pre-fab toils likely won't do exactly what you need or how you need it.

Example of my personal wall smoothing job showing use of pre-fab toils (reserve, goto) and a complete toil only from the base toil class (toilWork): protected override IEnumerable<Toil> MakeNewToils()
{
// Just on the off-chance the rock is on fire...
this.FailOnBurningImmobile( TargetIndex.A );
// Reserve the target
yield return Toils_Reserve.Reserve( TargetIndex.A );
// Go to the target
Toil toilGoto = Toils_Goto.GotoCell( TargetIndex.A, PathEndMode.Touch );
// Fail going to the target if it becomes unreachable
ToilFailConditions.FailOn< Toil >( toilGoto, ( Func< bool > )(() =>
{
if( Reachability.CanReach( pawn, (TargetInfo)TargetLocA, PathEndMode.Touch, Danger.None ) )
return false;
return true;
}));
yield return toilGoto;
// Now the work toil itself
Toil toilWork = new Toil
{
// Continue until done
defaultCompleteMode = ToilCompleteMode.Never,
// When the job starts...
initAction = new Action(() =>
{
// Get the rock resource
mineable = MineUtility.MineableInCell( TargetA.Cell );
// Get this miners speed based on stat
nextMinerStrike = (int)( (double)TicksPerStrike / (double)pawn.GetStatValue( StatDefOf.MiningSpeed, true ) );
minerTicks = 0;
} ),
// The work tick
tickAction = new Action(() =>
{
minerTicks += 1;
if( minerTicks < nextMinerStrike ) return;
// Reset counter, damage rock
minerTicks = 0;
mineable.HitPoints -= DamagePerStrike;
} )
};
// When should we stop?
toilWork.endConditions.Add( ( Func< JobCondition > )(() =>
{
// Go until the rock is fully damaged
if( mineable.HitPoints > 0 ) return JobCondition.Ongoing;
return JobCondition.Succeeded;
} ) );
// Do something when done
toilWork.AddFinishAction( new Action(() =>
{
// Clear the designation at this cell
Common.RemoveDesignationDefOfAt( DataWarehouse.DesignationDefs.SmoothWall, TargetA.Cell );
// Better have associated stone blocks...
string blocksDef = "Blocks" + mineable.def.defName;
ThingDef stoneBlocks = DefDatabase<ThingDef>.GetNamed( blocksDef, true );
// Replace the rock with a stone wall
GenSpawn.Spawn( ThingMaker.MakeThing( DataWarehouse.ThingDefs.Wall, stoneBlocks ), TargetA.Cell ).SetFaction( Faction.OfColony );
} ) );
// Some fun sounds while working
ToilEffects.WithSustainer( toilWork, ( Func< SoundDef > )(() =>
{
return DataWarehouse.SoundDefs.Recipe_Sculpt;
} ) );
// Some fun effects while working
ToilEffects.WithEffect( toilWork, "Mine", TargetIndex.A );
yield return toilWork;
// And we're done.
yield break;
}


For your needs, in your action delegates you would put your tool checks in and modify the job status, assign interrupt jobs, abort job on tool loss, etc.
(2*b)||!(2*b) - That is the question.
There are 10 kinds of people in this world - those that understand binary and those that don't.

Powered By

Haplo

The toils may be initialized, but the initAction should only be done one toil after the other.
- First it takes the tool, toil end (1 Tick if in backpack, multiple if it needs to be hauled first)
- Then it cuts the plant, ... ... ... a time later the plant is cut down, toil end (multiple ticks)
- Last it returns the tool
This should be done one after the other by the job controller of the pawn.

Edit: 1000101 was faster and is much more detailed :)

Wivex

If i want to make jobs queue (using jobs.jobQueue class), how can make these jobs being added one after another when previous job is finished (using jobs finishing conditions presumably).

If i just make something like:

pawn.jobs.jobQueue.EnqueueLast(job1);
pawn.jobs.jobQueue.EnqueueLast(job2);
pawn.jobs.jobQueue.EnqueueLast(job3);

they will be initialized simalteniously and i won't be able to trace changes in pawn's equipment after job1 is done, to do job2 properly, for example.

Wivex

Quote from: Tynan on July 25, 2015, 06:35:00 PM
You shouldn't use WorkGiver with JobQueue that way; it's really not designed for that. either write a single Job that does everything you want (like hunting) or write several JobGivers that each detect the conditions needed and start the appropriate job at the appropriate step (like construction).

I recommend option 2, as it can smoothly restart jobs that are half-done.

Wivex

#7
Ok, so, what is the proper way to do workgiver to store equipped weapon, if it meets some conditions? If i use PotentialWorkThingsGlobal for marking equipped pawn's weapons like:

        public override IEnumerable<Thing> PotentialWorkThingsGlobal(Pawn pawn)
        {
            return FindUnusedTool(pawn); // returns IEnumerable<Thing> list with one item: (Thing)pawn.equipment.Primary
            // currently equipped weapon for this pawn in other words
        }

then i will get exception error, as the target thing must be reachable on the map, not inside pawn's equipment list.
Maybe there is a way to make IEnumerable<IntVec3> PotentialWorkCellsGlobal check for free weapon strorages, and if it exists, start HaultoStorage jobs for certain weapons but i have no clue how to do that. Any suggestions?

isistoy

You could possibly add the tool dropping as a finish action of your cutting Toil, then if it still exists and can be reserved, try to engage hauling?
I suppose if last toil is not possible or previous one interrupted, the tool would be avail. and taken by any other hauler.
<Stay on the scene like a State machine>

Wivex

I think i really can do without toils at all. It breaks original concept of jobs, but works great for modding. I made some progress already and hope to release first version of mod soon.

isistoy

It breaks original concept of jobs, IF you don't need to encap them so they are chained in a way, like vanilla does sometimes, or to do any specifics.
<Stay on the scene like a State machine>

Wivex

Quote from: isistoy on July 28, 2015, 10:10:35 PM
It breaks original concept of jobs, IF you don't need to encap them so they are chained in a way, like vanilla does sometimes, or to do any specifics.
What do you mean?

isistoy

Sorry, but what do you mean by: it breaks original concept of Jobs? I don't understand if you talk about the way you did it or if you talk in general... Anyway. Nice that you found your way to do it without Toils.
<Stay on the scene like a State machine>

Wivex

How can i keep reservations on weapons between saves\loads? ExposeData tips requested.

1000101

Use ReservationUtility and let it worry about it?
(2*b)||!(2*b) - That is the question.
There are 10 kinds of people in this world - those that understand binary and those that don't.

Powered By