Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - colinfang

#1
I understand it was reported before in an earlier version but it hasn't been resolved and no save was attached on that one.

Here I have the save before & after the blinding pulse to help debugging

https://drive.google.com/file/d/1N5boOu3577uHd2iVD-fTlW44bFLNFvW5/view?usp=sharing







RimWorld 1.3.3200 rev726
UnityEngine.StackTraceUtility:ExtractStackTrace ()
Verse.Log:Message (string)
RimWorld.VersionControl:LogVersionNumber ()
Verse.Root:CheckGlobalInit ()
Verse.Root:Start ()
Verse.Root_Entry:Start ()

Loading game from file test_after with mods:
  - Ludeon.RimWorld
  - Ludeon.RimWorld.Royalty
  - Ludeon.RimWorld.Ideology
UnityEngine.StackTraceUtility:ExtractStackTrace ()
Verse.Log:Message (string)
Verse.SavedGameLoaderNow:LoadGameFromSaveFileNow (string)
Verse.Root_Play/<>c:<Start>b__1_1 ()
Verse.LongEventHandler:RunEventFromAnotherThread (System.Action)
Verse.LongEventHandler/<>c:<UpdateCurrentAsynchronousEvent>b__27_0 ()
System.Threading.ThreadHelper:ThreadStart_Context (object)
System.Threading.ExecutionContext:RunInternal (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool)
System.Threading.ExecutionContext:Run (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool)
System.Threading.ExecutionContext:Run (System.Threading.ExecutionContext,System.Threading.ContextCallback,object)
System.Threading.ThreadHelper:ThreadStart ()

