import { v4 as uuid } from 'uuid';
import { ERROR_CODE, InsufficientResourceError, NotFoundError } from '../errors';
import {Repository} from './base';
+import * as config from '../config';
import { BuildQueue, BuildQueueRepository } from './build-queue';
import { DateTime, Duration } from 'luxon';
import { UnitTrainingQueue, UnitTrainingQueueRepository } from './training-queue';
import { coalesce } from '../lib/util';
import { Building, BuildingRepository } from './buildings';
import { Unit, UnitRepository } from './unit';
-import _ from 'lodash';
+import _, { random } from 'lodash';
import { Army, ArmyQueue, ArmyRepository } from './army';
export type City = {
barracks: number;
special_attacker_trainer: number;
special_defender_trainer: number;
+}
+
+export type CityWithLocation = {
+ sector_id: number;
location_x: number;
location_y: number;
-}
+} & City;
export class CityRepository extends Repository<City> {
buildQueue: BuildQueueRepository;
this.armyRepository = new ArmyRepository();
}
- async create(accountId: string): Promise<City> {
+ async create(accountId: string): Promise<CityWithLocation> {
const info: City = {
id: uuid(),
owner: accountId,
barracks: 0,
special_attacker_trainer: 0,
special_defender_trainer: 0,
- location_x: _.random(0, 100),
- location_y: _.random(0, 100)
};
+ await this.Insert(info);
+
// placement can happen randomly
+ const availableSectors = config.AVAILABLE_SECTORS;
+ const sector = _.random(1, availableSectors);
- await this.Insert(info);
+ const location = {
+ sector_id: await this.getAvailableSector(),
+ location_x: random(0, 25),
+ location_y: random(0, 25)
+ }
+
+ await this.db.raw('insert into locations (sector_id, city_id, location_x, location_y) values (?, ?, ?, ?)', [
+ location.sector_id,
+ info.id,
+ location.location_x,
+ location.location_y
+ ]);
+
+ return {
+ ...info,
+ sector_id: location.sector_id,
+ location_x: location.location_x,
+ location_y: location.location_y
+ };
+ }
+
+ async getAvailableSector(): Promise<number> {
+ // figure out which sectors have space (40% fill rate at 25x25);
+ const availableSectors = await this.db.raw<{count: Number, sector_id: number}[]>(`select count(sector_id) as count, sector_id from locations group by sector_id`);
+ const sample = _.sample(availableSectors.filter(sector => sector.count < 250)) as {count: number, sector_id: number};
- return info;
+ if(!sample) {
+ return _.sortBy(availableSectors, 'sector_id').pop().sector_id+1;
+ }
+
+ return sample.sector_id;
}
- async save(city: City) {
+ async save(city: Partial<City>) {
await this.Save(city, {id: city.id});
return city;
}
- async getUsersCity(owner: string): Promise<City> {
- const city = await this.FindOne({
- owner
- });
+ async findById(cityId: string): Promise<CityWithLocation> {
+ const city = await this.db.raw<CityWithLocation[]>(`select c.*, l.sector_id, l.location_x, l.location_y from cities c
+ join locations l on c.id = l.city_id
+ where id = ? limit 1`, [cityId]);
if(!city) {
throw new NotFoundError('User has no city', ERROR_CODE.NO_CITY);
}
- return city;
+ return city.pop();
+
+ }
+
+ async getUsersCity(owner: string): Promise<CityWithLocation> {
+ const city = await this.db.raw<CityWithLocation[]>(`select c.*, l.sector_id, l.location_x, l.location_y from cities c
+ join locations l on c.id = l.city_id
+ where owner = ? limit 1`, [owner]);
+
+ if(!city) {
+ throw new NotFoundError('User has no city', ERROR_CODE.NO_CITY);
+ }
+
+ return city.pop();
+ }
+
+ findAllInSector(sector_id: number): Promise<CityWithLocation[]> {
+ return this.db.raw<CityWithLocation[]>(`select c.*, l.sector_id, l.location_x, l.location_y from cities c
+join locations l on c.id = l.city_id
+where l.sector_id = ?`, [sector_id]);
}
async buildBuilding(building: Building, amount: number, city: City): Promise<BuildQueue> {
* @param city1
* @param city2
*/
- distanceInSeconds(city1: City, city2: City): number {
+ distanceInSeconds(city1: CityWithLocation, city2: CityWithLocation): number {
return this.distanceInHours(city1, city2) * 60 * 60;
}
- distanceInHours(city1: City, city2: City): number {
+ distanceInHours(city1: CityWithLocation, city2: CityWithLocation): number {
const dist = Math.sqrt(
Math.pow((city2.location_x - city1.location_x), 2)
+
return power
}
- async attack(attacker: City, attacked: City, army: Army): Promise<ArmyQueue> {
+ async attack(attacker: CityWithLocation, attacked: CityWithLocation, army: Army): Promise<ArmyQueue> {
// validate the user has enough of a military!
if(attacker.soldiers < army.soldiers) {
throw new InsufficientResourceError('soldiers', army.soldiers, attacker.soldiers);