Difficulty system not based on wealth [1.0]

Started by seerdecker, August 09, 2018, 10:36:36 AM

Previous topic - Next topic

Nafensoriel

/snipersnip

Thank you for answering my questions. I think I understand your idea more entirely. This thread has gotten a ton of good ideas and insigts since I last visited it.

Hotel napkin theorycrafting isn't exactly the best but I could not find a situation exclusively tied to just +/- points that did not median out after a sufficient amount of time even if you included random multipliers. Using injuries and target destruction would, however, ensure the raids faced were at the very least challenging. The biggest problem I had was trying to fit in things like drop pod raids.. which skew any system drastically. An edge started raid, even sapper, is infinitely easier to defeat than drop pod raid right into the very worst part of your base.

This leads a team of engineers to talk about a video game for 2hrs driving to a remote site and the eventual consensus was "why have one threat system?". You almost hit the nail on the head with how manhunters work. Breaking the system into 3 different parts might also simplify both mod creation and future updates as well. (I don't for a second believe Tynan is going to ever be entirely done with rimworld)

So what if we broke down raids into 3 functions?
1] Animal Raids
2] Regular Raids
3] Special Raids

1] Animal raids would be fairly simple.. wealth systems work incredibly well already for this. Wealth+raidpoint would again eventually median out threat wise but keep a constant challenge.

2] Regular raids would be entirely point based as you describe yet to include a variable challenge they would also need a "pawn size limit". This secondary roll would create the simulation of tribal raids or space pirate raids by forcing a situation where you have more pawns or better-equipped pawns. Why separate this when it's so similar to animal raids? Keeping animal raids independent would allow for things like zombie mods more effectively.

3] Special raids would be your "breakers". Specific but independent raid designs like the poison ship or drop pod raid. The purpose of these types of raids is stress generation. As such they would have to be triggered by either time, wealth, pawn count, or even turret count. All ratios that can be adjusted by difficulty or storyteller type using preexisting ideologies. By allowing a triggerable raid type exclusive of regular raids you directly prevent a colony from full automation and create situations where you can breakpoint raids from being median locked.


This means you can have regular raids maintain pressure on a colony similar to the way pneumonia works for humans. It's always there.. and 90% of the time its not fatal. If you stumble through it can kick you in the pants hard though. By keeping regular raids additive/subtractive point based though it will rarely be entirely fatal.
Using special raids, however, lets the original idea of wealth being a consequence still exist. With these, you could say.. have a wealth trigger at 100k wealth that would then fire a raid in 1-7 days. The effects of this raid would impact the regular raids point totals but would be sized off your wealth instead of your success in defending against previous raids. Its exclusive purpose is to stress the player beyond the "kill box" and create situations where good loss and victory is meaningful(i mean this is rimworld. Death of pawns is 50% of the addiction)
This method also allows for different storytellers to have different effects on special raids as well as different effects based off difficulty. You could even completely remove them from "base builder" to satisfy that style of play.




Copperwire

I have been thinking about a system where the players condition is broken into different indicators (total wealth, deterrence, technology, animal wealth, etc) and the different "factions" (manhunters, pirates, raiders, mechs, and room for mods etc to add more - etc) make "do i raid" decisions and "what do i raid for" decisions.  Having each faction make those decisions independently and then having the attacks arrive later based on the nearest point of origin for that faction would create interesting noise.  You might end up with raids arriving simultaneously - but you get that already and that is story.

I think a number of people in this thread have been pointing at something along those lines and I hope we see it.

RemingtonRyder

Quote from: Dolphinizer on August 17, 2018, 04:53:44 PM
Everyone seems to be talking about avoiding wealth growth as a way of reducing raid size and seeing it as a negative thing, but perhaps it doesn't have to be that way.

Maybe some sort of 'mercenary' mechanic could be added to the game, allowing you to pay friendly factions as mercenaries to protect your colony. Essentially the idea behind this would be that you pay a 'subscription fee' to a faction, say X amount of wealth/silver every Y days and in exchange they protect you, either bolstering your defenses with heavily armed pawns of their own that would simply hang around your base (maybe even manning defense stations that you tell to man instead of simply running headlong into battle and dying like the current friendlies) or weakening enemy raids (simply reducing enemy raid size by some multiplier).

