Go binary/server for helping run/build sessions for the Maze Rats TTRPG
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

274 lines
7.7 KiB

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 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]
}
}