List all Animals on current map

Started by shkal, February 24, 2019, 02:39:26 PM

Previous topic - Next topic

shkal

Hey guys,

I started to dig into rimworld modding just today, so I am a complete noob on this topic. As of now, I managed to extend the GUI to add custom Buttons/Windows. So far so good.

But for the last couple of hours I struggled with iterating over all animals on the current map (in order to add them to a custom GUI). I spent some time looking through the AnimalTab and the BetterPawnControl Mods but I can't seem to get the hang of it.

My current understanding is that I somehow should use
Find.CurrentMap.mapPawns.AllPawns
but I don't understand how I can differentiate between Humans (Colonists, Prisoners) from the Animals.

I would be happy if you guys could advise me on how to loop over all (tamed) animals on the current map. A short piece of code would be appreciated but is not crucial, I guess.

Thanks,
Mario

Mehni

Hello Mario, welcome. Did you look at the mod AnimalTab, or did you look at the vanilla Animal or Wildlife Tab? The vanilla versions should be clear: Take a look at

// MainTabWindow_Wildlife
// MainTabWindow_Animals

and I suggest you get a decent decompiler for those. The latest version of ILSpy is very good. dnSpy is popular too. More here: https://rimworldwiki.com/wiki/Modding_Tutorials/Decompiling_source_code

shkal

Hey Mehni,

thanks for the quick response and the warm welcoming.
I forgot to mention that I set up my developing environment on my company laptop and security guidelines (-symantec) won't allow me to use the decompiler.

Nonetheless, I spent some more time looking through some mod sources on github.

So for anyone who is struggling with the same thing (and finds this through google or forum search) - my current approach looks like this:

            MapPawns Pawns = Find.CurrentMap.mapPawns;
            int Counter = 0;

            foreach(Pawn p in Pawns.PawnsInFaction(Faction.OfPlayer))
            {
                Counter++;
                Log.Message("Pawn "+Counter+":  "+p.Name.ToString());
                if (p.RaceProps.Animal)
                {
                    Log.Message("^ this is an animal");
                }
            }


..so the important thing is to check against RaceProps.Animal.
I am not sure if this is best practice, but it seems to work.

PS: I am not a C# developer, so feel free to advise improvements on my ugly code :)

Cheers,
Mario

Mehni

Ah, bummer. I won't link them here, but there are decompiled versions of RimWorld floating out there on the web. Nothing beats a decompiler though.

Your snippet can also be achieved with
Quoteint count = Find.CurrentMap.mapPawns.PawnsInFaction(Faction.OfPlayer).Count(x => x.RaceProps.Animal);
which uses LINQ. LINQ is great.

For the record, the //MainTabWindow_Animals does the following:


protected override IEnumerable<Pawn> Pawns => from p in Find.CurrentMap.mapPawns.PawnsInFaction(Faction.OfPlayer)
where p.RaceProps.Animal
select p;


which makes an IEnumerable with the tamed animals. This also uses LINQ, but my decompiler shows it uses the SQL-like statements. If I'd rewrite it, it'd be IEnumerable<Pawn> myEnumerable = Find.CurrentMap.mapPawns.PawnsInFaction(Faction.OfPlayer).Where(x => x.RaceProps.Animal) -- same result, different style.

Any and all of these approaches work (to be specific: that includes yours). Behind the scenes, LINQ doesn't do much more than a foreach either (but it has some clever optimisations).

Never one to miss an opportunity to self-plug, Numbers has a few snippets that could come in handy for you as well :V