From 16f452b08f96b84b5a21bcaadc1ceaff7a4c3802 Mon Sep 17 00:00:00 2001 From: Rakeela <32042842+Rakeela@users.noreply.github.com> Date: Fri, 27 Sep 2024 21:56:13 -0700 Subject: [PATCH] Start on Implementing PIPs --- koboldsim/cards.go | 982 ++++++++++++++++++++++----------------------- koboldsim/stats.go | 511 +++++------------------ koboldsim/util.go | 16 +- 3 files changed, 598 insertions(+), 911 deletions(-) diff --git a/koboldsim/cards.go b/koboldsim/cards.go index 25cb525..2a1ba0f 100644 --- a/koboldsim/cards.go +++ b/koboldsim/cards.go @@ -15,28 +15,26 @@ var cards = []Card{ EnactedDesc: cardsim.MsgStr("[current policy] Your war chief is presently monitoring the situation, building up your military, and securing your creches."), Do: func(p *Player) (cardsim.Message, error) { p.Stats.BasePopulation += 100 - p.Stats.ScavengingIncome += 0.01 - p.Stats.WarExpense += 0.02 - p.Stats.FoodSupply += 0.01 - p.Stats.ObesogenicFood -= 0.01 - p.Stats.ForeignRelations -= 0.02 - p.Stats.Rebellion -= 0.03 - p.Stats.Madness += 0.01 - p.Stats.Cruelty += 0.01 - p.Stats.Authoritarianism += 0.04 + p.Stats.Scavenging += 1 + p.Stats.Militarism += 2 + p.Stats.FoodSupply += 1 + p.Stats.ForeignRelations -= 2 + p.Stats.Rebellion -= 3 + p.Stats.Madness += 1 + p.Stats.Cruelty += 1 + p.Stats.Authoritarianism += 4 return cardsim.MsgStr("Kobolds are known to be born warriors."), nil }, Undo: func(p *Player) error { p.Stats.BasePopulation -= 100 - p.Stats.ScavengingIncome -= 0.01 - p.Stats.WarExpense -= 0.02 - p.Stats.FoodSupply -= 0.01 - p.Stats.ObesogenicFood += 0.01 - p.Stats.ForeignRelations += 0.02 - p.Stats.Rebellion += 0.03 - p.Stats.Madness -= 0.01 - p.Stats.Cruelty -= 0.01 - p.Stats.Authoritarianism -= 0.04 + p.Stats.Scavenging -= 1 + p.Stats.Militarism -= 2 + p.Stats.FoodSupply -= 1 + p.Stats.ForeignRelations += 2 + p.Stats.Rebellion += 3 + p.Stats.Madness -= 1 + p.Stats.Cruelty -= 1 + p.Stats.Authoritarianism -= 4 return nil }, CanDo: YesWeCan, @@ -46,24 +44,22 @@ var cards = []Card{ EnactedDesc: cardsim.MsgStr("[current policy] Your head miner is presently leading a project to dig as far away from the surface as possible."), Do: func(p *Player) (cardsim.Message, error) { p.Stats.BasePopulation += 40 - p.Stats.MiningIncome += 0.02 - p.Stats.ConstructionIncome += 0.01 - p.Stats.ManufacturingIncome += 0.01 - p.Stats.BureaucracyExpense += 0.01 - p.Stats.FoodSupply -= 0.01 - p.Stats.Secrecy += 0.03 - p.Stats.PointOfDimReturns += 0.01 + p.Stats.Mining += 2 + p.Stats.Construction += 1 + p.Stats.Manufacturing += 1 + p.Stats.Bureaucracy += 1 + p.Stats.FoodSupply -= 1 + p.Stats.Secrecy += 3 return cardsim.MsgStr("Kobolds are known to be cowards hiding in the dark."), nil }, Undo: func(p *Player) error { p.Stats.BasePopulation -= 40 - p.Stats.MiningIncome -= 0.02 - p.Stats.ManufacturingIncome -= 0.01 - p.Stats.ConstructionIncome -= 0.01 - p.Stats.BureaucracyExpense -= 0.01 - p.Stats.FoodSupply += 0.01 - p.Stats.Secrecy -= 0.03 - p.Stats.PointOfDimReturns += 0.01 + p.Stats.Mining -= 2 + p.Stats.Manufacturing -= 1 + p.Stats.Construction -= 1 + p.Stats.Bureaucracy -= 1 + p.Stats.FoodSupply += 1 + p.Stats.Secrecy -= 3 return nil }, CanDo: YesWeCan, @@ -73,26 +69,24 @@ var cards = []Card{ EnactedDesc: cardsim.MsgStr("[current policy] Military funding has been diverted into early childhood education."), Do: func(p *Player) (cardsim.Message, error) { p.Stats.BasePopulation -= 40 - p.Stats.ScavengingIncome -= 0.01 - p.Stats.WarExpense -= 0.02 - p.Stats.EducationExpense += 0.01 - p.Stats.FoodSupply -= 0.01 - p.Stats.ForeignRelations += 0.02 - p.Stats.PointOfDimReturns += 0.01 - p.Stats.Madness -= 0.01 - p.Stats.Cruelty -= 0.01 + p.Stats.Scavenging -= 1 + p.Stats.Militarism -= 2 + p.Stats.Education += 1 + p.Stats.FoodSupply -= 1 + p.Stats.ForeignRelations += 2 + p.Stats.Madness -= 1 + p.Stats.Cruelty -= 1 return cardsim.MsgStr("An undefended hunting outpost near the surface was recently wiped out by a raid."), nil }, Undo: func(p *Player) error { p.Stats.BasePopulation += 40 - p.Stats.ScavengingIncome += 0.01 - p.Stats.WarExpense += 0.02 - p.Stats.EducationExpense -= 0.01 - p.Stats.FoodSupply += 0.01 - p.Stats.ForeignRelations -= 0.02 - p.Stats.PointOfDimReturns -= 0.01 - p.Stats.Madness += 0.01 - p.Stats.Cruelty += 0.01 + p.Stats.Scavenging += 1 + p.Stats.Militarism += 2 + p.Stats.Education -= 1 + p.Stats.FoodSupply += 1 + p.Stats.ForeignRelations -= 2 + p.Stats.Madness += 1 + p.Stats.Cruelty += 1 return nil }, CanDo: YesWeCan, @@ -141,20 +135,18 @@ var cards = []Card{ EnactedDesc: cardsim.MsgStr("[current policy] Your bureaucrats are really looking forward to attending, where they can boast about how the festival boosted their salaries."), Do: func(p *Player) (cardsim.Message, error) { p.Stats.BasePopulation += 60 - p.Stats.ScavengingIncome += 0.01 - p.Stats.BureaucracyExpense += 0.03 - p.Stats.FoodSupply += 0.02 - p.Stats.PointOfDimReturns += 0.01 - p.Stats.Gullibility -= 0.01 + p.Stats.Scavenging += 1 + p.Stats.Bureaucracy += 3 + p.Stats.FoodSupply += 2 + p.Stats.Gullibility -= 1 return cardsim.MsgStr("Bureaucrats are considered pillars of society."), nil }, Undo: func(p *Player) error { p.Stats.BasePopulation -= 80 - p.Stats.ScavengingIncome -= 0.01 - p.Stats.BureaucracyExpense -= 0.03 - p.Stats.FoodSupply -= 0.02 - p.Stats.PointOfDimReturns -= 0.01 - p.Stats.Gullibility += 0.01 + p.Stats.Scavenging -= 1 + p.Stats.Bureaucracy -= 3 + p.Stats.FoodSupply -= 2 + p.Stats.Gullibility += 1 return nil }, CanDo: YesWeCan, @@ -164,46 +156,37 @@ var cards = []Card{ EnactedDesc: cardsim.MsgStr("[current policy] Your tribe's policy is clear: the festival of bureaucracy is a chance to trade workers with other kobold tribes in your attempts to build a more efficient government."), Do: func(p *Player) (cardsim.Message, error) { p.Stats.BasePopulation += 20 - p.Stats.BureaucracyExpense -= 0.01 - p.Stats.Gullibility -= 0.01 + p.Stats.Bureaucracy -= 1 + p.Stats.Gullibility -= 1 return cardsim.MsgStr("Immigrant bureaucrats complain about being under-appreciated."), nil }, Undo: func(p *Player) error { p.Stats.BasePopulation -= 20 - p.Stats.BureaucracyExpense += 0.01 - p.Stats.Gullibility += 0.01 + p.Stats.Bureaucracy += 1 + p.Stats.Gullibility += 1 return nil }, CanDo: func(b *BasicPolicy, p *Player) bool { - return p.Stats.BureaucracyExpense >= 0.02 && b.LastEnactedIdx != 3 + return p.Stats.Bureaucracy >= 2 && b.LastEnactedIdx != 3 }, - // CanDo: func(p *Player) bool { - // if p.Stats.GovBureaucracyExpense.Value >= 0.02 { - // return true - // } - // return false - // }, - // Original implementation preserved in the hopes that I'll learn this stuff. This is equivalent to the shorter code above. I'll probably remove this comment eventually. Sincerely, Rakeela. }, &BasicPolicy{ UnenactedDesc: cardsim.MsgStr(`Your Minister of Finance greets you exuberantly. "We've trimmed the wages of the bureaucracy beautifully, but maybe we can scout some outright volunteers. Some tribes are absolute disasters, after all.`), EnactedDesc: cardsim.MsgStr(`[current policy] It's hard to find volunteer workers at the festival, but your tribe is doing its best.`), Do: func(p *Player) (cardsim.Message, error) { p.Stats.BasePopulation += 10 - p.Stats.BureaucracyExpense -= 0.02 - p.Stats.PointOfDimReturns -= 0.01 - p.Stats.Rebellion += 0.01 + p.Stats.Bureaucracy -= 2 + p.Stats.Rebellion += 1 return cardsim.MsgStr("The local bureaucracy is staffed by volunteer labor."), nil }, Undo: func(p *Player) error { p.Stats.BasePopulation -= 10 - p.Stats.BureaucracyExpense += 0.02 - p.Stats.PointOfDimReturns += 0.01 - p.Stats.Rebellion -= 0.01 + p.Stats.Bureaucracy += 2 + p.Stats.Rebellion -= 1 return nil }, CanDo: func(b *BasicPolicy, p *Player) bool { - return p.Stats.BureaucracyExpense < 0.02 && b.LastEnactedIdx != 3 + return p.Stats.Bureaucracy < 2 && b.LastEnactedIdx != 3 }, }, &BasicPolicy{ @@ -211,12 +194,12 @@ var cards = []Card{ EnactedDesc: cardsim.MsgStr("Your tribe currently bans the Pan-Tribal Festival of Bureaucracy. There will be no local celebration without a policy change."), Do: func(p *Player) (cardsim.Message, error) { p.Stats.BasePopulation -= 80 - p.Stats.BureaucracyExpense -= 0.04 + p.Stats.Bureaucracy -= 4 return cardsim.MsgStr("A wave of bureaucrats just emigrated along with kobolds incensed by the tribe's lack of respect for administration."), nil }, Undo: func(p *Player) error { p.Stats.BasePopulation += 80 - p.Stats.BureaucracyExpense += 0.04 + p.Stats.Bureaucracy += 4 return nil }, CanDo: YesWeCan, @@ -241,16 +224,16 @@ var cards = []Card{ &DisabledPolicy{cardsim.MsgStr("Neglecting the event would be less popular and more inefficient than our current policy.")}, &FuncPolicy{ OptionTextFunc: func(p *Player) (cardsim.Message, error) { - if p.Stats.BureaucracyExpense >= -0.03 { + if p.Stats.Bureaucracy >= -3 { return cardsim.MsgStr("Permitting the festival will yield some immigration, but we'll be expected to rebuild our bureaucracy."), nil } return cardsim.MsgStr("Permitting the festival will yield some immigration."), nil }, EnactFunc: func(p *Player) (cardsim.Message, error) { - if p.Stats.BureaucracyExpense >= -0.03 { + if p.Stats.Bureaucracy >= -3 { return cardsim.MsgStr("A festival of bureaucracy just saw the tribe's bureaucracy rebuilt."), ErrKeepMessage } - return cardsim.MsgStr("A festival of bureaucracy brought in immigrants wondering at the tribe's lack thereof."), ErrKeepMessage + return cardsim.MsgStr("A festival of bureaucracy brought in immigrants wondering at the tribe's unbureaucratic ways."), ErrKeepMessage }, }, nil, // remember, if using a BasicPolicy, this uses EnactedDesc @@ -267,33 +250,31 @@ var cards = []Card{ UnenactedDesc: cardsim.MsgStr(`Speaking for the conservative opinion, one prospective merchant says, "We need illusions. Our priority needs to be studying or trading to acquire or create spells or artifacts of disguising. The high start-up costs will be balanced by subsequent free movement in a surface society that detests us."`), EnactedDesc: cardsim.MsgStr("[current policy] Your merchants rely on magical disguises to avoid being identified as kobolds while doing business on the surface."), Do: func(p *Player) (cardsim.Message, error) { - p.Stats.ManufacturingIncome += 0.03 - p.Stats.LogisticsExpense += 0.04 - p.Stats.MiningIncome += 0.01 - p.Stats.AlchemyIncome += 0.01 - p.Stats.GadgetryIncome += 0.01 - p.Stats.ScavengingIncome += 0.01 - p.Stats.FoodSupply += 0.01 - p.Stats.Secrecy += 0.05 - p.Stats.PointOfDimReturns += 0.05 - p.Stats.Rebellion += 0.01 - p.Stats.Greed += 0.01 - p.Stats.Gullibility += 0.01 + p.Stats.Manufacturing += 3 + p.Stats.Logistics += 4 + p.Stats.Mining += 1 + p.Stats.Alchemy += 1 + p.Stats.Gadgetry += 1 + p.Stats.Scavenging += 1 + p.Stats.FoodSupply += 1 + p.Stats.Secrecy += 5 + p.Stats.Rebellion += 1 + p.Stats.Greed += 1 + p.Stats.Gullibility += 1 return cardsim.MsgStr("Nobody ever meets a kobold merchant."), nil }, Undo: func(p *Player) error { - p.Stats.ManufacturingIncome -= 0.03 - p.Stats.LogisticsExpense -= 0.04 - p.Stats.MiningIncome -= 0.01 - p.Stats.AlchemyIncome -= 0.01 - p.Stats.GadgetryIncome -= 0.01 - p.Stats.ScavengingIncome -= 0.01 - p.Stats.FoodSupply -= 0.01 - p.Stats.Secrecy -= 0.05 - p.Stats.PointOfDimReturns -= 0.05 - p.Stats.Rebellion -= 0.01 - p.Stats.Greed -= 0.01 - p.Stats.Gullibility -= 0.01 + p.Stats.Manufacturing -= 3 + p.Stats.Logistics -= 4 + p.Stats.Mining -= 1 + p.Stats.Alchemy -= 1 + p.Stats.Gadgetry -= 1 + p.Stats.Scavenging -= 1 + p.Stats.FoodSupply -= 1 + p.Stats.Secrecy -= 5 + p.Stats.Rebellion -= 1 + p.Stats.Greed -= 1 + p.Stats.Gullibility -= 1 return nil }, CanDo: YesWeCan, @@ -303,42 +284,42 @@ var cards = []Card{ EnactedDesc: cardsim.MsgStr(`[current policy] Your merchants represent the tribe's peaceful ideals by visiting the surface openly despite occasional casaulties.`), Do: func(p *Player) (cardsim.Message, error) { p.Stats.BasePopulation -= 15 - p.Stats.ManufacturingIncome += 0.03 - p.Stats.LogisticsExpense += 0.01 - p.Stats.EconPlanExpense += 0.04 - p.Stats.MiningIncome += 0.01 - p.Stats.AlchemyIncome += 0.02 - p.Stats.GadgetryIncome += 0.02 - p.Stats.HospitalityIncome += 0.01 - p.Stats.PublishingIncome += 0.01 - p.Stats.ForeignRelExpense += 0.01 - p.Stats.WarExpense -= 0.01 - p.Stats.ScavengingIncome -= 0.01 - p.Stats.Secrecy -= 0.10 - p.Stats.ForeignRelations += 0.05 - p.Stats.FoodSupply += 0.03 - p.Stats.PointOfDimReturns += 0.1 - p.Stats.Cruelty -= 0.01 - return cardsim.MsgStr("The economic planning office formally plans for attrition among its merchants."), nil + p.Stats.Manufacturing += 3 + p.Stats.Logistics += 4 + p.Stats.Mining += 1 + p.Stats.Alchemy += 2 + p.Stats.Gadgetry += 2 + p.Stats.Hospitality += 1 + p.Stats.Publishing += 1 + p.Stats.ForeignRelExpense += 1 + p.Stats.Militarism -= 1 + p.Stats.Scavenging -= 1 + p.Stats.Secrecy -= 10 + p.Stats.ForeignRelations += 5 + p.Stats.FoodSupply += 3 + p.Stats.Greed += 1 + p.Stats.Madness -= 1 + p.Stats.Cruelty -= 1 + return cardsim.MsgStr("The tribe formally plans for attrition among its merchants."), nil }, Undo: func(p *Player) error { p.Stats.BasePopulation += 15 - p.Stats.ManufacturingIncome -= 0.03 - p.Stats.LogisticsExpense -= 0.01 - p.Stats.EconPlanExpense -= 0.04 - p.Stats.MiningIncome -= 0.01 - p.Stats.AlchemyIncome -= 0.02 - p.Stats.GadgetryIncome -= 0.02 - p.Stats.HospitalityIncome -= 0.01 - p.Stats.PublishingIncome -= 0.01 - p.Stats.ForeignRelExpense -= 0.01 - p.Stats.WarExpense += 0.01 - p.Stats.ScavengingIncome += 0.01 - p.Stats.Secrecy += 0.10 - p.Stats.ForeignRelations -= 0.05 - p.Stats.FoodSupply -= 0.03 - p.Stats.PointOfDimReturns += 0.1 - p.Stats.Cruelty -= 0.01 + p.Stats.Manufacturing -= 3 + p.Stats.Logistics -= 4 + p.Stats.Mining -= 1 + p.Stats.Alchemy -= 2 + p.Stats.Gadgetry -= 2 + p.Stats.Hospitality -= 1 + p.Stats.Publishing -= 1 + p.Stats.ForeignRelExpense -= 1 + p.Stats.Militarism += 1 + p.Stats.Scavenging += 1 + p.Stats.Secrecy += 10 + p.Stats.ForeignRelations -= 5 + p.Stats.FoodSupply -= 3 + p.Stats.Greed -= 1 + p.Stats.Madness -= 1 + p.Stats.Cruelty -= 1 return nil }, CanDo: YesWeCan, @@ -348,30 +329,28 @@ var cards = []Card{ EnactedDesc: cardsim.MsgStr("[current policy] Your merchants work with the underworld of the surface, forging connections with the people who are ironically less likely to murder them."), Do: func(p *Player) (cardsim.Message, error) { p.Stats.BasePopulation -= 5 - p.Stats.LogisticsExpense += 0.01 - p.Stats.ScavengingIncome += 0.03 - p.Stats.AlchemyIncome += 0.02 - p.Stats.GadgetryIncome += 0.03 - p.Stats.MiningIncome -= 0.01 - p.Stats.ManufacturingIncome -= 0.01 - p.Stats.HiddenRelPenalty += 0.02 // High is bad on this hidden stat. - p.Stats.PointOfDimReturns += 0.05 - p.Stats.Greed += 0.03 - p.Stats.Gullibility -= 0.02 + p.Stats.Logistics += 1 + p.Stats.Scavenging += 3 + p.Stats.Alchemy += 2 + p.Stats.Gadgetry += 2 + p.Stats.Mining -= 1 + p.Stats.Manufacturing -= 1 + p.Stats.HiddenRelPenalty += 2 // High is bad on this hidden stat. + p.Stats.Greed += 3 + p.Stats.Gullibility -= 2 return cardsim.MsgStr("Kobold merchants are instantly suspected of black market connections."), nil }, Undo: func(p *Player) error { p.Stats.BasePopulation += 5 - p.Stats.LogisticsExpense -= 0.01 - p.Stats.ScavengingIncome -= 0.03 - p.Stats.AlchemyIncome -= 0.02 - p.Stats.GadgetryIncome -= 0.03 - p.Stats.MiningIncome += 0.01 - p.Stats.ManufacturingIncome += 0.01 - p.Stats.HiddenRelPenalty -= 0.02 - p.Stats.PointOfDimReturns -= 0.05 - p.Stats.Greed -= 0.03 - p.Stats.Gullibility += 0.02 + p.Stats.Logistics -= 1 + p.Stats.Scavenging -= 3 + p.Stats.Alchemy -= 2 + p.Stats.Gadgetry -= 3 + p.Stats.Mining += 1 + p.Stats.Manufacturing += 1 + p.Stats.HiddenRelPenalty -= 2 + p.Stats.Greed -= 3 + p.Stats.Gullibility += 2 return nil }, CanDo: YesWeCan, @@ -380,23 +359,21 @@ var cards = []Card{ UnenactedDesc: cardsim.MsgStr(`A mining forebold registered for the right to attend this meeting, and says, "Surfacers can't be trusted and we shouldn't be supporting them with trade. We need underground roads to ease trade with other kobolds. It's a massive investment, but think of the reward: a world of underground civilizations supporting each other."`), EnactedDesc: cardsim.MsgStr(`[currentpolicy] Your merchants have been directed to focus on trade with other kobolds, and your miners are perpetually tunnelling towards other kobold tribes in pursuit of the dream of a world of underground roads.`), Do: func(p *Player) (cardsim.Message, error) { - p.Stats.MiningIncome += 0.05 - p.Stats.ConstructionIncome += 0.01 - p.Stats.LogisticsExpense += 0.06 - p.Stats.FoodSupply -= 0.01 - p.Stats.Secrecy += 0.15 - p.Stats.PointOfDimReturns += 0.1 - p.Stats.Gullibility -= 0.01 + p.Stats.Mining += 5 + p.Stats.Construction += 2 + p.Stats.Logistics += 4 + p.Stats.FoodSupply -= 1 + p.Stats.Secrecy += 15 + p.Stats.Gullibility -= 2 return cardsim.MsgStr("The vast network of caverns under the surface of the world is not a natural phenomenon."), nil }, Undo: func(p *Player) error { - p.Stats.MiningIncome -= 0.05 - p.Stats.ConstructionIncome -= 0.01 - p.Stats.LogisticsExpense -= 0.06 - p.Stats.FoodSupply += 0.01 - p.Stats.Secrecy -= 0.15 - p.Stats.PointOfDimReturns -= 0.1 - p.Stats.Gullibility += 0.01 + p.Stats.Mining -= 5 + p.Stats.Construction -= 2 + p.Stats.Logistics -= 4 + p.Stats.FoodSupply += 1 + p.Stats.Secrecy -= 15 + p.Stats.Gullibility += 2 return nil }, CanDo: YesWeCan, @@ -439,21 +416,25 @@ var cards = []Card{ UnenactedDesc: cardsim.MsgStr(`A calm and cheerful scribe says, "We should have public baths! We have more problems with flooding than drought in the deep underground, so we've got plenty of water. We just need to channel it. We could create a hub of social life while improving the health of everybody!"`), EnactedDesc: cardsim.MsgStr("[current policy] Public bathing is a staple social policy in the mines and warrens of your tribe."), Do: func(p *Player) (cardsim.Message, error) { - p.Stats.ConstructionIncome += 0.01 - p.Stats.QoLExpense += 0.01 - p.Stats.HealthcareExpense += 0.01 - p.Stats.ParksExpense += 0.04 - p.Stats.Madness -= 0.01 - p.Stats.Cruelty -= 0.01 + p.Stats.Construction += 2 + p.Stats.Welfare += 1 + p.Stats.Healthcare += 1 + p.Stats.ParksExpense += 4 + p.Stats.Hospitality += 1 + p.Stats.Madness -= 1 + p.Stats.Cruelty -= 1 + p.Stats.Greed -= 1 return cardsim.MsgStr("The company of others makes cold water tolerable."), nil }, Undo: func(p *Player) error { - p.Stats.ConstructionIncome -= 0.01 - p.Stats.QoLExpense -= 0.01 - p.Stats.HealthcareExpense -= 0.01 - p.Stats.ParksExpense -= 0.04 - p.Stats.Madness += 0.01 - p.Stats.Cruelty += 0.01 + p.Stats.Construction -= 2 + p.Stats.Welfare -= 1 + p.Stats.Healthcare -= 1 + p.Stats.ParksExpense -= 4 + p.Stats.Hospitality -= 1 + p.Stats.Madness += 1 + p.Stats.Cruelty += 1 + p.Stats.Greed += 1 return nil }, CanDo: YesWeCan, @@ -462,15 +443,15 @@ var cards = []Card{ UnenactedDesc: cardsim.MsgStr(`Your Minister of Finance is scandalized. "A free service? And such an expensive one to construct? No, no, no. The baths must pull their weight in the budget if they're ot be made at all. We must set an entry fee."`), EnactedDesc: cardsim.MsgStr("[current policy] Elite bathhouses serve paying customers in your largest sites."), Do: func(p *Player) (cardsim.Message, error) { - p.Stats.ConstructionIncome += 0.01 - p.Stats.HospitalityIncome += 0.01 - p.Stats.ParksExpense += 0.02 + p.Stats.Construction += 2 + p.Stats.Hospitality += 3 + p.Stats.ParksExpense += 2 return cardsim.MsgStr("A dirty body is the mark of the lower classes."), nil }, Undo: func(p *Player) error { - p.Stats.ConstructionIncome -= 0.01 - p.Stats.HospitalityIncome -= 0.01 - p.Stats.ParksExpense -= 0.02 + p.Stats.Construction -= 2 + p.Stats.Hospitality -= 3 + p.Stats.ParksExpense -= 2 return nil }, CanDo: YesWeCan, @@ -479,23 +460,29 @@ var cards = []Card{ UnenactedDesc: cardsim.MsgStr(`Your head diplomat chimes in with, "If you want to make the service self-sustaining, build a bath in a shallow cave and invite the surfacers in for a fee. Of course, we'll be giving up some of our secrecy, but it'll bring in foreign exchange to power our industries."`), EnactedDesc: cardsim.MsgStr("[current policy] Conservative kobolds are scandalized: not only has the tribe bathhouses, it has near-surface bathhouses for surfacers to visit."), Do: func(p *Player) (cardsim.Message, error) { - p.Stats.ConstructionIncome += 0.02 - p.Stats.HospitalityIncome += 0.03 - p.Stats.ManufacturingIncome += 0.01 - p.Stats.ParksExpense += 0.03 - p.Stats.FoodSupply += 0.01 - p.Stats.Secrecy -= 0.10 - p.Stats.Cruelty -= 0.01 + p.Stats.Construction += 2 + p.Stats.Hospitality += 4 + p.Stats.Manufacturing += 1 + p.Stats.ParksExpense += 3 + p.Stats.FoodSupply += 1 + p.Stats.ForeignRelExpense += 1 + p.Stats.ForeignRelations += 1 + p.Stats.HiddenRelPenalty -= -2 + p.Stats.Secrecy -= 10 + p.Stats.Cruelty -= 1 return cardsim.MsgStr("Surfacers stink less in the tribe's vicinity."), nil }, Undo: func(p *Player) error { - p.Stats.ConstructionIncome -= 0.02 - p.Stats.HospitalityIncome -= 0.03 - p.Stats.ManufacturingIncome -= 0.01 - p.Stats.ParksExpense -= 0.03 - p.Stats.FoodSupply -= 0.01 - p.Stats.Secrecy += 0.10 - p.Stats.Cruelty += 0.01 + p.Stats.Construction -= 2 + p.Stats.Hospitality -= 3 + p.Stats.Manufacturing -= 1 + p.Stats.ParksExpense -= 3 + p.Stats.FoodSupply -= 1 + p.Stats.ForeignRelExpense -= 1 + p.Stats.ForeignRelations -= 1 + p.Stats.HiddenRelPenalty += 2 + p.Stats.Secrecy += 10 + p.Stats.Cruelty += 1 return nil }, CanDo: YesWeCan, @@ -506,12 +493,12 @@ var cards = []Card{ EnactedDesc: cardsim.MsgStr("When is the right time to embark on building public baths? You can hew close to skeptics instead."), Do: func(p *Player) (cardsim.Message, error) { p.Stats.BasePopulation += 5 - p.Stats.Gullibility -= 0.01 + p.Stats.Gullibility -= 1 return cardsim.MsgStr("Opponents of public bathing won a recent political contest."), nil }, Undo: func(p *Player) error { p.Stats.BasePopulation -= 5 - p.Stats.Gullibility += 0.01 + p.Stats.Gullibility += 1 return nil }, CanDo: YesWeCan, @@ -524,7 +511,7 @@ var cards = []Card{ Desc: cardsim.MsgStr("Resources underground are perpetually tight. Even metallurgy is hard, because fuel is scarce. You can rely on neither field nor forest. The scavengers brave the surface regularly to beg, borrow, or steal the resources necessary to your society, but your war planners want to secure resources more aggressively."), After: ShuffleIntoBottomHalf, IsValid: func(c Card, p *Player) bool { - return p.Stats.WarExpense > 0 + return p.Stats.Militarism > 0 }, Policies: []Policy{ &BasicPolicy{ @@ -532,20 +519,24 @@ var cards = []Card{ EnactedDesc: cardsim.MsgStr("[current policy] The war department maintains your meagre conquest of a stretch of forest on the surface."), Do: func(p *Player) (cardsim.Message, error) { p.Stats.BasePopulation -= 20 - p.Stats.ForestryIncome += 0.03 - p.Stats.WarExpense += 0.01 - p.Stats.FoodSupply += 0.03 - p.Stats.Secrecy -= 0.10 - p.Stats.ForeignRelations -= 0.02 + p.Stats.Forestry += 3 + p.Stats.Militarism += 0.01 + p.Stats.FoodSupply += 3 + p.Stats.Secrecy -= 10 + p.Stats.ForeignRelations -= 2 + p.Stats.Madness -= 1 + p.Stats.Gullibility -= 1 return cardsim.MsgStr("A bunch of lumberjacks just disappeared in a forest."), nil }, Undo: func(p *Player) error { p.Stats.BasePopulation += 20 - p.Stats.ForestryIncome -= 0.03 - p.Stats.WarExpense -= 0.01 - p.Stats.FoodSupply -= 0.05 - p.Stats.Secrecy += 0.10 - p.Stats.ForeignRelations += 0.02 + p.Stats.Forestry -= 3 + p.Stats.Militarism -= 1 + p.Stats.FoodSupply -= 5 + p.Stats.Secrecy += 10 + p.Stats.ForeignRelations += 2 + p.Stats.Madness += 1 + p.Stats.Gullibility += 1 return nil }, CanDo: YesWeCan, @@ -555,22 +546,22 @@ var cards = []Card{ EnactedDesc: cardsim.MsgStr("[current policy] The 'ghost farms' in this region are worked by night as kobolds sparing their eyes avoid doing farmwork in daylight."), Do: func(p *Player) (cardsim.Message, error) { p.Stats.BasePopulation -= 40 - p.Stats.AgricultureIncome += 0.01 - p.Stats.WarExpense += 0.02 - p.Stats.FoodSupply += 0.06 - p.Stats.ObesogenicFood += 0.03 - p.Stats.Secrecy -= 0.20 - p.Stats.ForeignRelations -= 0.04 + p.Stats.Agriculture += 1 + p.Stats.Militarism += 2 + p.Stats.FoodSupply += 6 + p.Stats.Secrecy -= 20 + p.Stats.ForeignRelations -= 4 + p.Stats.Cruelty += 1 return cardsim.MsgStr("Some farmers fight like five kobolds, but few fight like forty."), nil }, Undo: func(p *Player) error { p.Stats.BasePopulation += 40 - p.Stats.AgricultureIncome -= 0.01 - p.Stats.WarExpense -= 0.02 - p.Stats.FoodSupply -= 0.06 - p.Stats.ObesogenicFood -= 0.03 - p.Stats.Secrecy += 0.20 - p.Stats.ForeignRelations += 0.04 + p.Stats.Agriculture -= 1 + p.Stats.Militarism -= 2 + p.Stats.FoodSupply -= 6 + p.Stats.Secrecy += 20 + p.Stats.ForeignRelations += 4 + p.Stats.Cruelty -= 1 return nil }, CanDo: YesWeCan, @@ -580,28 +571,28 @@ var cards = []Card{ EnactedDesc: cardsim.MsgStr("[current policy] Your warriors target caravans for intimidation and larceny."), Do: func(p *Player) (cardsim.Message, error) { p.Stats.BasePopulation -= 10 - p.Stats.ScavengingIncome += 0.02 - p.Stats.WarExpense += 0.01 - p.Stats.FoodSupply += 0.01 - p.Stats.Secrecy -= 0.05 - p.Stats.HiddenRelPenalty += 0.05 - p.Stats.Rebellion += 0.02 - p.Stats.Cruelty += 0.04 - p.Stats.Greed += 0.02 - p.Stats.Gullibility -= 0.01 + p.Stats.Scavenging += 2 + p.Stats.Militarism += 1 + p.Stats.FoodSupply += 1 + p.Stats.Secrecy -= 5 + p.Stats.HiddenRelPenalty += 5 + p.Stats.Rebellion += 2 + p.Stats.Cruelty += 4 + p.Stats.Greed += 2 + p.Stats.Gullibility -= 1 return cardsim.MsgStr("Some merchants are even cordial as they pay off kobold bandits."), nil }, Undo: func(p *Player) error { p.Stats.BasePopulation += 10 - p.Stats.ScavengingIncome -= 0.02 - p.Stats.WarExpense -= 0.01 - p.Stats.FoodSupply -= 0.01 - p.Stats.Secrecy += 0.05 - p.Stats.HiddenRelPenalty -= 0.05 - p.Stats.Rebellion -= 0.02 - p.Stats.Cruelty -= 0.04 - p.Stats.Greed -= 0.02 - p.Stats.Gullibility += 0.01 + p.Stats.Scavenging -= 2 + p.Stats.Militarism -= 1 + p.Stats.FoodSupply -= 1 + p.Stats.Secrecy += 5 + p.Stats.HiddenRelPenalty -= 5 + p.Stats.Rebellion -= 2 + p.Stats.Cruelty -= 4 + p.Stats.Greed -= 2 + p.Stats.Gullibility += 1 return nil }, CanDo: YesWeCan, @@ -611,17 +602,21 @@ var cards = []Card{ UnenactedDesc: cardsim.MsgStr("Rejecting militarism will set back the war department, but hunger will persist."), EnactedDesc: cardsim.MsgStr("[current policy] The war department has been rebuked for its aggression."), Do: func(p *Player) (cardsim.Message, error) { - p.Stats.Secrecy += 0.01 - p.Stats.WarExpense -= 0.01 - p.Stats.HiddenRelPenalty -= 0.01 - p.Stats.Cruelty -= 0.01 + p.Stats.Secrecy += 1 + p.Stats.Militarism -= 1 + p.Stats.HiddenRelPenalty -= 1 + p.Stats.Cruelty -= 1 + p.Stats.Madness -= 1 + p.Stats.Gullibility -= 1 return cardsim.MsgStr("Some of the warriors have had their fervor redirected into economic pursuits."), nil }, Undo: func(p *Player) error { - p.Stats.Secrecy -= 0.01 - p.Stats.WarExpense += 0.01 - p.Stats.HiddenRelPenalty += 0.01 - p.Stats.Cruelty += 0.01 + p.Stats.Secrecy -= 1 + p.Stats.Militarism += 1 + p.Stats.HiddenRelPenalty += 1 + p.Stats.Cruelty += 1 + p.Stats.Madness += 1 + p.Stats.Gullibility += 1 return nil }, CanDo: YesWeCan, @@ -634,49 +629,49 @@ var cards = []Card{ Desc: cardsim.MsgStr("Concerns have been raised about one of the categories of income that your tribe relies upon: books. To a lesser extent, art in general. Some artifacts, especially books, have the potential to give away details of your peoples' lives that help hostile powers locate your tribe."), After: ShuffleIntoBottomHalf, IsValid: func(c Card, p *Player) bool { - return p.Stats.PublishingIncome > 0 && p.Stats.ForeignRelations < 0 + return p.Stats.Publishing > 0 && p.Stats.ForeignRelations < 0 }, Policies: []Policy{ &BasicPolicy{ UnenactedDesc: cardsim.MsgStr(`The warrior who brought this to your attention stands at attention as they say, "The risk of discovery is an existential risk! Everything that makes us discoverable increases the intensity of conflict around our warrens, draining lives from our people! It is imperative that all respect our code of silence!"`), EnactedDesc: cardsim.MsgStr("[current policy] The tribe's warriors enforce a code of silence at spearpoint."), Do: func(p *Player) (cardsim.Message, error) { - p.Stats.Secrecy += 0.03 - p.Stats.PublishingIncome -= 0.05 - p.Stats.EducationExpense -= 0.01 - p.Stats.Madness += 0.02 - p.Stats.Authoritarianism += 0.02 - p.Stats.Gullibility -= 0.01 + p.Stats.Secrecy += 3 + p.Stats.Publishing -= 5 + p.Stats.Education -= 1 + p.Stats.Madness += 2 + p.Stats.Authoritarianism += 2 + p.Stats.Gullibility -= 1 return cardsim.MsgStr("No history records the sobbing of kobold scribes."), nil }, Undo: func(p *Player) error { - p.Stats.Secrecy -= 0.03 - p.Stats.PublishingIncome += 0.05 - p.Stats.EducationExpense += 0.01 - p.Stats.Madness -= 0.02 - p.Stats.Authoritarianism += 0.02 - p.Stats.Gullibility -= 0.01 + p.Stats.Secrecy -= 3 + p.Stats.Publishing += 5 + p.Stats.Education += 1 + p.Stats.Madness -= 2 + p.Stats.Authoritarianism += 2 + p.Stats.Gullibility -= 1 return nil }, CanDo: func(b *BasicPolicy, p *Player) bool { - return p.Stats.WarExpense > 0 + return p.Stats.Militarism > 0 }, }, &BasicPolicy{ UnenactedDesc: cardsim.MsgStr(`"No! Never! Er, maybe?" asks a dithering scribe, clutching a book worriedly to their chest. "Don't give it to the warriors! Can we set up an office of censorship to read everything we publish and send it back for edits if it would reveal anything about our location? We don't have to stop publishing; our traders buy food with foreign currencies you know!"`), EnactedDesc: cardsim.MsgStr("[current policy] The bureaucracy includes an office of censorship that reads all writings produced for sale to ensure that nothing is revealed about the location of any living kobolds."), Do: func(p *Player) (cardsim.Message, error) { - p.Stats.Secrecy += 0.01 - p.Stats.PublishingIncome -= 0.01 - p.Stats.BureaucracyExpense += 0.01 - p.Stats.Authoritarianism += 0.02 + p.Stats.Secrecy += 1 + p.Stats.Publishing -= 1 + p.Stats.Bureaucracy += 1 + p.Stats.Authoritarianism += 2 return cardsim.MsgStr("The censors are the most well-read kobolds in the tribe."), nil }, Undo: func(p *Player) error { - p.Stats.Secrecy -= 0.01 - p.Stats.PublishingIncome += 0.01 - p.Stats.BureaucracyExpense -= 0.01 - p.Stats.Authoritarianism -= 0.02 + p.Stats.Secrecy -= 1 + p.Stats.Publishing += 1 + p.Stats.Bureaucracy -= 1 + p.Stats.Authoritarianism -= 2 return nil }, CanDo: YesWeCan, @@ -685,31 +680,29 @@ var cards = []Card{ UnenactedDesc: cardsim.MsgStr(`Your Minister of Education wrings his claws sadly. "I know it'll be a monumental expense in paper and ink, but we need to encourage the writing of diaries. Our secrecy is already flawed; there are already attacks against us. Maybe our attackers will find diaries and understand that we aren't just thieves hiding under the earth."`), EnactedDesc: cardsim.MsgStr("[current policy] Even though recovered diaries point back to your outposts, your kobolds are encouraged to carry around diaries and record thoughts from their own lives into them."), Do: func(p *Player) (cardsim.Message, error) { - p.Stats.Secrecy -= 0.01 - p.Stats.PublishingIncome += 0.02 - p.Stats.EducationExpense += 0.03 - p.Stats.PropagandaExpense += 0.01 - p.Stats.HiddenRelPenalty -= 0.01 - p.Stats.PointOfDimReturns += 0.01 - p.Stats.Rebellion += 0.01 - p.Stats.Madness -= 0.01 - p.Stats.Cruelty -= 0.02 - p.Stats.Greed -= 0.01 - p.Stats.Gullibility -= 0.01 + p.Stats.Secrecy -= 1 + p.Stats.Publishing += 2 + p.Stats.Education += 3 + p.Stats.Propaganda += 1 + p.Stats.HiddenRelPenalty -= 1 + p.Stats.Rebellion += 1 + p.Stats.Madness -= 1 + p.Stats.Cruelty -= 2 + p.Stats.Greed -= 1 + p.Stats.Gullibility -= 1 return cardsim.MsgStr("Heartless surfacers burn recovered kobold diaries."), nil }, Undo: func(p *Player) error { - p.Stats.Secrecy += 0.01 - p.Stats.PublishingIncome -= 0.02 - p.Stats.EducationExpense -= 0.03 - p.Stats.PropagandaExpense -= 0.01 - p.Stats.HiddenRelPenalty += 0.01 - p.Stats.PointOfDimReturns -= 0.01 - p.Stats.Rebellion -= 0.01 - p.Stats.Madness += 0.01 - p.Stats.Cruelty += 0.02 - p.Stats.Greed += 0.01 - p.Stats.Gullibility += 0.01 + p.Stats.Secrecy += 1 + p.Stats.Publishing -= 2 + p.Stats.Education -= 3 + p.Stats.Propaganda -= 1 + p.Stats.HiddenRelPenalty += 1 + p.Stats.Rebellion -= 1 + p.Stats.Madness += 1 + p.Stats.Cruelty += 2 + p.Stats.Greed += 1 + p.Stats.Gullibility += 1 return nil }, CanDo: func(b *BasicPolicy, p *Player) bool { @@ -718,14 +711,14 @@ var cards = []Card{ }, &VerbosePolicy{ Default: &BasicPolicy{ - UnenactedDesc: cardsim.MsgStr("Rejecting this will inspire celebrations among the authors of your tribe."), + UnenactedDesc: cardsim.MsgStr("Although promoting diaries would do even more for literacy, rejecting this will still inspire celebrations among the authors of your tribe."), EnactedDesc: cardsim.MsgStr("The dangers posed by publishing information from your tribe can be denied without changes in policy."), Do: func(p *Player) (cardsim.Message, error) { - p.Stats.PublishingIncome += 0.01 + p.Stats.Publishing += 1 return cardsim.MsgStr("Kobold-hating adventurers know to be well-read."), nil }, Undo: func(p *Player) error { - p.Stats.PublishingIncome -= 0.01 + p.Stats.Publishing -= 1 return nil }, CanDo: YesWeCan, @@ -753,19 +746,19 @@ var cards = []Card{ UnenactedDesc: cardsim.MsgStr(`A friend of one of the afflicted managed to get onto your meeting schedule to say, "We work our miners hard. We need to give them more breaks. We need to fund more investments into healthcare, too. We can set our alchemists to work brewing potions that should help."`), EnactedDesc: cardsim.MsgStr("[current policy] Afflicted miners are being dosed with soothing alchemical teas."), Do: func(p *Player) (cardsim.Message, error) { - p.Stats.MiningIncome -= 0.01 - p.Stats.AlchemyIncome += 0.02 - p.Stats.HealthcareExpense += 0.01 // TODO: Implement population boost from Healthcare investment. Maybe 1000x HealthcareInvestment, so that each 0.01 Healthcare Investment is a bonus of 10 kobolds to the population? - p.Stats.PointOfDimReturns += 0.01 - p.Stats.Cruelty -= 0.01 + p.Stats.Mining -= 1 + p.Stats.Alchemy += 2 + p.Stats.Healthcare += 1 // TODO: Implement population boost from Healthcare investment. Maybe 1000x HealthcareInvestment, so that each 0.01 Healthcare Investment is a bonus of 10 kobolds to the population? + p.Stats.Cruelty -= 1 + p.Stats.Authoritarianism -= 2 return cardsim.MsgStr("Calming alchemical potions are displacing raw material sales in local markets."), nil }, Undo: func(p *Player) error { - p.Stats.MiningIncome += 0.01 - p.Stats.AlchemyIncome -= 0.02 - p.Stats.HealthcareExpense -= 0.01 - p.Stats.PointOfDimReturns -= 0.01 - p.Stats.Cruelty += 0.01 + p.Stats.Mining += 1 + p.Stats.Alchemy -= 2 + p.Stats.Healthcare -= 1 + p.Stats.Cruelty += 1 + p.Stats.Authoritarianism += 2 return nil }, CanDo: YesWeCan, @@ -775,23 +768,23 @@ var cards = []Card{ EnactedDesc: cardsim.MsgStr("[current policy] The mines are taut as crazy miners with explosives work under the oversight of armed guards."), Do: func(p *Player) (cardsim.Message, error) { p.Stats.BasePopulation -= 10 - p.Stats.MiningIncome += 0.03 - p.Stats.GadgetryIncome += 0.01 - p.Stats.WarExpense += 0.01 - p.Stats.PointOfDimReturns -= 0.01 - p.Stats.Rebellion += 0.01 - p.Stats.Madness += 0.04 - p.Stats.Authoritarianism -= 0.02 + p.Stats.Mining += 3 + p.Stats.Gadgetry += 1 + p.Stats.Militarism += 1 + p.Stats.Cruelty += 1 + p.Stats.Rebellion += 1 + p.Stats.Madness += 4 + p.Stats.Authoritarianism -= 2 return cardsim.MsgStr("Miners are known for their enthusiasm for explosives."), nil }, Undo: func(p *Player) error { - p.Stats.MiningIncome -= 0.03 - p.Stats.GadgetryIncome -= 0.01 - p.Stats.WarExpense -= 0.01 - p.Stats.PointOfDimReturns += 0.01 - p.Stats.Rebellion -= 0.01 - p.Stats.Madness -= 0.04 - p.Stats.Authoritarianism += 0.02 + p.Stats.Mining -= 3 + p.Stats.Gadgetry -= 1 + p.Stats.Militarism -= 1 + p.Stats.Cruelty += 1 + p.Stats.Rebellion -= 1 + p.Stats.Madness -= 4 + p.Stats.Authoritarianism += 2 return nil }, CanDo: YesWeCan, @@ -801,22 +794,20 @@ var cards = []Card{ EnactedDesc: cardsim.MsgStr("[current policy] Mine policy is to watch for this symptom and trial the afflicted in planar summoning."), Do: func(p *Player) (cardsim.Message, error) { p.Stats.BasePopulation -= 20 - p.Stats.MiningIncome -= 0.01 - p.Stats.PlanarIncome += 0.03 - p.Stats.ResearchSubsExpense += 0.01 - p.Stats.PointOfDimReturns += 0.02 - p.Stats.Madness += 0.04 - p.Stats.Authoritarianism -= 0.02 + p.Stats.Mining -= 1 + p.Stats.PlanarConnections += 3 + p.Stats.ResearchSubs += 1 + p.Stats.Madness += 4 + p.Stats.Authoritarianism -= 2 return cardsim.MsgStr("The tribe is infamous for producing crazed magi."), nil }, Undo: func(p *Player) error { p.Stats.BasePopulation += 20 - p.Stats.MiningIncome += 0.01 - p.Stats.PlanarIncome += 0.03 - p.Stats.ResearchSubsExpense -= 0.01 - p.Stats.PointOfDimReturns -= 0.02 - p.Stats.Madness -= 0.04 - p.Stats.Authoritarianism += 0.02 + p.Stats.Mining += 1 + p.Stats.PlanarConnections -= 3 + p.Stats.ResearchSubs -= 1 + p.Stats.Madness -= 4 + p.Stats.Authoritarianism += 2 return nil }, CanDo: YesWeCan, @@ -826,22 +817,20 @@ var cards = []Card{ EnactedDesc: cardsim.MsgStr("[current policy] The miners are dealing with laggard elements on their own."), Do: func(p *Player) (cardsim.Message, error) { p.Stats.BasePopulation -= 20 - p.Stats.MiningIncome += 0.01 - p.Stats.PointOfDimReturns += 0.01 - p.Stats.Rebellion -= 0.03 - p.Stats.Madness += 0.01 - p.Stats.Cruelty += 0.02 - p.Stats.Authoritarianism += 0.01 + p.Stats.Mining += 3 + p.Stats.Rebellion -= 3 + p.Stats.Madness += 1 + p.Stats.Cruelty += 2 + p.Stats.Authoritarianism += 1 return cardsim.MsgStr("Sufficient misery unmakes people."), nil }, Undo: func(p *Player) error { - p.Stats.BasePopulation += 10 - p.Stats.MiningIncome -= 0.01 - p.Stats.PointOfDimReturns -= 0.01 - p.Stats.Rebellion += 0.03 - p.Stats.Madness -= 0.01 - p.Stats.Cruelty -= 0.02 - p.Stats.Authoritarianism -= 0.01 + p.Stats.BasePopulation += 20 + p.Stats.Mining -= 3 + p.Stats.Rebellion += 3 + p.Stats.Madness -= 1 + p.Stats.Cruelty -= 2 + p.Stats.Authoritarianism -= 1 return nil }, CanDo: YesWeCan, @@ -851,15 +840,13 @@ var cards = []Card{ UnenactedDesc: cardsim.MsgStr("Maybe we can just work around the afflicted. Let them rest and find other employment."), EnactedDesc: cardsim.MsgStr("[current policy] We're encouraging the afflicted to rest and find other employment."), Do: func(p *Player) (cardsim.Message, error) { - p.Stats.MiningIncome -= 0.01 - p.Stats.PointOfDimReturns += 0.01 - p.Stats.Cruelty -= 0.01 + p.Stats.Mining -= 1 + p.Stats.Cruelty -= 1 return cardsim.MsgStr("Mining is just too strenuous for some kobolds."), nil }, Undo: func(p *Player) error { - p.Stats.MiningIncome += 0.01 - p.Stats.PointOfDimReturns -= 0.01 - p.Stats.Cruelty += 0.01 + p.Stats.Mining += 1 + p.Stats.Cruelty += 1 return nil }, CanDo: YesWeCan, @@ -887,7 +874,7 @@ var cards = []Card{ Desc: cardsim.MsgStr("Now that you've established a planar harvesting industry and have begun to tap into the economic potential of other planes of existence, your magi are seeking guidance as to where they should direct their efforts."), After: ShuffleIntoBottomHalf, IsValid: func(c Card, p *Player) bool { - return p.Stats.PlanarIncome > 0 + return p.Stats.PlanarConnections > 0 }, Policies: []Policy{ &BasicPolicy{ @@ -895,20 +882,20 @@ var cards = []Card{ EnactedDesc: cardsim.MsgStr("[current policy] Your colonies have established contact with energy planes to power spellcasting and potioneering."), Do: func(p *Player) (cardsim.Message, error) { p.Stats.BasePopulation -= 50 - p.Stats.PlanarIncome += 0.03 - p.Stats.AlchemyIncome += 0.01 - p.Stats.PointOfDimReturns += 0.06 - p.Stats.Rebellion -= 0.01 - p.Stats.Madness += 0.01 + p.Stats.PlanarConnections += 3 + p.Stats.Alchemy += 1 + p.Stats.Rebellion -= 1 + p.Stats.Madness += 1 + p.Stats.Gullibility -= 1 return cardsim.MsgStr("Surviving mage-students are really excited about their new curriculum."), nil }, Undo: func(p *Player) error { p.Stats.BasePopulation += 50 - p.Stats.PlanarIncome -= 0.03 - p.Stats.AlchemyIncome -= 0.01 - p.Stats.PointOfDimReturns -= 0.06 - p.Stats.Rebellion += 0.01 - p.Stats.Madness -= 0.01 + p.Stats.PlanarConnections -= 3 + p.Stats.Alchemy -= 1 + p.Stats.Rebellion += 1 + p.Stats.Madness -= 1 + p.Stats.Gullibility -= 1 return nil }, CanDo: YesWeCan, @@ -918,78 +905,80 @@ var cards = []Card{ EnactedDesc: cardsim.MsgStr("[current policy] Your tribe is locked in conflict with the lesser minions of elder horrors as it seeks to steal power from the mindbreaking realms."), Do: func(p *Player) (cardsim.Message, error) { p.Stats.BasePopulation -= 250 - p.Stats.PlanarIncome += 0.10 - p.Stats.AlchemyIncome += 0.05 - p.Stats.GadgetryIncome += 0.05 - p.Stats.WarExpense += 0.05 - p.Stats.ResearchSubsExpense += 0.01 - p.Stats.PointOfDimReturns += 0.5 - p.Stats.HiddenRelPenalty += 0.02 - p.Stats.Rebellion += 0.01 - p.Stats.Madness += 0.1 - p.Stats.Cruelty += 0.02 - p.Stats.Greed += 0.03 - p.Stats.Gullibility -= 0.02 - p.Stats.Authoritarianism -= 0.01 + p.Stats.PlanarConnections += 10 + p.Stats.Alchemy += 3 + p.Stats.Gadgetry += 3 + p.Stats.Militarism += 5 + p.Stats.ResearchSubs += 1 + p.Stats.HiddenRelPenalty += 2 + p.Stats.Rebellion += 1 + p.Stats.Madness += 1 + p.Stats.Cruelty += 2 + p.Stats.Greed += 3 + p.Stats.Gullibility -= 2 + p.Stats.Authoritarianism -= 1 return cardsim.MsgStr("Several caves have been abandoned to an infestation of eldritch horrors."), nil }, Undo: func(p *Player) error { p.Stats.BasePopulation += 250 - p.Stats.PlanarIncome -= 0.10 - p.Stats.AlchemyIncome -= 0.05 - p.Stats.GadgetryIncome -= 0.05 - p.Stats.WarExpense -= 0.05 - p.Stats.ResearchSubsExpense -= 0.01 - p.Stats.PointOfDimReturns -= 0.5 - p.Stats.HiddenRelPenalty -= 0.02 - p.Stats.Rebellion -= 0.01 - p.Stats.Madness -= 0.1 - p.Stats.Cruelty -= 0.02 - p.Stats.Greed -= 0.03 - p.Stats.Gullibility += 0.02 - p.Stats.Authoritarianism += 0.01 + p.Stats.PlanarConnections -= 10 + p.Stats.Alchemy -= 3 + p.Stats.Gadgetry -= 3 + p.Stats.Militarism -= 5 + p.Stats.ResearchSubs -= 0.01 + p.Stats.HiddenRelPenalty -= 2 + p.Stats.Rebellion -= 1 + p.Stats.Madness -= 1 + p.Stats.Cruelty -= 2 + p.Stats.Greed -= 3 + p.Stats.Gullibility += 2 + p.Stats.Authoritarianism += 1 return nil }, CanDo: func(b *BasicPolicy, p *Player) bool { - return p.Stats.PlanarIncome < 0.2 + return p.Stats.PlanarConnections > 2 }, }, &BasicPolicy{ UnenactedDesc: cardsim.MsgStr(`A stout kobold in a plain robe showed up to say, "Beware! Every planar connection leaks strange energies into the world. The safe connections, once we've the skill to make them, are to the well-ordered and benevolent realms. They have alchemical wonders and great wisdom, but moreover the energies that leak off the connection will make society more well-ordered rather than less."`), EnactedDesc: cardsim.MsgStr("[current policy] Your planar explorers have been directed to the planes of law in hopes of finding the secret of a well-governed society."), Do: func(p *Player) (cardsim.Message, error) { - p.Stats.BasePopulation += 1 - p.Stats.PlanarIncome += 0.03 - p.Stats.AlchemyIncome += 0.03 - p.Stats.PublishingIncome += 0.01 - p.Stats.BureaucracyExpense -= 0.01 - p.Stats.PointOfDimReturns += 0.2 - p.Stats.HiddenRelPenalty += 0.01 - p.Stats.Rebellion -= 0.04 - p.Stats.Madness -= 0.01 - p.Stats.Cruelty -= 0.05 - p.Stats.Greed -= 0.03 - p.Stats.Gullibility += 0.02 + p.Stats.BasePopulation += 20 + p.Stats.PlanarConnections += 3 + p.Stats.Alchemy += 4 + p.Stats.Publishing += 2 + p.Stats.Hospitality += 1 + p.Stats.Gadgetry += 1 + p.Stats.ParksExpense += 1 + p.Stats.HiddenRelPenalty -= 1 //High is bad on this stat. + p.Stats.Rebellion -= 4 + p.Stats.Madness -= 2 + p.Stats.Cruelty -= 5 + p.Stats.Greed -= 3 + p.Stats.Gullibility += 2 + p.Stats.Authoritarianism -= 2 // TODO: This will affect crime and corruption rates. return cardsim.MsgStr("Planar explorers find their work boring."), nil }, Undo: func(p *Player) error { - p.Stats.BasePopulation -= 1 - p.Stats.PlanarIncome -= 0.03 - p.Stats.AlchemyIncome -= 0.03 - p.Stats.PublishingIncome -= 0.01 - p.Stats.BureaucracyExpense += 0.01 - p.Stats.PointOfDimReturns -= 0.2 - p.Stats.HiddenRelPenalty -= 0.01 - p.Stats.Rebellion += 0.04 - p.Stats.Madness += 0.01 - p.Stats.Cruelty += 0.05 - p.Stats.Greed += 0.03 - p.Stats.Gullibility -= 0.02 + p.Stats.BasePopulation -= 20 + p.Stats.PlanarConnections -= 3 + p.Stats.Alchemy -= 4 + p.Stats.Publishing -= 2 + p.Stats.Hospitality -= 1 + p.Stats.Gadgetry -= 1 + p.Stats.ParksExpense -= 1 + p.Stats.HiddenRelPenalty += 1 + p.Stats.Rebellion += 4 + p.Stats.Madness += 2 + p.Stats.Cruelty += 5 + p.Stats.Greed += 3 + p.Stats.Gullibility -= 2 + p.Stats.Authoritarianism += 2 return nil }, CanDo: func(b *BasicPolicy, p *Player) bool { - return p.Stats.PlanarIncome > 0.11 + return p.Stats.PlanarConnections > 11 }, }, &BasicPolicy{ @@ -997,67 +986,69 @@ var cards = []Card{ EnactedDesc: cardsim.MsgStr("[current policy] Your planar harvesters try to scan the multiverse for scavenging targets."), Do: func(p *Player) (cardsim.Message, error) { p.Stats.BasePopulation -= 4 - p.Stats.AlchemyIncome += 0.01 - p.Stats.GadgetryIncome += 0.05 - p.Stats.ScavengingIncome += 0.05 - p.Stats.FoodSupply += 0.02 - p.Stats.Greed += 0.01 + p.Stats.Alchemy += 1 + p.Stats.Gadgetry += 5 + p.Stats.Scavenging += 5 + p.Stats.Publishing += 1 + p.Stats.Madness -= 1 + p.Stats.FoodSupply += 2 + p.Stats.Greed += 1 return cardsim.MsgStr("Planes without kobolds tell stories of reptillian infiltrators."), nil }, Undo: func(p *Player) error { p.Stats.BasePopulation += 4 - p.Stats.AlchemyIncome -= 0.01 - p.Stats.GadgetryIncome -= 0.05 - p.Stats.ScavengingIncome -= 0.05 - p.Stats.FoodSupply -= 0.02 - p.Stats.Greed -= 0.01 + p.Stats.Alchemy -= 1 + p.Stats.Gadgetry -= 5 + p.Stats.Scavenging -= 5 + p.Stats.Publishing -= 1 + p.Stats.Madness += 1 + p.Stats.FoodSupply -= 2 + p.Stats.Greed -= 1 return nil }, CanDo: func(b *BasicPolicy, p *Player) bool { - return p.Stats.PlanarIncome > 0.11 + return p.Stats.PlanarConnections > 11 }, }, &BasicPolicy{ - UnenactedDesc: cardsim.MsgStr(`The mage in the robe of eyes is in a celebratory mood, pouring glasses for anyone who'll take them from a bottle of some bluish-black liquid that you're pretty sure is dangerous. "Our planar harvesting potential has grown magnificently! If we target the elder planes, they'll... okay, they won't exactly fear us, but we can start taking some real treasures from them! Great spells! Exotic reagents! Unique treasures! Let's lean into the hazard!`), + UnenactedDesc: cardsim.MsgStr(`The mage in the robe of eyes is in a celebratory mood, pouring glasses for anyone who'll take them from a bottle of some bluish-black liquid that you're pretty sure is dangerous. "Our planar harvesting potential has grown magnificently! If we target deeper into the elder planes, they'll... okay, they won't exactly fear us, but we can start taking some real treasures from them! Great spells! Exotic reagents! Unique treasures! Let's lean into the hazard!`), EnactedDesc: cardsim.MsgStr("[current policy] Your tribe is sacrificing safety and sanity in thievery attempts that only serve the plans of the elder gods."), Do: func(p *Player) (cardsim.Message, error) { p.Stats.BasePopulation -= 350 - p.Stats.PlanarIncome += 0.15 - p.Stats.AlchemyIncome += 0.10 - p.Stats.GadgetryIncome += 0.10 - p.Stats.WarExpense += 0.05 - p.Stats.ResearchSubsExpense += 0.01 - p.Stats.PointOfDimReturns += 0.5 - p.Stats.ForeignRelations -= 0.05 - p.Stats.HiddenRelPenalty += 0.05 - p.Stats.Rebellion += 0.04 - p.Stats.Madness += 0.2 - p.Stats.Cruelty += 0.08 - p.Stats.Greed += 0.1 - p.Stats.Gullibility -= 0.05 - p.Stats.Authoritarianism -= 0.02 + p.Stats.PlanarConnections += 15 + p.Stats.Alchemy += 4 + p.Stats.Gadgetry += 5 + p.Stats.Militarism += 5 + p.Stats.ResearchSubs += 1 + p.Stats.ForeignRelations -= 5 + p.Stats.HiddenRelPenalty += 5 + p.Stats.Rebellion += 4 + p.Stats.Madness += 20 + p.Stats.Cruelty += 8 + p.Stats.Greed += 10 + p.Stats.Gullibility -= 5 + p.Stats.Authoritarianism -= 2 return cardsim.MsgStr("Nobody dares touch the treasures in the tribal vault."), nil }, Undo: func(p *Player) error { p.Stats.BasePopulation += 350 - p.Stats.PlanarIncome -= 0.15 - p.Stats.AlchemyIncome -= 0.10 - p.Stats.GadgetryIncome -= 0.10 - p.Stats.WarExpense -= 0.05 - p.Stats.ResearchSubsExpense -= 0.01 - p.Stats.PointOfDimReturns -= 0.5 - p.Stats.ForeignRelations += 0.05 - p.Stats.HiddenRelPenalty -= 0.05 - p.Stats.Rebellion -= 0.04 - p.Stats.Madness -= 0.2 - p.Stats.Cruelty -= 0.08 - p.Stats.Greed -= 0.1 - p.Stats.Gullibility += 0.05 - p.Stats.Authoritarianism += 0.02 + p.Stats.PlanarConnections -= 15 + p.Stats.Alchemy -= 4 + p.Stats.Gadgetry -= 5 + p.Stats.Militarism -= 5 + p.Stats.ResearchSubs -= 1 + p.Stats.ForeignRelations += 5 + p.Stats.HiddenRelPenalty -= 5 + p.Stats.Rebellion -= 4 + p.Stats.Madness -= 20 + p.Stats.Cruelty -= 8 + p.Stats.Greed -= 10 + p.Stats.Gullibility += 5 + p.Stats.Authoritarianism += 2 return nil }, CanDo: func(b *BasicPolicy, p *Player) bool { - return p.Stats.PlanarIncome >= 0.2 + return p.Stats.PlanarConnections >= 20 }, }, &VerbosePolicy{ @@ -1066,14 +1057,16 @@ var cards = []Card{ EnactedDesc: cardsim.MsgStr("Persisting in your rejection of planar targeting will change nothing."), Do: func(p *Player) (cardsim.Message, error) { p.Stats.BasePopulation += 40 - p.Stats.PlanarIncome -= 0.01 - p.Stats.Greed -= 0.01 + p.Stats.PlanarConnections -= 1 + p.Stats.Greed -= 1 + p.Stats.Authoritarianism -= 1 return cardsim.MsgStr("The tribe recently gained a few immigrants reassured by the lack of planar portals."), nil }, Undo: func(p *Player) error { p.Stats.BasePopulation -= 40 - p.Stats.PlanarIncome += 0.01 - p.Stats.Greed += 0.01 + p.Stats.PlanarConnections += 1 + p.Stats.Greed += 1 + p.Stats.Authoritarianism += 1 return nil }, CanDo: YesWeCan, @@ -1101,28 +1094,25 @@ var cards = []Card{ Desc: cardsim.MsgStr("Leading a starving kobold tribe can be pretty dreary. Rats, weeds, and tea made from moss... Even the VIPs are scrawny on this fare."), After: ShuffleIntoBottomHalf, IsValid: func(c Card, p *Player) bool { - return p.Stats.FoodSupply < 1 && p.Stats.StatObesity() < 10 + return p.Stats.FoodSupply < 30 }, Policies: []Policy{ &BasicPolicy{ - UnenactedDesc: cardsim.MsgStr(`A skinny minister hoists their claws at the roof in frustration as they say, "I can't take it anymore! I'm not meant to be this skinny! If it takes a whole shipment of ore to buy us a single cake, that's just the price of leadership!"`), + UnenactedDesc: cardsim.MsgStr(`A rail-thin minister hoists their claws at the roof in frustration as they say, "I can't take it anymore! I'm not meant to be this skinny! If it takes a whole shipment of ore to buy us a single cake, that's just the price of leadership!"`), EnactedDesc: cardsim.MsgStr("[current policy] Funding sweet treats for leadership is considered an economic priority."), Do: func(p *Player) (cardsim.Message, error) { - p.Stats.EconPlanExpense += 0.02 - p.Stats.FoodSupply += 0.01 - p.Stats.ObesogenicFood += 0.05 - p.Stats.ForeignRelations += 0.01 - p.Stats.Rebellion += 0.01 - p.Stats.Authoritarianism += 0.01 + p.Stats.FoodSupply += 1 + p.Stats.ForeignRelations += 1 + p.Stats.Rebellion += 1 + p.Stats.Authoritarianism += 1 // TODO: Make this increase corruption. return cardsim.MsgStr("High food prices are fitting kobolds into the economy."), nil }, Undo: func(p *Player) error { - p.Stats.EconPlanExpense -= 0.02 - p.Stats.FoodSupply -= 0.01 - p.Stats.ObesogenicFood -= 0.05 - p.Stats.Rebellion -= 0.01 - p.Stats.Authoritarianism -= 0.01 + p.Stats.FoodSupply -= 1 + p.Stats.ForeignRelations -= 1 + p.Stats.Rebellion -= 1 + p.Stats.Authoritarianism -= 1 return nil }, CanDo: YesWeCan, @@ -1131,18 +1121,22 @@ var cards = []Card{ UnenactedDesc: cardsim.MsgStr(`A loyal mining foreman looks at the minister with crossed arms. "We definitely need more food, but we absolutely shouldn't set the example of feasting while others starve. We need to expand the Quality of Life budget and declare a sweets ration. Let them eat cake!"`), EnactedDesc: cardsim.MsgStr("[current policy] A sweets ration helps kobolds face the challenge of hard labour."), Do: func(p *Player) (cardsim.Message, error) { - p.Stats.QoLExpense += 0.04 - p.Stats.FoodSupply += 0.02 - p.Stats.ObesogenicFood += 0.1 - p.Stats.PointOfDimReturns += 0.03 + p.Stats.Welfare += 4 + p.Stats.FoodSupply += 2 + p.Stats.Cruelty -= 1 + p.Stats.Rebellion -= 1 + p.Stats.Gullibility += 1 + p.Stats.Greed -= 1 // TODO: Implement QoL functions. return cardsim.MsgStr("The government is proud to secure sweets for the public."), nil }, Undo: func(p *Player) error { - p.Stats.QoLExpense -= 0.03 - p.Stats.FoodSupply -= 0.02 - p.Stats.ObesogenicFood -= 0.1 - p.Stats.Rebellion += 0.01 + p.Stats.Welfare -= 4 + p.Stats.FoodSupply -= 2 + p.Stats.Cruelty += 1 + p.Stats.Rebellion += 1 + p.Stats.Gullibility -= 1 + p.Stats.Greed += 1 return nil }, CanDo: YesWeCan, @@ -1151,17 +1145,15 @@ var cards = []Card{ UnenactedDesc: cardsim.MsgStr(`One of your tribe's few stout kobolds brings the issue up in the cafeteria. "I've heard rumour that some kobolds are begging for sweets. We need to take a stand against that and squelch it early! Fund nutrition research to prove what's truly healthy for kobolds!" They look at you sidelong. "And, uh. Hire me for the research. I want a food subsidy that I can call a long-term benefit to the tribe."`), EnactedDesc: cardsim.MsgStr("[current policy] Your tribe is paying some kobolds to track how food affects health. Current consensus: your tribe is underfed."), Do: func(p *Player) (cardsim.Message, error) { - p.Stats.ResearchSubsExpense += 0.01 - p.Stats.PublishingIncome += 0.01 - p.Stats.FoodSupply += 0.01 - p.Stats.ObesogenicFood -= 0.03 - p.Stats.PointOfDimReturns += 0.01 + p.Stats.ResearchSubs += 1 + p.Stats.Publishing += 1 + p.Stats.FoodSupply += 1 return cardsim.MsgStr("Eating lichen soup is remarkably thinning."), nil }, Undo: func(p *Player) error { - p.Stats.ResearchSubsExpense -= 0.01 - p.Stats.FoodSupply -= 0.01 - p.Stats.ObesogenicFood += 0.03 + p.Stats.ResearchSubs -= 1 + p.Stats.Publishing -= 1 + p.Stats.FoodSupply -= 1 return nil }, CanDo: YesWeCan, diff --git a/koboldsim/stats.go b/koboldsim/stats.go index 3e3a283..11bf49f 100644 --- a/koboldsim/stats.go +++ b/koboldsim/stats.go @@ -1,8 +1,6 @@ package koboldsim import ( - "math" - "git.chromaticdragon.app/kistaro/CardSimEngine/cardsim" ) @@ -10,277 +8,88 @@ import ( type KoboldMine struct { BasePopulation float64 `cardsim:"stathidden"` - MiningIncome float64 `cardsim:"stathidden" cardsim_name:"Mining Productivity"` - ScavengingIncome float64 `cardsim:"stathidden" cardsim_name:"Scavenging Productivity"` - AlchemyIncome float64 `cardsim:"stathidden" cardsim_name:"Alchemy Productivity"` - HospitalityIncome float64 `cardsim:"stathidden" cardsim_name:"Hospitality Productivity"` - AgricultureIncome float64 `cardsim:"stathidden" cardsim_name:"Agriculture Productivity"` - ManufacturingIncome float64 `cardsim:"stathidden" cardsim_name:"Manufacturing Productivity"` - PlanarIncome float64 `cardsim:"stathidden" cardsim_name:"Planar Productivity"` - PublishingIncome float64 `cardsim:"stathidden" cardsim_name:"Publishing Productivity"` - ForestryIncome float64 `cardsim:"stathidden" cardsim_name:"Forestry Productivity"` - FinanceIncome float64 `cardsim:"stathidden" cardsim_name:"Finance Productivity"` - GadgetryIncome float64 `cardsim:"stathidden" cardsim_name:"Gadgetry Productivity"` - FishingIncome float64 `cardsim:"stathidden" cardsim_name:"Fishing Productivity"` - ConstructionIncome float64 `cardsim:"stathidden" cardsim_name:"Construction Productivity"` + Mining float64 `cardsim:"stathidden" cardsim_name:"Mining"` + Scavenging float64 `cardsim:"stathidden" cardsim_name:"Scavenging"` + Alchemy float64 `cardsim:"stathidden" cardsim_name:"Alchemy"` + Hospitality float64 `cardsim:"stathidden" cardsim_name:"Hospitality"` + Agriculture float64 `cardsim:"stathidden" cardsim_name:"Agriculture"` + Manufacturing float64 `cardsim:"stathidden" cardsim_name:"Manufacturing"` + PlanarConnections float64 `cardsim:"stathidden" cardsim_name:"Planar Connections"` + Publishing float64 `cardsim:"stathidden" cardsim_name:"Publishing"` + Forestry float64 `cardsim:"stathidden" cardsim_name:"Forestry"` + Finance float64 `cardsim:"stathidden" cardsim_name:"Finance"` + Gadgetry float64 `cardsim:"stathidden" cardsim_name:"Gadgetry"` + Fishing float64 `cardsim:"stathidden" cardsim_name:"Fishing"` + Construction float64 `cardsim:"stathidden" cardsim_name:"Construction"` - PropagandaExpense float64 `cardsim:"stathidden" cardsim_name:"Propaganda Investment"` - BureaucracyExpense float64 `cardsim:"stathidden" cardsim_name:"Bureaucracy Investment"` - WarExpense float64 `cardsim:"stathidden" cardsim_name:"War Investment"` - QoLExpense float64 `cardsim:"stathidden" cardsim_name:"QoL Investment"` - LogisticsExpense float64 `cardsim:"stathidden" cardsim_name:"Logistics Investment"` - DragonSubsExpense float64 `cardsim:"stathidden" cardsim_name:"Dragon Subsidies Investment"` - ResearchSubsExpense float64 `cardsim:"stathidden" cardsim_name:"Research Subsidies Investment"` - EducationExpense float64 `cardsim:"stathidden" cardsim_name:"Education Investment"` - HealthcareExpense float64 `cardsim:"stathidden" cardsim_name:"Healthcare Investment"` - ForeignRelExpense float64 `cardsim:"stathidden" cardsim_name:"Foreign Relations Investment"` - PoliceExpense float64 `cardsim:"stathidden" cardsim_name:"Law Enforcement Investment"` - EconPlanExpense float64 `cardsim:"stathidden" cardsim_name:"Economic Planning Investment"` - ParksExpense float64 `cardsim:"stathidden" cardsim_name:"Parks and Aesthetics Investment"` - FaithExpense float64 `cardsim:"stathidden" cardsim_name:"Faith Investment"` + Propaganda float64 `cardsim:"stathidden" cardsim_name:"Propaganda"` + Bureaucracy float64 `cardsim:"stathidden" cardsim_name:"Bureaucracy"` + Militarism float64 `cardsim:"stathidden" cardsim_name:"Militarism"` + Welfare float64 `cardsim:"stathidden" cardsim_name:"Social Safety Net"` + Logistics float64 `cardsim:"stathidden" cardsim_name:"Logistics"` + DragonSubs float64 `cardsim:"stathidden" cardsim_name:"Dragon Subsidies"` + ResearchSubs float64 `cardsim:"stathidden" cardsim_name:"Research"` + Education float64 `cardsim:"stathidden" cardsim_name:"Education"` + Healthcare float64 `cardsim:"stathidden" cardsim_name:"Healthcare"` + ForeignRelExpense float64 `cardsim:"stathidden" cardsim_name:"Diplomatic Investment"` + Police float64 `cardsim:"stathidden" cardsim_name:"Law Enforcement"` + ParksExpense float64 `cardsim:"stathidden" cardsim_name:"City Beautification"` + Faith float64 `cardsim:"stathidden" cardsim_name:"Faith"` - FoodSupply float64 `cardsim:"stathidden"` - ObesogenicFood float64 `cardsim:"stathidden"` - ForeignRelations float64 `cardsim:"stathidden"` - HiddenRelPenalty float64 `cardsim:"stathidden"` // Lower is better. - Secrecy float64 `cardsim:"stathidden"` - PointOfDimReturns float64 `cardsim:"stathidden"` - Rebellion float64 `cardsim:"stathidden"` - Madness float64 `cardsim:"stathidden"` - Cruelty float64 `cardsim:"stat"` - Greed float64 `cardsim:"stat"` - Gullibility float64 `cardsim:"stathidden"` - Authoritarianism float64 `cardsim:"stat"` + FoodSupply float64 `cardsim:"stathidden"` + ForeignRelations float64 `cardsim:"stathidden"` + HiddenRelPenalty float64 `cardsim:"stathidden"` // Lower is better. + Secrecy float64 `cardsim:"stathidden"` + Rebellion float64 `cardsim:"stathidden"` + Madness float64 `cardsim:"stathidden"` + Cruelty float64 `cardsim:"stat"` + Greed float64 `cardsim:"stat"` + Gullibility float64 `cardsim:"stathidden"` + Authoritarianism float64 `cardsim:"stat"` - // AnotherExpense float64 `cardsim:"hiddenround5"` - // A different way of adding stats that is slightly more empowering. -} + Taxation float64 `cardsim:"stat"` + TaxEvasion float64 `cardsim:"stat"` + Squalor float64 `cardsim:"stat"` + // Idea for tax evasion. The corruption stat should increase tax evasion, while the squalor stat should reduce it. The link with corruption should be obvious, but for squalor: when the economy improves, more people have resources with which to attempt to protect their income. This is part of why rich people can evade taxes more effectively than poor people. It's not ALL lobbying advantage. -func (k *KoboldMine) ProductivityFunc(s float64) func() float64 { - return func() float64 { - return math.Max(s*float64(k.Kobolds()), 0) - } -} - -func (k *KoboldMine) ProductivityTotal() float64 { - total := math.Max(k.MiningIncome, 0.01) - total += math.Max(k.ScavengingIncome, 0) - total += math.Max(k.AlchemyIncome, 0) - total += math.Max(k.HospitalityIncome, 0) - total += math.Max(k.AgricultureIncome, 0) - total += math.Max(k.ManufacturingIncome, 0) - total += math.Max(k.PlanarIncome, 0) - total += math.Max(k.PublishingIncome, 0) - total += math.Max(k.FinanceIncome, 0) - total += math.Max(k.GadgetryIncome, 0) - total += math.Max(k.FishingIncome, 0) - total += math.Max(k.ConstructionIncome, 0.02) - total += math.Max(k.PropagandaExpense, 0) - total += math.Max(k.BureaucracyExpense, 0) - total += math.Max(k.WarExpense, 0) - total += math.Max(k.QoLExpense, 0) - total += math.Max(k.LogisticsExpense, 0) - total += math.Max(k.DragonSubsExpense, 0) - total += math.Max(k.ResearchSubsExpense, 0) - total += math.Max(k.EducationExpense, 0) - total += math.Max(k.HealthcareExpense, 0) - total += math.Max(k.ForeignRelExpense, 0) - total += math.Max(k.PoliceExpense, 0) - total += math.Max(k.EconPlanExpense, 0) - total += math.Max(k.ParksExpense, 0) - total += math.Max(k.FaithExpense, 0) - return total -} - -func (k *KoboldMine) ProductivityMultiplier() float64 { - return math.Pow(0.95, math.Max(0, k.ProductivityTotal()-k.PointOfDimReturns)) -} - -func (k *KoboldMine) TrueMiningIncome() float64 { - return math.Max(k.MiningIncome*k.ProductivityMultiplier(), 0.01) -} - -func (k *KoboldMine) TrueScavengingIncome() float64 { - return math.Max(k.ScavengingIncome*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TrueAlchemyIncome() float64 { - return math.Max(k.AlchemyIncome*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TrueHospitalityIncome() float64 { - return math.Max(k.HospitalityIncome*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TrueAgricultureIncome() float64 { - return math.Max(k.AgricultureIncome*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TrueManufacturingIncome() float64 { - return math.Max(k.ManufacturingIncome*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TruePlanarIncome() float64 { - return math.Max(k.PlanarIncome*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TruePublishingIncome() float64 { - return math.Max(k.PublishingIncome*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TrueForestryIncome() float64 { - return math.Max(k.ForestryIncome*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TrueFinanceIncome() float64 { - return math.Max(k.FinanceIncome*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TrueGadgetryIncome() float64 { - return math.Max(k.GadgetryIncome*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TrueFishingIncome() float64 { - return math.Max(k.FishingIncome*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TrueConstructionIncome() float64 { - return math.Max(k.ConstructionIncome*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TruePropagandaExpense() float64 { - return math.Max(k.PropagandaExpense*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TrueBureaucracyExpense() float64 { - return math.Max(k.BureaucracyExpense*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TrueWarExpense() float64 { - return math.Max(k.WarExpense*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TrueQoLExpense() float64 { - return math.Max(k.QoLExpense*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TrueLogisticsExpense() float64 { - return math.Max(k.LogisticsExpense*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TrueDragonSubsExpense() float64 { - return math.Max(k.DragonSubsExpense*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TrueResearchSubsExpense() float64 { - return math.Max(k.ResearchSubsExpense*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TrueEducationExpense() float64 { - return math.Max(k.EducationExpense*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TrueHealthcareExpense() float64 { - return math.Max(k.HealthcareExpense*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TrueForeignRelExpense() float64 { - return math.Max(k.ForeignRelExpense*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TruePoliceExpense() float64 { - return math.Max(k.PoliceExpense*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TrueEconPlanExpense() float64 { - return math.Max(k.EconPlanExpense*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TrueParksExpense() float64 { - return math.Max(k.ParksExpense*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TrueFaithExpense() float64 { - return math.Max(k.FaithExpense*k.ProductivityMultiplier(), 0) -} - -func (k *KoboldMine) TotalSectorIncome() float64 { - return float64(k.Kobolds()) * (math.Max(k.TrueMiningIncome(), 0.01) + math.Max(k.TrueScavengingIncome(), 0) + math.Max(k.TrueAlchemyIncome(), 0) + math.Max(k.TrueHospitalityIncome(), 0) + math.Max(k.TrueAgricultureIncome(), 0) + math.Max(k.TrueManufacturingIncome(), 0) + math.Max(k.TruePlanarIncome(), 0) + math.Max(k.TruePublishingIncome(), 0) + math.Max(k.TrueFinanceIncome(), 0) + math.Max(k.TrueGadgetryIncome(), 0) + math.Max(k.TrueFishingIncome(), 0) + math.Max(k.TrueConstructionIncome(), 0.02)) -} - -func (k *KoboldMine) TotalGovExpense() float64 { - return float64(k.Kobolds()) * (math.Max(k.TruePropagandaExpense(), 0) + math.Max(k.TrueBureaucracyExpense(), 0) + math.Max(k.TrueWarExpense(), 0) + math.Max(k.TrueQoLExpense(), 0) + math.Max(k.TrueLogisticsExpense(), 0) + math.Max(k.TrueDragonSubsExpense(), 0) + math.Max(k.TrueResearchSubsExpense(), 0) + math.Max(k.TrueEducationExpense(), 0) + math.Max(k.TrueHealthcareExpense(), 0) + math.Max(k.TrueForeignRelExpense(), 0) + math.Max(k.TruePoliceExpense(), 0) + math.Max(k.TrueEconPlanExpense(), 0) + math.Max(k.TrueParksExpense(), 0) + math.Max(k.TrueFaithExpense(), 0)) -} - -func (k *KoboldMine) Taxation() float64 { - return (k.TotalGovExpense() / (k.TotalSectorIncome() + k.TotalGovExpense())) * 100 -} - -// Idea for the tax rate. I could have a tracked "tax evasion" stat that drives up taxation (people who actually pay have to pay more) as well as a tracked "public pay ratio" stat that drives up taxation if the public sector is paid more than the private sector and vice versa if the public sector is paid less. Both stats should be exposed to the player. -// Idea for tax evasion. The corruption stat should increase tax evasion, while the squalor stat should reduce it. The link with corruption should be obvious, but for squalor: when the economy improves, more people have resources with which to attempt to protect their income. - -func (k *KoboldMine) StatTaxRate() float64 { - return k.Taxation() } func (k *KoboldMine) Kobolds() int64 { - return int64((k.BasePopulation + (k.HealthcareExpense * 1000)) * k.FoodSupply * (1 - 0.5*(k.StatObesity()/100)) * (1 + k.TrueForeignRelations())) + return int64((k.BasePopulation * (k.Healthcare + 1)) * (k.FoodSupply) * (100 + k.TrueForeignRelations()/100)) } +const ( + MinFoodSupply = 18 + MaxFoodSupply = 37 +) + func (k *KoboldMine) DisplayedFoodSupply() float64 { - return (k.FoodSupply - 1) * 100 + return 100 * (k.FoodSupply - MinFoodSupply) / (MaxFoodSupply - MinFoodSupply) //This returns a Policy Implementation Percentage, so that the player can see how good they are at capturing food supply. When it becomes possible for Food Supply to hit 0 and end the game in famine, the minimum survivable food supply PIP should be calculated and returned to the player as a warned-against failure threshold. } -func (k *KoboldMine) StatObesogenicity() float64 { - return (k.ObesogenicFood - 1) * 100 -} - -func (k *KoboldMine) StatObesity() float64 { - return (100 / (2.3 + math.Exp(-0.04*(k.DisplayedFoodSupply()-37)))) + 100/(2.3+math.Exp(-0.04*(k.StatObesogenicity()-37))) -} +//func (k *KoboldMine) StatObesity() float64 { +//return (100 / (2.3 + math.Exp(-0.04*(k.DisplayedFoodSupply()-37)))) + 100/(2.3+math.Exp(-0.04*(k.StatObesogenicity()-37))) +//} func (k *KoboldMine) TrueForeignRelations() float64 { - return (1 - math.Max(math.Min(k.Secrecy, 1), 0)) * (math.Max(math.Min(k.ForeignRelations, 1), -1) - k.HiddenRelPenalty) + openness := 100 - clamp(k.Secrecy, 0, 100) + effectiveRel := clamp(k.ForeignRelations, -100, 100) - k.HiddenRelPenalty + return openness / 100 * effectiveRel } +const ( + MinForeignRelations = -11 + MaxForeignRelations = 9 +) + func (k *KoboldMine) DisplayedForeignRelations() float64 { - return math.Max(math.Min((k.ForeignRelations*100), 100), -100) + return 100 * (k.ForeignRelations - MinForeignRelations) / (MaxForeignRelations - MinForeignRelations) } func (k *KoboldMine) DisplayedSecrecy() float64 { return k.Secrecy * 100 } -func (k *KoboldMine) SqualorReduction() float64 { - total := math.Max(k.MiningIncome, 0.01) - total -= math.Max(k.ScavengingIncome, 0) - total += math.Max(k.AlchemyIncome, 0) - total += math.Max(k.HospitalityIncome, 0) * 2 - total += math.Max(k.ManufacturingIncome, 0) - total += math.Max(k.PlanarIncome, 0) - total += math.Max(k.PublishingIncome, 0) * 2 - total += math.Max(k.FinanceIncome, 0) * 3 - total += math.Max(k.GadgetryIncome, 0) * 2 - total += math.Max(k.ConstructionIncome, 0.02) * 2 - total += math.Max(k.PropagandaExpense, 0) - total += math.Max(k.BureaucracyExpense, 0) * 2 - total += math.Max(k.QoLExpense, 0) * 20 - total += math.Max(k.LogisticsExpense, 0) - total += math.Max(k.ResearchSubsExpense, 0) * 2 - total += math.Max(k.EducationExpense, 0) * 4 - total += math.Max(k.HealthcareExpense, 0) * 6 - total += math.Max(k.ForeignRelExpense, 0) - total += math.Max(k.PoliceExpense, 0) - total += math.Max(k.EconPlanExpense, 0) - total += math.Max(k.ParksExpense, 0) * 3 - total += math.Max(k.FaithExpense, 0) * 10 - return total -} - -func (k *KoboldMine) StatSqualor() float64 { - return 100 * math.Pow(1.204, 1.2-(2*k.SqualorReduction())) -} - // So I want squalor to be a value between 100 and 0 that reduces readily at the start and then becomes increasingly difficult to further reduce. This initial squalor stat starts at about 80%, but may not change rapidly enough. Note that the 1.2-(2* sequence is meant to balance it at 80% starting point. If I change the velocity on the 2* side, I have to change the 1.2 offset as well. I really hope this math works, because I don't quite understand it. func (k *KoboldMine) StatChaos() float64 { @@ -298,122 +107,6 @@ func (k *KoboldMine) StatCorruption() float64 { func (k *KoboldMine) Stats() []cardsim.Stat { stats := cardsim.ExtractStats(k) funcs := []cardsim.Stat{ - cardsim.StatFunc( - "Mining Income", - k.ProductivityFunc(k.TrueMiningIncome()), - ), - cardsim.StatFunc( - "Scavenging Income", - k.ProductivityFunc(k.TrueScavengingIncome()), - ), - cardsim.StatFunc( - "Alchemy Income", - k.ProductivityFunc(k.TrueAlchemyIncome()), - ), - cardsim.StatFunc( - "Hospitality Income", - k.ProductivityFunc(k.TrueHospitalityIncome()), - ), - cardsim.StatFunc( - "Agriculture Income", - k.ProductivityFunc(k.TrueAgricultureIncome()), - ), - cardsim.StatFunc( - "Manufacturing Income", - k.ProductivityFunc(k.TrueManufacturingIncome()), - ), - cardsim.StatFunc( - "Planar Harvesting Income", - k.ProductivityFunc(k.TruePlanarIncome()), - ), - cardsim.StatFunc( - "Book Publishing Income", - k.ProductivityFunc(k.TruePublishingIncome()), - ), - cardsim.StatFunc( - "Forestry Income", - k.ProductivityFunc(k.TrueForestryIncome()), - ), - cardsim.StatFunc( - "Finance Income", - k.ProductivityFunc(k.TrueFinanceIncome()), - ), - cardsim.StatFunc( - "Gadgetry Income", - k.ProductivityFunc(k.TrueGadgetryIncome()), - ), - cardsim.StatFunc( - "Fishing Income", - k.ProductivityFunc(k.TrueFishingIncome()), - ), - cardsim.StatFunc( - "Construction Income", - k.ProductivityFunc(k.TrueConstructionIncome()), - ), - cardsim.StatFunc( - "Propaganda Expense", - k.ProductivityFunc(k.TruePropagandaExpense()), - ), - cardsim.StatFunc( - "Bureaucracy Expense", - k.ProductivityFunc(k.TrueBureaucracyExpense()), - ), - cardsim.StatFunc( - "War Expense", - k.ProductivityFunc(k.TrueWarExpense()), - ), - cardsim.StatFunc( - "QoL Expense", - k.ProductivityFunc(k.TrueQoLExpense()), - ), - cardsim.StatFunc( - "Logistics Expense", - k.ProductivityFunc(k.TrueLogisticsExpense()), - ), - cardsim.StatFunc( - "Dragon Subsidies", - k.ProductivityFunc(k.TrueDragonSubsExpense()), - ), - cardsim.StatFunc( - "Research Subsidies", - k.ProductivityFunc(k.TrueResearchSubsExpense()), - ), - cardsim.StatFunc( - "Education Expense", - k.ProductivityFunc(k.TrueEducationExpense()), - ), - cardsim.StatFunc( - "Healthcare Expense", - k.ProductivityFunc(k.TrueHealthcareExpense()), - ), - cardsim.StatFunc( - "Foreign Relations Expense", - k.ProductivityFunc(k.TrueForeignRelExpense()), - ), - cardsim.StatFunc( - "Law Enforcement Expense", - k.ProductivityFunc(k.TruePoliceExpense()), - ), - cardsim.StatFunc( - "Economic Planning Expense", - k.ProductivityFunc(k.TrueEconPlanExpense()), - ), - cardsim.StatFunc( - "Parks and Aesthetics Expense", - k.ProductivityFunc(k.TrueParksExpense()), - ), - cardsim.StatFunc( - "Faith Expense", - k.ProductivityFunc(k.TrueFaithExpense()), - ), - cardsim.StatFunc( - "Total Sector Income", - k.TotalSectorIncome, - ), - cardsim.StatFunc( - "Total Government Expense", - k.TotalGovExpense, - ), cardsim.StatFunc( "Foreign Relations", k.DisplayedForeignRelations, @@ -430,14 +123,6 @@ func (k *KoboldMine) Stats() []cardsim.Stat { "Kobolds", k.Kobolds, ), - cardsim.InvisibleStatFunc( - "Squalor Reduction", - k.SqualorReduction, - ), - cardsim.StatFunc( - "Squalor", - k.StatSqualor, - ), } stats = append(stats, funcs...) // cardsim.SortStats(stats) @@ -446,46 +131,42 @@ func (k *KoboldMine) Stats() []cardsim.Stat { func NewKoboldMine() *KoboldMine { return &KoboldMine{ - BasePopulation: 1025, - MiningIncome: 0.15, - ScavengingIncome: 0.1, - AlchemyIncome: 0.01, - HospitalityIncome: 0.0, - AgricultureIncome: 0.0, - ManufacturingIncome: 0.10, - PlanarIncome: 0.00, - PublishingIncome: 0.02, - ForestryIncome: 0.0, - FinanceIncome: 0.02, - GadgetryIncome: 0.03, - FishingIncome: 0.0, - ConstructionIncome: 0.05, - PropagandaExpense: 0.01, - BureaucracyExpense: 0.05, - WarExpense: 0.1, - QoLExpense: 0.01, - LogisticsExpense: 0.02, - DragonSubsExpense: 0.0, - ResearchSubsExpense: 0.0, - EducationExpense: 0.01, - HealthcareExpense: 0.01, - ForeignRelExpense: 0.0, - PoliceExpense: 0.03, - EconPlanExpense: 0.02, - ParksExpense: 0.0, - FaithExpense: 0.03, - FoodSupply: 0.20, - ForeignRelations: -0.40, - HiddenRelPenalty: -0.01, - Rebellion: 0.10, - Madness: 0.25, - Cruelty: 0.45, - Greed: 0.35, - Gullibility: 0.10, - Authoritarianism: 0.70, - Secrecy: .95, - PointOfDimReturns: 1.0, + BasePopulation: 1025, + Mining: 0, + Scavenging: 0, + Alchemy: 0, + Hospitality: 0, + Agriculture: 0, + Manufacturing: 0, + PlanarConnections: 0, + Publishing: 0, + Forestry: 0, + Finance: 0, + Gadgetry: 0, + Fishing: 0, + Construction: 0, + Propaganda: 0, + Bureaucracy: 0, + Militarism: 0, + Welfare: 0, + Logistics: 0, + DragonSubs: 0, + ResearchSubs: 0, + Education: 0, + Healthcare: 0, + ForeignRelExpense: 0, + Police: 0, + ParksExpense: 0, + Faith: 0, + FoodSupply: 20, + ForeignRelations: 0, + HiddenRelPenalty: 0, + Rebellion: 0, + Madness: 0, + Cruelty: 0, + Greed: 0, + Gullibility: 0, + Authoritarianism: 0, + Secrecy: 95, } } - -// Note that the mine initializes with 1.02 points of overall productivity. diff --git a/koboldsim/util.go b/koboldsim/util.go index b6cbd42..7251fb3 100644 --- a/koboldsim/util.go +++ b/koboldsim/util.go @@ -1,6 +1,10 @@ package koboldsim -import "math" +import ( + "math" + + "golang.org/x/exp/constraints" +) // Generic helper functions not directly attached to Card Sim Engine mechanics. @@ -20,3 +24,13 @@ func Mean(vals ...float64) float64 { } return total / float64(len(vals)) } + +func clamp[T constraints.Ordered](val, low, high T) T { + if val < low { + return low + } + if val > high { + return high + } + return val +}