Detected infinite stat recursion when evaluating Sight
UnityEngine.StackTraceUtility:ExtractStackTrace ()
Verse.Log:Error (string)
Verse.PawnCapacitiesHandler:GetLevel (Verse.PawnCapacityDef)
RimWorld.StatPart_SightPsychicSenstivityOffset:TryGetPsychicOffset (Verse.Thing,single&)
RimWorld.StatPart_SightPsychicSenstivityOffset:TransformValue (RimWorld.StatRequest,single&)
RimWorld.StatWorker:FinalizeValue (RimWorld.StatRequest,single&,bool)
RimWorld.StatWorker:GetValue (RimWorld.StatRequest,bool)
RimWorld.StatWorker:GetValue (Verse.Thing,bool)
RimWorld.StatExtension:GetStatValue (Verse.Thing,RimWorld.StatDef,bool)
Verse.PawnCapacityUtility:CalculateCapacityLevel (Verse.HediffSet,Verse.PawnCapacityDef,System.Collections.Generic.List`1<Verse.PawnCapacityUtility/CapacityImpactor>,bool)
Verse.PawnCapacitiesHandler:GetLevel (Verse.PawnCapacityDef)
RimWorld.StatWorker:GetValueUnfinalized (RimWorld.StatRequest,bool)
RimWorld.StatWorker:GetValue (RimWorld.StatRequest,bool)
RimWorld.StatWorker:GetValue (Verse.Thing,bool)
RimWorld.StatExtension:GetStatValue (Verse.Thing,RimWorld.StatDef,bool)
RimWorld.Verb_MeleeAttack:GetDodgeChance (Verse.LocalTargetInfo)
RimWorld.Verb_MeleeAttack:TryCastShot ()
Verse.Verb:TryCastNextBurstShot ()
Verse.Verb:WarmupComplete ()
Verse.Verb:TryStartCastOn (Verse.LocalTargetInfo,Verse.LocalTargetInfo,bool,bool,bool)
Verse.Verb:TryStartCastOn (Verse.LocalTargetInfo,bool,bool,bool)
RimWorld.Pawn_MeleeVerbs:TryMeleeAttack (Verse.Thing,Verse.Verb,bool)
Verse.AI.JobDriver_Wait:CheckForAutoAttack ()
Verse.AI.JobDriver_Wait:Notify_StanceChanged ()
Verse.Pawn_StanceTracker:SetStance (Verse.Stance)
Verse.Stance_Busy:Expire ()
Verse.Stance_Busy:StanceTick ()
Verse.Pawn_StanceTracker:StanceTrackerTick ()
Verse.Pawn:Tick ()
Verse.TickList:Tick ()
Verse.TickManager:DoSingleTick ()
Verse.TickManager:TickManagerUpdate ()
Verse.Game:UpdatePlay ()
Verse.Root_Play:Update ()


Hmm, just skimming the code, I am not sure I understand how it manages to calculate the sight without issue in a normal game.

Bascially, in order to get the sight in `Verse.PawnCapacitiesHandler:GetLevel`, the game has to check each hediff.
Blinding pulse would add Hediff `PsychicBlindness`, which affects sight by `<capacityFactorEffectMultiplier>PsychicSensitivity</capacityFactorEffectMultiplier>`
Then the game checks `PsychicSensitivity` via `RimWorld.StatWorker:GetValue`
This stat has `StatPart_SightPsychicSenstivityOffset`, which requires sight in `TryGetPsychicOffset`, hence the infinite loop
#2
Quote from: glass zebra on February 05, 2022, 04:16:08 PM
Weirdly enough, shooting one of your own pawns which has a shield belt and therefore also takes 0 damage does flag your own pawns as guilty.

Just had another look and you are right. Damage mitigation by armor occurs AFTER `PreApplyDamage` so it doesn't prevent enemy from getting guilty.

I corrected the bug report above.
#3
At the moment the game has 2 paths to trigger a guilty check.
- 1. When a pawn is downed and his `pawn.GetLord().LordJob` is of particular types, e.g. `LordJob_AssultColony`, via `GuiltyOnDowned`
- 2. When a pawn damages a player pawn.

I notice some scenarios feel like guilty actions but the current logic fails to mark guilty.

For example, in a quest of "Orbital Drone Hack", `LordJob` is `LordJob_AssultThings` which somehow is not considered `GuiltyOnDowned` from the first path.
Hence the game relies on the second path to determine the guilt of an enemy. Now lets look at the following cases

- An enemy hits my slaves. The code checks `pawn.HomeFaction.IsPlayer`, but slaves belong to their original faction. So in this case the game would consider the attacker innocent.

- An enemy is downed before he manages to deal any damage to my pawn. E.g. he hasn't got a chance to attack, his attack misses, his attack is dodged. The guilty check is in `Pawn_HealthTracker.PreApplyDamage`,  as long as the damage doesn't take place, he is considered innocent.

#4
I notice this when I was modding the game. XML misses `label` for certain tools

Not sure if this is on purpose

This is Ibex, you can  see it misses `<label>teeth</label>`


      <li>
        <capacities>
          <li>Bite</li>
        </capacities>
        <power>8</power>
        <cooldownTime>2</cooldownTime>
        <linkedBodyPartsGroup>Teeth</linkedBodyPartsGroup>
        <chanceFactor>0.5</chanceFactor>
      </li>


This is Deer, it is correct


      <li>
        <label>teeth</label>
        <capacities>
          <li>Bite</li>
        </capacities>
        <power>8</power>
        <cooldownTime>2</cooldownTime>
        <linkedBodyPartsGroup>Teeth</linkedBodyPartsGroup>
        <chanceFactor>0.5</chanceFactor>
      </li>
#5
Not sure if this is a bug or a feature, but I will write it down here any way.

I knew for a while that a plant is inedible if it is leafless. And the leafless temperature is controlled by 2 values `MinLeaflessTemperature=-10` &   `MaxLeaflessTemperature=-2`, which implies that a plant might lose its leaves as soon as the temperature drops below -2 °C

However, today I happened to notice in a game while the outdoor temperature is -9, none of the plants show leafless textures, so I started to wonder why...

Here is the relevant code (RimWorld 1.3) in `Plant.cs`, it is quite short and simple (at a first glance)

    protected virtual float LeaflessTemperatureThresh
    {
    get
    {
    float diff = MaxLeaflessTemperature - MinLeaflessTemperature;
    float leaflessThresh = ((this.HashOffset() * 0.01f) % diff) - diff + MaxLeaflessTemperature;
   
    return leaflessThresh;
    }
    }


The purpose of this code is to generate an instance dependent temperature threshold, so each plant has a different value which persists over its lifetime. This adds a bit of variation to ensure not all plants become leafless at the same time. Once we get the threshold, the leafless check is a straightforward `AmbientTemperature < threshold`. However from this code I cannot spot off the top of my head why `threshold` is not between (-10, -2)

... hours of debugging...

It turns out `HashOffset()` often returns a negative value. In fact, it is seeded by `ThingIDNumber`. One can verify that as long as there are less than 20M spawned things in the game in history, `HashOffset` is always negative.

C# remainder operator % has a feature that it would return negative values for negative operand. So `(this.HashOffset() * 0.01f) % diff)` has a range of (-8, 0), which gives the final leafless temperature threshold as (-18, -10). Myth solved.

A side note: The code converts the hash into single precision floats. Hence quite a lot of precisions are lost before  `%` operation. As a consequence plants that are spawned close in time together would have very similar leafless temperature threshold.
#6
Releases / [1.3] Infection Info
January 27, 2022, 01:28:01 PM
Ever wondering why those nasty infections occur so often? This is a QoL mod that attempts to demystify the infection mechanism behind the scenes. It displays the real time infection chance and uncovers the influential factors. It provides an option to make the infection chance rely on the current room cleanliness instead of that at the time of tending. (Default off)

Hope you like it. Any suggestions please let me know!

Base Game Mechanism For Infection


     
  • Some injuries might develop infections.
     
  • Once a susceptible injury occurs, the game does a first pass RNG check using the base infection chance.
     
  • If success, a random number is picked to determine how soon the wound will be infected. This number ranges between 6 hours to 18 hours, referred as danger period.
     
  • At the scheduled time, a second RNG check is done to determine whether the infection is applied eventually. The chance here is based on a few factors e.g. tend quality, tend room cleanliness, injury severity.
What Does This Mod Do


     
  • It shows the infection chance breakdown in the injury tooltip.
     
  • It shows how soon the injury would enter or leave the danger period.
     
  • Injuries that are susceptible to infection are marked with a special color icon.
     

       
    • White for grace period
       
    • Yellow for danger period with a low infection risk
       
    • Red for danger period with a high infection risk
       
  • It provides an option to make the infection chance rely on the current room cleanliness instead of that at the time of tending. (Default off)
     

       
    • This makes field tending viable as the patients benefit from resting in a clean room after battle.
       
    • No longer have to remember to clean the room just before tending.
       
What Does This Mod NOT Do


     
  • The default mode does not alter game mechanism.
     
  • It does not modify saves. Safe to add or remove at any time.
     
  • It does not reveal when exactly the infection would happen. (Visible in Dev mod already)
     
  • It does not reveal ahead of time that an injury will not develop an infection if the first RNG check doesn't pass.


Steam
GitHub

#7
Releases / [1.3] My First Mod: Grazing Info
January 25, 2022, 10:12:29 AM
Here is my first mod attempt, a little QoL patch that displays summary of grazing nutrition in the pen.

Hope you like it. Any suggestions please let me know!

Remarks


  • In the original game, the nutrition of a plant is adjusted by its growth.
  • The actual nutrition for grazing is growth * this value.
  • Note the effect of growth is quadratic. I.e. nutrition per growth is proportional to growth
  • Hence it is wasteful if a plant is consumed prematurely.



Steam
GitHub
#8
The correct behavior depends on what `MeleeDodgeChanceOutdoorsDarkOffset` is supposed to mean:

- If this is a stats that an attacker owns that would impact his target (e.g. like an aura), the value in xml should be negative (e.g. -0.1 instead of 0.1) if we want to debuff his enemy.
- If this is a stats that offsets the MeleeDodgeChance of the owner, then the game code should do `pawn.GetStatValue` rather than `caster.GetStatValue`

Either way it would be nice to add more clarity to the description of `StatDef.MeleeDodgeChanceOutdoorsDarkOffset`
#9
The description of the darkness Combat precept says "We fight better in the dark"

we have


      <MeleeHitChanceOutdoorsDarkOffset>0.1</MeleeHitChanceOutdoorsDarkOffset>
      <MeleeHitChanceOutdoorsLitOffset>-0.1</MeleeHitChanceOutdoorsLitOffset>
      <MeleeHitChanceIndoorsDarkOffset>0.1</MeleeHitChanceIndoorsDarkOffset>
      <MeleeHitChanceIndoorsLitOffset>-0.1</MeleeHitChanceIndoorsLitOffset>
      <MeleeDodgeChanceOutdoorsDarkOffset>0.1</MeleeDodgeChanceOutdoorsDarkOffset>
      <MeleeDodgeChanceOutdoorsLitOffset>-0.1</MeleeDodgeChanceOutdoorsLitOffset>
      <MeleeDodgeChanceIndoorsDarkOffset>0.1</MeleeDodgeChanceIndoorsDarkOffset>
      <MeleeDodgeChanceIndoorsLitOffset>-0.1</MeleeDodgeChanceIndoorsLitOffset>


Note both `MeleeDodgeChanceOutdoorsDarkOffset` & `MeleeHitChanceOutdoorsDarkOffset` are 0.1

The code seems to add the attacker's offset to the target's dodge chance, which implies the target dodges more often in the dark.

It feels like a debuff to fight in the dark. Is this intended behavior?


Code is attached for reference


In the function RimWorld.Verb_MeleeAttack.GetDodgechance


        Pawn pawn = target.Thing as Pawn;
        ...
        float num = pawn.GetStatValue(StatDefOf.MeleeDodgeChance);

if (ModsConfig.IdeologyActive && target.HasThing)
{
if (DarknessCombatUtility.IsOutdoorsAndLit(target.Thing))
{
num += caster.GetStatValue(StatDefOf.MeleeDodgeChanceOutdoorsLitOffset);
}
else if (DarknessCombatUtility.IsOutdoorsAndDark(target.Thing))
{
num += caster.GetStatValue(StatDefOf.MeleeDodgeChanceOutdoorsDarkOffset);
}
else if (DarknessCombatUtility.IsIndoorsAndDark(target.Thing))
{
num += caster.GetStatValue(StatDefOf.MeleeDodgeChanceIndoorsDarkOffset);
}
else if (DarknessCombatUtility.IsIndoorsAndLit(target.Thing))
{
num += caster.GetStatValue(StatDefOf.MeleeDodgeChanceIndoorsLitOffset);
}
}