package models import ( "errors" "strconv" "time" "github.com/arnaucube/gogame/database" "gopkg.in/mgo.v2/bson" ) type Process struct { // if Title == "", is not active, and can build other buildings/research Title string // building name / research name + level Building string Ends time.Time CountDown int64 } type Resources struct { Metal int64 Crystal int64 Deuterium int64 Energy int64 } type Planet struct { Db *database.Db Id bson.ObjectId `json:"id" bson:"_id,omitempty"` LastUpdated time.Time Size int64 // fields/slots Name string OwnerId bson.ObjectId Resources Resources Buildings map[string]int64 CurrentBuild Process Research Process /* Buildings types (in the map, all in lowcase): MetalMine int64 CrystalMine int64 DeuteriumMine int64 EnergyMine int64 FusionReactor int64 RoboticsFactory int64 Shipyard int64 RessearchLab int64 */ } func NewPlanet(db *database.Db, size int64, name string, ownerId bson.ObjectId) (*Planet, error) { newPlanet := Planet{ Db: db, LastUpdated: time.Now(), Size: size, Name: name, OwnerId: ownerId, Resources: Resources{ Metal: 500, Crystal: 500, Deuterium: 500, Energy: 500, }, } // in case that wants to start with resources plants newPlanet.Buildings = make(map[string]int64) newPlanet.Buildings["metalmine"] = 1 newPlanet.Buildings["crystalmine"] = 1 newPlanet.Buildings["deuteriummine"] = 1 // newPlanet.Buildings["energymine"] = 1 err := db.Planets.Insert(newPlanet) if err != nil { return nil, err } var planet *Planet err = db.Planets.Find(bson.M{"ownerid": newPlanet.OwnerId}).One(&planet) if err != nil { return nil, err } return planet, nil } func (p *Planet) GetFromDb() error { var planet Planet err := p.Db.Planets.Find(bson.M{"_id": p.Id}).One(&planet) if err != nil { return err } planet.Db = p.Db p = &planet return nil } func (p *Planet) StoreInDb() error { p.LastUpdated = time.Now() err := p.Db.Planets.Update(bson.M{"_id": p.Id}, p) return err } // GetResources updates the values of resources and returns the value, also updates the user.Resources // Resource types: metal, crystal, deuterium, energy func (p *Planet) GetResources() (*Resources, error) { // get current values err := p.GetFromDb() if err != nil { return nil, err } // calculate Delta time = currentTime - p.LastUpdated delta := time.Since(p.LastUpdated).Seconds() // get Resource-Plant level in each planet // and calculate growth = ResourcePlant.Level for each planet var metalGrowth, crystalGrowth, deuteriumGrowth int64 metalGrowth = metalGrowth + MetalGrowth(p.Buildings["metalmine"], int64(delta)) crystalGrowth = crystalGrowth + CrystalGrowth(p.Buildings["crystalmine"], int64(delta)) deuteriumGrowth = deuteriumGrowth + DeuteriumGrowth(p.Buildings["deuteriummine"], int64(delta)) // get energy generated and used energyGenerated := int64(500) energyGenerated = energyGenerated + SolarGrowth(p.Buildings["energymine"]) var energyUsed int64 energyUsed = energyUsed + MetalMineEnergyConsumption(p.Buildings["metalmine"]) energyUsed = energyUsed + CrystalMineEnergyConsumption(p.Buildings["crystalmine"]) energyUsed = energyUsed + DeuteriumMineEnergyConsumption(p.Buildings["deuteriummine"]) // calculate newAmount = oldAmount + growth p.Resources.Metal = p.Resources.Metal + metalGrowth p.Resources.Crystal = p.Resources.Crystal + crystalGrowth p.Resources.Deuterium = p.Resources.Deuterium + deuteriumGrowth p.Resources.Energy = energyGenerated - energyUsed // store new amount to user db err = p.StoreInDb() if err != nil { return nil, err } return &p.Resources, nil } // SpendResources checks if user has enough resources, then substracts the resources, and updates the amounts in the database func (p *Planet) SpendResources(r Resources) error { err := p.GetFromDb() if err != nil { return err } if p.Resources.Metal < r.Metal { return errors.New("not enough metal resources") } if p.Resources.Crystal < r.Crystal { return errors.New("not enough crystal resources") } if p.Resources.Deuterium < r.Deuterium { return errors.New("not enough deuterium resources") } // if p.Resources.Energy < r.Energy { // return errors.New("not enough energy resources") // } p.Resources.Metal = p.Resources.Metal - r.Metal p.Resources.Crystal = p.Resources.Crystal - r.Crystal p.Resources.Deuterium = p.Resources.Deuterium - r.Deuterium p.Resources.Energy = p.Resources.Energy - r.Energy err = p.StoreInDb() if err != nil { return err } return nil } func (p *Planet) GetBuildingCost(building string) (Resources, error) { switch building { case "metalmine": return MetalMineCost(p.Buildings["metalmine"] + 1), nil case "crystalmine": return CrystalMineCost(p.Buildings["crystalmine"] + 1), nil case "deuteriummine": return DeuteriumMineCost(p.Buildings["deuteriummine"] + 1), nil case "energymine": return EnergyMineCost(p.Buildings["energymine"] + 1), nil case "fusionreactor": return FussionReactorCost(p.Buildings["fusionreactor"] + 1), nil case "roboticsfactory": return RoboticsFactoryCost(p.Buildings["roboticsfactory"] + 1), nil case "shipyard": return ShipyardCost(p.Buildings["shipyard"] + 1), nil case "metalstorage": return MetalStorageCost(p.Buildings["metalstorage"] + 1), nil case "crystalstorage": return CrystalStorageCost(p.Buildings["crystalstorage"] + 1), nil case "deuteriumstorage": return DeuteriumStorageCost(p.Buildings["deuteriumstorage"] + 1), nil case "ressearchlab": return RessearchLabCost(p.Buildings["ressearchlab"] + 1), nil case "alliancedepot": return AllianceDepotCost(p.Buildings["alliancedepot"] + 1), nil case "missilesilo": return MissileSiloCost(p.Buildings["missilesilo"] + 1), nil case "spacedock": return SpaceDockCost(p.Buildings["spacedock"] + 1), nil default: return Resources{}, errors.New("building not found") } } // CheckCurrentBuild checks if the planet has a ongoing building in process, and if has finished // in case that has finished, updates it in db func (p *Planet) CheckCurrentBuild() (bool, error) { if p.CurrentBuild.Title != "" { // the planet is building something, check if has ended if p.CurrentBuild.Ends.Unix() < time.Now().Unix() { // add points for resources spend resourcesNeeded, err := p.GetBuildingCost(p.CurrentBuild.Building) if err != nil { return true, err } // add points for resources used (each 1000 units, 1 point points := ResourcesToPoints(resourcesNeeded) // 1000 point is the 1 point for the building points += 1000 err = AddPoints(p.Db, p.OwnerId, points) if err != nil { return true, err } // upgrade level of building in planet p.Buildings[p.CurrentBuild.Building] += 1 // substrate the energy used by the building var usedEnergy int64 if p.CurrentBuild.Building == "metalmine" { usedEnergy = MetalMineEnergyConsumption(p.Buildings["metalmine"]) } else if p.CurrentBuild.Building == "crystalmine" { usedEnergy = CrystalMineEnergyConsumption(p.Buildings["crystalmine"]) } else if p.CurrentBuild.Building == "deuteriummine" { usedEnergy = DeuteriumMineEnergyConsumption(p.Buildings["deuteriummine"]) } p.Resources.Energy = p.Resources.Energy - usedEnergy // build end p.CurrentBuild.Title = "" p.CurrentBuild.Building = "" // store in db err = p.Db.Planets.Update(bson.M{"_id": p.Id}, p) if err != nil { return true, err } return false, nil } p.CurrentBuild.CountDown = p.CurrentBuild.Ends.Unix() - time.Now().Unix() return true, nil } return false, nil } func (p *Planet) UpgradeBuilding(building string) error { busy, err := p.CheckCurrentBuild() if err != nil { return err } if busy { return errors.New("busy") } // get current building level, and get the needed resources for next level resourcesNeeded, err := p.GetBuildingCost(building) if err != nil { return err } resourcesNeeded.Energy = 0 // get time cost of the build timei64 := ConstructionTime(resourcesNeeded, p.Buildings["roboticsfactory"]) endsTime := time.Now().Add(time.Second * time.Duration(timei64)) // if user have enough resources to upgrade the building, upgrade the building err = p.SpendResources(resourcesNeeded) if err != nil { return err } // add current task to planet p.CurrentBuild.Building = building p.CurrentBuild.Title = building + " - Level " + strconv.Itoa(int(p.Buildings[building])+1) p.CurrentBuild.Ends = endsTime p.CurrentBuild.CountDown = p.CurrentBuild.Ends.Unix() - time.Now().Unix() // store planet in db err = p.Db.Planets.Update(bson.M{"_id": p.Id}, p) return nil } func ResourcesToPoints(r Resources) int64 { p := int64(0) p = p + r.Metal p = p + r.Crystal p = p + r.Deuterium p = p + r.Energy return p }