A system like this is already in place in the game with friendlies, but imo they tend to be a little too useless lategame since their numbers and gear seem vary so much. A dedicated money for protection system would provide a way to sacrifice wealth for safety, allowing people who simply want to get rich without having significant setbacks for it to have a proper system to achieve their goals, slowing their growth but preventing catastrophic losses in raids. This would also allow players to increase their wealth growth rate at the cost of greater risk of catastrophe from raids.

I don't know if this is a good idea, but it's just a thought I had

Seems like a good idea to me. Tie this into the factions system as well. For example, the savage tribals or rough outlanders might be happier with you (change their resting goodwill) if you keep paying them protection money. Why would they raid a colony when they can just milk them for cash?


bbqftw

#78
for posterity since certain parties like strawmanning

QuoteBasically the system is somewhat complex and very often incorrectly simplified down to something like "points=wealth*constantFactor" which really isn't even close to the truth.

ok, lets not be imprecise when we can be more precise

roughly speaking, raid point proportionality is to following factors - wealthfactor + pawnfactor + animalcombatfactor

where

wealthfactor = a * (itemwealth + pawnwealth + animalwealth + buildingwealth/2)
pawnfactor = b * number of pawns * totalwealth(? unsure about this one, maybe it only counts certain subclasses of wealth)
animalcombat factor = c * combatfactor, sum over all release capable animals

global modifiers to raid points are time factor, and adaptation factor modified for difficulty (base 0.4-1.5, appx 0.76 to 1.2 on proper difficulty)

in which case you can reduce raid point calc down to, rp = x ( y * total wealth * (1 + z * number of pawns) + combatanimalcontribution)

this ( y * total wealth * (1 + z * number of pawns) ) element even with a scenario with hundreds of combat animals contributes to over 80% of the raid point calculation.

how close am I? realistically I just spent about an evening and I didn't look at the code too closely. happy to have the math corrected

RawCode

I will explain raid point caclulation fully, including all factors and relative contribution of said factors:
(special note, not all events rely on threat points fully, some rely on points just to calculate if event can spawn or not, others are hardlinked to specific wealth like)
VisitorGiftChanceFactorFromPlayerWealthCurve
with hardcoded 30k boundary for "expected" gifts

source of method that does most of job, raw image:

// Token: 0x06000F9F RID: 3999 RVA: 0x000859B0 File Offset: 0x00083DB0
public static float DefaultThreatPointsNow(IIncidentTarget target)
{
float playerWealthForStoryteller = target.PlayerWealthForStoryteller;
float num = StorytellerUtility.PointsPerWealthCurve.Evaluate(playerWealthForStoryteller);
float num2 = 0f;
foreach (Pawn pawn in target.PlayerPawnsForStoryteller)
{
float num3 = 0f;
if (pawn.IsFreeColonist)
{
num3 = StorytellerUtility.PointsPerColonistByWealthCurve.Evaluate(playerWealthForStoryteller);
}
else if (pawn.RaceProps.Animal && pawn.Faction == Faction.OfPlayer && !pawn.Downed && pawn.training.CanAssignToTrain(TrainableDefOf.Release).Accepted)
{
num3 = 0.08f * pawn.kindDef.combatPower;
if (target is Caravan)
{
num3 *= 0.7f;
}
}
if (num3 > 0f)
{
if (pawn.ParentHolder != null && pawn.ParentHolder is Building_CryptosleepCasket)
{
num3 *= 0.3f;
}
num3 = Mathf.Lerp(num3, num3 * pawn.health.summaryHealth.SummaryHealthPercent, 0.65f);
num2 += num3;
}
}
float num4 = num + num2;
num4 *= target.IncidentPointsRandomFactorRange.RandomInRange;
float totalThreatPointsFactor = Find.StoryWatcher.watcherAdaptation.TotalThreatPointsFactor;
float num5 = Mathf.Lerp(1f, totalThreatPointsFactor, Find.Storyteller.difficulty.adaptationEffectFactor);
num4 *= num5;
num4 *= Find.Storyteller.difficulty.threatScale;
num4 *= Find.Storyteller.def.pointsFactorFromDaysPassed.Evaluate((float)GenDate.DaysPassed);
return Mathf.Clamp(num4, 35f, 20000f);
}


