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 - Syrus

#1
Once again I'm at a loss where else to post this, and this is surely not a bug...just a request.

Short version:
Please allow for multiple ThingDefs in the ThingRequest-struct, instead of just one.

Long version:
I'm adding multiple versions of a building, which has a job associated with it, to the game via a mod.
The WorkGiver for that job checks via the PotentialWorkThingRequest-property, which is a ThingRequest, whether any defs exist which are valid for the job. The problem is, ThingRequest only allows for one ThingDef or a ThingRequestGroup, which is an enum and therefore can't be extended (easily?). If the struct checked a list instead (or as well), it would allow for multiple ThingDefs, which can come in handy for things like I intend to do.

To be precise, I want to add differently sized versions of the Biosculpter Pod and Neural Supercharger, but for Biosculpter Pod the WorkGiver_HaulToBiosculpterPod can only check for the original def (not yet sure how I can work around that) and for the Neural Supercharger JobGiver_GetNeuralSupercharge.ClosestSupercharger can also only check for the original def (fairly easy to fix).

Unless I'm missing something obvious of how else this can be achieved?
#2
While technically speaking not a bug, this really seems excessive, considering that the method only checks if there aren't any more enemies on a caravan map.
It is also called for the player's home map(s).

Maybe it would make sense to add a "CompTickRare" for "WorldObjectComp"s which's states do not require constant checking and skip checking on player home maps altogether?


(I noticed this because I made a mod that attaches some functionality to the CompTick, and which requires to know when all enemies on a caravan map are defeated.)
#3
General Discussion / Re: Caravan exit weirdness
November 14, 2021, 05:43:24 PM
Yes, they do. But setting up a caravan is not a simple task when you have lots of things to sell.
It would be a lot better if they respected some logic - zones as far as possible, closest exit, exit in the direction the caravan is moving if possible...
#4
General Discussion / Caravan exit weirdness
November 02, 2021, 03:38:31 PM
Why do caravans do this when leaving the map?



There are three big open and safe exit areas to the south-east, but my caravan decides that the literally longest route is the one they shall take, despite the fact that it leads right past a mech cluster with almost two dozen angry mechs in it, despite the fact that it is outside their allowed zone and despite the fact that the caravan will travel to the south east when on the world map.

Why do they do this? How can this be prevented?
Why is there no option to pick which direction a caravan should leave in or why is it not dependend on map movement at least?
#5
This is technically not a bug, but I'm unsure where else to post it and it is somewhat of a technical issue still.

While creating a mod for an overlay to display the beauty of every map tile, I noticed that the method used to calculate average surrounding beauty for a pawn's beauty need (BeautyUtility.AverageBeautyPerceptible) takes up quite a significant amount of calculation time for such a rather insignificant part of the game.

I had come upon this quite some time ago already, in 1.2 or 1.1 even, but only now did some further testing on it.
Considering how execution time measurements are always a rather iffy thing to do, often inaccurate or faulty, I don't think posting numbers makes much sense, but I believe that my tests did point out a performance issue that could maybe somehow be optimized or at least improved.
It would probably be best for you to test it yourself.



What does BeautyUtility.AverageBeautyPerceptible do?
It checks, in a circular pattern, every surrounding cell for its beauty value and calculates an average perceived beauty.
You can see it in action when when activating the "beauty display" (typically 't' key).
#6
Can we please have the option of replanting trees with a single action instead of having to "extract" them and then replanting them via the minified thing?
It should make the colonist dig up the tree, carry the minified tree over and then replant it at the designated place, without requiring anymore interaction.

Something like this:


As you can see, I've tried to mod this in, but in all honesty I'm not experienced enough to make it happen.
There's just a lot of things I don't know about when it comes to how the jobs are generated, or how to get from a thing to a blueprint without having a minified tree...
While I did get a preview blueprint to show for the tree, it's as far as I could before getting stuck. Not to mention the need to add the extract tree job before replanting.


Anyway, would be much better to have this as a base game functionality, considering the need to replant a lot of trees when playing an Ideology which doesn't enjoy the good old Man vs Nature concept of life.

It'd also be good if "Extract Trees" was available as a drag-selection order. The functionality is there, I accidentally had it work that way at some point, there's just no button for it.
#7
Sorry for the late response, but I finally got around to working on this again.
I've looked at the dnSpy tool, it looks very powerful, though I've not been able to figure out how to make it work. Or rather, it gave me a headache.

