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 {
|
||||
constructor(
|
||||
public readonly id: GeneralID,
|
||||
public name: GeneralName,
|
||||
){
|
||||
private _raw: IGeneralEntity;
|
||||
private _city: WeakRef<City>;
|
||||
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 { City } from "./City.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 { IGeneralEntity } from "./GeneralEntity.js";
|
||||
import type { Troop } from "./Troop.js";
|
||||
import type { ITroopEntity } from "./TroopEntity.js";
|
||||
|
||||
export interface IResourceController{
|
||||
addCity(city: City): Promise<boolean>;
|
||||
addNation(nation: Nation): Promise<boolean>;
|
||||
addGeneral(general: General): Promise<boolean>;
|
||||
addCity(city: City): boolean;
|
||||
addNation(nation: Nation): boolean;
|
||||
addGeneral(general: General): boolean;
|
||||
|
||||
getCity(id: CityID, allowFromDB?: boolean): Promise<City | undefined>;
|
||||
getNation(id: NationID, allowFromDB?: boolean): Promise<Nation | undefined>;
|
||||
getGeneral(id: GeneralID, allowFromDB?: boolean): Promise<General | undefined>;
|
||||
getCity(id: CityID): City | undefined;
|
||||
getNation(id: NationID): Nation | undefined;
|
||||
getGeneral(id: GeneralID): General | undefined;
|
||||
|
||||
getGameEnv(): Promise<GameEnv>;
|
||||
|
||||
findAllNations(): Promise<Map<NationID, Nation>>;
|
||||
findAllCities(): Promise<Map<CityID, City>>;
|
||||
findAllGenerals(): Promise<Map<GeneralID, General>>;
|
||||
findAllNations(): Map<NationID, Nation>;
|
||||
findAllCities(): Map<CityID, City>;
|
||||
findAllGenerals(): Map<GeneralID, General>;
|
||||
|
||||
findCityByNation(id: NationID): Promise<Map<CityID, City>>;
|
||||
findGeneralByNation(id: NationID): Promise<Map<GeneralID, General>>;
|
||||
findGeneralByCity(id: CityID, nationID?: NationID): Promise<Map<GeneralID, General>>;
|
||||
findGeneralByTroop(id: TroopID): Map<GeneralID, General>;
|
||||
|
||||
findGeneralByQuery(query: unknown): Promise<Map<GeneralID, General>>;
|
||||
findCityByQuery(query: unknown): Promise<Map<CityID, City>>;
|
||||
findNationByQuery(query: unknown): Promise<Map<NationID, Nation>>;
|
||||
findCityByNation(id: NationID): Map<CityID, City>;
|
||||
findGeneralByNation(id: NationID): Map<GeneralID, General>;
|
||||
findGeneralByCity(id: CityID, nationID?: NationID): Map<GeneralID, General>;
|
||||
|
||||
reset(): void;
|
||||
save(): Promise<void>;
|
||||
getTroop(id: TroopID, nation: NationID): Troop | undefined;
|
||||
|
||||
_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;
|
||||
|
||||
@@ -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 정의
|
||||
export type OwnerID = number & { zz_t?: "OwnerID" };
|
||||
export type OwnerName = string & { zz_t?: "OwnerName" };
|
||||
|
||||
export type GeneralID = number & { zz_t?: "GeneralID" };
|
||||
export type GeneralName = string & { zz_t?: "GeneralName" };
|
||||
|
||||
export type CityID = number & { zz_t?: "CityID" };
|
||||
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 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 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이어서 확장하고자 하는경우 아래와 같이 확장 가능
|
||||
```ts
|
||||
declare module "@/defs.js" {
|
||||
|
||||
@@ -2,6 +2,7 @@ export * from "./bsonify.js"
|
||||
export * from "./jsonify.js"
|
||||
export * from "./error.js"
|
||||
export * from "./unwrap.js"
|
||||
export * from "./must.js"
|
||||
export * from "./types.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