Source code with inlines, factors and comments:
      // Token: 0x06000F9F RID: 3999 RVA: 0x000859B0 File Offset: 0x00083DB0
public static float DefaultThreatPointsNow(IIncidentTarget target)
{
float playerWealthForStoryteller = target.PlayerWealthForStoryteller;
//WealthItems + WealthPawns + WealthBuildings * 0.5f
//summ of all wealth with wealth of building halved

//this value range from 10k to 500k

float pointsfromwealth = StorytellerUtility.PointsPerWealthCurve.Evaluate(playerWealthForStoryteller);
//set of curves, that should be related to wealth, but actually out of sync
//new CurvePoint(  14 000f, 0f)
//new CurvePoint( 400 000f, 2400f)
//new CurvePoint( 700 000f, 3600f)
//new CurvePoint(1000 000f, 4200f)

//curve is singular entity, it means, that before 14k you have zero points from wealth, but then points are steadily increasing
//hitting 400k wealth result in 2400 points, then points growth is slowed
//curve is not random, you may calculate exact points for any wealth
//before in range from 14 to 400k each 160 units of wealth gives 1 point here, then 250 per 1

//this value range from 0 to 3600


float pawnvalueacc = 0f;
foreach (Pawn pawn in target.PlayerPawnsForStoryteller)
{
float num3 = 0f;
if (pawn.IsFreeColonist)
{
num3 = StorytellerUtility.PointsPerColonistByWealthCurve.Evaluate(playerWealthForStoryteller);
//CurvePoint(10000f, 15f)
//CurvePoint(400000f, 140f)
//CurvePoint(1000000f, 200f)

//this means, that each free pawn contribute here based on overall wealth
//more wealth - more contribution


}
else if (pawn.RaceProps.Animal && pawn.Faction == Faction.OfPlayer && !pawn.Downed && pawn.training.CanAssignToTrain(TrainableDefOf.Release).Accepted)
{
num3 = 0.08f * pawn.kindDef.combatPower;
//animals provide almost no effect here
//i will take average value of 100 for muffalo
//as result each animal contribute fixed 8 points compared to 140 for normal colonist
}
if (num3 > 0f)
{
if (pawn.ParentHolder != null && pawn.ParentHolder is Building_CryptosleepCasket)
{
num3 *= 0.3f;
//cryosleep stored pawns contribute 0.3 of base value
}
num3 = Mathf.Lerp(num3, num3 * pawn.health.summaryHealth.SummaryHealthPercent, 0.65f);
//wounded pawns contribute greatly decreased value
pawnvalueacc += num3;
}
}

float unfactoredpoints = pointsfromwealth + pawnvalueacc;
//this is "that" line of code, that generate unfactored threat points for all events
//this based on wealth and number of colonists
//with expected wealth rate 0-400k
//wealth will contribute 2600 points and pawns 140 each
//basically, 10 pawns at this stage will contribute exactly half of raid points - 1400
//pawn contribution in raw wealth is 160*140 == 22 400
//this means, getting single pawn equal to getting 22 400 wealth

unfactoredpoints *= target.IncidentPointsRandomFactorRange.RandomInRange;
//random factor from storyteller

float totalThreatPointsFactor = Find.StoryWatcher.watcherAdaptation.TotalThreatPointsFactor;
//<li>(-40, 0.40)</li>
//<li>(  0, 0.80)</li>
//<li>( 60, 1.20)</li>
//<li>(120, 1.60)</li>
//basically you get one adaptation per day and lose adaptation when colonist goes down in combat (6) or die (28)
//keeping everyone alive will result in 1.60 points in 120 days
//killing one colonist each 28 days (human sacrifice!) will keep raid power at bay
float adaptationfactor = Mathf.Lerp(1f, totalThreatPointsFactor, Find.Storyteller.difficulty.adaptationEffectFactor);
//linean factor from difficulty

unfactoredpoints *= adaptationfactor;
//apply adaptation
//at this stage adaptation may give up to
//(2600+1400)*1.6 == 6400 points total 2400 factor
//almost as much as 400k wealth and almost two times higher then 10 pawns population

unfactoredpoints *= Find.Storyteller.difficulty.threatScale;
//2.6 for merciless, 1.6 for savage and 1.0 for rought
//in case of savage, this line will contribute much more points then wealth and pawns combined multiple times



unfactoredpoints *= Find.Storyteller.def.pointsFactorFromDaysPassed.Evaluate((float)GenDate.DaysPassed);
//40 days grace period


return Mathf.Clamp(unfactoredpoints, 35f, 20000f);
}

