UNPKG

@civ-clone/core-city-build

Version:

The repo contains the `CityBuild` and associated objects that deal with building items in `City`s.

158 lines (131 loc) 4.29 kB
import { AvailableCityBuildItemsRegistry, instance as availableCityBuildItemsRegistryInstance, } from './AvailableCityBuildItemsRegistry'; import { DataObject, IDataObject, } from '@civ-clone/core-data-object/DataObject'; import { RuleRegistry, instance as ruleRegistryInstance, } from '@civ-clone/core-rule/RuleRegistry'; import Build from './Rules/Build'; import BuildItem from './BuildItem'; import { BuildProgress } from './Yields'; import { IBuildable as Buildable } from './Buildable'; import BuildingCancelled from './Rules/BulidingCancelled'; import BuildingComplete from './Rules/BulidingComplete'; import City from '@civ-clone/core-city/City'; import { IConstructor } from '@civ-clone/core-registry/Registry'; import Yield from '@civ-clone/core-yield/Yield'; export interface ICityBuild extends IDataObject { add(production: Yield): void; available(): BuildItem[]; build(ItemToBuild: Buildable): void; building(): BuildItem | null; check(): void; cost(): BuildProgress; getAvailable(Item: Buildable): BuildItem; progress(): BuildProgress; remaining(): number; revalidate(): void; } export class CityBuild extends DataObject implements ICityBuild { #availableCityBuildItemsRegistry: AvailableCityBuildItemsRegistry; #building: BuildItem | null = null; #city: City; #cost: BuildProgress = new BuildProgress(Infinity); #progress: BuildProgress = new BuildProgress(); #ruleRegistry: RuleRegistry; constructor( city: City, availableCityBuildItemsRegistry: AvailableCityBuildItemsRegistry = availableCityBuildItemsRegistryInstance, ruleRegistry: RuleRegistry = ruleRegistryInstance ) { super(); this.#availableCityBuildItemsRegistry = availableCityBuildItemsRegistry; this.#city = city; this.#ruleRegistry = ruleRegistry; this.addKey( 'available', 'building', 'city', 'cost', 'progress', 'remaining' ); } add(production: Yield): void { this.#progress.add(production); } available(): BuildItem[] { const buildRules = this.#ruleRegistry.get(Build); // TODO: this still feels awkward... It's either this, or every rule has to be 'either it isn't this thing we're // checking or it is and it meets the condition' or it's this. It'd be nice to be able to just filter the list in a // more straightforward way... return ( this.#availableCityBuildItemsRegistry.filter( (BuildItem: Buildable): boolean => buildRules .filter((rule: Build): boolean => rule.validate(this.city(), BuildItem) ) .every((rule: Build): boolean => rule.process(this.city(), BuildItem).validate() ) ) as Buildable[] ).map( (available) => new BuildItem(available, this.city(), this.#ruleRegistry) ); } build(ItemToBuild: Buildable): void { const buildItem = this.getAvailable(ItemToBuild); if (!buildItem) { throw new TypeError( `Cannot build ${ItemToBuild.name}, it's not available.` ); } this.#building = buildItem; this.#cost.set(this.#building.cost().value()); } building(): BuildItem | null { return this.#building; } check(): IDataObject | null { if (this.#progress.value() >= this.#cost.value() && this.#building) { const built = this.#building.item().build(this.#city, this.#ruleRegistry); this.#progress.set(0); this.#building = null; this.#cost.set(Infinity); this.#ruleRegistry.process(BuildingComplete, this, built); return built; } return null; } city(): City { return this.#city; } cost(): BuildProgress { return this.#cost; } getAvailable(Item: Buildable): BuildItem { return this.available().filter( (available: BuildItem): boolean => available.item() === Item )[0]; } progress(): BuildProgress { return this.#progress; } remaining(): number { return this.#cost.value() - this.#progress.value(); } revalidate(): void { if (this.#building && !this.getAvailable(this.#building.item())) { this.#building = null; this.#cost.set(Infinity); this.#ruleRegistry.process(BuildingCancelled, this); } } } export default CityBuild;