Compare commits
16 Commits
refactor-p
...
a4692712cc
Author | SHA1 | Date | |
---|---|---|---|
a4692712cc | |||
2ca8f3ed13 | |||
0a39cc76d6
|
|||
de7092cf4b
|
|||
d434e50897
|
|||
95a30cb522 | |||
f3fd0c582f | |||
de98cf8fb3 | |||
d66e17a279 | |||
caa5c2e60c | |||
159508b202 | |||
301d8ae161
|
|||
e0dad09045 | |||
ccd141ddc5 | |||
d2f89f5bd4 | |||
f7bed6c4b9
|
4
go.mod
4
go.mod
@ -2,4 +2,6 @@ module git.chromaticdragon.app/kistaro/KoboldSim
|
|||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require git.chromaticdragon.app/kistaro/CardSimEngine v0.1.3
|
require git.chromaticdragon.app/kistaro/CardSimEngine v0.3.0
|
||||||
|
|
||||||
|
require golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
|
||||||
|
8
go.sum
8
go.sum
@ -1,4 +1,4 @@
|
|||||||
git.chromaticdragon.app/kistaro/CardSimEngine v0.1.2 h1:+8KVFhSxXbQO7CPzmL89sJ+qjgU4J42Z5OinGuDMR0U=
|
git.chromaticdragon.app/kistaro/CardSimEngine v0.3.0 h1:PYpW6+XTod3oJHf7JyfCpHMCdGILrvyqSJPH/71FMRQ=
|
||||||
git.chromaticdragon.app/kistaro/CardSimEngine v0.1.2/go.mod h1:VFaOagdbtM6gH87ioHent8v76nDh9PddpymMqWdrLfI=
|
git.chromaticdragon.app/kistaro/CardSimEngine v0.3.0/go.mod h1:FYuoJHaK7lDI8Fwf4lZY2Y+8P9zavT4oLvSFUG6drw4=
|
||||||
git.chromaticdragon.app/kistaro/CardSimEngine v0.1.3 h1:rNaDDXnPVoMpavpx4vR9k30sl0nO0BnCe32nZbAF2IM=
|
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
|
||||||
git.chromaticdragon.app/kistaro/CardSimEngine v0.1.3/go.mod h1:VFaOagdbtM6gH87ioHent8v76nDh9PddpymMqWdrLfI=
|
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
|
@ -1,26 +1,32 @@
|
|||||||
package koboldsim
|
package koboldsim
|
||||||
|
|
||||||
import "git.chromaticdragon.app/kistaro/CardSimEngine/cardsim"
|
import (
|
||||||
|
"git.chromaticdragon.app/kistaro/CardSimEngine/cardsim"
|
||||||
|
)
|
||||||
|
|
||||||
var cards = []Card{
|
var cards = []Card{
|
||||||
&SwitchingCard{
|
&SwitchingCard{ // Card 1
|
||||||
Name: cardsim.MsgStr("Warborn"),
|
Name: cardsim.MsgStr("Warborn"),
|
||||||
Desc: cardsim.MsgStr(" A surge of anti-kobold sentiment has been reported by your spies on the surface and your military is concerned that anti-kobold vigilantes will attack the tunnels in the near-future. They want to build up now while doing so is safe."),
|
Desc: cardsim.MsgStr(" A surge of anti-kobold sentiment has been reported by your spies on the surface and your military is concerned that anti-kobold vigilantes will attack the tunnels in the near-future. They want to build up now while doing so is safe."),
|
||||||
After: ShuffleIntoBottomHalf,
|
After: ShuffleIntoBottomHalf,
|
||||||
Policies: []Policy{
|
Policies: []Policy{
|
||||||
&BasicPolicy{
|
&BasicPolicy{
|
||||||
UnenactedDesc: cardsim.MsgStr(`Your war chief paces irritably. "We have terrible threats facing us. It's imperative that we build up our numbers in both the military and the domestic sense. I want creches under military control and a good hunting crew to supply them with food."`),
|
UnenactedDesc: cardsim.MsgStr(`Your war chief paces irritably. "We have terrible threats facing us. It's imperative that we build up our numbers in both the military and the domestic sense. I want creches under military control and a good hunting crew to supply them with food."`),
|
||||||
EnactedDesc: cardsim.MsgStr("[current policy] Your war chief is presntly monitoring the situation, building up your military, and securing your creches."),
|
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) {
|
Do: func(p *Player) (cardsim.Message, error) {
|
||||||
p.Stats.Kobolds.Value += 100
|
p.Stats.BasePopulation += 100
|
||||||
p.Stats.SectorScavengingIncome.Value += 0.01
|
p.Stats.ScavengingIncome += 0.01
|
||||||
p.Stats.GovWarExpense.Value += 0.02
|
p.Stats.WarExpense += 0.02
|
||||||
|
p.Stats.FoodSupply += 0.01
|
||||||
|
p.Stats.ForeignRelations -= 0.02
|
||||||
return cardsim.MsgStr("Kobolds are known to be born warriors."), nil
|
return cardsim.MsgStr("Kobolds are known to be born warriors."), nil
|
||||||
},
|
},
|
||||||
Undo: func(p *Player) error {
|
Undo: func(p *Player) error {
|
||||||
p.Stats.Kobolds.Value -= 100
|
p.Stats.BasePopulation -= 100
|
||||||
p.Stats.SectorScavengingIncome.Value -= 0.01
|
p.Stats.ScavengingIncome -= 0.01
|
||||||
p.Stats.GovWarExpense.Value -= 0.02
|
p.Stats.WarExpense -= 0.02
|
||||||
|
p.Stats.FoodSupply -= 0.01
|
||||||
|
p.Stats.ForeignRelations += 0.02
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
CanDo: YesWeCan,
|
CanDo: YesWeCan,
|
||||||
@ -29,15 +35,21 @@ var cards = []Card{
|
|||||||
UnenactedDesc: cardsim.MsgStr(`Your head miner considers the matter worriedly. "Creches under military control? No. That would invite chaos. We need to dig deeper; we can have a peaceful, orderly society if we just get far enough away from surfacers."`),
|
UnenactedDesc: cardsim.MsgStr(`Your head miner considers the matter worriedly. "Creches under military control? No. That would invite chaos. We need to dig deeper; we can have a peaceful, orderly society if we just get far enough away from surfacers."`),
|
||||||
EnactedDesc: cardsim.MsgStr("[current policy] Your head miner is presently leading a project to dig as far away from the surface as possible."),
|
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) {
|
Do: func(p *Player) (cardsim.Message, error) {
|
||||||
p.Stats.Kobolds.Value += 40
|
p.Stats.BasePopulation += 40
|
||||||
p.Stats.SectorMiningIncome.Value += 0.02
|
p.Stats.MiningIncome += 0.02
|
||||||
p.Stats.GovBureaucracyExpense.Value += 0.01
|
p.Stats.BureaucracyExpense += 0.01
|
||||||
|
p.Stats.FoodSupply -= 0.01
|
||||||
|
p.Stats.Secrecy += 0.02
|
||||||
|
p.Stats.PointOfDimReturns += 0.01
|
||||||
return cardsim.MsgStr("Kobolds are known to be cowards hiding in the dark."), nil
|
return cardsim.MsgStr("Kobolds are known to be cowards hiding in the dark."), nil
|
||||||
},
|
},
|
||||||
Undo: func(p *Player) error {
|
Undo: func(p *Player) error {
|
||||||
p.Stats.Kobolds.Value -= 40
|
p.Stats.BasePopulation -= 40
|
||||||
p.Stats.SectorMiningIncome.Value -= 0.02
|
p.Stats.MiningIncome -= 0.02
|
||||||
p.Stats.GovBureaucracyExpense.Value -= 0.01
|
p.Stats.BureaucracyExpense -= 0.01
|
||||||
|
p.Stats.FoodSupply += 0.01
|
||||||
|
p.Stats.Secrecy -= 0.02
|
||||||
|
p.Stats.PointOfDimReturns += 0.01
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
CanDo: YesWeCan,
|
CanDo: YesWeCan,
|
||||||
@ -46,15 +58,23 @@ var cards = []Card{
|
|||||||
UnenactedDesc: cardsim.MsgStr(`Your nursery director is incensed. "Creches under military control? Never! Let young kobolds play! In fact, cut the military just for suggesting this. The threats facing us are completely overstated."`),
|
UnenactedDesc: cardsim.MsgStr(`Your nursery director is incensed. "Creches under military control? Never! Let young kobolds play! In fact, cut the military just for suggesting this. The threats facing us are completely overstated."`),
|
||||||
EnactedDesc: cardsim.MsgStr("[current policy] Military funding has been diverted into early childhood education."),
|
EnactedDesc: cardsim.MsgStr("[current policy] Military funding has been diverted into early childhood education."),
|
||||||
Do: func(p *Player) (cardsim.Message, error) {
|
Do: func(p *Player) (cardsim.Message, error) {
|
||||||
p.Stats.Kobolds.Value -= 40
|
p.Stats.BasePopulation -= 40
|
||||||
p.Stats.SectorScavengingIncome.Value -= 0.01
|
p.Stats.ScavengingIncome -= 0.01
|
||||||
p.Stats.GovWarExpense.Value -= 0.02
|
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
|
||||||
return cardsim.MsgStr("An undefended hunting outpost near the surface was recently wiped out by a raid."), nil
|
return cardsim.MsgStr("An undefended hunting outpost near the surface was recently wiped out by a raid."), nil
|
||||||
},
|
},
|
||||||
Undo: func(p *Player) error {
|
Undo: func(p *Player) error {
|
||||||
p.Stats.Kobolds.Value += 40
|
p.Stats.BasePopulation += 40
|
||||||
p.Stats.SectorScavengingIncome.Value += 0.01
|
p.Stats.ScavengingIncome += 0.01
|
||||||
p.Stats.GovWarExpense.Value += 0.02
|
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
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
CanDo: YesWeCan,
|
CanDo: YesWeCan,
|
||||||
@ -63,11 +83,11 @@ var cards = []Card{
|
|||||||
Default: &BasicPolicy{
|
Default: &BasicPolicy{
|
||||||
UnenactedDesc: cardsim.MsgStr("This isn't about a disaster and can probably be safely dismissed."),
|
UnenactedDesc: cardsim.MsgStr("This isn't about a disaster and can probably be safely dismissed."),
|
||||||
Do: func(p *Player) (cardsim.Message, error) {
|
Do: func(p *Player) (cardsim.Message, error) {
|
||||||
p.Stats.Kobolds.Value += 20
|
p.Stats.BasePopulation += 20
|
||||||
return cardsim.MsgStr("Creche control doesn't shift that easily."), nil
|
return cardsim.MsgStr("Creche control doesn't shift that easily."), nil
|
||||||
},
|
},
|
||||||
Undo: func(p *Player) error {
|
Undo: func(p *Player) error {
|
||||||
p.Stats.Kobolds.Value -= 20
|
p.Stats.BasePopulation -= 20
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
CanDo: YesWeCan,
|
CanDo: YesWeCan,
|
||||||
@ -91,9 +111,9 @@ var cards = []Card{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, // end of "Warborn" policies
|
}, // End of "Warborn" policies
|
||||||
}, // end of "Warborn" card
|
}, // End of "Warborn" card
|
||||||
&SwitchingCard{
|
&SwitchingCard{ // Card 2
|
||||||
Name: cardsim.MsgStr("International Festival of Bureaucracy"),
|
Name: cardsim.MsgStr("International Festival of Bureaucracy"),
|
||||||
Desc: cardsim.MsgStr(" Good times are upon us! A great festival has been declared between many kobold nations, celebrating the orderly nature of the kobold soul! That's right, it's the International Festival of Bureaucracy!"),
|
Desc: cardsim.MsgStr(" Good times are upon us! A great festival has been declared between many kobold nations, celebrating the orderly nature of the kobold soul! That's right, it's the International Festival of Bureaucracy!"),
|
||||||
After: ShuffleIntoBottomHalf,
|
After: ShuffleIntoBottomHalf,
|
||||||
@ -102,62 +122,77 @@ var cards = []Card{
|
|||||||
UnenactedDesc: cardsim.MsgStr(`Your Minister of Administration is practically jumping for joy. "This is our opportunity to prove that we're a prosperous society and attract some fresh blood! We need to raise salaries in the bureaucracy, grant some time off to our bureaucrats to make sure they can attend, and try to hire talent away from other nations. We'll forge productive trade relations by this, you'll see!`),
|
UnenactedDesc: cardsim.MsgStr(`Your Minister of Administration is practically jumping for joy. "This is our opportunity to prove that we're a prosperous society and attract some fresh blood! We need to raise salaries in the bureaucracy, grant some time off to our bureaucrats to make sure they can attend, and try to hire talent away from other nations. We'll forge productive trade relations by this, you'll see!`),
|
||||||
EnactedDesc: cardsim.MsgStr("[current policy] Your bureaucrats are really looking forward to attending, where they can boast about how the festival boosted their salaries."),
|
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) {
|
Do: func(p *Player) (cardsim.Message, error) {
|
||||||
p.Stats.Kobolds.Value += 80
|
p.Stats.BasePopulation += 80
|
||||||
p.Stats.SectorScavengingIncome.Value += 0.01
|
p.Stats.ScavengingIncome += 0.01
|
||||||
p.Stats.GovBureaucracyExpense.Value += 0.03
|
p.Stats.BureaucracyExpense += 0.03
|
||||||
|
p.Stats.FoodSupply += 0.01
|
||||||
|
p.Stats.PointOfDimReturns += 0.01
|
||||||
return cardsim.MsgStr("Bureaucrats are considered pillars of society."), nil
|
return cardsim.MsgStr("Bureaucrats are considered pillars of society."), nil
|
||||||
},
|
},
|
||||||
Undo: func(p *Player) error {
|
Undo: func(p *Player) error {
|
||||||
p.Stats.Kobolds.Value -= 80
|
p.Stats.BasePopulation -= 80
|
||||||
p.Stats.SectorScavengingIncome.Value -= 0.01
|
p.Stats.ScavengingIncome -= 0.01
|
||||||
p.Stats.GovBureaucracyExpense.Value -= 0.03
|
p.Stats.BureaucracyExpense -= 0.03
|
||||||
|
p.Stats.FoodSupply -= 0.01
|
||||||
|
p.Stats.PointOfDimReturns -= 0.01
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
CanDo: YesWeCan,
|
CanDo: YesWeCan,
|
||||||
},
|
},
|
||||||
&BasicPolicy{
|
&BasicPolicy{
|
||||||
UnenactedDesc: cardsim.MsgStr(`Your Minister of Education pulls you aside. "This is a good opportunity to head-hunt. Bureaucrats from many nations will be attending. There's got to be some who'll work for less than the ones we've got. There are some terribly threatened or impoverished communities among kobolds.`),
|
UnenactedDesc: cardsim.MsgStr(`Your Minister of Finance pulls you aside. "This is a good opportunity to head-hunt. Bureaucrats from many nations will be attending. There's got to be some who'll work for less than the ones we've got. There are some terribly threatened or impoverished communities among kobolds.`),
|
||||||
EnactedDesc: cardsim.MsgStr("Your nation's policy is clear: the festival of bureaucracy is a chance to trade workers with other kobold nations in your attempts to build a more efficient government."),
|
EnactedDesc: cardsim.MsgStr("[current policy] Your nation's policy is clear: the festival of bureaucracy is a chance to trade workers with other kobold nations in your attempts to build a more efficient government."),
|
||||||
Do: func(p *Player) (cardsim.Message, error) {
|
Do: func(p *Player) (cardsim.Message, error) {
|
||||||
p.Stats.Kobolds.Value += 20
|
p.Stats.BasePopulation += 20
|
||||||
p.Stats.GovBureaucracyExpense.Value -= 0.01
|
p.Stats.BureaucracyExpense -= 0.01
|
||||||
return cardsim.MsgStr("Immigrant bureaucrats complain about being under-appreciated."), nil
|
return cardsim.MsgStr("Immigrant bureaucrats complain about being under-appreciated."), nil
|
||||||
},
|
},
|
||||||
Undo: func(p *Player) error {
|
Undo: func(p *Player) error {
|
||||||
p.Stats.Kobolds.Value -= 20
|
p.Stats.BasePopulation -= 20
|
||||||
p.Stats.GovBureaucracyExpense.Value += 0.01
|
p.Stats.BureaucracyExpense += 0.01
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
CanDo: YesWeCan,
|
CanDo: func(b *BasicPolicy, p *Player) bool {
|
||||||
// TODO(rakeela): This option should only be available if the fourth option isn't the status quo and bureaucratic expense is at least 0.02.
|
return p.Stats.BureaucracyExpense >= 0.02 && 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{
|
&BasicPolicy{
|
||||||
UnenactedDesc: cardsim.MsgStr(`Your Minister of Education greets you exuberantly. "We've trimmed the wages of the bureaucracy beautifully, but maybe we can scout some outright volunteers. Some nations are absolute disasters, after all.`),
|
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 nations are absolute disasters, after all.`),
|
||||||
EnactedDesc: cardsim.MsgStr(`It's hard to find volunteer workers at the festival, but your nation is doing its best to equip the bureaucracy with exactly that.`),
|
EnactedDesc: cardsim.MsgStr(`[current policy] It's hard to find volunteer workers at the festival, but your nation is doing its best.`),
|
||||||
Do: func(p *Player) (cardsim.Message, error) {
|
Do: func(p *Player) (cardsim.Message, error) {
|
||||||
p.Stats.Kobolds.Value += 10
|
p.Stats.BasePopulation += 10
|
||||||
p.Stats.GovBureaucracyExpense.Value -= 0.02
|
p.Stats.BureaucracyExpense -= 0.02
|
||||||
|
p.Stats.PointOfDimReturns -= 0.01
|
||||||
return cardsim.MsgStr("The local bureaucracy is staffed by volunteer labor."), nil
|
return cardsim.MsgStr("The local bureaucracy is staffed by volunteer labor."), nil
|
||||||
},
|
},
|
||||||
Undo: func(p *Player) error {
|
Undo: func(p *Player) error {
|
||||||
p.Stats.Kobolds.Value -= 10
|
p.Stats.BasePopulation -= 10
|
||||||
p.Stats.GovBureaucracyExpense.Value += 0.02
|
p.Stats.BureaucracyExpense += 0.02
|
||||||
|
p.Stats.PointOfDimReturns += 0.01
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
CanDo: YesWeCan,
|
CanDo: func(b *BasicPolicy, p *Player) bool {
|
||||||
// TODO(rakeela): This option should only be available if the fourth option isn't the status quo and bureaucratic expense is less than 0.02.
|
return p.Stats.BureaucracyExpense < 0.02 && b.LastEnactedIdx != 3
|
||||||
|
},
|
||||||
},
|
},
|
||||||
&BasicPolicy{
|
&BasicPolicy{
|
||||||
UnenactedDesc: cardsim.MsgStr(`One of your non-bureaucrat friends has been in a foul temper. "This festival is everything that's wrong with our society. Life isn't about filling out forms. We ought to snub this festival and outright fire some bureaucrats."`),
|
UnenactedDesc: cardsim.MsgStr(`One of your non-bureaucrat friends has been in a foul temper. "This festival is everything that's wrong with our society. Life isn't about filling out forms. We ought to snub this festival and outright fire some bureaucrats."`),
|
||||||
EnactedDesc: cardsim.MsgStr("Your nation currently bans the International Festival of Bureaucracy. There will be no local celebration without a policy change."),
|
EnactedDesc: cardsim.MsgStr("Your nation currently bans the International Festival of Bureaucracy. There will be no local celebration without a policy change."),
|
||||||
Do: func(p *Player) (cardsim.Message, error) {
|
Do: func(p *Player) (cardsim.Message, error) {
|
||||||
p.Stats.Kobolds.Value -= 80
|
p.Stats.BasePopulation -= 80
|
||||||
p.Stats.GovBureaucracyExpense.Value -= 0.04
|
p.Stats.BureaucracyExpense -= 0.04
|
||||||
return cardsim.MsgStr("A wave of bureaucrats just emigrated along with kobolds incensed by the nation's lack of respect for administration."), nil
|
return cardsim.MsgStr("A wave of bureaucrats just emigrated along with kobolds incensed by the nation's lack of respect for administration."), nil
|
||||||
},
|
},
|
||||||
Undo: func(p *Player) error {
|
Undo: func(p *Player) error {
|
||||||
p.Stats.Kobolds.Value += 80
|
p.Stats.BasePopulation += 80
|
||||||
p.Stats.GovBureaucracyExpense.Value += 0.04
|
p.Stats.BureaucracyExpense += 0.04
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
CanDo: YesWeCan,
|
CanDo: YesWeCan,
|
||||||
@ -167,28 +202,28 @@ var cards = []Card{
|
|||||||
UnenactedDesc: cardsim.MsgStr("Some kobolds will attend the festival on their own, and we may face desertions for our lack of interest."),
|
UnenactedDesc: cardsim.MsgStr("Some kobolds will attend the festival on their own, and we may face desertions for our lack of interest."),
|
||||||
EnactedDesc: cardsim.MsgStr("Some kobolds will attend the festival on their own. No change is expected."),
|
EnactedDesc: cardsim.MsgStr("Some kobolds will attend the festival on their own. No change is expected."),
|
||||||
Do: func(p *Player) (cardsim.Message, error) {
|
Do: func(p *Player) (cardsim.Message, error) {
|
||||||
p.Stats.Kobolds.Value -= 20
|
p.Stats.BasePopulation -= 20
|
||||||
return cardsim.MsgStr("A festival of bureaucracy lured away a few kobolds to other nations."), nil
|
return cardsim.MsgStr("A festival of bureaucracy lured away a few kobolds to other nations."), nil
|
||||||
},
|
},
|
||||||
Undo: func(p *Player) error {
|
Undo: func(p *Player) error {
|
||||||
p.Stats.Kobolds.Value += 20
|
p.Stats.BasePopulation += 20
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
CanDo: YesWeCan,
|
CanDo: YesWeCan,
|
||||||
},
|
},
|
||||||
Variants: []Policy{
|
Variants: []Policy{
|
||||||
nil,
|
nil,
|
||||||
&DisabledPolicy{cardsim.MsgStr("TODO(rakeela): write")},
|
&DisabledPolicy{cardsim.MsgStr("Neglecting the event would be less popular and more inefficient than our current policy.")},
|
||||||
&DisabledPolicy{cardsim.MsgStr("TODO(rakeela): write")},
|
&DisabledPolicy{cardsim.MsgStr("Neglecting the event would be less popular and more inefficient than our current policy.")},
|
||||||
&FuncPolicy{
|
&FuncPolicy{
|
||||||
OptionTextFunc: func(p *Player) (cardsim.Message, error) {
|
OptionTextFunc: func(p *Player) (cardsim.Message, error) {
|
||||||
if p.Stats.GovBureaucracyExpense.Value >= -0.03 {
|
if p.Stats.BureaucracyExpense >= -0.03 {
|
||||||
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, but we'll be expected to rebuild our bureaucracy."), nil
|
||||||
}
|
}
|
||||||
return cardsim.MsgStr("Permitting the festival will yield some immigration."), nil
|
return cardsim.MsgStr("Permitting the festival will yield some immigration."), nil
|
||||||
},
|
},
|
||||||
EnactFunc: func(p *Player) (cardsim.Message, error) {
|
EnactFunc: func(p *Player) (cardsim.Message, error) {
|
||||||
if p.Stats.GovBureaucracyExpense.Value >= -0.03 {
|
if p.Stats.BureaucracyExpense >= -0.03 {
|
||||||
return cardsim.MsgStr("A festival of bureaucracy just saw the nation's bureaucracy rebuilt."), ErrKeepMessage
|
return cardsim.MsgStr("A festival of bureaucracy just saw the nation's bureaucracy rebuilt."), ErrKeepMessage
|
||||||
}
|
}
|
||||||
return cardsim.MsgStr("A festival of bureaucracy brought in immigrants wondering at the nation's lack thereof."), ErrKeepMessage
|
return cardsim.MsgStr("A festival of bureaucracy brought in immigrants wondering at the nation's lack thereof."), ErrKeepMessage
|
||||||
@ -197,8 +232,560 @@ var cards = []Card{
|
|||||||
nil, // remember, if using a BasicPolicy, this uses EnactedDesc
|
nil, // remember, if using a BasicPolicy, this uses EnactedDesc
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
}, // End of "International Festival of Bureaucracy" policies
|
||||||
|
}, // End of "International Festival of Bureaucracy" card
|
||||||
|
&SwitchingCard{ // Card 3
|
||||||
|
Name: cardsim.MsgStr("Two Kobolds in a Trenchcoat"),
|
||||||
|
Desc: cardsim.MsgStr("An inevitable concern of kobold nations is the matter of trade. Trade with other kobolds is straightforward, but overland caravans are unsafe. Trade with surfacers is vital... but achieving it is difficult owing to anti-kobold sentiment."),
|
||||||
|
After: ShuffleIntoBottomHalf,
|
||||||
|
Policies: []Policy{
|
||||||
|
&BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr(`Speaking for the conservative opinion, one prospective merchant says, "We need illusions. Our priority needs to be trading to acquire or studying to create artifacts of disguising. The high start-up costs are balanced by free movement in a surface society that frequently detests us."`),
|
||||||
|
EnactedDesc: cardsim.MsgStr("[current policy] Your merchants rely on magical disguises to avoid being caught 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
|
||||||
|
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
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CanDo: YesWeCan,
|
||||||
|
},
|
||||||
|
&BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr(`Brave but potentially headstrong, another prospective merchant has attended the meeting with the following plea. "We must take head-on the risk of improving the status of kobolds! Only by bravely attending the surface markets can we hope to achieve a true and lasting peace!"`),
|
||||||
|
EnactedDesc: cardsim.MsgStr(`[current policy] Your merchants represent the nation'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.PointOfDimReturns += 0.1
|
||||||
|
return cardsim.MsgStr("The economic planning office 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.PointOfDimReturns += 0.1
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CanDo: YesWeCan,
|
||||||
|
},
|
||||||
|
&BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr(`A shady scavenger has offered to change careers and become a merchant. Their proposal is, "Surfacers aren't all monolithic. If we talk to the ones who are more greedy than prejudiced, we don't need magic or idealism. We can get in contact with lots of people who'll buy lots of things from us, even things that might not have a market if we worked with 'good' merchants."`),
|
||||||
|
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
|
||||||
|
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
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CanDo: YesWeCan,
|
||||||
|
},
|
||||||
|
&BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr(`A mining forebold registered for the right to attend this meeting, and says, "Surfacers can't be trusted in business and we shouldn't be supporting them with trade. We need underground roads to ease trade with other kobold nations. It's a massive investment in smoothing current tunnels and digging new ones, 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 nations 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
|
||||||
|
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
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CanDo: YesWeCan,
|
||||||
|
},
|
||||||
|
&VerbosePolicy{
|
||||||
|
Default: &BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr("Refusing an opportunity to expand trade is merely an opportunity cost. It won't make conditions any better or worse."),
|
||||||
|
EnactedDesc: cardsim.MsgStr("Refusing an opportunity to expand trade is merely an opportunity cost. It won't make conditions any better or worse."),
|
||||||
|
Do: func(p *Player) (cardsim.Message, error) {
|
||||||
|
return cardsim.MsgStr("Kobold merchants are known for their talent at standing on each others' shoulders."), nil
|
||||||
|
},
|
||||||
|
Undo: func(p *Player) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CanDo: YesWeCan,
|
||||||
|
},
|
||||||
|
Variants: []Policy{
|
||||||
|
&BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr("Rejecting the illusion-based merchantry will cost us a great deal in trade and surface food, but we can certainly do it."),
|
||||||
|
},
|
||||||
|
&BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr("Rejecting our idealistic approach to surface trade will save a few lives directly, but consider how much we need the food we buy from the surfacers."),
|
||||||
|
},
|
||||||
|
&BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr("The surfacers' governments would certainly prefer us to abandon our underworld connections in their cities. Do we care about that? We'd be giving up some excellent profits."),
|
||||||
|
},
|
||||||
|
&BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr("Some of the miners would be heartbroken by abandoning the dream of underground roads, but the tax savings would be significant. Reducing our trade focus on other kobolds does however carry some risk."),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
}, // End of "Two Kobolds in a Trenchcoat" policies
|
||||||
|
}, // End of "Two Kobolds in a Trenchcoat" card
|
||||||
|
&SwitchingCard{ // Card 4
|
||||||
|
Name: cardsim.MsgStr("A Recipe for Stewed Kobold"),
|
||||||
|
Desc: cardsim.MsgStr("Nobody would ever say a kobold smells bad, but the constant burrowing, manufacturing, and metallurgy that is common to kobold life is still a recipe for tense muscles and filthy scales. A slate of proposals have come through to address this issue."),
|
||||||
|
After: ShuffleIntoBottomHalf,
|
||||||
|
Policies: []Policy{
|
||||||
|
&BasicPolicy{
|
||||||
|
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 nation."),
|
||||||
|
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
|
||||||
|
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
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CanDo: YesWeCan,
|
||||||
|
},
|
||||||
|
&BasicPolicy{
|
||||||
|
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
|
||||||
|
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
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CanDo: YesWeCan,
|
||||||
|
},
|
||||||
|
&BasicPolicy{
|
||||||
|
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 nation 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
|
||||||
|
return cardsim.MsgStr("Surfacers stink less in the nation'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
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CanDo: YesWeCan,
|
||||||
|
},
|
||||||
|
&VerbosePolicy{
|
||||||
|
Default: &BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr("Bathhouses are a big project that would displace other things in the city center. You could use the space for housing and services instead."),
|
||||||
|
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
|
||||||
|
return cardsim.MsgStr("Opponents of public bathing won a recent political contest."), nil
|
||||||
|
},
|
||||||
|
Undo: func(p *Player) error {
|
||||||
|
p.Stats.BasePopulation -= 5
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CanDo: YesWeCan,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, // End of "A Recipe for Stewed Kobold" policies
|
||||||
|
}, // End of "A Recipe for Stewed Kobold" card
|
||||||
|
&SwitchingCard{ // Card 5
|
||||||
|
Name: cardsim.MsgStr("Raid on Surface Proposed"),
|
||||||
|
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
|
||||||
|
},
|
||||||
|
Policies: []Policy{
|
||||||
|
&BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr(`A scavenger clad in sturdy clothes says, "We need to secure a section of forests on the surface. The surfacers strip too many of the forests bare; if we dispatch patrols ready to fight we can keep them from assarting the forest for their endlessly growing farm communities."`),
|
||||||
|
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
|
||||||
|
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
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CanDo: YesWeCan,
|
||||||
|
},
|
||||||
|
&BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr(`A very scrawny warrior waves a spear over their head and says, "That's not going far enough! We need to seize farmland of our own! Come on, we shouldn't have to eat cave lichen while the surfacers eat roast pig!"`),
|
||||||
|
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.Secrecy -= 0.20
|
||||||
|
p.Stats.ForeignRelations -= 0.04
|
||||||
|
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.Secrecy += 0.20
|
||||||
|
p.Stats.ForeignRelations += 0.04
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CanDo: YesWeCan,
|
||||||
|
},
|
||||||
|
&BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr(`A warrior kobold of quiet intensity says, "We shouldn't try to hold terrain on the surface. We'll give ourselves away to surfacers who otherwise can't pinpoint us. Remember that we can't stand against their armies. What we can do without violating secrecy is focus on banditry. We've mapped activity in our area; it's time to go in for a harvest of our own."`),
|
||||||
|
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
|
||||||
|
// TODO: Subtract a compassion point.
|
||||||
|
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
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CanDo: YesWeCan,
|
||||||
|
},
|
||||||
|
&VerbosePolicy{
|
||||||
|
Default: &BasicPolicy{
|
||||||
|
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
|
||||||
|
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
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CanDo: YesWeCan,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, // End of "Raid on Surface Proposed" policies
|
||||||
|
}, // End of "Raid on Surface Proposed" card
|
||||||
|
&SwitchingCard{ // Card 6
|
||||||
|
Name: cardsim.MsgStr("Book Publishing Threat to Secrecy?"),
|
||||||
|
Desc: cardsim.MsgStr("Concerns have been raised about one of the categories of income that your nation 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 nation."),
|
||||||
|
After: ShuffleIntoBottomHalf,
|
||||||
|
IsValid: func(c Card, p *Player) bool {
|
||||||
|
return p.Stats.PublishingIncome > 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] What published material emanates from your nation is strictly censored of details that could indicate the locations of your homes."),
|
||||||
|
Do: func(p *Player) (cardsim.Message, error) {
|
||||||
|
p.Stats.Secrecy += 0.03
|
||||||
|
p.Stats.PublishingIncome -= 0.05
|
||||||
|
p.Stats.EducationExpense -= 0.01
|
||||||
|
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
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CanDo: YesWeCan,
|
||||||
|
},
|
||||||
|
&BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr(`"No! Never! Er, maybe?" asks a dithering scribe, clutching a book worriedly to their chest. "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
|
||||||
|
return cardsim.MsgStr("The censors are the most well-read kobolds in the nation."), nil
|
||||||
|
},
|
||||||
|
Undo: func(p *Player) error {
|
||||||
|
p.Stats.Secrecy -= 0.01
|
||||||
|
p.Stats.PublishingIncome += 0.01
|
||||||
|
p.Stats.BureaucracyExpense -= 0.01
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CanDo: YesWeCan,
|
||||||
|
},
|
||||||
|
&BasicPolicy{
|
||||||
|
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
|
||||||
|
// TODO: Add a compassion point
|
||||||
|
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
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CanDo: func(b *BasicPolicy, p *Player) bool {
|
||||||
|
return p.Stats.Secrecy < 1
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&VerbosePolicy{
|
||||||
|
Default: &BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr("Rejecting this will inspire celebrations among the authors of your nation."),
|
||||||
|
EnactedDesc: cardsim.MsgStr("The dangers posed by publishing information from your nation can be denied without changes in policy."),
|
||||||
|
Do: func(p *Player) (cardsim.Message, error) {
|
||||||
|
p.Stats.PublishingIncome += 0.01
|
||||||
|
return cardsim.MsgStr("Kobold-hating adventurers know to be well-read."), nil
|
||||||
|
},
|
||||||
|
Undo: func(p *Player) error {
|
||||||
|
p.Stats.PublishingIncome -= 0.01
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CanDo: YesWeCan,
|
||||||
|
},
|
||||||
|
Variants: []Policy{
|
||||||
|
&BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr("Your publishing industry is struggling under harsh restrictions and would soar if relieved."),
|
||||||
|
},
|
||||||
|
&BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr("The censorship office spends tax money to crimp tax income. It has opponents, though they stay quiet."),
|
||||||
|
},
|
||||||
|
&BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr("Rescinding the diary initiative would ding literacy rates, but it should save on taxes."),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, // End of "Book Publishing Threat to Security?" policies"
|
||||||
|
}, // End of "Book Publishing Threat to Security?" card
|
||||||
|
&SwitchingCard{ // Card 7
|
||||||
|
Name: cardsim.MsgStr("The Eyes that Break the Ground"),
|
||||||
|
Desc: cardsim.MsgStr("Some of our miners have recently been afflicted with shared hallucinations. They report feelings of being watched in dark areas - which, thanks to kobold darkvision, is nearly everywhere - as well as seeing black blotches covered in eyes on various surfaces. They're refusing to continue work."),
|
||||||
|
After: ShuffleIntoBottomHalf,
|
||||||
|
Policies: []Policy{
|
||||||
|
&BasicPolicy{
|
||||||
|
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.02
|
||||||
|
// Note to self, add a compassion point.
|
||||||
|
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.02
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CanDo: YesWeCan,
|
||||||
|
},
|
||||||
|
&BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr(`Your chief economic planner regards the report with surprise and disgust. "This is just an excuse for slacking! Send them back into the mines and hire guards to make sure they double production! And if they see any more eyes in the dark, tell 'em to shatter 'em with detonation charges!"`),
|
||||||
|
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
|
||||||
|
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
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CanDo: YesWeCan,
|
||||||
|
},
|
||||||
|
&BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr(`The head of your Ministry of Soothsaying, Shamanism, and Otherworldly Affairs has reacted with unsettling enthusiasm. "Shared hallucinations? Of eyes in the dark? This is an opportunity! We just need to train the afflicted into new spellcasters. We could learn the kind of secrets that shatter minds!"`),
|
||||||
|
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
|
||||||
|
// TODO: Add more consequences to questionable planar contacts.
|
||||||
|
return cardsim.MsgStr("The nation 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
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CanDo: YesWeCan,
|
||||||
|
},
|
||||||
|
&BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr(`The manager of one of the affected signed the right papers to get into this meeting. Regarding you coldly, they say, "They're slacking, and it's not right to permit anything to change. We don't soothing alchemicals, we don't need new guards, adding explosives to the mix is just chaotic, and recruiting crazies as spellcasters is insane itself. Trust in the miners to enforce discipline amongst each other. We don't permit anyone to hold up production."`),
|
||||||
|
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
|
||||||
|
// TODO: Take away a point of compassion for this.
|
||||||
|
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
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CanDo: YesWeCan,
|
||||||
|
},
|
||||||
|
&VerbosePolicy{
|
||||||
|
Default: &BasicPolicy{
|
||||||
|
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
|
||||||
|
// TODO: Add a compassion point.
|
||||||
|
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
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CanDo: YesWeCan,
|
||||||
|
},
|
||||||
|
Variants: []Policy{
|
||||||
|
&BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr("Rescinding the healthcare subsidy might ding our population, but it shouldn't ding our compassion. We're being very gentle with these people."),
|
||||||
|
},
|
||||||
|
&BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr("Withdrawing explosives access from the suffering may be a life-saving action..."),
|
||||||
|
},
|
||||||
|
&BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr("Maybe we should close the door on this particular otherworldly contact source."),
|
||||||
|
},
|
||||||
|
&BasicPolicy{
|
||||||
|
UnenactedDesc: cardsim.MsgStr("The miners' culture is harsh! There's an argument here for protecting the suffering."),
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, // end of "The Eyes that Break the Ground" policies
|
||||||
|
}, // end of "The Eyes that Break the Ground" card
|
||||||
} // end of card list
|
} // end of card list
|
||||||
|
|
||||||
func initDeck(d *cardsim.Deck[*KoboldMine]) {
|
func initDeck(d *cardsim.Deck[*KoboldMine]) {
|
||||||
|
@ -42,12 +42,62 @@ type Policy interface {
|
|||||||
// previously, and undoes it before doing a different one. It is always valid
|
// previously, and undoes it before doing a different one. It is always valid
|
||||||
// to draw.
|
// to draw.
|
||||||
type SwitchingCard struct {
|
type SwitchingCard struct {
|
||||||
|
// Name contains the name of the card, displayed as its title in the
|
||||||
|
// action selection menu and in the card detail page itself.
|
||||||
Name cardsim.Message
|
Name cardsim.Message
|
||||||
|
|
||||||
|
// Desc contains the event description for the card, displayed in the
|
||||||
|
// card detail page.
|
||||||
Desc cardsim.Message
|
Desc cardsim.Message
|
||||||
|
|
||||||
|
// IsUrgent marks a card as urgent. If the player has any urgent cards
|
||||||
|
// in hand, they cannot act on any non-urgent cards (or permanent actions
|
||||||
|
// not marked as urgent).
|
||||||
IsUrgent bool
|
IsUrgent bool
|
||||||
|
|
||||||
|
// After is invoked after the player has chosen to act on this card and
|
||||||
|
// the chosen option has been fully enacted. If the card should be returned
|
||||||
|
// to the deck, After is responsible for doing this!
|
||||||
|
//
|
||||||
|
// If After is not provided, ShuffleIntoBottomHalf is used as a fallback.
|
||||||
|
//
|
||||||
|
// The first argument to After is the card itself. This will be type
|
||||||
|
// *SwitchingCard. It's represented as Card so general "After" functions
|
||||||
|
// that can be used with multiple card types (for example,
|
||||||
|
// ShuffleIntoBottomHalf) can be trivially implemented.
|
||||||
|
//
|
||||||
|
// If the card cannot be drawn into the hand because it has an IsValid
|
||||||
|
// check and the check fails, After is invoked with a nil CardOption.
|
||||||
|
// ShuffleIntoBottomHalf works just fine with a nil argument here.
|
||||||
|
// (It doesn't care about the CardOption at all.)
|
||||||
After func(Card, *Player, CardOption) error
|
After func(Card, *Player, CardOption) error
|
||||||
|
|
||||||
|
// IsValid is used to check whether the card can be drawn into the hand.
|
||||||
|
// If it cannot, After is immediately invoked (whenever the card was
|
||||||
|
// being drawn, which is probably the "draw" stage of the turn but can
|
||||||
|
// happen any time if something else causes the player to draw cards) with
|
||||||
|
// a nil CardOption (because no option was selected) and the SwitchingCard
|
||||||
|
// does not invoke any option and does not change its active policy.
|
||||||
|
//
|
||||||
|
// The first argument to IsValid is the card itself. This will be type
|
||||||
|
// *SwitchingCard. It's presented via the Card interface to support
|
||||||
|
// general validity functions that could be used with arbitrary kinds of Card.
|
||||||
|
IsValid func(Card, *Player) bool
|
||||||
|
|
||||||
|
// Policies contains the options the player may choose between. Policy is
|
||||||
|
// a more specific type than CardOption; a Policy can be un-enacted.
|
||||||
|
// Unenactment of the previous policy before selecting a new one is the
|
||||||
|
// core feature of SwitchingCard.
|
||||||
Policies []Policy
|
Policies []Policy
|
||||||
|
|
||||||
|
// lastPolicy stores the last policy selected for this card. It's used
|
||||||
|
// extensively by SwitchingCard's logic.
|
||||||
lastPolicy Policy
|
lastPolicy Policy
|
||||||
|
|
||||||
|
// ShowUnavailable controls whether options for which Enabled() = false
|
||||||
|
// should be presented to the player at all. Indexes for "last enacted"
|
||||||
|
// still refer to the original list, not the shortened one.
|
||||||
|
ShowUnavailable bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Title implements Card.
|
// Title implements Card.
|
||||||
@ -61,7 +111,12 @@ func (s *SwitchingCard) Urgent(*Player) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Drawn implements Card.
|
// Drawn implements Card.
|
||||||
func (s *SwitchingCard) Drawn(*Player) bool {
|
func (s *SwitchingCard) Drawn(p *Player) bool {
|
||||||
|
if s.IsValid != nil && !s.IsValid(s, p) {
|
||||||
|
err := s.Then(p, nil)
|
||||||
|
p.ReportError(err) // can't do anything with the error right now
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +126,7 @@ func (s *SwitchingCard) EventText(*Player) (cardsim.Message, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Options implements Card.
|
// Options implements Card.
|
||||||
func (s *SwitchingCard) Options(*Player) ([]CardOption, error) {
|
func (s *SwitchingCard) Options(player *Player) ([]CardOption, error) {
|
||||||
lastIdx := -1
|
lastIdx := -1
|
||||||
for i, p := range s.Policies {
|
for i, p := range s.Policies {
|
||||||
if p.Is(s.lastPolicy) {
|
if p.Is(s.lastPolicy) {
|
||||||
@ -79,18 +134,21 @@ func (s *SwitchingCard) Options(*Player) ([]CardOption, error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret := make([]CardOption, len(s.Policies))
|
ret := make([]CardOption, 0, len(s.Policies))
|
||||||
for i, p := range s.Policies {
|
for _, p := range s.Policies {
|
||||||
p.LastEnacted(lastIdx, s.lastPolicy)
|
p.LastEnacted(lastIdx, s.lastPolicy)
|
||||||
ret[i] = p
|
if s.ShowUnavailable || p.Enabled(player) {
|
||||||
|
ret = append(ret, p)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then implements Card.
|
// Then implements Card.
|
||||||
func (s *SwitchingCard) Then(p *Player, o CardOption) error {
|
func (s *SwitchingCard) Then(p *Player, o CardOption) error {
|
||||||
newPolicy := o.(Policy)
|
|
||||||
var errs cardsim.ErrorCollector
|
var errs cardsim.ErrorCollector
|
||||||
|
if o != nil {
|
||||||
|
newPolicy := o.(Policy)
|
||||||
if s.lastPolicy != nil && !newPolicy.Is(s.lastPolicy) {
|
if s.lastPolicy != nil && !newPolicy.Is(s.lastPolicy) {
|
||||||
err := s.lastPolicy.Unenact(p)
|
err := s.lastPolicy.Unenact(p)
|
||||||
if cardsim.IsSeriousError(err) {
|
if cardsim.IsSeriousError(err) {
|
||||||
@ -99,12 +157,22 @@ func (s *SwitchingCard) Then(p *Player, o CardOption) error {
|
|||||||
errs.Add(err)
|
errs.Add(err)
|
||||||
}
|
}
|
||||||
s.lastPolicy = o.(Policy)
|
s.lastPolicy = o.(Policy)
|
||||||
|
}
|
||||||
if s.After != nil {
|
if s.After != nil {
|
||||||
errs.Add(s.After(s, p, o))
|
errs.Add(s.After(s, p, o))
|
||||||
|
} else {
|
||||||
|
// Fallback: Shuffle the card back into the bottom half of the deck.
|
||||||
|
errs.Add(ShuffleIntoBottomHalf(s, p, o))
|
||||||
|
|
||||||
}
|
}
|
||||||
return errs.Emit()
|
return errs.Emit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CurrentlyEnacted returns the currently enacted Policy, if any.
|
||||||
|
func (s *SwitchingCard) CurrentlyEnacted() Policy {
|
||||||
|
return s.lastPolicy
|
||||||
|
}
|
||||||
|
|
||||||
// BasicPolicy is a straightfoward implementation of Policy. If the currently
|
// BasicPolicy is a straightfoward implementation of Policy. If the currently
|
||||||
// enacted option is re-enacted, it refunds the player's action point.
|
// enacted option is re-enacted, it refunds the player's action point.
|
||||||
type BasicPolicy struct {
|
type BasicPolicy struct {
|
||||||
@ -114,25 +182,29 @@ type BasicPolicy struct {
|
|||||||
NothingChanged cardsim.Message
|
NothingChanged cardsim.Message
|
||||||
Do func(*Player) (cardsim.Message, error)
|
Do func(*Player) (cardsim.Message, error)
|
||||||
Undo func(*Player) error
|
Undo func(*Player) error
|
||||||
CanDo func(*Player) bool
|
CanDo func(*BasicPolicy, *Player) bool
|
||||||
|
|
||||||
currentlyEnacted bool
|
CurrentlyEnacted bool
|
||||||
|
LastEnactedPolicy Policy
|
||||||
|
LastEnactedIdx int
|
||||||
}
|
}
|
||||||
|
|
||||||
// YesWeCan returns true. It's the default value for BasicPolicy.CanDo / BasicPolicy.CanUndo.
|
// YesWeCan returns true. It's the default value for BasicPolicy.CanDo / BasicPolicy.CanUndo.
|
||||||
func YesWeCan(*Player) bool {
|
func YesWeCan(*BasicPolicy, *Player) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// LastEnacted notifies b about the last-enacted policy in its group. It updates
|
// LastEnacted notifies b about the last-enacted policy in its group. It updates
|
||||||
// b.currentlyEnacted accordingly.
|
// b.currentlyEnacted accordingly.
|
||||||
func (b *BasicPolicy) LastEnacted(_ int, p Policy) {
|
func (b *BasicPolicy) LastEnacted(i int, p Policy) {
|
||||||
b.currentlyEnacted = b.Is(p)
|
b.LastEnactedPolicy = p
|
||||||
|
b.LastEnactedIdx = i
|
||||||
|
b.CurrentlyEnacted = b.Is(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OptionText implements CardOption.
|
// OptionText implements CardOption.
|
||||||
func (b *BasicPolicy) OptionText(*Player) (cardsim.Message, error) {
|
func (b *BasicPolicy) OptionText(*Player) (cardsim.Message, error) {
|
||||||
if b.currentlyEnacted {
|
if b.CurrentlyEnacted {
|
||||||
if b.EnactedDesc == nil {
|
if b.EnactedDesc == nil {
|
||||||
return nil, ErrUnimplemented
|
return nil, ErrUnimplemented
|
||||||
}
|
}
|
||||||
@ -149,22 +221,19 @@ func (b *BasicPolicy) Enact(p *Player) (cardsim.Message, error) {
|
|||||||
if b.Do == nil {
|
if b.Do == nil {
|
||||||
return nil, ErrUnimplemented
|
return nil, ErrUnimplemented
|
||||||
}
|
}
|
||||||
if b.currentlyEnacted {
|
if b.CurrentlyEnacted {
|
||||||
p.ActionsRemaining++
|
p.ActionsRemaining++
|
||||||
if b.NothingChanged == nil {
|
if b.NothingChanged == nil {
|
||||||
b.NothingChanged = cardsim.MsgStr("You continue your current approach.")
|
b.NothingChanged = cardsim.MsgStr("You continue your current approach.")
|
||||||
}
|
}
|
||||||
return b.NothingChanged, nil
|
return b.NothingChanged, nil
|
||||||
}
|
}
|
||||||
if b.Enabled(p) {
|
|
||||||
return b.Do(p)
|
return b.Do(p)
|
||||||
}
|
}
|
||||||
return nil, ErrOptionNotEnabled
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unenact implements Policy.
|
// Unenact implements Policy.
|
||||||
func (b *BasicPolicy) Unenact(p *Player) error {
|
func (b *BasicPolicy) Unenact(p *Player) error {
|
||||||
if !b.currentlyEnacted {
|
if !b.CurrentlyEnacted {
|
||||||
return ErrPolicyNotEnacted
|
return ErrPolicyNotEnacted
|
||||||
}
|
}
|
||||||
if b.Undo == nil {
|
if b.Undo == nil {
|
||||||
@ -175,13 +244,13 @@ func (b *BasicPolicy) Unenact(p *Player) error {
|
|||||||
|
|
||||||
// Enabled implements CardOption.
|
// Enabled implements CardOption.
|
||||||
func (b *BasicPolicy) Enabled(p *Player) bool {
|
func (b *BasicPolicy) Enabled(p *Player) bool {
|
||||||
if b.currentlyEnacted {
|
if b.CurrentlyEnacted {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if b.CanDo == nil {
|
if b.CanDo == nil {
|
||||||
panic(ErrUnimplemented)
|
panic(ErrUnimplemented)
|
||||||
}
|
}
|
||||||
return b.CanDo(p)
|
return b.CanDo(b, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BasicPolicy) Is(p Policy) bool {
|
func (b *BasicPolicy) Is(p Policy) bool {
|
||||||
@ -195,14 +264,14 @@ func (b *BasicPolicy) Is(p Policy) bool {
|
|||||||
// policy. Which policy is used is determined by what the previous policy for
|
// policy. Which policy is used is determined by what the previous policy for
|
||||||
// the card was (as reported via a call to LastEnacted):
|
// the card was (as reported via a call to LastEnacted):
|
||||||
//
|
//
|
||||||
// * If no policy has yet been enacted, use FirstTime.
|
// - If no policy has yet been enacted, use FirstTime.
|
||||||
// * If a policy has been enacted, use the Policy at the slot in Variants
|
// - If a policy has been enacted, use the Policy at the slot in Variants
|
||||||
// that corresponds to the slot (on the Card) of the currently-enacted policy.
|
// that corresponds to the slot (on the Card) of the currently-enacted policy.
|
||||||
// * If the policy retrieved in this way returns ErrUnimplemented, throw away
|
// - If the policy retrieved in this way returns ErrUnimplemented, throw away
|
||||||
// its response and use Default instead. For Enabled, which does not have
|
// its response and use Default instead. For Enabled, which does not have
|
||||||
// an error component to its return value, look for ErrUnimplemented as the
|
// an error component to its return value, look for ErrUnimplemented as the
|
||||||
// argument to a Panic call, instead.
|
// argument to a Panic call, instead.
|
||||||
// * If the policy retrieved in this way returns ErrKeepMessage when Enact
|
// - If the policy retrieved in this way returns ErrKeepMessage when Enact
|
||||||
// is called, it calls Default for the side effects but ignores its message,
|
// is called, it calls Default for the side effects but ignores its message,
|
||||||
// retaining the message from the original call. This is to avoid having to
|
// retaining the message from the original call. This is to avoid having to
|
||||||
// repeat the same Enact function except with different text each time.
|
// repeat the same Enact function except with different text each time.
|
||||||
@ -257,7 +326,7 @@ func (v *VerbosePolicy) fillDefaults() {
|
|||||||
for len(v.Variants) <= v.lastIdx {
|
for len(v.Variants) <= v.lastIdx {
|
||||||
v.Variants = append(v.Variants, v.Default)
|
v.Variants = append(v.Variants, v.Default)
|
||||||
}
|
}
|
||||||
if v.lastIdx > 0 && v.Variants[v.lastIdx] == nil {
|
if v.lastIdx >= 0 && v.Variants[v.lastIdx] == nil {
|
||||||
v.Variants[v.lastIdx] = v.Default
|
v.Variants[v.lastIdx] = v.Default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
package koboldsim
|
package koboldsim
|
||||||
|
|
||||||
import "git.chromaticdragon.app/kistaro/CardSimEngine/cardsim"
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.chromaticdragon.app/kistaro/CardSimEngine/cardsim"
|
||||||
|
)
|
||||||
|
|
||||||
func InitPlayer() *Player {
|
func InitPlayer() *Player {
|
||||||
p := cardsim.InitPlayer(NewKoboldMine())
|
p := cardsim.InitPlayer(NewKoboldMine())
|
||||||
@ -13,6 +17,16 @@ func InitPlayer() *Player {
|
|||||||
Name: cardsim.MsgStr("All Stats"),
|
Name: cardsim.MsgStr("All Stats"),
|
||||||
Intro: cardsim.MsgStr("All available statistics."),
|
Intro: cardsim.MsgStr("All available statistics."),
|
||||||
},
|
},
|
||||||
|
&cardsim.BasicStatsPanel[*KoboldMine]{
|
||||||
|
Name: cardsim.MsgStr("Per Capita Economic"),
|
||||||
|
Intro: cardsim.MsgStr("Yield and Investment per Capita"),
|
||||||
|
Filter: cardsim.All(
|
||||||
|
cardsim.VisibleOrDebug[*KoboldMine],
|
||||||
|
func(p *Player, s cardsim.Stat) bool {
|
||||||
|
return strings.Contains(s.StatName(), "Productivity") || strings.Contains(s.StatName(), "Investment")
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
p.Prompt = &cardsim.BasicStatsPanel[*KoboldMine]{
|
p.Prompt = &cardsim.BasicStatsPanel[*KoboldMine]{
|
||||||
Name: cardsim.MsgStr("The Kobold Mine"),
|
Name: cardsim.MsgStr("The Kobold Mine"),
|
||||||
@ -21,6 +35,7 @@ func InitPlayer() *Player {
|
|||||||
"Kobolds",
|
"Kobolds",
|
||||||
"Total Sector Income",
|
"Total Sector Income",
|
||||||
"Total Government Expense",
|
"Total Government Expense",
|
||||||
|
"Tax Rate",
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
p.State = cardsim.GameActive
|
p.State = cardsim.GameActive
|
||||||
|
@ -1,52 +1,356 @@
|
|||||||
package koboldsim
|
package koboldsim
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
|
|
||||||
"git.chromaticdragon.app/kistaro/CardSimEngine/cardsim"
|
"git.chromaticdragon.app/kistaro/CardSimEngine/cardsim"
|
||||||
)
|
)
|
||||||
|
|
||||||
// KoboldMine is the state of a kobold mine.
|
// KoboldMine is the state of a kobold mine.
|
||||||
type KoboldMine struct {
|
type KoboldMine struct {
|
||||||
Kobolds cardsim.Stored[int64]
|
BasePopulation float64 `cardsim:"stathidden"`
|
||||||
|
|
||||||
SectorMiningIncome cardsim.Stored[float64]
|
MiningIncome float64 `cardsim:"stathidden" cardsim_name:"Mining Productivity"`
|
||||||
SectorScavengingIncome cardsim.Stored[float64]
|
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"`
|
||||||
|
|
||||||
GovBureaucracyExpense cardsim.Stored[float64]
|
PropagandaExpense float64 `cardsim:"stathidden" cardsim_name:"Propaganda Investment"`
|
||||||
GovWarExpense cardsim.Stored[float64]
|
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"`
|
||||||
|
|
||||||
|
FoodSupply float64 `cardsim:"stathidden"`
|
||||||
|
ForeignRelations float64 `cardsim:"stathidden"`
|
||||||
|
HiddenRelPenalty float64 `cardsim:"stathidden"` // Lower is better.
|
||||||
|
Secrecy float64 `cardsim:"stathidden"`
|
||||||
|
PointOfDimReturns float64 `cardsim:"stathidden"`
|
||||||
|
|
||||||
|
// AnotherExpense float64 `cardsim:"hiddenround5"`
|
||||||
|
// A different way of adding stats that is slightly more empowering.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *KoboldMine) ProductivityFunc(s *cardsim.Stored[float64]) func() float64 {
|
func (k *KoboldMine) ProductivityFunc(s float64) func() float64 {
|
||||||
return func() float64 {
|
return func() float64 {
|
||||||
return s.Value * float64(k.Kobolds.Value)
|
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 {
|
func (k *KoboldMine) TotalSectorIncome() float64 {
|
||||||
return float64(k.Kobolds.Value) * (k.SectorMiningIncome.Value + k.SectorScavengingIncome.Value)
|
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 {
|
func (k *KoboldMine) TotalGovExpense() float64 {
|
||||||
return float64(k.Kobolds.Value) * (k.GovBureaucracyExpense.Value + k.GovWarExpense.Value)
|
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) TotalEconomicPotential() float64 {
|
||||||
|
// return (math.Max(k.MiningIncome, 0.01) + math.Max(k.ScavengingIncome, 0) + math.Max(k.AlchemyIncome, 0) + math.Max(k.HospitalityIncome, 0) + math.Max(k.AgricultureIncome, 0) + math.Max(k.ManufacturingIncome, 0) + math.Max(k.PlanarIncome, 0) + math.Max(k.PublishingIncome, 0) + math.Max(k.FinanceIncome, 0) + math.Max(k.GadgetryIncome, 0) + math.Max(k.FishingIncome, 0) + math.Max(k.ConstructionIncome, 0.02) + math.Max(k.PropagandaExpense, 0) + math.Max(k.BureaucracyExpense, 0) + math.Max(k.WarExpense, 0) + math.Max(k.QoLExpense, 0) + math.Max(k.LogisticsExpense, 0) + math.Max(k.DragonSubsExpense, 0) + math.Max(k.ResearchSubsExpense, 0) + math.Max(k.EducationExpense, 0) + math.Max(k.HealthcareExpense, 0) + math.Max(k.ForeignRelExpense, 0) + math.Max(k.PoliceExpense, 0) + math.Max(k.EconPlanExpense, 0) + math.Max(k.ParksExpense, 0) + math.Max(k.FaithExpense, 0)) * 0.8
|
||||||
|
//} // This function is intended to enable "crowding out" effects at a later point. Each +1 increase in overall productivity increases TEP by 0.8, losing 1/5th of the gain to crowding, which should manifest as everything else declining. Now, uh... How do I scale investments by the TEP stat? With a simple multiplication, a 0.01 increase to mining would be a 0.08 increase to everything else. Damn it, I thought I had something.
|
||||||
|
|
||||||
|
func (k *KoboldMine) Taxation() float64 {
|
||||||
|
return (k.TotalGovExpense() / (k.TotalSectorIncome() + k.TotalGovExpense())) * 100
|
||||||
|
}
|
||||||
|
|
||||||
|
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()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) DisplayedFoodSupply() float64 {
|
||||||
|
return (k.FoodSupply - 1) * 100
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) StatObesity() float64 {
|
||||||
|
return 100 / (2.3 + math.Exp(-0.04*(k.DisplayedFoodSupply()-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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) DisplayedForeignRelations() float64 {
|
||||||
|
return math.Max(math.Min((k.ForeignRelations*100), 100), -100)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) DisplayedSecrecy() float64 {
|
||||||
|
return k.Secrecy * 100
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *KoboldMine) Stats() []cardsim.Stat {
|
func (k *KoboldMine) Stats() []cardsim.Stat {
|
||||||
stats := cardsim.ExtractStats(k)
|
stats := cardsim.ExtractStats(k)
|
||||||
funcs := []cardsim.Stat{
|
funcs := []cardsim.Stat{
|
||||||
cardsim.StatFunc(
|
cardsim.StatFunc(
|
||||||
"Total Sector Mining Income",
|
"Mining Income",
|
||||||
k.ProductivityFunc(&k.SectorMiningIncome),
|
k.ProductivityFunc(k.TrueMiningIncome()),
|
||||||
),
|
),
|
||||||
cardsim.StatFunc(
|
cardsim.StatFunc(
|
||||||
"Total Sector Scavenging Income",
|
"Scavenging Income",
|
||||||
k.ProductivityFunc(&k.SectorScavengingIncome),
|
k.ProductivityFunc(k.TrueScavengingIncome()),
|
||||||
),
|
),
|
||||||
cardsim.StatFunc(
|
cardsim.StatFunc(
|
||||||
"Total Government Bureaucracy Expense",
|
"Alchemy Income",
|
||||||
k.ProductivityFunc(&k.GovBureaucracyExpense),
|
k.ProductivityFunc(k.TrueAlchemyIncome()),
|
||||||
),
|
),
|
||||||
cardsim.StatFunc(
|
cardsim.StatFunc(
|
||||||
"Total Government War Expense",
|
"Hospitality Income",
|
||||||
k.ProductivityFunc(&k.GovWarExpense),
|
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(
|
cardsim.StatFunc(
|
||||||
"Total Sector Income",
|
"Total Sector Income",
|
||||||
@ -56,33 +360,64 @@ func (k *KoboldMine) Stats() []cardsim.Stat {
|
|||||||
"Total Government Expense",
|
"Total Government Expense",
|
||||||
k.TotalGovExpense,
|
k.TotalGovExpense,
|
||||||
),
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Foreign Relations",
|
||||||
|
k.DisplayedForeignRelations,
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Secrecy",
|
||||||
|
k.DisplayedSecrecy,
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Food Supply",
|
||||||
|
k.DisplayedFoodSupply,
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Kobolds",
|
||||||
|
k.Kobolds,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
stats = append(stats, funcs...)
|
stats = append(stats, funcs...)
|
||||||
cardsim.SortStats(stats)
|
// cardsim.SortStats(stats)
|
||||||
return stats
|
return stats
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewKoboldMine() *KoboldMine {
|
func NewKoboldMine() *KoboldMine {
|
||||||
return &KoboldMine{
|
return &KoboldMine{
|
||||||
Kobolds: cardsim.Stored[int64]{
|
BasePopulation: 1030,
|
||||||
Name: "Kobolds",
|
MiningIncome: 0.15,
|
||||||
Value: 1000,
|
ScavengingIncome: 0.1,
|
||||||
},
|
AlchemyIncome: 0.01,
|
||||||
SectorMiningIncome: cardsim.Stored[float64]{
|
HospitalityIncome: 0.0,
|
||||||
Name: "Sector Mining Income",
|
AgricultureIncome: 0.0,
|
||||||
Value: 0.15,
|
ManufacturingIncome: 0.10,
|
||||||
},
|
PlanarIncome: 0.00,
|
||||||
SectorScavengingIncome: cardsim.Stored[float64]{
|
PublishingIncome: 0.02,
|
||||||
Name: "Sector Scavenging Income",
|
ForestryIncome: 0.0,
|
||||||
Value: 0.1,
|
FinanceIncome: 0.02,
|
||||||
},
|
GadgetryIncome: 0.03,
|
||||||
GovBureaucracyExpense: cardsim.Stored[float64]{
|
FishingIncome: 0.0,
|
||||||
Name: "Government Bureaucracy Expense",
|
ConstructionIncome: 0.05,
|
||||||
Value: 0.05,
|
PropagandaExpense: 0.01,
|
||||||
},
|
BureaucracyExpense: 0.05,
|
||||||
GovWarExpense: cardsim.Stored[float64]{
|
WarExpense: 0.1,
|
||||||
Name: "Government War Expense",
|
QoLExpense: 0.01,
|
||||||
Value: 0.1,
|
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,
|
||||||
|
Secrecy: .95,
|
||||||
|
PointOfDimReturns: 1.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note that the mine initializes with 1.02 points of overall productivity.
|
||||||
|
Reference in New Issue
Block a user