I have been coding together a mod that prints out a lot of data in detail, things like apparel, weapons, plants and now animals. Does not do anything fancy, but for someone like me, who loves to have spreadsheets for all kinds of stuff, this has been very lovely.
Anyway, after quite some searching - and even more headache - I thought I had found what I was looking for with this:
thingDef.GetStatValueAbstract(StatDefOf.MeleeWeapon_AverageDPS);
(With "thingDef" being the Animal's thingDef.) But sadly it does not seem to be correct either.

I tried "StatDefOf.MeleeDPS" before, but that just throws warnings:
"Getting MeleeDPS stat for [ANIMAL] without concrete pawn. This always returns 0."
... refuses to return a melee DPS without actually having a pawn. Great when you want to have the base value.

Why does this have to be so brain-twistingly difficult?
All I want is to know the freaking DPS that's displayed ingame, without any modifiers.


EDIT:
Alright, ILSpy has been helpful instead, and digging through code I think I figured out how the melee DPS for the info cards is calculated.
From that I derived the calculation for the raw values.

No guarantee that this is correct, but from some quick testing the values did appear to be correct.
One exception is that some pawns may not have body parts for manipulation, always incurring a -12 modifier on the post-process curve.
Also, all animals have a default melee skill of 4, as far as I can tell, so their DPS post-process curve is modified by +4. This results in a final value of 62% where no further modification is applied. If, as mentioned before, the animal has no manipulation body parts, the post-process curve value is -8, resulting in a 18% final value with which the calculated DPS is multiplied.

To calculate the theoretical raw DPS, as appears to be done for the infocards (which in no way has to represent the actual DPS, mind you):

Step 1:
Find the HighestInitialWeight of all thingDef.tools with:
   initialWeight = tool.damage * (1 + (tool.armorPenetration < 0 ? tool.damage * 0.015f : tool.armorPenetration)) / tool.cooldownTime * tool.chanceFactor

Step 2:
Via the InitialWeight all Tools are divided into three Categories: Best, Mid, Worst.
   Best gets a SelectionWeight-total of 0.75; condition: InitialWeight >= HighestInitialWeight * 0.95
   Mid gets a SelectionWeight-total of 0.25; condition: InitialWeight >= HighestInitialWeight * 0.25
   Worst gets no SelectionWeight, as in, these attacks are ignored, apparently.
We have to iterate over the thingDef.tools list again to find which category each tool falls into.
For later we also need to remember how many tools are in the Best and Mid category each; these two values will be BestCount and MidCount.

Step 3:
Calculate the BestCategoryFactor and MidCategoryFactor:
   BestCategoryFactor = 1 / BestCount * 0.75
   MidCategoryFactor = 1 / MidCount * 0.25
Iterate over the list once again to save the factor for each tool as well as to add up all factors so we know the TotalCategoryFactor.

Step 4:
Iterate over the list one last time to sum up the TotalDamage and TotalCooldown via:
   TotalDamage = CategoryFactor / TotalCategoryFactor * Damage
   TotalCooldown = CategoryFactor / TotalCategoryFactor * Cooldown
Using these two values, which are the ones displayed in the breakdown for DPS on the infocard, the raw TotalDPS can be calculated.
   TotalDPS = TotalDamage / TotalCooldown
Remember, this is the raw value. For the final DPS the pawn's skill - for animals apparently always 4 - as well as other factors have to be kept in mind and the proper post-process curve be applied. The default for animals would be TotalDPS * 62% = "Infocard-DPS".


My code:
private static float CalculateDPS(List<Tool> tools)
{
float highestInitialWeight = 0f;
int catMidCount = 0, catBestCount = 0;
List<DamageCalc> allDPSList = new List<DamageCalc>();

// find highest initial weight
foreach (var tool in tools)
{
float damage = tool.power;
float cooldown = tool.cooldownTime;
float armorPenetration = tool.armorPenetration;
float chanceFactor = tool.chanceFactor;
float initialWeight = damage * (1f + (armorPenetration < 0f ? damage * 0.015f : armorPenetration)) / cooldown * chanceFactor;
highestInitialWeight = Math.Max(initialWeight, highestInitialWeight);
}

// save each tools details
foreach (var tool in tools)
{
float damage = tool.power;
float cooldown = tool.cooldownTime;
float armorPenetration = tool.armorPenetration;
float chanceFactor = tool.chanceFactor;
float initialWeight = damage * (1f + (armorPenetration < 0f ? damage * 0.015f : armorPenetration)) / cooldown * chanceFactor;

int cat;
// worst
if (initialWeight < highestInitialWeight * 0.25f)
continue;
// mid
else if (initialWeight < highestInitialWeight * 0.95f)
{
cat = 1;
catMidCount++;
}
// best
else
{
cat = 2;
catBestCount++;
}

allDPSList.Add(new DamageCalc
{
Damage = damage,
Cooldown = cooldown,
ArmorPenetration = armorPenetration,
ChanceFactor = chanceFactor,
InitialWeight = initialWeight,
Cat = cat,
});
}

// calculate weighting factor
float factorCatMid = 1f / catMidCount * 0.25f;
float factorCatBest = 1f / catBestCount * 0.75f;
float factorCatTotal = 0f;
foreach (var dps in allDPSList)
{
switch (dps.Cat)
{
case 1:
dps.FactorCat = factorCatMid;
break;
case 2:
dps.FactorCat = factorCatBest;
break;
default:
continue;
}
factorCatTotal += dps.FactorCat;
}

float totalDamage = 0, totalCooldown = 0;
foreach (var dps in allDPSList)
{
totalDamage += dps.FactorCat / factorCatTotal * dps.Damage;
totalCooldown += dps.FactorCat / factorCatTotal * dps.Cooldown;
}
return totalDamage / totalCooldown;
}

private class DamageCalc
{
public float Damage;
public float Cooldown;
public float ArmorPenetration;
public float ChanceFactor;
public float InitialWeight;
public int Cat;
public float FactorCat;
public float DPS => Damage / Cooldown;
}




Oh, and obviously, getting the DPS for already existing pawns is much, much easier.
This was all for getting the theoretical raw DPS from a ThingDef.

If you now tell me there's an easier way, I'll name a colonist after you and do Rimworld-things to them.
#8
I've been trying to figure out how the melee DPS calculation (for animals) works. Of course, I'm not sure whether the calculation even represents the actual DPS ingame, but I'd still like to know the magic behind it.

I've figure out:
- The highest DPS attack makes up 75% of the total DPS. If there are several of the same, highest DPS, they will split the 75% chance among them.
- All attacks with more than 25% of the highest DPS divide the remaining 25% of the total DPS among them.
- All attacks with 25% or less of the highest DPS are ignored.

This, I'm quite certain of, so far.

The problem arises when the "chanceFactor" property is used, as I cannot figure out how exactly it comes into the calculation.


All tests were done using three attacks ("tools"). All chanceFactors for the various cases were found by testing.
All uninjured animals have a melee skill of 4, resulting in a 62% multiplier for the average DPS.
Damage values are just for testing, obviously they are quite ridiculous for ingame stuff.

Never did a 5th case, but there should be one where the third attack will be ignored when the chanceFactor is very low.

----- Test 1, damage is 10, 10, 40, cooldown is always 1. -----
Case 1, baseline:

DamageCooldownchanceFactor%_of_Total
10110%
10110%
4011100%
Results in "average" damage of 40, dps of 24.8.

Case 2, chanceFactor < 1.0 & >= 0.19:

DamageCooldownchanceFactor%_of_Total
101112.5%
101112.5%
4010.1975%
Results in "average" damage of 32.5, dps of 20.15.

Case 3, chanceFactor = 0.18:

DamageCooldownchanceFactor%_of_Total
101133.3...%
101133.3...%
4010.1833.3...%
Results in "average" damage of 20, dps of 12.4.

Case 4, chanceFactor <= 0.17:

DamageCooldownchanceFactor%_of_Total
101137.5%
101137.5%
4010.1725%
Results in "average" damage of 17.5, dps of 10.85.


----- Test 2, damage is 20, 20, 80, cooldown is always 1. -----
Case 1, baseline:

DamageCooldownchanceFactor%_of_Total
20110%
20110%
8011100%
Results in "average" damage of 80, dps of 49.6.

Case 2, chanceFactor < 1.0 & >= 0.16:

DamageCooldownchanceFactor%_of_Total
201112.5%
201112.5%
8010.1675%
Results in "average" damage of 65, dps of 40.3.

Case 3, chanceFactor = 0.15:

DamageCooldownchanceFactor%_of_Total
201133.3...%
201133.3...%
8010.1533.3...%
Results in "average" damage of 40, dps of 24.8.

Case 4, chanceFactor <= 0.14:

DamageCooldownchanceFactor%_of_Total
201137.5%
201137.5%
8010.1425%
Results in "average" damage of 35, dps of 21.7.


----- Test 3, damage is 1, 1, 4, cooldown is always 1. -----
Case 1, baseline:

DamageCooldownchanceFactor%_of_Total
1110%
1110%
411100%
Results in "average" damage of 4, dps of 2.48.

Case 2, chanceFactor < 1.0 & >= 0.26:

DamageCooldownchanceFactor%_of_Total
11112.5%
11112.5%
410.2675%
Results in "average" damage of 3.25, dps of 2.02.

Case 3, chanceFactor = 0.25:

DamageCooldownchanceFactor%_of_Total
11133.3...%
11133.3...%
410.2533.3...%
Results in "average" damage of 2, dps of 1.24.

Case 4, chanceFactor <= 0.22:

DamageCooldownchanceFactor%_of_Total
11137.5%
11137.5%
410.2225%
Results in "average" damage of 1.75, dps of 1.09.


----- Test 4, damage is always 1, cooldown is 1, 1, 0.25. -----
Case 1, baseline:

DamageCooldownchanceFactor%_of_Total
1110%
1110%
10.251100%
Results in "average" cooldown of 0.25, dps of 2.48.

Case 2, chanceFactor < 1.0 & >= 0.27:

DamageCooldownchanceFactor%_of_Total
11112.5%
11112.5%
10.250.2775%
Results in "average" cooldown of 0.44, dps of 1.42.

Case 3, chanceFactor = 0.26:

DamageCooldownchanceFactor%_of_Total
11133.3...%
11133.3...%
10.250.2633.3...%
Results in "average" cooldown of 0.75, dps of 0.83.

Case 4, chanceFactor <= 0.23:

DamageCooldownchanceFactor%_of_Total
11137.5%
11137.5%
10.250.2325%
Results in "average" cooldown of 0.81, dps of 0.76.


I just cannot figure out why the chanceFactor is different for these tests.
My assumption was, that if the damage is doubled for all attacks, the chanceFactor required to get a different calculation case would be the same.
But it's not. And the same behaviour appears when chanceFactor is set in a way that one attack "out-DPS-weights" the others enough to be the only attack picked. (Using a high factor of more than 1. The 4x-the-damage limit mentioned at the start, chanceFactor seems to modify that somehow.)
#9
Help / Re: Turret using different kinds of ammo?
September 07, 2020, 02:37:02 PM
My intention was to have a turret which can fire different kinds of projectiles, using shells as "ammo".
As it sounds like this is only doable in code and there isn't another node besides "defaultProjectile".


First part would be to add a button to the UI for that turret.
From another mod I modified I know how to add new elements to the UI, not sure about a button, but I feel like it can't be too difficult.

This button would then allow for toggling between different projectiles / firing modes.
My first thought was that it could just replace the bullet used by the turret, but I would love to try out a shotgun-kind of turret, which would require modifying the burstShotCount (and maybe ticksBetweenBurstShots) of the turret's gun as well. Doubt this is impossible just need to figure out how to replace and modify the parts I want to change.

The big problem is at the same time also just a "nice to have", to be honest.
I would like to have the turret use a different ammo depending on the projectile chosen.
The one currently active would be the one displayed, and colonists would deliver that ammo. Of course it would be even nicer if all used ammos was displayed and pawns delivered all of them. This would probably require a lot of code magic, switching the storages or such...
Considering the problems that arise from it all, I'm second guessing whether this is actually a good idea to try to implement. It would probably be a lot easier to just go with the normal "ammo" as in other turrets and just use something like Uranium or such to "fuel" the gun.

So let's ignore the complicated storage problem.


Why did I write this post at all then?
I was wondering if there was an easy way of doing that because of the "default" part in "defaultProjectile".
Of course I also welcome any advice for making the switchable-projectiles work and what to look out for.
Haven't yet taken a look into how to actually do this, hopefully I'll get around to trying some stuff soon, sounds like a fun idea.


@LWM
You posted while I was typing up the post (yeah, I had something to eat inbetween, took some time :p ), I'll have a look at the code.
Since it's entirely for personal use only, and won't be released to the public, the license shouldn't be a problem?!
Just like to see what I can do, it usually ends up overpowered anyway!
#10
Help / Turret using different kinds of ammo?
September 05, 2020, 08:21:08 PM
I was wondering if there is a(n easy) way to make turrets use different kinds of ammo with different kinds of effects without the use of coding, or whether there is a not too complicated way for accomplishing this?

Seeing as defaultProjectile is part of the weapon for turrets, is there a "non-defaultProjectile"?

Then again, I'm not completely averse to coding, it's just that I was wondering if there was a simpler solution or maybe some tipps on how to achieve it.
EDIT: Thinking about this, if it is only possible via code, I'm pretty sure I know how to do switching between different projectiles, but not how to have more than one "fuel" storage for different kinds of ammo. Of course I could just figure out a way to have just one fuel, but that kind of defeats the whole purpose of "different kinds of ammo". Hm...difficult decision.
#11
Help / Re: Using thingClass="..." for PatchOperations
September 05, 2020, 03:40:22 PM
Oh, I had no idea I can do that. Now that's a nice way to do what I had initially attempted. (Assuming it works, haven't tested it yet.)
Wish the Wiki had a bit more on XPaths, or maybe I'm just blind and missed it.

