You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

309 lines
8.8 KiB

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