RawCode

// Token: 0x06000F9F RID: 3999 RVA: 0x000859B0 File Offset: 0x00083DB0
public static float DefaultThreatPointsNow(IIncidentTarget target)
{
//threat points calculation simplified for wealth of 400k

float pointsfromwealth = target.PlayerWealthForStoryteller / 167;
float pointsfrompawns = target.PlayerPawnsForStoryteller.numberofnonprisoners * 140;

//expected contribution 66-34
float unfactoredpoints = pointsfromwealth + pointsfrompawns;

unfactoredpoints *= 0.011*adaptdays;

return Mathf.Clamp(unfactoredpoints, 35f, 20000f);
}


adaptation is major factor with 50% contribution, 33 wealth and 17 population, animals contribution will be less then 1% as long as you do not have large amount of trained animals (only trained animals count)

bbqftw

thanks.

just to be clear, adaptation works purely as a multiplicative modifier to the unfactored points?

zizard

#82
The formula is specailised to wealth values of 14-400k, which encompass most of gameplay. The effects of animals and wounded colonists is ignored. In this case, the raid point formula is:

[6.22(W-14) + C(0.32W + 11.8 )] * F

W: (wealth with buildings halved) / 1000
C: number of colonists
F: factors, comprising:

F = AF * DF * TF * RF

AF: adaptation factor; 0.4 to 1.6 on low difficulty, 0.76 to 1.24 on extreme
DF: difficulty factor for storyteller
TF: time factor, from 0.7 at 10 days to 1.0 at 40 days and 1.0 afterwards
RF: random factor, 0.5-1.5 for Randy storyteller

RF is irrelevant. DF and TF are basically constant. Considering only days after 40, the raid point formula is:

[6.22(W-14) + C(0.32W + 11.8 )] * DF * AF

This can be further simplified by estimating the number of colonists from wealth. The practical minimum wealth gain per pawn is about 3k, comprising 1.5k from pawn value, 1k from clothing, and 0.5k for room. Therefore a conservative estimate for wealth to colonist ratio is 5k each, and a practical upper bound to colonists is wealth / 3k. The constant wealth offset of 14 in the first term is also neglected. These approximations yield (with 5k wealth / colonist):

[8.6W + 0.064W^2] * DF * AF

The first factor of 8.6 will vary between 6.22 (for 0 colonists) to about 10 (for 3k wealth per colonist) depending on the wealth to pawn ratio. The squared term only increases the importance of wealth control. It is responsible for perceived increase in raid point scaling around 200k wealth.

This analysis is consistent with the author's earlier test of killing 6 of 7 colonists while holding wealth constant; i.e. that raid points is proportional to wealth multiplied by a number which varies by no more than a factor of 2 on extreme.

TLDR: it's basically wealth * constant * pity factor if you're on easy mode

RawCode

adaptation just multiply points by it's value, making things a bit easier or a bit harder.

killing colonists intentionally allows you to get easier raids and easy replacements, as both adaptation and population intent will work toward restoring your numbers.

basically, all calculations are inside single method, you can propose any formula you like and i will brew modification for testing (you can do same with harmony but still)

also it's possible to brew modification that will take formula from XML compile and evaluate it, this can allow you to change it on fly and check results instantly.

Wanderer_joins

Quote from: NiftyAxolotl on August 17, 2018, 12:43:19 AM
What are some ways the player could opt in to more dangerous raids at their own pace, in a mechanically and thematically coherent way? What are some lures that would make a player want or have to do those things?

I think that's more the way to go. The difficulty system is quite good, it's reactive and offers and great scale from rough to merciless. What is needed is more different pathways mid game.

You want to chill? stay on the main path.

You want to go to war? destroy an enemy base (hostile -> at war) to trigger a "war cycle", a special cycle mid way between the ship sequence and a normal cassandra 'oncycle'. Why would you go to war? to deliver prisoners in an enemy base, to get a good amount of plasteel, gold, to test your defense system before the ship start up, to help a faction at war with pirates and get a long term diplomatic buff, great gifts, for fun, etc...