Definitely going to look through my patches and see if I can't use that elsewhere though, I feel like I had more constructs that could improved that way.

(Mind you, this is only for my personal mods, I don't release anything to the public, so dread not for the horribleness I conjure up in my attempts to get things to work!)

EDIT:
Thanks, that "and not(...)" really works wonderfully!
Found another mod that does some crazy XPath things, so I ended up with "/Defs/ThingDef[@ParentName="AutocannonTurret"]/comps[not(descendant::li[@Class="..."])]" for something I wanted to add if it does not exist. Makes all that PatchOperationConditional stuff I did so far unnecessary, which I'm quite happy for.
#12
Help / Re: Using thingClass="..." for PatchOperations
September 04, 2020, 03:44:18 PM
Good idea, had not thought of that at all.

It tested it, and it works.
But I noticed with this solution that no matter if the other solution had worked, I'd run into other problems (missing haul weight on some things, no thingCategories on others, mostly). Those can of course be fixed, but with the same trouble as before...

In the end I think I will go with "manually" adding the necessary things to the turrets I want minifiable.
Mainly I was wondering what I was doing wrong or whether I had some faulty syntax or misunderstanding of how the PatchOperationConditional works.
#13
Help / Using thingClass="..." for PatchOperations
September 02, 2020, 03:04:30 PM
I've been trying to make all turrets "minifiable". The problem with that is, I also want to apply it to all mods.

After some reading, thinking and figuring out the neatest way of doing that, I came up with a solution that should add <minifiedDef>MinifiedThing</minifiedDef> to all ThingDefs where thingClass = "Building_TurretGun".

The problem is, it does not work.
While there are no errors, there is no change either.
It seems like the <minifiedDef>MinifiedThing</minifiedDef> does not get applied.


<Operation Class="PatchOperationConditional">
<xpath>/Defs/ThingDef[thingClass="Building_TurretGun"]/minifiedDef</xpath>
<match Class="PatchOperationReplace">
<xpath>/Defs/ThingDef[thingClass="Building_TurretGun"]/minifiedDef</xpath>
<value>
<minifiedDef>MinifiedThing</minifiedDef>
</value>
</match>
<nomatch Class="PatchOperationAdd">
<xpath>/Defs/ThingDef[thingClass="Building_TurretGun"]</xpath>
<value>
<minifiedDef>MinifiedThing</minifiedDef>
</value>
</nomatch>
</Operation>
<Operation Class="PatchOperationConditional">
<success>Always</success>
<xpath>/Defs/ThingDef[thingClass="Building_TurretGun"]/thingCategories</xpath>
<nomatch Class="PatchOperationAdd">
<xpath>/Defs/ThingDef[thingClass="Building_TurretGun"]</xpath>
<value>
<thingCategories>
  <li>BuildingsSecurity</li>
</thingCategories>
</value>
</nomatch>
</Operation>


When I change nomatch to match (while removing the match-part) for the minifiedDef-operation, the game will - as expected! - throw errors for duplicate minifiedDefs on all turrets which are already minifiable. To me is a sign that the code should work.
Another interesting thing I noticed was, that if I change the added value to be faulty, the game will throw errors as expected.

Previously I also tried using @Name="AutocannonTurret" instead of thingClass="Building_TurretGun". This worked, but sadly not all of the turrets I wish to change inherit from the AutocannonTurret-AbstractClass.
One thing to note though, this change was only for adding minifiedDef, and it still worked correctly, so the adding of the thingCategories seems to work - at least for vanilla turrets.


I've spend quite too much time on this issue already and I just cannot figure out what I'm doing wrong here, though I'm not fully knowledgable when it comes to this modding, from my understanding this should should...
Of course, there are other options, like adding it all directly to each thing I wish to change and that would probably work. By now that would also be less time and work spend on this, but a better solution would be appreciable.