arnaucube
arnaucube/gogame-oldPublic Self-hosted repository
gogame-old/models/planet.go
models/planet.goRaw
  1package models
  2
  3import (
  4	"errors"
  5	"fmt"
  6	"strconv"
  7	"time"
  8
  9	"github.com/arnaucube/gogame/database"
 10	"gopkg.in/mgo.v2/bson"
 11)
 12
 13type Process struct {
 14	// if Title == "", is not active, and can build other buildings/research
 15	Title     string // building name / research name + level
 16	Building  string
 17	Ends      time.Time
 18	CountDown int64
 19}
 20
 21type Resources struct {
 22	Metal     int64
 23	Crystal   int64
 24	Deuterium int64
 25	Energy    int64
 26}
 27
 28type Planet struct {
 29	Db           *database.Db
 30	Id           bson.ObjectId `json:"id" bson:"_id,omitempty"`
 31	LastUpdated  time.Time
 32	Size         int64 // fields/slots
 33	Name         string
 34	OwnerId      bson.ObjectId
 35	Resources    Resources
 36	Buildings    map[string]int64
 37	CurrentBuild Process
 38	Research     Process
 39	/*
 40		Buildings types (in the map, all in lowcase):
 41		   	MetalMine       int64
 42		   	CrystalMine     int64
 43		   	DeuteriumMine   int64
 44		   	EnergyMine      int64
 45		   	FusionReactor   int64
 46		   	RoboticsFactory int64
 47		   	Shipyard        int64
 48		   	RessearchLab    int64
 49	*/
 50}
 51
 52func NewPlanet(db *database.Db, size int64, name string, ownerId bson.ObjectId) (*Planet, error) {
 53	newPlanet := Planet{
 54		Db:          db,
 55		LastUpdated: time.Now(),
 56		Size:        size,
 57		Name:        name,
 58		OwnerId:     ownerId,
 59		Resources: Resources{
 60			Metal:     500,
 61			Crystal:   500,
 62			Deuterium: 500,
 63			Energy:    500,
 64		},
 65	}
 66
 67	// in case that wants to start with resources plants
 68	newPlanet.Buildings = make(map[string]int64)
 69	newPlanet.Buildings["metalmine"] = 1
 70	newPlanet.Buildings["crystalmine"] = 1
 71	newPlanet.Buildings["deuteriummine"] = 1
 72	// newPlanet.Buildings["energymine"] = 1
 73
 74	err := db.Planets.Insert(newPlanet)
 75	if err != nil {
 76		return nil, err
 77	}
 78
 79	var planet *Planet
 80	err = db.Planets.Find(bson.M{"ownerid": newPlanet.OwnerId}).One(&planet)
 81	if err != nil {
 82		return nil, err
 83	}
 84
 85	return planet, nil
 86}
 87
 88func (p *Planet) GetFromDb() error {
 89	var planet Planet
 90	err := p.Db.Planets.Find(bson.M{"_id": p.Id}).One(&planet)
 91	if err != nil {
 92		return err
 93	}
 94	planet.Db = p.Db
 95	p = &planet
 96	return nil
 97}
 98
 99func (p *Planet) StoreInDb() error {
100	p.LastUpdated = time.Now()
101	err := p.Db.Planets.Update(bson.M{"_id": p.Id}, p)
102	return err
103
104}
105
106// GetResources updates the values of resources and returns the value, also updates the user.Resources
107// Resource types: metal, crystal, deuterium, energy
108func (p *Planet) GetResources() (*Resources, error) {
109	// get current values
110	err := p.GetFromDb()
111	if err != nil {
112		return nil, err
113	}
114	// calculate Delta time = currentTime - p.LastUpdated
115	delta := time.Since(p.LastUpdated).Seconds()
116
117	// get Resource-Plant level in each planet
118	// and calculate growth = ResourcePlant.Level for each planet
119	var metalGrowth, crystalGrowth, deuteriumGrowth, energyGrowth int64
120	metalGrowth = metalGrowth + MetalGrowth(p.Buildings["metalmine"], int64(delta))
121	crystalGrowth = crystalGrowth + MetalGrowth(p.Buildings["crystalmine"], int64(delta))
122	deuteriumGrowth = deuteriumGrowth + MetalGrowth(p.Buildings["deuteriummine"], int64(delta))
123	energyGrowth = energyGrowth + MetalGrowth(p.Buildings["energymine"], int64(delta))
124
125	// calculate newAmount = oldAmount + growth
126	p.Resources.Metal = p.Resources.Metal + metalGrowth
127	p.Resources.Crystal = p.Resources.Crystal + crystalGrowth
128	p.Resources.Deuterium = p.Resources.Deuterium + deuteriumGrowth
129	p.Resources.Energy = p.Resources.Energy + energyGrowth
130
131	// store new amount to user db
132	err = p.StoreInDb()
133	if err != nil {
134		return nil, err
135	}
136
137	return &p.Resources, nil
138}
139
140// SpendResources checks if user has enough resources, then substracts the resources, and updates the amounts in the database
141func (p *Planet) SpendResources(r Resources) error {
142	err := p.GetFromDb()
143	if err != nil {
144		return err
145	}
146	if p.Resources.Metal < r.Metal {
147		return errors.New("not enough metal resources")
148	}
149	if p.Resources.Crystal < r.Crystal {
150		return errors.New("not enough crystal resources")
151	}
152	if p.Resources.Deuterium < r.Deuterium {
153		return errors.New("not enough deuterium resources")
154	}
155	if p.Resources.Energy < r.Energy {
156		return errors.New("not enough energy resources")
157	}
158
159	p.Resources.Metal = p.Resources.Metal - r.Metal
160	p.Resources.Crystal = p.Resources.Crystal - r.Crystal
161	p.Resources.Deuterium = p.Resources.Deuterium - r.Deuterium
162	p.Resources.Energy = p.Resources.Energy - r.Energy
163
164	err = p.StoreInDb()
165	if err != nil {
166		return err
167	}
168	return nil
169}
170func (p *Planet) GetBuildingCost(building string) (Resources, error) {
171	switch building {
172	case "metalmine":
173		return MetalMineCost(p.Buildings["metalmine"] + 1), nil
174	case "crystalmine":
175		return CrystalMineCost(p.Buildings["crystalmine"] + 1), nil
176	case "deuteriummine":
177		return DeuteriumMineCost(p.Buildings["deuteriummine"] + 1), nil
178	case "energymine":
179		return EnergyMineCost(p.Buildings["energymine"] + 1), nil
180	case "fusionreactor":
181		return FussionReactorCost(p.Buildings["fusionreactor"] + 1), nil
182	case "roboticsfactory":
183		return RoboticsFactoryCost(p.Buildings["roboticsfactory"] + 1), nil
184	case "shipyard":
185		return ShipyardCost(p.Buildings["shipyard"] + 1), nil
186	case "metalstorage":
187		return MetalStorageCost(p.Buildings["metalstorage"] + 1), nil
188	case "crystalstorage":
189		return CrystalStorageCost(p.Buildings["crystalstorage"] + 1), nil
190	case "deuteriumstorage":
191		return DeuteriumStorageCost(p.Buildings["deuteriumstorage"] + 1), nil
192	case "ressearchlab":
193		return RessearchLabCost(p.Buildings["ressearchlab"] + 1), nil
194	case "alliancedepot":
195		return AllianceDepotCost(p.Buildings["alliancedepot"] + 1), nil
196	case "missilesilo":
197		return MissileSiloCost(p.Buildings["missilesilo"] + 1), nil
198	case "spacedock":
199		return SpaceDockCost(p.Buildings["spacedock"] + 1), nil
200	default:
201		return Resources{}, errors.New("building not found")
202	}
203
204}
205
206// CheckCurrentBuild checks if the planet has a ongoing building in process, and if has finished
207// in case that has finished, updates it in db
208func (p *Planet) CheckCurrentBuild() (bool, error) {
209	if p.CurrentBuild.Title != "" {
210		// the planet is building something, check if has ended
211		if p.CurrentBuild.Ends.Unix() < time.Now().Unix() {
212			// add points for resources spend
213			resourcesNeeded, err := p.GetBuildingCost(p.CurrentBuild.Building)
214			if err != nil {
215				return true, err
216			}
217
218			// add points for resources used (each 1000 units, 1 point
219			points := ResourcesToPoints(resourcesNeeded)
220			// 1000 point is the 1 point for the building
221			points += 1000
222			err = AddPoints(p.Db, p.OwnerId, points)
223			if err != nil {
224				return true, err
225			}
226
227			// upgrade level of building in planet
228			p.Buildings[p.CurrentBuild.Building] += 1
229
230			// substrate the energy used by the building
231			var usedEnergy int64
232			if p.CurrentBuild.Building == "metalmine" {
233				usedEnergy = MetalMineEnergyConsumption(p.Buildings["metalmine"])
234			} else if p.CurrentBuild.Building == "crystalmine" {
235				usedEnergy = CrystalMineEnergyConsumption(p.Buildings["crystalmine"])
236			} else if p.CurrentBuild.Building == "deuteriummine" {
237				usedEnergy = DeuteriumMineEnergyConsumption(p.Buildings["deuteriummine"])
238			}
239			p.Resources.Energy = p.Resources.Energy - usedEnergy
240
241			// build end
242			p.CurrentBuild.Title = ""
243			p.CurrentBuild.Building = ""
244
245			// store in db
246			err = p.Db.Planets.Update(bson.M{"_id": p.Id}, p)
247			if err != nil {
248				return true, err
249			}
250			return false, nil
251		}
252		p.CurrentBuild.CountDown = p.CurrentBuild.Ends.Unix() - time.Now().Unix()
253
254		return true, nil
255	}
256	return false, nil
257
258}
259
260func (p *Planet) UpgradeBuilding(building string) error {
261	busy, err := p.CheckCurrentBuild()
262	if err != nil {
263		return err
264	}
265	if busy {
266		return errors.New("busy")
267	}
268
269	// get current building level, and get the needed resources for next level
270	resourcesNeeded, err := p.GetBuildingCost(building)
271	if err != nil {
272		return err
273	}
274	// get time cost of the build
275	timei64 := ConstructionTime(resourcesNeeded, p.Buildings["roboticsfactory"])
276	endsTime := time.Now().Add(time.Second * time.Duration(timei64))
277
278	// if user have enough resources to upgrade the building, upgrade the building
279	err = p.SpendResources(resourcesNeeded)
280	if err != nil {
281		return err
282	}
283
284	// add current task to planet
285	p.CurrentBuild.Building = building
286	p.CurrentBuild.Title = building + " - Level " + strconv.Itoa(int(p.Buildings[building])+1)
287	p.CurrentBuild.Ends = endsTime
288	p.CurrentBuild.CountDown = p.CurrentBuild.Ends.Unix() - time.Now().Unix()
289
290	// store planet in db
291	err = p.Db.Planets.Update(bson.M{"_id": p.Id}, p)
292	return nil
293}
294
295func ResourcesToPoints(r Resources) int64 {
296	p := int64(0)
297	p = p + r.Metal
298	p = p + r.Crystal
299	p = p + r.Deuterium
300	p = p + r.Energy
301	fmt.Println("p", p)
302	return p
303}