Add mobreport.py

This commit is contained in:
Andrew Miner
2025-10-06 22:44:46 -06:00
parent 4637137152
commit 3d6f9800ec
17 changed files with 179 additions and 44 deletions
+15 -3
View File
@@ -67,8 +67,20 @@ def span(*altitudes):
if not altitudes: return ANYWHERE if not altitudes: return ANYWHERE
result = Altitude("", ANYWHERE.top, ANYWHERE.bottom) result = Altitude("", ANYWHERE.top, ANYWHERE.bottom)
for altitude in altitudes: bottom = OVERGROUND[-1].top
result.bottom = min(result.bottom, altitude.bottom) bottomAltitude = None
result.top = max(result.top, altitude.top)
top = UNDERGROUND[-1].bottom
topAltitude = None
for altitude in altitudes:
if altitude.bottom < bottom:
bottom = altitude.bottom
bottomAltitude = altitude
if altitude.top > top:
top = altitude.top
topAltitude = altitude
result = Altitude(f"{bottomAltitude.name}-{topAltitude.name}", bottom, top)
return result return result
+1 -1
View File
@@ -13,6 +13,6 @@ def mergeRange():
# Tests ############################################################################################ # Tests ############################################################################################
def test_span(merged): def test_span(merged):
assert merged.name == "lowlands-uplands"
assert merged.bottom == A.LOWLANDS.bottom assert merged.bottom == A.LOWLANDS.bottom
assert merged.top == A.UPLANDS.top assert merged.top == A.UPLANDS.top
+24
View File
@@ -31,6 +31,30 @@ class BiomeFilter:
self.required = required or [] self.required = required or []
self.prohibited = prohibited or [] self.prohibited = prohibited or []
def __str__(self) -> str:
required = []
for trait in self.required:
if isinstance(trait, BiomeTrait):
required.append(str(trait))
else:
required.append("(" +
", ".join([str(option) for option in trait]) +
")")
prohibited = []
for trait in self.prohibited:
prohibited.append(str(trait))
result = "".join([
"must:[",
", ".join(required),
"], not:[",
", ".join(prohibited),
"]"
])
return result
def __repr__(self) -> str: def __repr__(self) -> str:
return f"BiomeFilter([{repr(self.required)}], [{repr(self.prohibited)}])" return f"BiomeFilter([{repr(self.required)}], [{repr(self.prohibited)}])"
+16 -6
View File
@@ -5,7 +5,7 @@ class Group:
How many individuals are commonly present when encountering a creature. How many individuals are commonly present when encountering a creature.
""" """
def __init__(self, name, smallest, largest): def __init__(self, name:str, smallest:int, largest:int):
self.name = name self.name = name
self.smallest = smallest self.smallest = smallest
self.largest = largest self.largest = largest
@@ -42,9 +42,19 @@ ALL = [SOLO, PAIR, FAMILY, TROUP, HERD]
def merge(*groups): def merge(*groups):
if not groups: return SOLO if not groups: return SOLO
result = Group(f"", HERD.largest, SOLO.smallest) smallest = ALL[-1].largest
for group in groups: smallestGroup = None
result.smallest = min(result.smallest, group.smallest)
result.largest = max(result.largest, group.largest)
return result largest = ALL[0].smallest
largestGroup = None
for group in groups:
if group.smallest < smallest:
smallest = group.smallest
smallestGroup = group
if group.largest > largest:
largest = group.largest
largestGroup = group
return Group(f"{smallestGroup.name}-{largestGroup.name}", smallest, largest)
+18 -8
View File
@@ -1,16 +1,26 @@
from tungston.core.fauna.mob import Mob from collections.abc import Iterator
from tungston.core.fauna.mobplacement import MobPlacement
# Class ############################################################################################ # Class ############################################################################################
class MobCatalog: class MobCatalog:
def __init__(self, mobs): def __init__(self, mobs:tuple[MobPlacement]):
self.mobs = mobs self._mobs = {}
def findByName(self, name): for mob in mobs:
for mob in self.mobs: self.add(mob)
if mob.name == name:
return mob
return None def add(self, placement:MobPlacement) -> "MobCatalog":
if placement.mob.name in self._mobs:
raise Exception("Catalog already has an entry for {mob.name}")
self._mobs[placement.mob.name] = placement
def all(self) -> Iterator[MobPlacement]:
for placement in self._mobs.values():
yield placement
def get(self, name:str) -> MobPlacement:
return self._mobs.get(name, None)
+1 -1
View File
@@ -30,7 +30,7 @@ def test_cowsInFields(placement):
"Placement{" + "Placement{" +
"habitats: [" + "habitats: [" +
"Habitat{" + "Habitat{" +
"altitude: [anywhere <-64 to 320>], " + "altitude: anywhere <-64 to 320>, " +
"biomeFilter: BiomeFilter([[Flora<field>, Heat<temperate>]], [[]]), " + "biomeFilter: BiomeFilter([[Flora<field>, Heat<temperate>]], [[]]), " +
"seasons: [Season<spring>, Season<summer>, Season<autumn>, Season<winter>], " + "seasons: [Season<spring>, Season<summer>, Season<autumn>, Season<winter>], " +
"group: solo<1 to 1>, " + "group: solo<1 to 1>, " +
@@ -44,7 +44,7 @@ def test_repr(bifIron):
assert repr(bifIron) == ( assert repr(bifIron) == (
"MetalDeposit<bifiron>{" + "MetalDeposit<bifiron>{" +
"scarcity: Scarcity<common>, " + "scarcity: Scarcity<common>, " +
"biomeFilter: BiomeFilter([[Flora<forest>]], [[]]), " + "biomeFilter: must:[Flora: forest], not:[], " +
"inclusions: [" + "inclusions: [" +
"Inclusion{mineral: iron, weight: 60}, " + "Inclusion{mineral: iron, weight: 60}, " +
"Inclusion{mineral: quartz, weight: 40}" + "Inclusion{mineral: quartz, weight: 40}" +
+8 -8
View File
@@ -22,7 +22,7 @@ class Habitat:
def __init__( def __init__(
self, self,
altitudes:tuple[Altitude]|Altitude=[A.ANYWHERE], altitude:Altitude=A.ANYWHERE,
biomeFilter:BiomeFilter=None, biomeFilter:BiomeFilter=None,
seasons:tuple[Season]=E.ALL, seasons:tuple[Season]=E.ALL,
@@ -30,7 +30,7 @@ class Habitat:
location:Location=L.OUTSIDE, location:Location=L.OUTSIDE,
scarcity:Scarcity=C.SPARSE, scarcity:Scarcity=C.SPARSE,
): ):
self.altitudes = (altitudes,) if isinstance(altitudes, Altitude) else altitudes self.altitude = altitude
self.biomeFilter = biomeFilter or BiomeFilter() self.biomeFilter = biomeFilter or BiomeFilter()
self.seasons = (seasons,) if isinstance(seasons, Season) else seasons self.seasons = (seasons,) if isinstance(seasons, Season) else seasons
@@ -41,19 +41,19 @@ class Habitat:
self._source = None self._source = None
def __str__(self) -> str: def __str__(self) -> str:
return "".join(str(p) for p in [ return "".join([str(p) for p in [
self.altitude, "|", ", ".join(self.altitude), "|",
self.biomeFilter, "|", self.biomeFilter, "|",
self.seasons, "|", ", ".join([str(s) for s in self.seasons]), "|",
self.group, "|", self.group, "|",
self.location, "|", self.location, "|",
self.scarcity self.scarcity
]) ]])
def __repr__(self) -> str: def __repr__(self) -> str:
return "".join([str(p) for p in [ return "".join([str(p) for p in [
"Habitat{", "Habitat{",
"altitude: [", ", ".join([repr(a) for a in self.altitudes]), "], ", "altitude: ", repr(self.altitude), ", ",
"biomeFilter: ", repr(self.biomeFilter), ", ", "biomeFilter: ", repr(self.biomeFilter), ", ",
"seasons: [", ", ".join([repr(s) for s in self.seasons]), "], ", "seasons: [", ", ".join([repr(s) for s in self.seasons]), "], ",
"group: ", repr(self.group), ", ", "group: ", repr(self.group), ", ",
@@ -64,7 +64,7 @@ class Habitat:
def derive(self, **kwargs): def derive(self, **kwargs):
kwargs = { kwargs = {
"altitudes": self.altitudes, "altitude": self.altitude,
"biomeFilter": self.biomeFilter, "biomeFilter": self.biomeFilter,
"seasons": self.seasons, "seasons": self.seasons,
"group": self.group, "group": self.group,
+1 -1
View File
@@ -35,7 +35,7 @@ def test_derive(derivedHabitats):
def test_repr(fields): def test_repr(fields):
assert repr(fields) == ( assert repr(fields) == (
"Habitat{" + "Habitat{" +
"altitude: [anywhere <-64 to 320>], " + "altitude: anywhere <-64 to 320>, " +
"biomeFilter: BiomeFilter([[Flora<field>, Heat<temperate>]], [[]]), " + "biomeFilter: BiomeFilter([[Flora<field>, Heat<temperate>]], [[]]), " +
"seasons: [Season<spring>, Season<summer>, Season<autumn>, Season<winter>], " + "seasons: [Season<spring>, Season<summer>, Season<autumn>, Season<winter>], " +
"group: solo<1 to 1>, " + "group: solo<1 to 1>, " +
+1 -1
View File
@@ -25,7 +25,7 @@ def test_repr(placement):
"Placement{" + "Placement{" +
"habitats: [" + "habitats: [" +
"Habitat{" + "Habitat{" +
"altitude: [anywhere <-64 to 320>], " + "altitude: anywhere <-64 to 320>, " +
"biomeFilter: BiomeFilter([[Flora<field>, Heat<temperate>]], [[]]), " + "biomeFilter: BiomeFilter([[Flora<field>, Heat<temperate>]], [[]]), " +
"seasons: [Season<spring>, Season<summer>, Season<autumn>, Season<winter>], " + "seasons: [Season<spring>, Season<summer>, Season<autumn>, Season<winter>], " +
"group: solo<1 to 1>, " + "group: solo<1 to 1>, " +
+49
View File
@@ -0,0 +1,49 @@
from tungston.core.world import World
from tungston.core.ecology.biometrait import BiomeTrait
from tungston.generator.markdown.report import Report
# Class ############################################################################################
class MobReport(Report):
def __init__(self, world:World):
super().__init__("mobs.md", world)
def build(self):
for placement in self.world.mobs.all():
self.line(f"# Mob: {placement.mob.name} <{placement.mob.gameId}>")
self.line()
self.indent()
for index, habitat in enumerate(placement.habitats, start=1):
self.text("* habitat ").line(index)
self.indent()
self.text("* altitude: ").line(habitat.altitude)
self.line("* biomeFilter:")
self.indent()
for trait in habitat.biomeFilter.required:
if isinstance(trait, BiomeTrait):
self.text("* ").line(trait)
else:
category = type(trait[0]).__name__
self.text("* ").\
text(category).\
text(": any of: ").\
line(", ".join([t.name for t in trait]))
for trait in habitat.biomeFilter.prohibited:
category = type(trait[0]).__name__
self.text("* ").text(category).text(": not: ").line(trait.name)
self.outdent()
self.text("* seasons: ").line(", ".join([s.name for s in habitat.seasons]))
self.text("* group: ").line(habitat.group.name)
self.text("* location: ").line(habitat.location.name)
self.text("* scarcity: ").line(habitat.scarcity.name)
self.outdent()
self.outdent()
self.line()
@@ -0,0 +1,7 @@
import tungston.modpack.mysteriousisland.biomecatalog
# Class ############################################################################################
def test_syntax():
pass
@@ -0,0 +1,7 @@
import tungston.modpack.mysteriousisland.depositcatalog
# Class ############################################################################################
def test_syntax():
pass
@@ -0,0 +1,7 @@
import tungston.modpack.mysteriousisland.mineralcatalog
# Class ############################################################################################
def test_syntax():
pass
+13 -13
View File
@@ -23,22 +23,22 @@ import tungston.core.season as SE
CATALOG = MobCatalog([ CATALOG = MobCatalog([
MobPlacement(Mob("armadillo", "minecraft:armadillo", AC.NOCTURNAL), MobPlacement(Mob("armadillo", "minecraft:armadillo", AC.NOCTURNAL),
Habitat( Habitat(
altitudes = AL.LOWLANDS, altitude = AL.LOWLANDS,
biomeFilter = BF([HE.SUBTROPICAL, HU.DRY]), biomeFilter = BF([HE.SUBTROPICAL, HU.DRY]),
seasons = SE.SUMMER, seasons = SE.SUMMER,
group = GR.merge(GR.SOLO, GR.PAIR), group = GR.merge(GR.SOLO, GR.PAIR),
scarcity = SC.COMMON scarcity = SC.COMMON
).derive( ).derive(
altitudes = AL.UPLANDS, scarcity = SC.SPARSE, altitude = AL.UPLANDS, scarcity = SC.SPARSE,
).derive( ).derive(
altitudes = AL.LOWLANDS, seasons = SE.excluding(SE.SUMMER), scarcity = SC.UNCOMMON, altitude = AL.LOWLANDS, seasons = SE.excluding(SE.SUMMER), scarcity = SC.UNCOMMON,
).derive( ).derive(
altitudes = AL.UPLANDS, scarcity = SC.UNUSUAL, altitude = AL.UPLANDS, scarcity = SC.UNUSUAL,
) )
), ),
MobPlacement(Mob("black_bear", "bearminimum:black_bear"), MobPlacement(Mob("black_bear", "bearminimum:black_bear"),
Habitat( Habitat(
altitudes = AL.span(AL.LOWLANDS, AL.ALPINE), altitude = AL.span(AL.LOWLANDS, AL.ALPINE),
biomeFilter = BF([HE.TEMPERATE, FL.FOREST]), biomeFilter = BF([HE.TEMPERATE, FL.FOREST]),
seasons = SE.AUTUMN, seasons = SE.AUTUMN,
group = GR.merge(GR.SOLO, GR.FAMILY), group = GR.merge(GR.SOLO, GR.FAMILY),
@@ -59,7 +59,7 @@ CATALOG = MobCatalog([
), ),
MobPlacement(Mob("blaze", "minecraft:blaze"), MobPlacement(Mob("blaze", "minecraft:blaze"),
Habitat( Habitat(
altitudes = AL.PLUTONIC, altitude = AL.PLUTONIC,
location = LO.CAVE, location = LO.CAVE,
group = GR.SOLO, group = GR.SOLO,
scarcity = SC.UNCOMMON scarcity = SC.UNCOMMON
@@ -68,7 +68,7 @@ CATALOG = MobCatalog([
MobPlacement(Mob("bogged", "minecraft:bogged")), MobPlacement(Mob("bogged", "minecraft:bogged")),
MobPlacement(Mob("brown_bear", "bearminimum:brown_bear"), MobPlacement(Mob("brown_bear", "bearminimum:brown_bear"),
Habitat( Habitat(
altitudes = AL.span(AL.LOWLANDS, AL.ALPINE), altitude = AL.span(AL.LOWLANDS, AL.ALPINE),
biomeFilter = BF([HE.BOREAL, (FL.CANOPY, FL.FOREST)]), biomeFilter = BF([HE.BOREAL, (FL.CANOPY, FL.FOREST)]),
seasons = SE.AUTUMN, seasons = SE.AUTUMN,
group = GR.SOLO, group = GR.SOLO,
@@ -82,19 +82,19 @@ CATALOG = MobCatalog([
MobPlacement(Mob("bulwark", "immersiveengineering:bulwark")), MobPlacement(Mob("bulwark", "immersiveengineering:bulwark")),
MobPlacement(Mob("camel", "minecraft:camel", AC.CREPUSCULAR), MobPlacement(Mob("camel", "minecraft:camel", AC.CREPUSCULAR),
Habitat( Habitat(
altitudes = AL.span(AL.DUNES, AL.LOWLANDS), altitude = AL.span(AL.DUNES, AL.LOWLANDS),
biomeFilter = BF([HE.TROPICAL, HU.DRY, FL.BARREN, SO.SANDY]), biomeFilter = BF([HE.TROPICAL, HU.DRY, FL.BARREN, SO.SANDY]),
seasons = SE.WET, seasons = SE.DRY,
group = GR.FAMILY, group = GR.FAMILY,
scarcity = SC.UNUSUAL, scarcity = SC.UNUSUAL,
).derive( ).derive(
seasons = SE.DRY, scarcity = SC.RARE seasons = SE.WET, scarcity = SC.RARE
) )
), ),
MobPlacement(Mob("cat", "minecraft:cat")), MobPlacement(Mob("cat", "minecraft:cat")),
MobPlacement(Mob("cave_spider", "minecraft:cave_spider"), MobPlacement(Mob("cave_spider", "minecraft:cave_spider"),
Habitat( Habitat(
altitudes = AL.span(AL.OVERBURDEN, AL.HILLS), altitude = AL.span(AL.OVERBURDEN, AL.HILLS),
location = LO.CAVE, location = LO.CAVE,
group = GR.SOLO, group = GR.SOLO,
scarcity = SC.UNCOMMON scarcity = SC.UNCOMMON
@@ -102,7 +102,7 @@ CATALOG = MobCatalog([
), ),
MobPlacement(Mob("chameleon", "cold_sweat:chameleon"), MobPlacement(Mob("chameleon", "cold_sweat:chameleon"),
Habitat( Habitat(
altitudes = AL.span(AL.DUNES, AL.UPLANDS), altitude = AL.span(AL.DUNES, AL.UPLANDS),
biomeFilter = BF([HE.TROPICAL, HU.WET, [FL.CANOPY, FL.FOREST]]), biomeFilter = BF([HE.TROPICAL, HU.WET, [FL.CANOPY, FL.FOREST]]),
seasons = SE.WET, seasons = SE.WET,
group = GR.merge(GR.SOLO, GR.FAMILY), group = GR.merge(GR.SOLO, GR.FAMILY),
@@ -119,7 +119,7 @@ CATALOG = MobCatalog([
), ),
MobPlacement(Mob("chicken", "minecraft:chicken"), MobPlacement(Mob("chicken", "minecraft:chicken"),
Habitat( Habitat(
altitudes = AL.span(AL.LOWLANDS, AL.UPLANDS), altitude = AL.span(AL.LOWLANDS, AL.UPLANDS),
biomeFilter = BF([HE.TROPICAL, HU.WET, FL.within(FL.CANOPY, FL.CLEARING)]), biomeFilter = BF([HE.TROPICAL, HU.WET, FL.within(FL.CANOPY, FL.CLEARING)]),
seasons = SE.SUMMER, seasons = SE.SUMMER,
group = GR.TROUP, group = GR.TROUP,
@@ -0,0 +1,7 @@
import tungston.modpack.mysteriousisland.mobcatalog
# Class ############################################################################################
def test_syntax():
pass
+3 -1
View File
@@ -7,6 +7,7 @@ from tungston.modpack.mysteriousisland.mobcatalog import CATALOG as mobs
from tungston.generator.markdown.reportwriter import ReportWriter from tungston.generator.markdown.reportwriter import ReportWriter
from tungston.generator.markdown.biomereport import BiomeReport from tungston.generator.markdown.biomereport import BiomeReport
from tungston.generator.markdown.mineralreport import MineralReport from tungston.generator.markdown.mineralreport import MineralReport
from tungston.generator.markdown.mobreport import MobReport
import sys import sys
@@ -23,7 +24,8 @@ class Runner(BaseRunner):
def _command_writeReports(self): def _command_writeReports(self):
writer = ReportWriter([ writer = ReportWriter([
BiomeReport(self.world), BiomeReport(self.world),
MineralReport(self.world) MineralReport(self.world),
MobReport(self.world),
]) ])
writer.write(REPORT_PATH) writer.write(REPORT_PATH)