From 63fb04d2fa52adf802053b443657ec2630b9cf09 Mon Sep 17 00:00:00 2001 From: Steve Date: Wed, 25 May 2022 16:45:23 -0400 Subject: [PATCH] load from data --- data/converters.toml | 12 +++++ data/items.toml | 4 ++ data/tech.toml | 18 ++++++- simulator/converter.go | 78 +++++++++++++++++++-------- simulator/craftcost.go | 7 +++ simulator/item.go | 73 +++++++++++++++++++++++++ simulator/item_table.go | 39 -------------- simulator/object.go | 2 + simulator/plant.go | 77 -------------------------- simulator/player.go | 44 +++++++++++---- simulator/pod.go | 13 +++-- simulator/resource.go | 90 +++++++++++++++++++++++++++++++ simulator/simulator.go | 73 +++++++++++++++++++------ simulator/{science.go => tech.go} | 25 +++------ simulator/tile.go | 10 +++- 15 files changed, 374 insertions(+), 191 deletions(-) create mode 100644 simulator/craftcost.go create mode 100644 simulator/item.go delete mode 100644 simulator/item_table.go delete mode 100644 simulator/plant.go create mode 100644 simulator/resource.go rename simulator/{science.go => tech.go} (59%) diff --git a/data/converters.toml b/data/converters.toml index b7d359d..6e138a2 100644 --- a/data/converters.toml +++ b/data/converters.toml @@ -5,4 +5,16 @@ displayName = "Tea Pulper" source = "tea" output = "brick" rate = 5 +icon = "m" +[[converter.costs]] +name = "tea" +value = 10 +[[converter]] +itemid = 4 +name = "teaPlanter" +displayName = "Tea Planter" +source = "" +output = "tea" +rate = 0 +icon = "w" diff --git a/data/items.toml b/data/items.toml index 0d85c1f..cd82a02 100644 --- a/data/items.toml +++ b/data/items.toml @@ -3,9 +3,13 @@ itemid = 1 name = "tea" displayName = "Tea" buildable = true +icon = "p" rate = 3 [[resource]] itemid = 2 name = "brick" displayName = "Tea Bricks" +icon = "b" +buildable = false + diff --git a/data/tech.toml b/data/tech.toml index 91f112a..adf2471 100644 --- a/data/tech.toml +++ b/data/tech.toml @@ -2,5 +2,19 @@ techid = 1 name = "teaConverter" displayName = "Tea Pulper" -unlocks = ["2"] -require = [] +unlocks = ["teaConverter"] +[[tech.requires]] +name = "tea" +value = 10 + +[[tech]] +techid = 2 +name = "teaPlanting" +displayName = "Tea Planters" +unlocks = ["teaPlanter"] +[[tech.requires]] +name = "tea" +value = 20 +[[tech.requires]] +name = "teaConverter" +value = 1 diff --git a/simulator/converter.go b/simulator/converter.go index 9d86f0c..69d045c 100644 --- a/simulator/converter.go +++ b/simulator/converter.go @@ -1,53 +1,87 @@ package simulator import ( - "fmt" + "github.com/BurntSushi/toml" ) //Converter is a object that converts one item into another per tick type Converter struct { - kind itemType - rate int - source itemType - output itemType - owner *Player + Id itemType `toml:"itemid"` + Name string `toml:"name"` + DisplayName string `toml:"displayName"` + Icon string `toml:"icon"` + Rate int `toml:"rate"` + source itemType + SourceName string `toml:"source"` + output itemType + OutputName string `toml:"output"` + owner *Player + Costs []CraftCost +} + +type converters struct { + Converter []Converter +} + +//ID returns id +func (c Converter) ID() itemType { + return c.Id } func newConverter(k itemType, o *Player) *Converter { - return &Converter{ - kind: k, - rate: getConverter(k).rate, - source: getConverter(k).source, - output: getConverter(k).output, - owner: o, + var res Converter + if template, ok := GlobalItems[k]; ok { + temp := template.(Converter) + res.DisplayName = temp.DisplayName + res.Name = temp.Name + res.Icon = temp.Icon + res.source = lookupByName(temp.SourceName).ID() + res.output = lookupByName(temp.OutputName).ID() + res.Rate = temp.Rate + res.Costs = temp.Costs + res.owner = o + return &res } + return &Converter{} } //Tick one iteration func (c *Converter) Tick() { - if c.owner.Resources[c.source] > c.rate { - c.owner.Resources[c.source] = c.owner.Resources[c.source] - c.rate + if c.source == 0 { + c.owner.Resources[c.output] = c.owner.Resources[c.output] + 1 + } else if c.owner.Resources[c.source] > c.Rate { + c.owner.Resources[c.source] = c.owner.Resources[c.source] - c.Rate c.owner.Resources[c.output] = c.owner.Resources[c.output] + 1 } } -func (c *Converter) String() string { - return Lookup(c.kind).Render() +func (c Converter) String() string { + return c.Name +} + +func (c Converter) Render() string { + return c.Icon } //Describe returns human useful string -func (c *Converter) Describe() string { - output := getConverter(c.kind).output - return fmt.Sprintf("a %v converter that outputs %v", Lookup(c.kind).Name(), Lookup(output).Name()) +func (c Converter) Describe() string { + return c.DisplayName } //Type returns consumer -func (c *Converter) Type() ObjectType { +func (c Converter) Type() ObjectType { return consumerObject } -func getConverter(k itemType) converterEntry { - return Lookup(k).(converterEntry) +func loadConverters(filename string) { + var res converters + _, err := toml.DecodeFile(filename, &res) + if err != nil { + panic(err) + } + for _, v := range res.Converter { + newItem(v.ID(), v) + } } type converterEntry struct { diff --git a/simulator/craftcost.go b/simulator/craftcost.go new file mode 100644 index 0000000..07a8199 --- /dev/null +++ b/simulator/craftcost.go @@ -0,0 +1,7 @@ +package simulator + +//CraftCost is a cost relation for crafting an item +type CraftCost struct { + Name string + Value int +} diff --git a/simulator/item.go b/simulator/item.go new file mode 100644 index 0000000..5569b61 --- /dev/null +++ b/simulator/item.go @@ -0,0 +1,73 @@ +package simulator + +import "strconv" + +//GlobalItems table +var GlobalItems map[itemType]item +var nameToItem map[string]itemType + +type item interface { + ID() itemType + Type() ObjectType + Render() string + String() string + Describe() string +} + +//ItemEntry is a human/ui friendly item description +type ItemEntry interface { + String() string + Render() string + ID() itemType +} + +type itemType int + +func (i itemType) String() string { + return strconv.Itoa(int(i)) +} + +type empty struct { +} + +func (e empty) ID() itemType { + return itemType(0) +} + +func (e empty) Type() ObjectType { + return emptyObject +} + +func (e empty) String() string { + return "" +} + +func (e empty) Render() string { + return "" +} +func (e empty) Describe() string { + return "an empty item" +} + +func initItems() { + GlobalItems = make(map[itemType]item) + nameToItem = make(map[string]itemType) +} + +func newItem(id itemType, obj item) { + if _, ok := GlobalItems[id]; ok { + panic("trying to add item that already exists") + } + if id == 0 || obj.String() == "" { + panic("trying to add undeclared empty item") + } + GlobalItems[id] = obj + nameToItem[obj.String()] = id +} + +func lookupByName(name string) item { + if res, ok := nameToItem[name]; ok { + return GlobalItems[res] + } + return empty{} +} diff --git a/simulator/item_table.go b/simulator/item_table.go deleted file mode 100644 index 7185bfd..0000000 --- a/simulator/item_table.go +++ /dev/null @@ -1,39 +0,0 @@ -package simulator - -import "strconv" - -//ItemEntry is a human/ui friendly item description -type ItemEntry interface { - Name() string - Render() string - ID() string -} - -type itemType int - -func (i itemType) String() string { - return strconv.Itoa(int(i)) -} - -const ( - itemPlantTea itemType = iota + 1 - itemPlantWood - - convertPulper -) - -//GlobalItemList of all items -var GlobalItemList = []itemType{itemPlantTea, itemPlantWood, convertPulper} - -//Lookup returns a human friendly item entry -func Lookup(id itemType) ItemEntry { - switch id { - case itemPlantTea: - return plantEntry{itemPlantTea, 1, "tea"} - case itemPlantWood: - return plantEntry{itemPlantWood, 10, "wood"} - case convertPulper: - return converterEntry{convertPulper, 5, "teaConverter", itemPlantTea, itemPlantWood} - } - return nil -} diff --git a/simulator/object.go b/simulator/object.go index d1d3a4f..bb34c6f 100644 --- a/simulator/object.go +++ b/simulator/object.go @@ -14,4 +14,6 @@ type ObjectType int const ( producerObject ObjectType = iota consumerObject + resourceObject + emptyObject ) diff --git a/simulator/plant.go b/simulator/plant.go deleted file mode 100644 index 50d7dd8..0000000 --- a/simulator/plant.go +++ /dev/null @@ -1,77 +0,0 @@ -package simulator - -import ( - "fmt" - "strconv" -) - -//Plant is a plant that grows per tick -type Plant struct { - kind itemType - value int - growth int - rate int -} - -func getPlant(k itemType) plantEntry { - return Lookup(k).(plantEntry) -} - -func newPlant(k itemType) *Plant { - return &Plant{ - kind: k, - value: 0, - growth: 0, - rate: getPlant(k).rate, - } -} - -//Tick one iteration -func (p *Plant) Tick() { - p.growth++ - if p.growth > p.rate { - p.value++ - p.growth = 0 - } -} - -//Get produced plant -func (p *Plant) Get() Produce { - var pro Produce - pro.Value = p.value - pro.Kind = p.kind - p.value = 0 - return pro -} - -func (p *Plant) String() string { - return Lookup(p.kind).Render() -} - -//Describe returns a human useful string -func (p *Plant) Describe() string { - return fmt.Sprintf("a %v plant with %v value", Lookup(p.kind).Name(), strconv.Itoa(p.value)) -} - -//Type returns producer -func (p *Plant) Type() ObjectType { - return producerObject -} - -type plantEntry struct { - id itemType - rate int - name string -} - -func (p plantEntry) Render() string { - return "w" -} - -func (p plantEntry) Name() string { - return p.name -} - -func (p plantEntry) ID() string { - return p.id.String() -} diff --git a/simulator/player.go b/simulator/player.go index 9bf01db..e16ca71 100644 --- a/simulator/player.go +++ b/simulator/player.go @@ -2,14 +2,16 @@ package simulator import ( "fmt" + "sort" "strconv" ) //Player is a player controlled mob type Player struct { Resources map[itemType]int + Inventory map[itemType]int Craftables map[itemType]struct{} - Techs map[TechID]struct{} + Techs map[TechID]Tech CurrentTile *Tile log []string logIndex int @@ -17,17 +19,24 @@ type Player struct { //NewPlayer initializes a player func NewPlayer() *Player { - return &Player{Resources: make(map[itemType]int), Techs: make(map[TechID]struct{})} + return &Player{Resources: make(map[itemType]int), Techs: make(map[TechID]Tech), Craftables: make(map[itemType]struct{})} } func (p *Player) String() string { var res string res += "Resources: \n" - for _, i := range GlobalItemList { - if p.Resources[i] != 0 { - res += fmt.Sprintf("%v: %v\n", Lookup(i).Name(), p.Resources[i]) + var ress []int + for k, v := range p.Resources { + if v != 0 { + //res += fmt.Sprintf("%v: %v\n", GlobalItems[k].Describe(), v) + ress = append(ress, int(k)) } } + sort.Ints(ress) + for _, k := range ress { + id := itemType(k) + res += fmt.Sprintf("%v: %v\n", GlobalItems[id], p.Resources[id]) + } res += "\nLocation: \n" if p.CurrentTile != nil { res += p.CurrentTile.String() @@ -36,13 +45,28 @@ func (p *Player) String() string { } func (p *Player) research() { - for k, v := range p.Resources { - if k == itemPlantTea && v > 10 { - if _, ok := p.Techs[techPulper]; !ok { - p.Techs[techPulper] = struct{}{} - p.Announce("New Tech: Pulper") + for _, tech := range GlobalTechs { + if _, ok := p.Techs[tech.ID]; ok { + continue + } + + i := 0 + for _, v := range tech.Requires { + req := lookupByName(v.Name) + if p.Resources[req.ID()] >= v.Value { + i++ } } + if i == len(tech.Requires) { + p.Techs[tech.ID] = tech + for _, v := range tech.Unlocks { + itm := lookupByName(v) + if itm.Type() != emptyObject { + p.Craftables[itm.ID()] = struct{}{} + } + } + p.Announce(fmt.Sprintf("New Tech: %v", tech.DisplayName)) + } } } diff --git a/simulator/pod.go b/simulator/pod.go index 7c9c13b..f402b6b 100644 --- a/simulator/pod.go +++ b/simulator/pod.go @@ -16,14 +16,21 @@ func (p *Pod) Tick() { for i := range p.Tiles { for _, v := range p.Tiles[i] { if v.Building != nil { - v.Building.Tick() + if v.Building.Type() == consumerObject { + obj := v.Building.(*Converter) + obj.Tick() + } + if v.Building.Type() == resourceObject { + obj := v.Building.(*Resource) + obj.Tick() + } } } } } //Place an item on a tile -func (p *Pod) Place(item Object, x, y int) bool { +func (p *Pod) Place(item item, x, y int) bool { if p.Tiles[x][y].Building == nil { p.Tiles[x][y].Building = item return true @@ -53,7 +60,7 @@ func (p *Pod) String() string { if v.User != nil { res += "@" } else if v.Building != nil { - res += v.Building.String() + res += v.Building.Render() } else { res += "." } diff --git a/simulator/resource.go b/simulator/resource.go new file mode 100644 index 0000000..147ad38 --- /dev/null +++ b/simulator/resource.go @@ -0,0 +1,90 @@ +package simulator + +import ( + "log" + + "github.com/BurntSushi/toml" +) + +//Resource is a game resource; can be planted +type Resource struct { + Id itemType `toml:"itemid"` + Name string `toml:"name"` + DisplayName string `toml:"displayName"` + Buildable bool `toml:"buildable"` + Rate int `toml:"rate"` + Icon string `toml:"icon"` + value int + growth int +} + +type resources struct { + Resource []Resource +} + +func newResource(k itemType) *Resource { + var res Resource + if template, ok := GlobalItems[k]; ok { + temp := template.(Resource) + res.DisplayName = temp.DisplayName + res.Id = k + res.Name = temp.Name + res.Buildable = temp.Buildable + res.Rate = temp.Rate + res.Icon = temp.Icon + res.value = 0 + res.growth = 0 + return &res + } + return &Resource{} +} + +func (r *Resource) Tick() { + if !r.Buildable { + return + } + r.growth++ + if r.growth > r.Rate { + r.value++ + r.growth = 0 + } +} + +func (r *Resource) Get() Produce { + var pro Produce + pro.Value = r.value + pro.Kind = r.Id + r.value = 0 + return pro +} + +func (r Resource) String() string { + return r.Name +} + +func (r Resource) Render() string { + return r.Icon +} + +func (r Resource) Describe() string { + return r.DisplayName +} +func loadResources(filename string) { + var res resources + foo, err := toml.DecodeFile(filename, &res) + log.Println(foo.Undecoded()) + if err != nil { + panic(err) + } + for _, v := range res.Resource { + newItem(v.Id, v) + } +} + +func (r Resource) ID() itemType { + return r.Id +} + +func (r Resource) Type() ObjectType { + return resourceObject +} diff --git a/simulator/simulator.go b/simulator/simulator.go index 92a744b..61da41f 100644 --- a/simulator/simulator.go +++ b/simulator/simulator.go @@ -2,6 +2,7 @@ package simulator import ( "fmt" + "log" "strings" "time" ) @@ -19,8 +20,23 @@ type Simulator struct { func NewSimulator() *Simulator { pod := newPod() player := NewPlayer() + log.Println("loading items") + initItems() + log.Println("loading techs") loadTechs("data/tech.toml") - pod.Place(newPlant(itemPlantTea), 4, 4) + log.Println("loading resources") + loadResources("data/items.toml") + log.Println("loading converters") + loadConverters("data/converters.toml") + if len(GlobalItems) < 1 { + panic("Loaded items but nothing in global items table") + } + if len(GlobalTechs) < 1 { + panic("Loaded items but nothing in global items table") + } + pod.Place(newResource(lookupByName("tea").ID()), 4, 4) + player.Resources[itemType(1)] = 30 + player.Resources[itemType(3)] = 5 pod.Tiles[0][0].User = player player.Announce("Game started") return &Simulator{pod, player, 0, 0, 0, make(chan bool)} @@ -43,12 +59,12 @@ func (s *Simulator) Input(cmd string) { switch cmdS[0] { case "get": if cur.Building != nil { - if cur.Building.Type() == producerObject { - build := cur.Building.(Producer) + if cur.Building.Type() == resourceObject { + build := cur.Building.(*Resource) prod := build.Get() if prod.Kind != 0 && prod.Value > 0 { s.Player.Resources[prod.Kind] = s.Player.Resources[prod.Kind] + prod.Value - s.Player.Announce(fmt.Sprintf("Gathered %v %v", prod.Value, Lookup(prod.Kind).Name())) + s.Player.Announce(fmt.Sprintf("Gathered %v %v", prod.Value, GlobalItems[prod.Kind].Describe())) } } } @@ -57,15 +73,26 @@ func (s *Simulator) Input(cmd string) { return } item := cmdS[1] - if item == itemPlantTea.String() { - res := s.Place.Place(newPlant(itemPlantTea), s.Px, s.Py) + s.Player.Announce(fmt.Sprintf("placing %v", item)) + obj := lookupByName(item) + switch obj.Type() { + case emptyObject: + return + //case producerObject: + //obj = obj.(Producer) + case consumerObject: + obj2 := obj.(Converter) + res := s.Place.Place(newConverter(obj2.ID(), s.Player), s.Px, s.Py) if res { - s.Player.Resources[itemPlantTea] = s.Player.Resources[itemPlantTea] - 1 + s.Player.Resources[obj2.ID()] = s.Player.Resources[obj2.ID()] - 1 } - } else if item == convertPulper.String() { - res := s.Place.Place(newConverter(convertPulper, s.Player), s.Px, s.Py) - if res { - s.Player.Resources[convertPulper] = s.Player.Resources[convertPulper] - 1 + case resourceObject: + obj2 := obj.(Resource) + if obj2.Buildable { + res := s.Place.Place(newResource(obj2.ID()), s.Px, s.Py) + if res { + s.Player.Resources[obj2.ID()] = s.Player.Resources[obj2.ID()] - 1 + } } } case "craft": @@ -73,15 +100,27 @@ func (s *Simulator) Input(cmd string) { return } item := cmdS[1] - if item == convertPulper.String() { - if _, ok := s.Player.Techs[techPulper]; ok { - if s.Player.Resources[itemPlantTea] > 5 { - - s.Player.Resources[convertPulper] = s.Player.Resources[convertPulper] + 1 - s.Player.Resources[itemPlantTea] = s.Player.Resources[itemPlantTea] - 5 + s.Player.Announce(fmt.Sprintf("Crafting %v", item)) + obj := lookupByName(item) + switch obj.Type() { + case emptyObject: + return + case consumerObject: + obj2 := obj.(Converter) + i := 0 + for _, v := range obj2.Costs { + if s.Player.Resources[lookupByName(v.Name).ID()] >= v.Value { + i++ } } + if i == len(obj2.Costs) { + for _, v := range obj2.Costs { + s.Player.Resources[lookupByName(v.Name).ID()] = s.Player.Resources[lookupByName(v.Name).ID()] - v.Value + } + s.Player.Resources[lookupByName(obj2.String()).ID()] = s.Player.Resources[lookupByName(obj2.String()).ID()] + 1 + } } + case "left": res := s.Place.MovePlayer(s.Px, s.Py, s.Px, s.Py-1) if res != nil { diff --git a/simulator/science.go b/simulator/tech.go similarity index 59% rename from simulator/science.go rename to simulator/tech.go index ed9c2fa..a33f82d 100644 --- a/simulator/science.go +++ b/simulator/tech.go @@ -6,14 +6,14 @@ import "github.com/BurntSushi/toml" type TechID int type relation struct { - name string - value int + Name string + Value int } //Tech is a tech level type Tech struct { - ID int `toml:"techid"` - DisplayName string `toml:"display_name"` + ID TechID `toml:"techid"` + DisplayName string `toml:"displayName"` Name string `toml:"name"` Requires []relation `toml:"requires"` Unlocks []string `toml:"unlocks"` @@ -23,24 +23,11 @@ const ( techPulper TechID = iota ) -//GlobalTechList list of all techs -var GlobalTechList = []TechID{techPulper} - //GlobalTechs list of all techs var GlobalTechs []Tech type techs struct { - tech []Tech -} - -//LookupTech converts a tech ID to an item ID -func LookupTech(id TechID) ItemEntry { - switch id { - case techPulper: - return converterEntry{convertPulper, 5, "teaConverter", itemPlantTea, itemPlantWood} - } - return nil - + Tech []Tech } func lookupTechByName(name string) Tech { @@ -58,5 +45,5 @@ func loadTechs(filename string) { if err != nil { panic(err) } - GlobalTechs = res.tech + GlobalTechs = res.Tech } diff --git a/simulator/tile.go b/simulator/tile.go index a5b75d2..4e5c605 100644 --- a/simulator/tile.go +++ b/simulator/tile.go @@ -6,14 +6,20 @@ import ( //Tile is a tile type Tile struct { - Building Object + Building item User *Player } func (t *Tile) String() string { var res string if t.Building != nil { - res += fmt.Sprintf("There is a %v here\n", t.Building.Describe()) + if t.Building.Type() == resourceObject { + obj := t.Building.(*Resource) + res += fmt.Sprintf("There is a %v here with value %v\n", obj.Describe(), obj.value) + } else if t.Building.Type() == consumerObject { + obj := t.Building.(*Converter) + res += fmt.Sprintf("There is a %v here\n", obj.Describe()) + } } else { res += "Nothing here" }