@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
text/typescript
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;