package tables import ( "mazeratsgen/internal/data" "mazeratsgen/internal/dice" "mazeratsgen/internal/helpers" "strings" ) type CityTable struct { Roller *dice.Roller } type Building struct { Type string Rooms []string TacticalFeature string } type Faction struct { Type string Trait string Goal string } type City struct { Theme string Events []string DistrictThemes []string Activities []string TacticalStreetFeatures []string UpperClassBuildings []Building LowerClassBuildings []Building Factions []Faction Seed int64 } func (t *CityTable) Generate() City { upperClassBuildings := []Building{} // Future Jerry...the weird `for` loops here are to add noise to the seed. // This will break if more than 6 rooms are needed for i := 0; i < 3; i++ { tempRoller := dice.NewRoller(t.Roller.Seed + int64(i)) tempTable := CityTable{Roller: tempRoller} upperClassBuildings = append(upperClassBuildings, tempTable.UpperClassBuilding(tempRoller.TableRoll())) } lowerClassBuildings := []Building{} for i := 4; i < 7; i++ { tempRoller := dice.NewRoller(t.Roller.Seed + int64(i)) tempTable := CityTable{Roller: tempRoller} lowerClassBuildings = append(lowerClassBuildings, tempTable.LowerClassBuilding(tempRoller.TableRoll())) } return City{ Theme: t.CityTheme(t.Roller.TableRoll()), Events: helpers.GenUniqueItems(3, t.CityEvent, t.Roller.Seed), DistrictThemes: helpers.GenUniqueItems(3, t.DistrictTheme, t.Roller.Seed), UpperClassBuildings: upperClassBuildings, LowerClassBuildings: lowerClassBuildings, Activities: helpers.GenUniqueItems(3, t.CityActivity, t.Roller.Seed), TacticalStreetFeatures: helpers.GenUniqueItems(3, t.TacticalStreetFeature, t.Roller.Seed), Factions: []Faction{ t.GenFaction(t.Roller.TableRoll()), t.GenFaction(t.Roller.TableRoll()), }, Seed: t.Roller.Seed, } } func (t *CityTable) Template() string { return strings.TrimSpace(` Theme: {{.Theme}} Events: {{- range $event := .Events}} {{$event}} {{- end}} District Themes: {{- range $theme := .DistrictThemes}} {{$theme}} {{- end}} Upper Class Buildings: {{- range $building := .UpperClassBuildings}} {{$building.Type}}: Tactical Feature: {{$building.TacticalFeature}} Rooms: {{- range $room := $building.Rooms}} {{$room}} {{- end}} {{- end}} Lower Class Buildings: {{- range $building := .LowerClassBuildings}} {{$building.Type}}: Tactical Feature: {{$building.TacticalFeature}} Rooms: {{- range $room := $building.Rooms}} {{$room}} {{- end}} {{- end}} Activities: {{- range $activity := .Activities }} {{$activity}} {{- end}} Tactical Street Features: {{- range $feature := .TacticalStreetFeatures }} {{$feature}} {{- end}} Factions: {{- range $faction := .Factions }} {{$faction.Type}}: Trait: {{$faction.Trait}} Goal: {{$faction.Goal}} {{- end}} Seed: {{.Seed}} `) } func (t *CityTable) CityTheme(roll [2]int) string { switch roll { case [2]int{1, 1}: roll, _ := t.Roller.Roll("1d6") return MonsterTable{Roller: t.Roller}.Base(roll[0]) case [2]int{2, 1}: return t.CityActivity(t.Roller.TableRoll()) case [2]int{2, 2}: return t.CityEvent(t.Roller.TableRoll()) case [2]int{2, 5}: return t.DistrictTheme(t.Roller.TableRoll()) case [2]int{2, 6}: return NPCTable{Roller: t.Roller}.DivineDomain(t.Roller.TableRoll()) case [2]int{3, 1}: return t.GenFaction(t.Roller.TableRoll()).Type case [2]int{3, 5}: return t.LowerClassBuilding(t.Roller.TableRoll()).Type case [2]int{4, 2}: return "Pick an existing NPC" case [2]int{4, 4}: return MagicTable{Roller: t.Roller}.PhysicalElement(t.Roller.TableRoll()) case [2]int{6, 4}: return t.UpperClassBuilding(t.Roller.TableRoll()).Type default: return data.Tables.City.CityThemes[roll[0]-1][roll[1]-1] } } func (t *CityTable) CityEvent(roll [2]int) string { switch roll { case [2]int{1, 4}: return t.GenFaction(t.Roller.TableRoll()).Type default: return data.Tables.City.CityEvents[roll[0]-1][roll[1]-1] } } func (t *CityTable) DistrictTheme(roll [2]int) string { npcTable := NPCTable{Roller: t.Roller} switch roll { case [2]int{1, 2}: return npcTable.CivilizedNPC(t.Roller.TableRoll()) case [2]int{4, 1}: return t.LowerClassBuilding(t.Roller.TableRoll()).Type case [2]int{6, 1}: return npcTable.UnderworldNPC(t.Roller.TableRoll()) case [2]int{6, 2}: return t.UpperClassBuilding(t.Roller.TableRoll()).Type case [2]int{6, 4}: return npcTable.WildernessNPC(t.Roller.TableRoll()) default: return data.Tables.City.DistrictThemes[roll[0]-1][roll[1]-1] } } func (t CityTable) GenBuilding(roll int) Building { if roll <= 3 { return t.LowerClassBuilding(t.Roller.TableRoll()) } return t.UpperClassBuilding(t.Roller.TableRoll()) } func (t CityTable) UpperClassBuilding(roll [2]int) Building { return Building{ Type: data.Tables.City.UpperClassBuildings[roll[0]-1][roll[1]-1], TacticalFeature: t.TacticalBuildingFeature(t.Roller.TableRoll()), Rooms: helpers.GenUniqueItems(3, t.BuildingRoom, t.Roller.Seed), } } func (t CityTable) LowerClassBuilding(roll [2]int) Building { return Building{ Type: data.Tables.City.LowerClassBuildings[roll[0]-1][roll[1]-1], TacticalFeature: t.TacticalBuildingFeature(t.Roller.TableRoll()), Rooms: helpers.GenUniqueItems(3, t.BuildingRoom, t.Roller.Seed), } } func (t CityTable) BuildingRoom(roll [2]int) string { switch roll { case [2]int{3, 1}: return MazeTable{Roller: t.Roller}.DungeonRoom(t.Roller.TableRoll()) default: return data.Tables.City.BuildingRooms[roll[0]-1][roll[1]-1] } } func (t CityTable) TacticalStreetFeature(roll [2]int) string { mazeTable := MazeTable{Roller: t.Roller} switch roll { case [2]int{2, 3}: return t.CityActivity(t.Roller.TableRoll()) case [2]int{3, 4}: return mazeTable.DungeonActivity(t.Roller.TableRoll()) case [2]int{6, 6}: return WildTable{Roller: t.Roller}.WildernessActivity(t.Roller.TableRoll()) default: return data.Tables.City.TacticalStreetFeatures[roll[0]-1][roll[1]-1] } } func (t CityTable) TacticalBuildingFeature(roll [2]int) string { switch roll { case [2]int{1, 1}: roll, _ := t.Roller.Roll("1d6") return MonsterTable{Roller: t.Roller}.Base(roll[0]) + "Nests" default: return data.Tables.City.TacticalBuildingFeatures[roll[0]-1][roll[1]-1] } } func (t CityTable) FactionTrait(roll [2]int) string { switch roll { case [2]int{4, 4}: return NPCTable{Roller: t.Roller}.Personality(t.Roller.TableRoll()) default: return data.Tables.City.FactionTraits[roll[0]-1][roll[1]-1] } } func (t CityTable) GenFaction(roll [2]int) Faction { return Faction{ Type: data.Tables.City.Factions[roll[0]-1][roll[1]-1], Trait: t.FactionTrait(t.Roller.TableRoll()), Goal: t.FactionGoal(t.Roller.TableRoll()), } } func (t CityTable) FactionGoal(roll [2]int) string { switch roll { case [2]int{1, 6}: return "Control " + t.GenFaction(t.Roller.TableRoll()).Type case [2]int{2, 3}: return "Kill a specific monster" case [2]int{2, 4}: return "Defeat " + t.GenFaction(t.Roller.TableRoll()).Type case [2]int{4, 4}: return "Infiltrate " + t.GenFaction(t.Roller.TableRoll()).Type default: return data.Tables.City.FactionGoals[roll[0]-1][roll[1]-1] } } func (t CityTable) CityActivity(roll [2]int) string { switch roll { case [2]int{2, 5}: return MazeTable{Roller: t.Roller}.DungeonActivity(t.Roller.TableRoll()) case [2]int{4, 2}: return NPCTable{Roller: t.Roller}.Mission(t.Roller.TableRoll()) case [2]int{6, 6}: return WildTable{Roller: t.Roller}.WildernessActivity(t.Roller.TableRoll()) default: return data.Tables.City.CityActivities[roll[0]-1][roll[1]-1] } }