wip: 개체 작업 진행
This commit is contained in:
@@ -1,10 +1,149 @@
|
|||||||
import type { GeneralID, GeneralName } from "./defs.js";
|
import { InvalidArgument } from "@sammo/util";
|
||||||
|
import { City } from "./City.js";
|
||||||
|
import type { IGeneralEntity } from "./GeneralEntity.js";
|
||||||
|
import type { IResourceController } from "./ResourceController.js";
|
||||||
|
import { DiplomaticPermission, type CityID, type GeneralID, type GeneralName, type NationID, OfficerLevel, type TroopID } from "./defs.js";
|
||||||
|
import { must } from "@sammo/util";
|
||||||
|
import { Nation } from "./Nation.js";
|
||||||
|
import { Troop } from "./Troop.js";
|
||||||
|
|
||||||
export class General {
|
export class General {
|
||||||
constructor(
|
private _raw: IGeneralEntity;
|
||||||
public readonly id: GeneralID,
|
private _city: WeakRef<City>;
|
||||||
public name: GeneralName,
|
private _troop: WeakRef<Troop> | undefined;
|
||||||
){
|
private _nation: WeakRef<Nation>;
|
||||||
|
|
||||||
|
public get raw(): Readonly<IGeneralEntity> {
|
||||||
|
return this._raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
raw: IGeneralEntity,
|
||||||
|
private resourceController: IResourceController
|
||||||
|
){
|
||||||
|
this._raw = raw;
|
||||||
|
this._city = new WeakRef(must(resourceController.getCity(raw.cityID)));
|
||||||
|
this._nation = new WeakRef(must(resourceController.getNation(raw.nationID)));
|
||||||
|
|
||||||
|
if(raw.troopID){
|
||||||
|
this._troop = new WeakRef(must(resourceController.getTroop(raw.troopID, raw.nationID)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get id(): GeneralID {
|
||||||
|
return this._raw.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get name(): GeneralName {
|
||||||
|
return this._raw.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public update(callback: (oldRaw: IGeneralEntity) => void, force = false) {
|
||||||
|
|
||||||
|
const oldCityID = this._raw.cityID;
|
||||||
|
const oldTroopID = this._raw.troopID;
|
||||||
|
const oldNationID = this._raw.nationID;
|
||||||
|
|
||||||
|
callback(this._raw);
|
||||||
|
|
||||||
|
if(!force){
|
||||||
|
if(oldCityID !== this._raw.cityID){
|
||||||
|
throw new Error("City change by update() is not allowed");
|
||||||
|
}
|
||||||
|
if(oldTroopID !== this._raw.troopID){
|
||||||
|
throw new Error("Troop change by update() is not allowed");
|
||||||
|
}
|
||||||
|
if(oldNationID !== this._raw.nationID){
|
||||||
|
throw new Error("Nation change by update() is not allowed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.resourceController.setDirtyGeneral(this._raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
public changeCity(city: CityID | City){
|
||||||
|
const cityObj: City = (()=>{
|
||||||
|
if(city instanceof City){
|
||||||
|
return city;
|
||||||
|
}
|
||||||
|
const cityObj = this.resourceController.getCity(city);
|
||||||
|
if(!cityObj){
|
||||||
|
throw new InvalidArgument(`City not found: ${city}`);
|
||||||
|
}
|
||||||
|
return cityObj;
|
||||||
|
})();
|
||||||
|
this._city = new WeakRef(cityObj);
|
||||||
|
|
||||||
|
//TODO: ResourceController에게 알려야 하는가?
|
||||||
|
this.update((raw) => {
|
||||||
|
raw.cityID = cityObj.id;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public quitTroop(){
|
||||||
|
if(!this._troop){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const troop = must(this._troop.deref());
|
||||||
|
if(troop.leader.id === this._raw.id){
|
||||||
|
troop.destructTroop(false);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
troop.members.delete(this._raw.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._troop = undefined;
|
||||||
|
this.update((raw) => {
|
||||||
|
raw.troopID = undefined;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public changeTroop(troop: TroopID | Troop){
|
||||||
|
this.quitTroop();
|
||||||
|
|
||||||
|
const troopObj: Troop = (()=>{
|
||||||
|
if(troop instanceof Troop){
|
||||||
|
return troop;
|
||||||
|
}
|
||||||
|
const troopObj = this.resourceController.getTroop(troop, this._raw.nationID);
|
||||||
|
if(!troopObj){
|
||||||
|
throw new InvalidArgument(`Troop not found: ${troop}`);
|
||||||
|
}
|
||||||
|
return troopObj;
|
||||||
|
})();
|
||||||
|
|
||||||
|
troopObj.members.set(this._raw.id, new WeakRef(this));
|
||||||
|
this._troop = new WeakRef(troopObj);
|
||||||
|
this.update((raw) => {
|
||||||
|
raw.troopID = troopObj.id;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public changeNation(nation: NationID | Nation){
|
||||||
|
const nationObj: Nation = (()=>{
|
||||||
|
if(nation instanceof Nation){
|
||||||
|
return nation;
|
||||||
|
}
|
||||||
|
const nationObj = this.resourceController.getNation(nation);
|
||||||
|
if(!nationObj){
|
||||||
|
throw new InvalidArgument(`Nation not found: ${nation}`);
|
||||||
|
}
|
||||||
|
return nationObj;
|
||||||
|
})();
|
||||||
|
this._nation = new WeakRef(nationObj);
|
||||||
|
this.quitTroop();
|
||||||
|
|
||||||
|
this.update((raw) => {
|
||||||
|
raw.nationID = nationObj.id;
|
||||||
|
raw.diplomaticPermission = DiplomaticPermission.none;
|
||||||
|
if(nationObj.id == 0){
|
||||||
|
raw.officerLevel = OfficerLevel.none;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
raw.officerLevel = OfficerLevel.normal;
|
||||||
|
}
|
||||||
|
raw.nationBelong = 0;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import type { DiplomaticPermission, GeneralID, GeneralName, ItemID, ItemKeyType, NationID, NpcType, OfficerLevel, OwnerID, OwnerName, PersonalityType, SpecialityDomesticType, SpecialityWarType, TroopID } from "./defs.js";
|
||||||
|
|
||||||
|
export interface IGeneralEntity {
|
||||||
|
id: GeneralID;
|
||||||
|
owner?: OwnerID;
|
||||||
|
npcType: NpcType;
|
||||||
|
affinity?: number;
|
||||||
|
picture?: string;
|
||||||
|
name: GeneralName;
|
||||||
|
ownerName?: OwnerName;
|
||||||
|
nationID: NationID;
|
||||||
|
cityID: number;
|
||||||
|
troopID?: TroopID;
|
||||||
|
|
||||||
|
leadership: number;
|
||||||
|
leadershipExp: number;
|
||||||
|
strength: number;
|
||||||
|
strengthExp: number;
|
||||||
|
intel: number;
|
||||||
|
intelExp: number;
|
||||||
|
|
||||||
|
injury: number;
|
||||||
|
|
||||||
|
experience: number;
|
||||||
|
dedication: number;
|
||||||
|
|
||||||
|
experienceLevel: number;
|
||||||
|
dedicationLevel: number;
|
||||||
|
|
||||||
|
dex: number[];
|
||||||
|
|
||||||
|
officerLevel: OfficerLevel;
|
||||||
|
diplomaticPermission: DiplomaticPermission;
|
||||||
|
|
||||||
|
gold: number;
|
||||||
|
rice: number;
|
||||||
|
|
||||||
|
item: Record<ItemKeyType, {
|
||||||
|
id: ItemID,
|
||||||
|
option: Record<string, unknown>,
|
||||||
|
}>;
|
||||||
|
|
||||||
|
turntime: Date;
|
||||||
|
|
||||||
|
killturn: number;
|
||||||
|
|
||||||
|
age: number;
|
||||||
|
startAge: number;
|
||||||
|
|
||||||
|
nationBelong: number;
|
||||||
|
|
||||||
|
betray: number;
|
||||||
|
|
||||||
|
personality: PersonalityType;
|
||||||
|
specialityDomestic: SpecialityDomesticType;
|
||||||
|
specialityWar: SpecialityWarType;
|
||||||
|
|
||||||
|
defenceTrain: number;
|
||||||
|
|
||||||
|
npcSpec?: {
|
||||||
|
msg?: string;
|
||||||
|
bornyear: number;
|
||||||
|
deadyear: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,34 +1,53 @@
|
|||||||
import type { General } from "./General.js";
|
import type { General } from "./General.js";
|
||||||
import type { City } from "./City.js";
|
import type { City } from "./City.js";
|
||||||
import type { Nation } from "./Nation.js";
|
import type { Nation } from "./Nation.js";
|
||||||
import type { CityID, GeneralID, NationID } from "./defs.js";
|
import type { CityID, GeneralID, NationID, TroopID } from "./defs.js";
|
||||||
import type { GameEnv } from "./GameEnv.js";
|
import type { GameEnv } from "./GameEnv.js";
|
||||||
|
import type { IGeneralEntity } from "./GeneralEntity.js";
|
||||||
|
import type { Troop } from "./Troop.js";
|
||||||
|
import type { ITroopEntity } from "./TroopEntity.js";
|
||||||
|
|
||||||
export interface IResourceController{
|
export interface IResourceController{
|
||||||
addCity(city: City): Promise<boolean>;
|
addCity(city: City): boolean;
|
||||||
addNation(nation: Nation): Promise<boolean>;
|
addNation(nation: Nation): boolean;
|
||||||
addGeneral(general: General): Promise<boolean>;
|
addGeneral(general: General): boolean;
|
||||||
|
|
||||||
getCity(id: CityID, allowFromDB?: boolean): Promise<City | undefined>;
|
getCity(id: CityID): City | undefined;
|
||||||
getNation(id: NationID, allowFromDB?: boolean): Promise<Nation | undefined>;
|
getNation(id: NationID): Nation | undefined;
|
||||||
getGeneral(id: GeneralID, allowFromDB?: boolean): Promise<General | undefined>;
|
getGeneral(id: GeneralID): General | undefined;
|
||||||
|
|
||||||
getGameEnv(): Promise<GameEnv>;
|
getGameEnv(): Promise<GameEnv>;
|
||||||
|
|
||||||
findAllNations(): Promise<Map<NationID, Nation>>;
|
findAllNations(): Map<NationID, Nation>;
|
||||||
findAllCities(): Promise<Map<CityID, City>>;
|
findAllCities(): Map<CityID, City>;
|
||||||
findAllGenerals(): Promise<Map<GeneralID, General>>;
|
findAllGenerals(): Map<GeneralID, General>;
|
||||||
|
|
||||||
findCityByNation(id: NationID): Promise<Map<CityID, City>>;
|
findGeneralByTroop(id: TroopID): Map<GeneralID, General>;
|
||||||
findGeneralByNation(id: NationID): Promise<Map<GeneralID, General>>;
|
|
||||||
findGeneralByCity(id: CityID, nationID?: NationID): Promise<Map<GeneralID, General>>;
|
|
||||||
|
|
||||||
findGeneralByQuery(query: unknown): Promise<Map<GeneralID, General>>;
|
findCityByNation(id: NationID): Map<CityID, City>;
|
||||||
findCityByQuery(query: unknown): Promise<Map<CityID, City>>;
|
findGeneralByNation(id: NationID): Map<GeneralID, General>;
|
||||||
findNationByQuery(query: unknown): Promise<Map<NationID, Nation>>;
|
findGeneralByCity(id: CityID, nationID?: NationID): Map<GeneralID, General>;
|
||||||
|
|
||||||
reset(): void;
|
getTroop(id: TroopID, nation: NationID): Troop | undefined;
|
||||||
save(): Promise<void>;
|
|
||||||
|
_getAllChanges(): Map<unknown, unknown>;
|
||||||
|
|
||||||
|
|
||||||
|
setDirtyGeneral(rawGeneral: IGeneralEntity): void;
|
||||||
|
setDirtyCity(city: City): void;
|
||||||
|
setDirtyTroop(troop: Troop | TroopID): void;
|
||||||
|
setDirtyNation(nation: Nation): void;
|
||||||
|
|
||||||
|
createGeneral(raw: IGeneralEntity): General;
|
||||||
|
createTroop(raw: ITroopEntity): Troop;
|
||||||
|
//createNation
|
||||||
|
|
||||||
|
reserveDeleteTroop(id: TroopID): void;
|
||||||
|
reserveDeleteGeneral(id: GeneralID): void;
|
||||||
|
reserveDeleteNation(id: NationID): void;
|
||||||
|
|
||||||
|
resetAndFill(): Promise<void>;
|
||||||
|
saveAll(): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let _resourceController: IResourceController | undefined = undefined;
|
let _resourceController: IResourceController | undefined = undefined;
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { must } from "@sammo/util";
|
||||||
|
import type { General } from "./General.js";
|
||||||
|
import type { IResourceController } from "./ResourceController.js";
|
||||||
|
import type { ITroopEntity } from "./TroopEntity.js";
|
||||||
|
import type { GeneralID } from "./defs.js";
|
||||||
|
|
||||||
|
export class Troop {
|
||||||
|
private _raw: ITroopEntity
|
||||||
|
private _leader: WeakRef<General>;
|
||||||
|
private _members: Map<GeneralID, WeakRef<General>>;
|
||||||
|
|
||||||
|
public get raw(): Readonly<ITroopEntity> {
|
||||||
|
return this._raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
raw: ITroopEntity,
|
||||||
|
private resourceController: IResourceController
|
||||||
|
){
|
||||||
|
this._raw = raw;
|
||||||
|
|
||||||
|
const members = resourceController.findGeneralByTroop(raw.id);
|
||||||
|
const leaderID: GeneralID = raw.id as GeneralID;
|
||||||
|
this._leader = new WeakRef(must(members.get(leaderID)));
|
||||||
|
members.delete(leaderID);
|
||||||
|
this._members = new Map([...members].map(([k,v])=>[k, new WeakRef(v)]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public get id(){
|
||||||
|
return this._raw.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get leader(){
|
||||||
|
return must(this._leader.deref());
|
||||||
|
}
|
||||||
|
|
||||||
|
public get members(): Map<GeneralID, WeakRef<General>> {
|
||||||
|
return this._members;
|
||||||
|
}
|
||||||
|
|
||||||
|
public destructTroop(withLeader = true){
|
||||||
|
this._members.forEach((member)=>{
|
||||||
|
const general = must(member.deref());
|
||||||
|
general.quitTroop();
|
||||||
|
});
|
||||||
|
if(withLeader){
|
||||||
|
this.leader.quitTroop();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.resourceController.reserveDeleteTroop(this._raw.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import type { NationID, TroopID } from "./defs.js";
|
||||||
|
|
||||||
|
export interface ITroopEntity {
|
||||||
|
id: TroopID;
|
||||||
|
nationID: NationID;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
@@ -1,10 +1,16 @@
|
|||||||
//Type 정의
|
//Type 정의
|
||||||
|
export type OwnerID = number & { zz_t?: "OwnerID" };
|
||||||
|
export type OwnerName = string & { zz_t?: "OwnerName" };
|
||||||
|
|
||||||
export type GeneralID = number & { zz_t?: "GeneralID" };
|
export type GeneralID = number & { zz_t?: "GeneralID" };
|
||||||
export type GeneralName = string & { zz_t?: "GeneralName" };
|
export type GeneralName = string & { zz_t?: "GeneralName" };
|
||||||
|
|
||||||
export type CityID = number & { zz_t?: "CityID" };
|
export type CityID = number & { zz_t?: "CityID" };
|
||||||
export type CityName = string & { zz_t?: "CityName" };
|
export type CityName = string & { zz_t?: "CityName" };
|
||||||
|
|
||||||
|
export type TroopID = number & { zz_t?: "TroopID" };
|
||||||
|
export type TroopName = string & { zz_t?: "TroopName" };
|
||||||
|
|
||||||
export type NationID = number & { zz_t?: "NationID" };
|
export type NationID = number & { zz_t?: "NationID" };
|
||||||
export type NationName = string & { zz_t?: "NationName" };
|
export type NationName = string & { zz_t?: "NationName" };
|
||||||
|
|
||||||
@@ -12,6 +18,62 @@ export type NationName = string & { zz_t?: "NationName" };
|
|||||||
export type NationType = string & { zz_t?: "NationType" };
|
export type NationType = string & { zz_t?: "NationType" };
|
||||||
export type StrategicTurnType = string & { zz_t?: "StrategicTurnType" };
|
export type StrategicTurnType = string & { zz_t?: "StrategicTurnType" };
|
||||||
|
|
||||||
|
export type ItemKeyType = 'horse' | 'weapon' | 'book' | 'item';
|
||||||
|
export type ItemID = string & { zz_t?: "ItemID" };
|
||||||
|
|
||||||
|
export type PersonalityType = string & { zz_t?: "PersonalityType" };
|
||||||
|
export type SpecialityDomesticType = string & { zz_t?: "SpecialityDomesticType" };
|
||||||
|
export type SpecialityWarType = string & { zz_t?: "SpecialityWarType" };
|
||||||
|
|
||||||
|
export const enum NpcType {
|
||||||
|
user = 0,
|
||||||
|
user_borrowed_npc = 1,
|
||||||
|
npc_scenario = 2,
|
||||||
|
npc_generated = 3,
|
||||||
|
npc_strategic = 4,
|
||||||
|
npc_troop_leader = 5,
|
||||||
|
npc_unselectable = 6,
|
||||||
|
|
||||||
|
npc_invader = 9,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum OfficerLevel {
|
||||||
|
//재야
|
||||||
|
none = 0,
|
||||||
|
|
||||||
|
//일반
|
||||||
|
normal = 1,
|
||||||
|
|
||||||
|
//도시관직
|
||||||
|
cityLeadership = 2,
|
||||||
|
cityIntel = 3,
|
||||||
|
cityStrength = 4,
|
||||||
|
|
||||||
|
//지력관직
|
||||||
|
staffIntel3 = 5,
|
||||||
|
staffIntel2 = 7,
|
||||||
|
staffIntel1 = 9,
|
||||||
|
|
||||||
|
//무력관직
|
||||||
|
staffStrength3 = 6,
|
||||||
|
staffStrength2 = 8,
|
||||||
|
staffStrength1 = 10,
|
||||||
|
|
||||||
|
//참모
|
||||||
|
staffChief = 11,
|
||||||
|
|
||||||
|
//군주
|
||||||
|
lord = 12,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum DiplomaticPermission {
|
||||||
|
none = 0,
|
||||||
|
audit = 1,
|
||||||
|
ambassador = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 전용 stat이어서 확장하고자 하는경우 아래와 같이 확장 가능
|
/* 전용 stat이어서 확장하고자 하는경우 아래와 같이 확장 가능
|
||||||
```ts
|
```ts
|
||||||
declare module "@/defs.js" {
|
declare module "@/defs.js" {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ export * from "./bsonify.js"
|
|||||||
export * from "./jsonify.js"
|
export * from "./jsonify.js"
|
||||||
export * from "./error.js"
|
export * from "./error.js"
|
||||||
export * from "./unwrap.js"
|
export * from "./unwrap.js"
|
||||||
|
export * from "./must.js"
|
||||||
export * from "./types.js"
|
export * from "./types.js"
|
||||||
export * from "./web.js"
|
export * from "./web.js"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import type { Nullable } from './types.js';
|
||||||
|
import { NotNullExpected } from "./error.js";
|
||||||
|
|
||||||
|
export function must<T>(result: Nullable<T>): T {
|
||||||
|
if (result === null || result === undefined) {
|
||||||
|
throw new NotNullExpected();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function must_any<T>(result: Nullable<unknown>): T {
|
||||||
|
if (result === null || result === undefined) {
|
||||||
|
throw new NotNullExpected();
|
||||||
|
}
|
||||||
|
return result as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrType<T> = { new(msg?: string): T }
|
||||||
|
|
||||||
|
export function must_err<T, ErrT extends Error>(result: Nullable<T>, errType: ErrType<ErrT>, errMsg?: string): T {
|
||||||
|
if (result === null || result === undefined) {
|
||||||
|
throw new errType(errMsg);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user