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