Empty homes draw settlers: growth follows housing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
0b7e4a722f
commit
8fbab37a56
3 changed files with 33 additions and 7 deletions
|
|
@ -35,7 +35,8 @@ Alpha. One season, bandit raids plus two friendly events, six buildings.
|
||||||
Construction also saws planks, which higher building levels need, and
|
Construction also saws planks, which higher building levels need, and
|
||||||
builds fittings from them: one-off upgrades a building takes once it's high
|
builds fittings from them: one-off upgrades a building takes once it's high
|
||||||
enough (shelving, a well pump, weapon racks, enchanted chests…). Population eats food,
|
enough (shelving, a well pump, weapon racks, enchanted chests…). Population eats food,
|
||||||
pays taxes, and grows up to the housing cap; it promotes the settlement
|
pays taxes, and fills the houses (empty homes draw settlers, about a
|
||||||
|
quarter of all homes a day, plus slow family growth); it promotes the settlement
|
||||||
(hamlet, village at 40, town at 90, city at 150), which raises building
|
(hamlet, village at 40, town at 90, city at 150), which raises building
|
||||||
level caps and gives everyone bank slots.
|
level caps and gives everyone bank slots.
|
||||||
- **Prayer:** enemies drop bones; burying them gives prayer points and
|
- **Prayer:** enemies drop bones; burying them gives prayer points and
|
||||||
|
|
|
||||||
17
game.py
17
game.py
|
|
@ -482,7 +482,10 @@ TIERS = [
|
||||||
{"name": "City", "pop": 150, "cap": 99, "houses": 8},
|
{"name": "City", "pop": 150, "cap": 99, "houses": 8},
|
||||||
]
|
]
|
||||||
TIER_SLOTS = 2
|
TIER_SLOTS = 2
|
||||||
GROWTH_PER_DAY = 0.12
|
# Growth: empty homes draw settlers (a share of all the homes, every day, while
|
||||||
|
# any stand empty), and families grow a little on their own.
|
||||||
|
SETTLERS_PER_HOME = 0.25
|
||||||
|
FAMILY_GROWTH = 0.05
|
||||||
|
|
||||||
RAID_GROUPS = ["the Red Hand", "the Hollow Crows", "the Blackwater gang", "the Ashen Wolves",
|
RAID_GROUPS = ["the Red Hand", "the Hollow Crows", "the Blackwater gang", "the Ashen Wolves",
|
||||||
"the Gallows Men", "the Thornback raiders"]
|
"the Gallows Men", "the Thornback raiders"]
|
||||||
|
|
@ -1431,6 +1434,13 @@ def _start_next(world: dict, now: float) -> None:
|
||||||
log(world, now, "build", f"{verb} the {BUILDINGS[key]['name'].lower()} began")
|
log(world, now, "build", f"{verb} the {BUILDINGS[key]['name'].lower()} began")
|
||||||
|
|
||||||
|
|
||||||
|
def growth_per_day(world: dict) -> float:
|
||||||
|
"""New townsfolk a day while there's room: settlers for the homes, plus families."""
|
||||||
|
pop = world["city"]["population"]
|
||||||
|
growth = SETTLERS_PER_HOME * housing(world) + FAMILY_GROWTH * pop
|
||||||
|
return growth * city_rate(world, "growth") * (1.25 if fitted(world, "furniture") else 1)
|
||||||
|
|
||||||
|
|
||||||
def population_view(world: dict) -> dict:
|
def population_view(world: dict) -> dict:
|
||||||
"""Population, food and tier, with the numbers that explain them."""
|
"""Population, food and tier, with the numbers that explain them."""
|
||||||
city = world["city"]
|
city = world["city"]
|
||||||
|
|
@ -1439,7 +1449,7 @@ def population_view(world: dict) -> dict:
|
||||||
if city["starving"]:
|
if city["starving"]:
|
||||||
growth = -pop * 0.04
|
growth = -pop * 0.04
|
||||||
elif pop < room - 0.01:
|
elif pop < room - 0.01:
|
||||||
growth = max(0.5, pop * GROWTH_PER_DAY)
|
growth = growth_per_day(world)
|
||||||
elif pop > room:
|
elif pop > room:
|
||||||
growth = -pop * 0.05
|
growth = -pop * 0.05
|
||||||
else:
|
else:
|
||||||
|
|
@ -1619,8 +1629,7 @@ def _city_hour(world: dict, now: float) -> None:
|
||||||
else:
|
else:
|
||||||
room = housing(world)
|
room = housing(world)
|
||||||
if pop < room:
|
if pop < room:
|
||||||
growth = max(0.5, pop * GROWTH_PER_DAY) * city_rate(world, "growth") * (1.25 if fitted(world, "furniture") else 1)
|
city["population"] = min(room, pop + growth_per_day(world) / 24)
|
||||||
city["population"] = min(room, pop + growth / 24)
|
|
||||||
elif pop > room:
|
elif pop > room:
|
||||||
city["population"] = max(room, pop - pop * 0.05 / 24)
|
city["population"] = max(room, pop - pop * 0.05 / 24)
|
||||||
city["treasury"] += pop * 0.5 / 24
|
city["treasury"] += pop * 0.5 / 24
|
||||||
|
|
|
||||||
|
|
@ -302,11 +302,26 @@ class CityTest(unittest.TestCase):
|
||||||
world = world_with()
|
world = world_with()
|
||||||
city = world["city"]
|
city = world["city"]
|
||||||
city["stockpile"] = {"bread": 5}
|
city["stockpile"] = {"bread": 5}
|
||||||
|
city["population"] = 25.0 # a full town, so nobody new arrives
|
||||||
world["next_fortune_at"] = None # no lucky wagon of bread
|
world["next_fortune_at"] = None # no lucky wagon of bread
|
||||||
game.tick(world, T0 + 4 * game.DAY, random.Random(1))
|
game.tick(world, T0 + 4 * game.DAY, random.Random(1))
|
||||||
self.assertTrue(city["starving"])
|
self.assertTrue(city["starving"])
|
||||||
self.assertNotIn("bread", city["stockpile"])
|
self.assertNotIn("bread", city["stockpile"])
|
||||||
self.assertLess(city["population"], 20)
|
self.assertLess(city["population"], 25)
|
||||||
|
|
||||||
|
def test_empty_homes_draw_settlers(self):
|
||||||
|
world = world_with()
|
||||||
|
city = world["city"]
|
||||||
|
city["population"] = 22.0
|
||||||
|
city["buildings"]["houses"]["level"] = 2
|
||||||
|
city["stockpile"] = {"bread": 10_000}
|
||||||
|
self.assertAlmostEqual(game.growth_per_day(world), 0.25 * 65 + 0.05 * 22)
|
||||||
|
for _hour in range(24):
|
||||||
|
game._city_hour(world, T0)
|
||||||
|
self.assertGreater(city["population"], 38)
|
||||||
|
for _hour in range(48):
|
||||||
|
game._city_hour(world, T0)
|
||||||
|
self.assertEqual(city["population"], 65)
|
||||||
|
|
||||||
def test_fed_city_grows_to_housing_and_pays_tax(self):
|
def test_fed_city_grows_to_housing_and_pays_tax(self):
|
||||||
world = world_with()
|
world = world_with()
|
||||||
|
|
@ -899,8 +914,9 @@ class ConstructionTest(unittest.TestCase):
|
||||||
|
|
||||||
def test_townsfolk_trickle_and_levels_help(self):
|
def test_townsfolk_trickle_and_levels_help(self):
|
||||||
world = self.site()
|
world = self.site()
|
||||||
|
world["city"]["population"] = 25.0 # houses full, so the workforce stays put
|
||||||
game.tick(world, T0 + 10 * game.HOUR, random.Random(1))
|
game.tick(world, T0 + 10 * game.HOUR, random.Random(1))
|
||||||
self.assertAlmostEqual(world["city"]["construction"]["labour"], 10 * 20 * game.TOWNSFOLK_LABOUR, delta=5)
|
self.assertAlmostEqual(world["city"]["construction"]["labour"], 10 * 25 * game.TOWNSFOLK_LABOUR, delta=5)
|
||||||
p = world["players"]["bob"]
|
p = world["players"]["bob"]
|
||||||
self.assertEqual(game.labour_per_action(p), 1)
|
self.assertEqual(game.labour_per_action(p), 1)
|
||||||
p["xp"]["construction"] = game.XP_TABLE[40]
|
p["xp"]["construction"] = game.XP_TABLE[40]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue