1 import { v4 as uuid } from 'uuid';
2 import { ERROR_CODE, InsufficientResourceError, NotFoundError } from '../errors';
3 import {Repository} from './base';
4 import { BuildQueue, BuildQueueRepository } from './build-queue';
5 import { DateTime, Duration } from 'luxon';
6 import { UnitTrainingQueue, UnitTrainingQueueRepository } from './training-queue';
7 import { coalesce } from '../lib/util';
8 import { Building, BuildingRepository } from './buildings';
9 import { Unit, UnitRepository } from './unit';
10 import _ from 'lodash';
11 import { Army, ArmyQueue, ArmyRepository } from './army';
30 special_attacker_trainer: number;
31 special_defender_trainer: number;
36 export class CityRepository extends Repository<City> {
37 buildQueue: BuildQueueRepository;
38 buildingRepository: BuildingRepository;
39 unitRepository: UnitRepository;
40 unitTrainigQueue: UnitTrainingQueueRepository;
41 armyRepository: ArmyRepository;
45 this.buildingRepository = new BuildingRepository();
46 this.buildQueue = new BuildQueueRepository();
47 this.unitRepository = new UnitRepository();
48 this.unitTrainigQueue = new UnitTrainingQueueRepository();
49 this.armyRepository = new ArmyRepository();
52 async create(accountId: string): Promise<City> {
70 special_attacker_trainer: 0,
71 special_defender_trainer: 0,
72 location_x: _.random(0, 100),
73 location_y: _.random(0, 100)
76 // placement can happen randomly
78 await this.Insert(info);
83 async save(city: City) {
84 await this.Save(city, {id: city.id});
88 async getUsersCity(owner: string): Promise<City> {
89 const city = await this.FindOne({
94 throw new NotFoundError('User has no city', ERROR_CODE.NO_CITY);
100 async buildBuilding(building: Building, amount: number, city: City): Promise<BuildQueue> {
101 const freeSpace = city.totalSpace - city.usedSpace;
103 if(freeSpace < building.land) {
104 throw new InsufficientResourceError('land', building.land, freeSpace);
107 if(city.gold < building.gold) {
108 throw new InsufficientResourceError('gold', building.gold, city.gold);
111 if(city.ore < building.ore) {
112 throw new InsufficientResourceError('ore', building.ore, city.ore);
115 if(city.logs < building.logs) {
116 throw new InsufficientResourceError('logs', building.logs, city.logs);
119 city.usedSpace += (building.land * amount);
120 city.gold -= (building.gold * amount);
121 city.ore -= (building.ore * amount);
122 city.logs -= (building.logs * amount);
124 await this.save(city);
126 const due = Duration.fromObject({ hours: building.time});
127 const queue = await this.buildQueue.create(
129 DateTime.now().plus({ milliseconds: due.as('milliseconds') }).toMillis(),
138 * Returns the distance in seconds
142 distanceInSeconds(city1: City, city2: City): number {
143 return this.distanceInHours(city1, city2) * 60 * 60;
146 distanceInHours(city1: City, city2: City): number {
147 const dist = Math.sqrt(
148 Math.pow((city2.location_x - city1.location_x), 2)
150 Math.pow((city2.location_y - city1.location_y), 2)
153 return _.round(dist/4, 2);
157 async train(unit: Unit, amount: number, city: City): Promise<UnitTrainingQueue> {
158 if(city.gold < unit.gold) {
159 throw new InsufficientResourceError('gold', unit.gold, city.gold);
162 if(city.bushels < unit.bushels) {
163 throw new InsufficientResourceError('bushels', unit.bushels, city.bushels);
166 if(city.population < coalesce(unit.population, 0)) {
167 throw new InsufficientResourceError('population', unit.population, city.population);
170 if(city.soldiers < coalesce(unit.soldiers, 0)) {
171 throw new InsufficientResourceError('soldiers', unit.soldiers, city.soldiers);
174 if(city.attackers < coalesce(unit.attackers, 0)) {
175 throw new InsufficientResourceError('attackers', unit.attackers, city.attackers);
178 if(city.defenders < coalesce(unit.defenders, 0)) {
179 throw new InsufficientResourceError('defenders', unit.defenders, city.defenders);
182 // validate that they have enough of the buildings to support this
184 // ok they have everything, lets update their city
185 // and create the entry in the training queue
187 city.gold -= unit.gold * amount;
188 city.bushels -= unit.bushels * amount;
189 city.population -= coalesce(unit.population, 0) * amount;
190 city.soldiers -= coalesce(unit.soldiers, 0) * amount;
191 city.attackers -= coalesce(unit.attackers, 0) * amount;
192 city.defenders -= coalesce(unit.defenders, 0) * amount;
194 await this.save(city);
196 const due = Duration.fromObject({ hours: unit.time});
197 const queue = await this.unitTrainigQueue.create(
199 DateTime.now().plus({ milliseconds: due.as('milliseconds') }).toMillis(),
207 async power(checkUnits: {soldiers: number, attackers: number, defenders: number, sp_attackers: number, sp_defenders: number}): Promise<number> {
208 const units = _.keyBy(await this.unitRepository.list(), 'slug');
211 _.each(checkUnits, (count, slug) => {
213 power += units[slug].attack * count;
222 async attack(attacker: City, attacked: City, army: Army): Promise<ArmyQueue> {
223 // validate the user has enough of a military!
224 if(attacker.soldiers < army.soldiers) {
225 throw new InsufficientResourceError('soldiers', army.soldiers, attacker.soldiers);
227 if(attacker.attackers < army.attackers) {
228 throw new InsufficientResourceError('attackers', army.attackers, attacker.attackers);
230 if(attacker.defenders < army.defenders) {
231 throw new InsufficientResourceError('defenders', army.defenders, attacker.defenders);
233 if(attacker.sp_attackers < army.sp_attackers) {
234 throw new InsufficientResourceError('sp_attackers', army.sp_attackers, attacker.sp_attackers);
236 if(attacker.sp_defenders < army.sp_defenders) {
237 throw new InsufficientResourceError('sp_defenders', army.sp_defenders, attacker.sp_defenders);
240 // ok, it's a real army lets send it off!
241 attacker.soldiers -= army.soldiers;
242 attacker.attackers -= army.attackers;
243 attacker.defenders -= army.defenders;
244 attacker.sp_attackers -= army.sp_attackers;
245 attacker.sp_defenders -= army.sp_defenders;
247 await this.save(attacker);
249 return this.armyRepository.create(
254 this.distanceInSeconds(attacker, attacked)
258 async getBuildQueues(owner: string): Promise<BuildQueue[]> {
259 return this.buildQueue.list(owner);
262 async getUnitTrainingQueues(owner: string): Promise<UnitTrainingQueue[]> {
263 return this.unitTrainigQueue.list(owner);