diff --git a/@sammo/game_logic/src/City.ts b/@sammo/game_logic/src/City.ts index 64fc1dc..f030bf9 100644 --- a/@sammo/game_logic/src/City.ts +++ b/@sammo/game_logic/src/City.ts @@ -1,26 +1,72 @@ import type { ICityEntity } from "./Entity/CityEntity.js"; import { LazyEntityUpdater } from "./LazyEntityUpdater.js"; import type { IResourceController } from "./IResourceController.js"; -import type { CityID } from "./defs.js"; +import type { CityID, GeneralID, NationID } from "./defs.js"; +import type { General } from "./General.js"; +import { must } from "@sammo/util"; export class City extends LazyEntityUpdater{ protected readonly _indirectNames: (keyof ICityEntity)[] = ["id", "nationID"]; protected readonly _entityType = "City"; protected readonly _raw: ICityEntity; + protected _members = new Map>; + protected _otherMembers = new Map>>; + constructor( raw: ICityEntity, - protected readonly rsc: IResourceController + protected readonly rsc: IResourceController, + bootstrapInfo: { + general: GeneralID[]; + } ){ super(); this._raw = raw; + this._bootstrapInfo = bootstrapInfo; } + private _bootstrapInfo?: ConstructorParameters[2]; public bootstrap(){ - //do nothing; + const rsc = this.rsc; + + const bootstrapInfo = must(this._bootstrapInfo); + delete this._bootstrapInfo; + + const nationID = this._raw.nationID; + + for(const id of bootstrapInfo.general){ + const general = must(rsc.general(id)); + this.notifyJoinGeneral(general); + } } get id(): CityID { return this._raw.id; } + + notifyJoinGeneral(general: General){ + const nationID = general.raw.nationID; + if(nationID === this._raw.nationID){ + this._members.set(general.id, new WeakRef(general)); + return; + } + + let otherMembers = this._otherMembers.get(nationID); + if(!otherMembers){ + otherMembers = new Map(); + this._otherMembers.set(nationID, otherMembers); + } + otherMembers.set(general.id, new WeakRef(general)); + } + + notifyQuitGeneral(general: General){ + const nationID = general.raw.nationID; + if(nationID === this._raw.nationID){ + this._members.delete(general.id); + return; + } + + const otherMembers = must(this._otherMembers.get(nationID)); + otherMembers.delete(general.id); + } } \ No newline at end of file diff --git a/@sammo/game_logic/src/General.ts b/@sammo/game_logic/src/General.ts index d9d9692..b9ca34b 100644 --- a/@sammo/game_logic/src/General.ts +++ b/@sammo/game_logic/src/General.ts @@ -1,4 +1,4 @@ -import { InvalidArgument } from "@sammo/util"; +import { InvalidArgument, NotYetImplemented } from "@sammo/util"; import { City } from "./City.js"; import type { IGeneralEntity } from "./Entity/GeneralEntity.js"; import type { IResourceController } from "./IResourceController.js"; @@ -41,7 +41,7 @@ export class General extends LazyEntityUpdater { this._squad = new WeakRef(must(rsc.squad(raw.squadID, raw.nationID))); } - //TODO: iActionHandlers 초기화 + this.reconfigureIActionHandlers(); } public get id(): GeneralID { @@ -86,23 +86,16 @@ export class General extends LazyEntityUpdater { return this._raw.officerLevel as number as CityOfficerLevel; } - public changeCity(city: CityID | City){ - const cityObj: City = (()=>{ - if(city instanceof City){ - return city; - } - const cityObj = this.rsc.city(city); - if(!cityObj){ - throw new InvalidArgument(`City not found: ${city}`); - } - return cityObj; - })(); - this._city = new WeakRef(cityObj); + public changeCity(city: City){ + this.city.notifyQuitGeneral(this); - //TODO: ResourceController에게 알려야 하는가? + this._city = new WeakRef(city); this.forceUpdate((raw) => { - raw.cityID = cityObj.id; + raw.cityID = city.id; }); + city.notifyJoinGeneral(this); + + this.reconfigureIActionHandlers(); } public quitSquad(){ @@ -115,7 +108,7 @@ export class General extends LazyEntityUpdater { squad.destroySquad(false); } else{ - squad.members.delete(this._raw.id); + squad.notifyQuitGeneral(this); } this._squad = undefined; @@ -124,45 +117,25 @@ export class General extends LazyEntityUpdater { }); } - public changeSquad(squad: SquadID | Squad){ + public changeSquad(squad: Squad){ this.quitSquad(); - const squadObj: Squad = (()=>{ - if(squad instanceof Squad){ - return squad; - } - const squadObj = this.rsc.squad(squad, this._raw.nationID); - if(!squadObj){ - throw new InvalidArgument(`Squad not found: ${squad}`); - } - return squadObj; - })(); - - squadObj.members.set(this._raw.id, new WeakRef(this)); - this._squad = new WeakRef(squadObj); + this._squad = new WeakRef(squad); this.forceUpdate((raw) => { - raw.squadID = squadObj.id; + raw.squadID = squad.id; }); + squad.notifyJoinGeneral(this); } - public changeNation(nation: NationID | Nation){ - const nationObj: Nation = (()=>{ - if(nation instanceof Nation){ - return nation; - } - const nationObj = this.rsc.nation(nation); - if(!nationObj){ - throw new InvalidArgument(`Nation not found: ${nation}`); - } - return nationObj; - })(); - this._nation = new WeakRef(nationObj); + public changeNation(nation: Nation){ this.quitSquad(); + this.nation.notifyQuitGeneral(this); + this._nation = new WeakRef(nation); this.forceUpdate((raw) => { - raw.nationID = nationObj.id; + raw.nationID = nation.id; raw.diplomaticPermission = DiplomaticPermission.none; - if(nationObj.id == 0){ + if(nation.id == 0){ raw.officerLevel = OfficerLevel.none; } else{ @@ -170,6 +143,13 @@ export class General extends LazyEntityUpdater { } raw.nationJoinedYearMonth = this.rsc.gameEnv.raw.yearMonth; }); + nation.notifyJoinGeneral(this); + + this.reconfigureIActionHandlers(); + } + + private reconfigureIActionHandlers(){ + throw new NotYetImplemented(); } public updateTurnTime(){ @@ -178,7 +158,6 @@ export class General extends LazyEntityUpdater { this.forceUpdate((raw) => { raw.turnTime = addSeconds(raw.turnTime, secondsPerTurn); }); - } public compareTurnTime(other: General): number { @@ -197,5 +176,4 @@ export class General extends LazyEntityUpdater { } return 0; } - } \ No newline at end of file diff --git a/@sammo/game_logic/src/Nation.ts b/@sammo/game_logic/src/Nation.ts index 513c7b0..384ad1b 100644 --- a/@sammo/game_logic/src/Nation.ts +++ b/@sammo/game_logic/src/Nation.ts @@ -26,8 +26,8 @@ export class Nation extends LazyEntityUpdater { protected readonly cityList = new Map>; - protected readonly generalList = new Map>; protected readonly squadList = new Map>; + protected readonly generalList = new Map>; protected readonly npcGeneralList = new Map>(); protected readonly userGeneralList = new Map>(); @@ -60,47 +60,19 @@ export class Nation extends LazyEntityUpdater { const bootstrapInfo = must(this._bootstrapInfo); delete this._bootstrapInfo; - const nationID = this._raw.id; - const bootstrapCityID = bootstrapInfo.city; for (const city of bootstrapCityID.map((id) => must(rsc.cityList.get(id)))) { - this.cityList.set(city.id, new WeakRef(city)); + this.notifyJoinCity(city); } const bootstrapSquadID = bootstrapInfo.squad; - for (const squad of rsc.squadList.values()) { - if (squad.raw.nationID !== nationID) { - continue; - } - this.squadList.set(squad.id, new WeakRef(squad)); + for(const squad of bootstrapSquadID.map((id) => must(rsc.squadList.get(id)))){ + this.notifyAddSquad(squad); } const bootstrapGeneralID = bootstrapInfo.general; for (const general of bootstrapGeneralID.map((id) => must(rsc.generalList.get(id)))) { - this.generalList.set(general.id, new WeakRef(general)); - - if (general.isUser) { - this.userGeneralList.set(general.id, new WeakRef(general)); - if (categorizeGeneral(general) === "war") { - this.warUserGeneralList.set(general.id, new WeakRef(general)); - } - else { - this.domesticUserGeneralList.set(general.id, new WeakRef(general)); - } - } else { - this.npcGeneralList.set(general.id, new WeakRef(general)); - if (categorizeGeneral(general) === "war") { - this.warNPCGeneralList.set(general.id, new WeakRef(general)); - } - else { - this.domesticNPCGeneralList.set(general.id, new WeakRef(general)); - } - } - - const staffLevel = general.staffLevel; - if (staffLevel !== undefined) { - this.staffList.set(staffLevel, new WeakRef(general)); - } + this.notifyJoinGeneral(general); } } @@ -112,12 +84,23 @@ export class Nation extends LazyEntityUpdater { return this._raw.name; } - public _kickGeneral(general: General | GeneralID) { - //국가에서 장수가 빠져나가는 경우, class내 변수에 대한 처리. - if (!(general instanceof General)) { - general = must(this.generalList.get(general)?.deref()); - } + public notifyAddSquad(squad: Squad) { + this.squadList.set(squad.id, new WeakRef(squad)); + } + public notifyDestroySquad(squad: Squad) { + this.squadList.delete(squad.id); + } + + public notifyJoinCity(city: City) { + this.cityList.set(city.id, new WeakRef(city)); + } + + public notifyQuitCity(city: City) { + this.cityList.delete(city.id); + } + + public notifyQuitGeneral(general: General) { const generalID = general.id; this.generalList.delete(generalID); @@ -133,7 +116,6 @@ export class Nation extends LazyEntityUpdater { } const staffLevel = general.staffLevel; - if (staffLevel !== undefined) { this.staffList.delete(staffLevel); @@ -147,26 +129,31 @@ export class Nation extends LazyEntityUpdater { } } - public notifyDestroySquad(squad: Squad) { - this.squadList.delete(squad.id); - } - - public _joinGeneral(general: General | GeneralID) { + public notifyJoinGeneral(general: General) { //국가에 장수가 들어오는 경우, class내 변수에 대한 처리. - if (!(general instanceof General)) { - general = must(this.rsc.general(general)); - } - const generalID = general.id; + this.generalList.set(generalID, new WeakRef(general)); + if (general.isUser) { this.userGeneralList.set(generalID, new WeakRef(general)); + if (categorizeGeneral(general) === "war") { + this.warUserGeneralList.set(generalID, new WeakRef(general)); + } + else { + this.domesticUserGeneralList.set(generalID, new WeakRef(general)); + } } else { this.npcGeneralList.set(generalID, new WeakRef(general)); + if (categorizeGeneral(general) === "war") { + this.warNPCGeneralList.set(generalID, new WeakRef(general)); + } + else { + this.domesticNPCGeneralList.set(generalID, new WeakRef(general)); + } } const staffLevel = general.staffLevel; - if (staffLevel !== undefined) { this.staffList.set(staffLevel, new WeakRef(general)); } diff --git a/@sammo/game_logic/src/Squad.ts b/@sammo/game_logic/src/Squad.ts index 18473a1..dcd3aab 100644 --- a/@sammo/game_logic/src/Squad.ts +++ b/@sammo/game_logic/src/Squad.ts @@ -73,6 +73,14 @@ export class Squad extends LazyEntityUpdater { }); } - //joinSquad은 General에서 처리 - //quitSquad은 General에서 처리 + public notifyQuitGeneral(general: General) { + if(general.id === this._leader.deref()?.id){ + throw new Error("Leader cannot quit"); + } + this._members.delete(general.id); + } + + public notifyJoinGeneral(general: General) { + this._members.set(general.id, new WeakRef(general)); + } } \ No newline at end of file diff --git a/@sammo/server/src/ResourceController.ts b/@sammo/server/src/ResourceController.ts index 0aa23db..ba0a975 100644 --- a/@sammo/server/src/ResourceController.ts +++ b/@sammo/server/src/ResourceController.ts @@ -167,7 +167,6 @@ export class ResourceController implements IResourceController { } resetAndFill(): Promise { - this._generalByTurnTime.clear(); this.nationList.clear(); this.cityList.clear(); this.squadList.clear(); @@ -186,16 +185,38 @@ export class ResourceController implements IResourceController { const generalIDByNation = new Map(); + const generalIDByCity = new Map(); const generalIDListBySquad = new Map(); for (const rawSquad of rawSquadList.values()) { generalIDListBySquad.set(rawSquad.id, []); } for (const rawGeneral of rawGeneralList.values()) { - if (!rawGeneral.squadID) { - continue; + if (rawGeneral.squadID) { + let list = generalIDListBySquad.get(rawGeneral.squadID); + if (!list) { + list = []; + generalIDListBySquad.set(rawGeneral.squadID, list); + } + list.push(rawGeneral.id); + } + + { + let list = generalIDByNation.get(rawGeneral.nationID); + if (!list) { + list = []; + generalIDByNation.set(rawGeneral.nationID, list); + } + list.push(rawGeneral.id); + } + + { + let list = generalIDByCity.get(rawGeneral.cityID); + if (!list) { + list = []; + generalIDByCity.set(rawGeneral.cityID, list); + } + list.push(rawGeneral.id); } - const list = must(generalIDListBySquad.get(rawGeneral.squadID)); - list.push(rawGeneral.id); } const cityIDByNation = new Map(); @@ -214,9 +235,7 @@ export class ResourceController implements IResourceController { this._gameEnv.bootstrap(); for (const rawNation of rawNationList.values()) { - const nation = new Nation( - rawNation, - this, + const nation = new Nation(rawNation, this, { general: must(generalIDByNation.get(rawNation.id)), squad: squadIDByNation.get(rawNation.id) ?? [], @@ -226,7 +245,9 @@ export class ResourceController implements IResourceController { this.nationList.set(nation.id, nation); } for (const rawCity of rawCityList.values()) { - const city = new City(rawCity, this); + const city = new City(rawCity, this, { + general: generalIDByCity.get(rawCity.id) ?? [] + }); this.cityList.set(city.id, city); } for (const rawSquad of rawSquadList.values()) { @@ -247,6 +268,11 @@ export class ResourceController implements IResourceController { //iAction때문에 general의 lazyInit이 가장 마지막 this.generalList.forEach(general => general.bootstrap()); + //턴순서 정렬! + const generalList = Array.from(this.generalList.values()); + generalList.sort((a, b) => a.compareTurnTime(b)); + this._generalByTurnTime = new Denque(generalList); + throw new NotYetImplemented(); }