load from data

This commit is contained in:
stryan 2022-05-25 16:45:23 -04:00
parent 8503d07da7
commit 63fb04d2fa
15 changed files with 374 additions and 191 deletions

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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 {

7
simulator/craftcost.go Normal file
View File

@ -0,0 +1,7 @@
package simulator
//CraftCost is a cost relation for crafting an item
type CraftCost struct {
Name string
Value int
}

73
simulator/item.go Normal file
View File

@ -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{}
}

View File

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

View File

@ -14,4 +14,6 @@ type ObjectType int
const (
producerObject ObjectType = iota
consumerObject
resourceObject
emptyObject
)

View File

@ -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()
}

View File

@ -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))
}
}
}

View File

@ -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 += "."
}

90
simulator/resource.go Normal file
View File

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

View File

@ -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 {

View File

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

View File

@ -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"
}