feat: eslint 적용 및 관련 코드 일괄 수정

This commit is contained in:
2026-01-05 15:46:47 +00:00
parent cb312f02b3
commit c965b1120f
387 changed files with 39808 additions and 38800 deletions
+99 -99
View File
@@ -10,8 +10,8 @@ Command keys match class names and file names; they are the values stored in
- Nation: `buildNationCommandClass($type)` in `legacy/hwe/func_converter.php`
- `null` or empty `type` defaults to `휴식`
- Post-req turn cooldowns are stored in:
- `next_execute` KV (per general)
- `nation_env` KV (per nation)
- `next_execute` KV (per general)
- `nation_env` KV (per nation)
## Execution Semantics (BaseCommand)
@@ -29,106 +29,106 @@ which commands are available via `GameConst` overrides. See
## General Commands (`legacy/hwe/sammo/Command/General/`)
| Key | Display name (actionName) |
| --- | --- |
| `che_NPC능동` | `NPC능동` |
| `che_강행` | `강행` |
| `che_거병` | `거병` |
| `che_건국` | `건국` |
| `che_견문` | `견문` |
| `che_군량매매` | `군량매매` |
| `che_귀환` | `귀환` |
| `che_기술연구` | `기술 연구` |
| `che_내정특기초기화` | `내정 특기 초기화` |
| `che_농지개간` | `농지 개간` |
| `che_단련` | `단련` |
| `che_등용` | `등용` |
| `che_등용수락` | `등용 수락` |
| `che_랜덤임관` | `무작위 국가로 임관` |
| `che_모반시도` | `모반시도` |
| `che_모병` | `모병` |
| `che_무작위건국` | `무작위 도시 건국` |
| `che_물자조달` | `물자조달` |
| `che_방랑` | `방랑` |
| `che_사기진작` | `사기진작` |
| `che_상업투자` | `상업 투자` |
| `che_선동` | `선동` |
| `che_선양` | `선양` |
| `che_성벽보수` | `성벽 보수` |
| `che_소집해제` | `소집해제` |
| `che_수비강화` | `수비 강화` |
| `che_숙련전환` | `숙련전환` |
| `che_요양` | `요양` |
| `che_은퇴` | `은퇴` |
| `che_이동` | `이동` |
| `che_인재탐색` | `인재탐색` |
| `che_임관` | `임관` |
| `che_장비매매` | `장비매매` |
| `che_장수대상임관` | `장수를 따라 임관` |
| `che_전투태세` | `전투태세` |
| `che_전투특기초기화` | `전투 특기 초기화` |
| `che_접경귀환` | `접경귀환` |
| `che_정착장려` | `정착 장려` |
| `che_주민선정` | `주민 선정` |
| `che_증여` | `증여` |
| `che_집합` | `집합` |
| `che_징병` | `징병` |
| `che_첩보` | `첩보` |
| `che_출병` | `출병` |
| `che_치안강화` | `치안 강화` |
| `che_탈취` | `탈취` |
| `che_파괴` | `파괴` |
| `che_하야` | `하야` |
| `che_해산` | `해산` |
| `che_헌납` | `헌납` |
| `che_화계` | `화계` |
| `che_훈련` | `훈련` |
| `cr_건국` | `건국` |
| `cr_맹훈련` | `맹훈련` |
| `휴식` | `휴식` |
| Key | Display name (actionName) |
| -------------------- | ------------------------- |
| `che_NPC능동` | `NPC능동` |
| `che_강행` | `강행` |
| `che_거병` | `거병` |
| `che_건국` | `건국` |
| `che_견문` | `견문` |
| `che_군량매매` | `군량매매` |
| `che_귀환` | `귀환` |
| `che_기술연구` | `기술 연구` |
| `che_내정특기초기화` | `내정 특기 초기화` |
| `che_농지개간` | `농지 개간` |
| `che_단련` | `단련` |
| `che_등용` | `등용` |
| `che_등용수락` | `등용 수락` |
| `che_랜덤임관` | `무작위 국가로 임관` |
| `che_모반시도` | `모반시도` |
| `che_모병` | `모병` |
| `che_무작위건국` | `무작위 도시 건국` |
| `che_물자조달` | `물자조달` |
| `che_방랑` | `방랑` |
| `che_사기진작` | `사기진작` |
| `che_상업투자` | `상업 투자` |
| `che_선동` | `선동` |
| `che_선양` | `선양` |
| `che_성벽보수` | `성벽 보수` |
| `che_소집해제` | `소집해제` |
| `che_수비강화` | `수비 강화` |
| `che_숙련전환` | `숙련전환` |
| `che_요양` | `요양` |
| `che_은퇴` | `은퇴` |
| `che_이동` | `이동` |
| `che_인재탐색` | `인재탐색` |
| `che_임관` | `임관` |
| `che_장비매매` | `장비매매` |
| `che_장수대상임관` | `장수를 따라 임관` |
| `che_전투태세` | `전투태세` |
| `che_전투특기초기화` | `전투 특기 초기화` |
| `che_접경귀환` | `접경귀환` |
| `che_정착장려` | `정착 장려` |
| `che_주민선정` | `주민 선정` |
| `che_증여` | `증여` |
| `che_집합` | `집합` |
| `che_징병` | `징병` |
| `che_첩보` | `첩보` |
| `che_출병` | `출병` |
| `che_치안강화` | `치안 강화` |
| `che_탈취` | `탈취` |
| `che_파괴` | `파괴` |
| `che_하야` | `하야` |
| `che_해산` | `해산` |
| `che_헌납` | `헌납` |
| `che_화계` | `화계` |
| `che_훈련` | `훈련` |
| `cr_건국` | `건국` |
| `cr_맹훈련` | `맹훈련` |
| `휴식` | `휴식` |
## Nation Commands (`legacy/hwe/sammo/Command/Nation/`)
| Key | Display name (actionName) |
| --- | --- |
| `che_감축` | `감축` |
| `che_국기변경` | `국기변경` |
| `che_국호변경` | `국호변경` |
| `che_급습` | `급습` |
| `che_몰수` | `몰수` |
| `che_무작위수도이전` | `무작위 수도 이전` |
| `che_물자원조` | `원조` |
| `che_발령` | `발령` |
| `che_백성동원` | `백성동원` |
| `che_부대탈퇴지시` | `부대 탈퇴 지시` |
| `che_불가침수락` | `불가침 수락` |
| `che_불가침제의` | `불가침 제의` |
| `che_불가침파기수락` | `불가침 파기 수락` |
| `che_불가침파기제의` | `불가침 파기 제의` |
| `che_선전포고` | `선전포고` |
| `che_수몰` | `수몰` |
| `che_의병모집` | `의병모집` |
| `che_이호경식` | `이호경식` |
| `che_종전수락` | `종전 수락` |
| `che_종전제의` | `종전 제의` |
| `che_증축` | `증축` |
| `che_천도` | `천도` |
| `che_초토화` | `초토화` |
| `che_포상` | `포상` |
| `che_피장파장` | `피장파장` |
| `che_필사즉생` | `필사즉생` |
| `che_허보` | `허보` |
| `cr_인구이동` | `인구이동` |
| `event_극병연구` | `극병 연구` |
| `event_대검병연구` | `대검병 연구` |
| `event_무희연구` | `무희 연구` |
| `event_산저병연구` | `산저병 연구` |
| `event_상병연구` | `상병 연구` |
| `event_원융노병연구` | `원융노병 연구` |
| `event_음귀병연구` | `음귀병 연구` |
| `event_화륜차연구` | `화륜차 연구` |
| `event_화시병연구` | `화시병 연구` |
| `휴식` | `휴식` |
| Key | Display name (actionName) |
| -------------------- | ------------------------- |
| `che_감축` | `감축` |
| `che_국기변경` | `국기변경` |
| `che_국호변경` | `국호변경` |
| `che_급습` | `급습` |
| `che_몰수` | `몰수` |
| `che_무작위수도이전` | `무작위 수도 이전` |
| `che_물자원조` | `원조` |
| `che_발령` | `발령` |
| `che_백성동원` | `백성동원` |
| `che_부대탈퇴지시` | `부대 탈퇴 지시` |
| `che_불가침수락` | `불가침 수락` |
| `che_불가침제의` | `불가침 제의` |
| `che_불가침파기수락` | `불가침 파기 수락` |
| `che_불가침파기제의` | `불가침 파기 제의` |
| `che_선전포고` | `선전포고` |
| `che_수몰` | `수몰` |
| `che_의병모집` | `의병모집` |
| `che_이호경식` | `이호경식` |
| `che_종전수락` | `종전 수락` |
| `che_종전제의` | `종전 제의` |
| `che_증축` | `증축` |
| `che_천도` | `천도` |
| `che_초토화` | `초토화` |
| `che_포상` | `포상` |
| `che_피장파장` | `피장파장` |
| `che_필사즉생` | `필사즉생` |
| `che_허보` | `허보` |
| `cr_인구이동` | `인구이동` |
| `event_극병연구` | `극병 연구` |
| `event_대검병연구` | `대검병 연구` |
| `event_무희연구` | `무희 연구` |
| `event_산저병연구` | `산저병 연구` |
| `event_상병연구` | `상병 연구` |
| `event_원융노병연구` | `원융노병 연구` |
| `event_음귀병연구` | `음귀병 연구` |
| `event_화륜차연구` | `화륜차 연구` |
| `event_화시병연구` | `화시병 연구` |
| `휴식` | `휴식` |
## Open Questions / Follow-ups
+30 -30
View File
@@ -110,18 +110,18 @@ Nation-level choices run only for NPCs (`npc >= 2`) or for autorun users:
### Major Action Groups
- **Troop movement**
- `do부대전방발령`, `do부대후방발령`, `do부대구출발령`
- user/NPC versions to move generals between front/back/supply cities
- checks `frontCities`, `supplyCities`, `last발령`, and war route.
- `do부대전방발령`, `do부대후방발령`, `do부대구출발령`
- user/NPC versions to move generals between front/back/supply cities
- checks `frontCities`, `supplyCities`, `last발령`, and war route.
- **Resource distribution**
- `do유저장포상`, `doNPC포상`, `doNPC몰수`
- uses resource floors (`reqNation*`, `reqNPC*`, `reqHuman*`)
- weighted by target general's deficit and recent activity.
- `do유저장포상`, `doNPC포상`, `doNPC몰수`
- uses resource floors (`reqNation*`, `reqNPC*`, `reqHuman*`)
- weighted by target general's deficit and recent activity.
- **Diplomacy**
- `do불가침제의`: respond to assistance requests with NAP offer.
- `do선전포고`: probabilistic declaration when strong enough.
- `do불가침제의`: respond to assistance requests with NAP offer.
- `do선전포고`: probabilistic declaration when strong enough.
- **Capital relocation**
- `do천도`: moves capital based on population, dev, and connectivity.
- `do천도`: moves capital based on population, dev, and connectivity.
## General Turn Behavior (`chooseGeneralTurn`)
@@ -131,26 +131,26 @@ General-level decisions are layered:
2. Reserved command is honored if valid (unless `휴식`).
3. Immediate recovery if `injury > cureThreshold`.
4. Special cases:
- NPC troop leaders (type 5) always `집합`.
- wanderers decide on founding / moving / disbanding.
- NPC troop leaders (type 5) always `집합`.
- wanderers decide on founding / moving / disbanding.
5. Iterate policy `priority`, invoking `do{Action}`.
6. Fallback to `do중립`.
### Major Action Groups
- **Domestic development**
- `do일반내정`, `do전쟁내정`, `do긴급내정`
- weighted by `city` dev rates and general type flags.
- `do일반내정`, `do전쟁내정`, `do긴급내정`
- weighted by `city` dev rates and general type flags.
- **War preparation**
- `do징병`, `do전투준비`, `do출병`
- strict checks on crew, train, atmos, population and diplomacy state.
- `do징병`, `do전투준비`, `do출병`
- strict checks on crew, train, atmos, population and diplomacy state.
- **Mobility**
- `do전방워프`, `do후방워프`, `do내정워프`, `do귀환`
- uses `front/supply/backup` cities and population thresholds.
- `do전방워프`, `do후방워프`, `do내정워프`, `do귀환`
- uses `front/supply/backup` cities and population thresholds.
- **Resource handling**
- `do금쌀구매` (trade), `doNPC헌납` (donation).
- `do금쌀구매` (trade), `doNPC헌납` (donation).
- **Neutral behavior**
- `do중립` selects between `물자조달`, `인재탐색`, `견문`.
- `do중립` selects between `물자조달`, `인재탐색`, `견문`.
## Data Fields Accessed (Representative)
@@ -171,21 +171,21 @@ GeneralAI reads or writes:
To port the AI to an in-memory state model without behavior drift:
- **Snapshot-first**
- Build a per-turn `GameSnapshot` containing env, nation, cities, generals,
diplomacy, and nation_env. `GeneralAI` should read only from this snapshot.
- Build a per-turn `GameSnapshot` containing env, nation, cities, generals,
diplomacy, and nation_env. `GeneralAI` should read only from this snapshot.
- **Derived caches**
- Cache `DiplomacyState`, `CityBuckets`, `GeneralBuckets`, and `WarRoute`.
- Use lazy recalculation and invalidate only the affected region/city/general
after a command is applied.
- Cache `DiplomacyState`, `CityBuckets`, `GeneralBuckets`, and `WarRoute`.
- Use lazy recalculation and invalidate only the affected region/city/general
after a command is applied.
- **Deterministic ordering**
- For candidate lists, sort by ID before weighted RNG to preserve parity.
- RNG seeding should keep the exact per-turn seed scheme to ensure replay.
- For candidate lists, sort by ID before weighted RNG to preserve parity.
- RNG seeding should keep the exact per-turn seed scheme to ensure replay.
- **Command evaluation**
- Keep `hasFullConditionMet()` semantics intact by providing the same
generalized context (`general`, `city`, `nation`, `dest*`).
- Keep `hasFullConditionMet()` semantics intact by providing the same
generalized context (`general`, `city`, `nation`, `dest*`).
- **Policy snapshots**
- Cache policy values per turn and avoid reading KVStorage per action.
- When policies change, treat it as an explicit state transition.
- Cache policy values per turn and avoid reading KVStorage per action.
- When policies change, treat it as an explicit state transition.
These guidelines mirror the current "derive once, then select via priority"
pattern and minimize resimulation deltas in the rewrite.
+9 -9
View File
@@ -27,18 +27,18 @@ The `AuctionInfo` and `AuctionInfoDetail` DTOs are serialized into
## Core Auction Flow
1. **Open**
- `AuctionBasicResource::openResourceAuction()` validates amounts and
host resources, then inserts `ng_auction`.
- `AuctionUniqueItem::openItemAuction()` checks availability, reserves
unique items, and posts a global history notice.
- `AuctionBasicResource::openResourceAuction()` validates amounts and
host resources, then inserts `ng_auction`.
- `AuctionUniqueItem::openItemAuction()` checks availability, reserves
unique items, and posts a global history notice.
2. **Bid**
- Bids are inserted into `ng_auction_bid` and can extend the close date.
- Reverse auctions (when `detail->isReverse`) sort by lowest bid.
- Bids are inserted into `ng_auction_bid` and can extend the close date.
- Reverse auctions (when `detail->isReverse`) sort by lowest bid.
3. **Close** (`tryFinish()` from the concrete auction)
- If no bids: `rollbackAuction()` returns resources to host and logs.
- With bids: `finishAuction()` transfers resources, logs, and sends messages.
- If no bids: `rollbackAuction()` returns resources to host and logs.
- With bids: `finishAuction()` transfers resources, logs, and sends messages.
4. **Processing**
- `processAuction()` scans auctions past `close_date` and finishes them.
- `processAuction()` scans auctions past `close_date` and finishes them.
## Unique Item Auctions
+2 -2
View File
@@ -9,8 +9,8 @@ engine. References include `legacy/hwe/sammo/CityConstBase.php`,
`CityConstBase` defines default city data and lookup helpers:
- `CityConstBase::$initCity` includes:
- id, name, level, population, agri/comm/secu/def/wall,
region, coordinates, and path connections.
- id, name, level, population, agri/comm/secu/def/wall,
region, coordinates, and path connections.
- Region and level maps convert labels to numeric codes.
- `CityConstBase::byID()` and `byName()` return `CityInitialDetail` objects.
@@ -21,8 +21,8 @@ Constraints declare required inputs using bit flags:
- `REQ_GENERAL`, `REQ_CITY`, `REQ_NATION`
- `REQ_DEST_GENERAL`, `REQ_DEST_CITY`, `REQ_DEST_NATION`
- `REQ_ARG` with typed sub-flags:
- `REQ_STRING_ARG`, `REQ_INT_ARG`, `REQ_NUMERIC_ARG`, `REQ_BOOLEAN_ARG`,
`REQ_ARRAY_ARG`, `REQ_BACKED_ENUM_ARG`
- `REQ_STRING_ARG`, `REQ_INT_ARG`, `REQ_NUMERIC_ARG`, `REQ_BOOLEAN_ARG`,
`REQ_ARRAY_ARG`, `REQ_BACKED_ENUM_ARG`
`Constraint::checkInputValues()` enforces these expectations and throws if
inputs are missing or malformed.
@@ -51,7 +51,6 @@ Constraints are organized by domain:
Each constraint sets a failure reason string used by UI and logs.
## Rewrite References
Rewrite constraint contracts and runtime split details live in
+9 -9
View File
@@ -22,23 +22,23 @@ and `diplomacy`.
- `Message::getMessagesFromMailBox()` reads by mailbox/type with validity
filtering (`valid_until > now`).
- `Message::buildFromArray()` instantiates:
- `DiplomaticMessage` when type is diplomacy.
- `ScoutMessage` or `RaiseInvaderMessage` when `option.action` matches.
- `DiplomaticMessage` when type is diplomacy.
- `ScoutMessage` or `RaiseInvaderMessage` when `option.action` matches.
## DiplomaticMessage Flow
`DiplomaticMessage` wraps diplomacy requests and accepts/rejects them:
- Supported actions:
- `TYPE_NO_AGGRESSION` (불가침)
- `TYPE_CANCEL_NA` (불가침 파기)
- `TYPE_STOP_WAR` (종전)
- `TYPE_NO_AGGRESSION` (불가침)
- `TYPE_CANCEL_NA` (불가침 파기)
- `TYPE_STOP_WAR` (종전)
- `agreeMessage()` validates:
- message is still valid
- receiver is a valid diplomacy officer
- message is still valid
- receiver is a valid diplomacy officer
- On accept, builds and runs the matching nation command:
- `che_불가침수락`, `che_불가침파기수락`, `che_종전수락`
- Uses `NoRNG` to avoid randomness in acceptance.
- `che_불가침수락`, `che_불가침파기수락`, `che_종전수락`
- Uses `NoRNG` to avoid randomness in acceptance.
Each acceptance updates `diplomacyDetail` with command briefs and writes logs.
+13 -12
View File
@@ -8,8 +8,8 @@ updates. References include `legacy/hwe/func_time_event.php`,
## Entry Points
- `TurnExecutionHelper::executeAllCommand()`
- Calls `runEventHandler(PRE_MONTH)``preUpdateMonthly()``turnDate()`
`runEventHandler(MONTH)``postUpdateMonthly()`.
- Calls `runEventHandler(PRE_MONTH)``preUpdateMonthly()``turnDate()`
`runEventHandler(MONTH)``postUpdateMonthly()`.
- Event actions under `legacy/hwe/sammo/Event/Action/` execute concrete
economic updates.
@@ -19,13 +19,14 @@ updates. References include `legacy/hwe/func_time_event.php`,
- Collects all nations and cities, groups generals per nation.
- Computes income using:
- `getGoldIncome()` / `getRiceIncome()` from `func_time_event.php`.
- `getWallIncome()` for rice from walls (same file).
- `getGoldIncome()` / `getRiceIncome()` from `func_time_event.php`.
- `getWallIncome()` for rice from walls (same file).
- Applies nation tax rate (`rate_tmp`) and salary ratio (`bill`).
- Pays generals by `dedication` via `getBill()`.
- Updates `nation_env.prev_income_{gold|rice}` and logs to history.
Key dependencies:
- `GameConst::$basegold`, `GameConst::$baserice` for minimum reserves.
- `GameConst::$resourceActionAmountGuide` for action amounts.
- `iAction::onCalcNationalIncome()` for nation-type modifiers.
@@ -36,8 +37,8 @@ Key dependencies:
- City stats decay by 1% (agri/comm/secu/def/wall).
- Population growth via `popIncrease()`:
- Uses tax rate (`rate_tmp`) and security.
- Nation type can adjust via `onCalcNationalIncome('pop', ...)`.
- Uses tax rate (`rate_tmp`) and security.
- Nation type can adjust via `onCalcNationalIncome('pop', ...)`.
- General and nation resources decay (15% bands based on thresholds).
## War Income and Casualties (ProcessWarIncome)
@@ -62,16 +63,16 @@ Key dependencies:
- Nation level increases based on number of level≥4 cities.
- On level-up:
- Grants gold/rice bonus.
- Logs global and national history.
- Initializes `nation_turn` rows for new chief levels.
- Runs a unique-item lottery (deterministic RNG tagged `nationLevelUp`).
- Grants gold/rice bonus.
- Logs global and national history.
- Initializes `nation_turn` rows for new chief levels.
- Runs a unique-item lottery (deterministic RNG tagged `nationLevelUp`).
## City Trade Rates (RandomizeCityTradeRate)
- Once per month, adjusts `city.trade` with deterministic RNG:
- Probability depends on city level.
- Trade rate ranges 95105 when triggered.
- Probability depends on city level.
- Trade rate ranges 95105 when triggered.
## RNG Notes
+15 -15
View File
@@ -8,10 +8,10 @@ references include `legacy/hwe/sammo/TurnExecutionHelper.php`,
## Entry Points
- `TurnExecutionHelper::runEventHandler(EventTarget $eventTarget)`
- Loads `event` table rows by target and priority, evaluates conditions, and
runs actions.
- Loads `event` table rows by target and priority, evaluates conditions, and
runs actions.
- `StaticEventHandler::handleEvent(...)`
- Invoked by many commands and API handlers to run per-action static hooks.
- Invoked by many commands and API handlers to run per-action static hooks.
## Event Table Schema
@@ -46,18 +46,18 @@ city occupation (`EventTarget::OCCUPY_CITY`, called by some commands).
class instances:
- **Condition**
- Supports logic combinators (`and`, `or`, `xor`, `not`) via
`Event\Condition\Logic`.
- Built-in condition types include:
- `Date`, `DateRelative`, `Interval`
- `RemainNation`
- `ConstBool`
- Conditions return `{ value, chain }` for tracing.
- Supports logic combinators (`and`, `or`, `xor`, `not`) via
`Event\Condition\Logic`.
- Built-in condition types include:
- `Date`, `DateRelative`, `Interval`
- `RemainNation`
- `ConstBool`
- Conditions return `{ value, chain }` for tracing.
- **Action**
- Actions are classes under `Event/Action/` with `run(array $env)`.
- The dispatcher instantiates them from `action` arrays like
`['ProcessIncome', 'gold']`.
- Actions are classes under `Event/Action/` with `run(array $env)`.
- The dispatcher instantiates them from `action` arrays like
`['ProcessIncome', 'gold']`.
## Common Event Actions (Examples)
@@ -90,8 +90,8 @@ Static events are hooks triggered directly by commands/APIs:
`legacy/hwe/sammo/GameConstBase.php`. Scenario JSON can override it:
- `legacy/hwe/scenario/scenario_911.json` (only observed override in repo)
- `sammo\\API\\Troop\\JoinTroop``event_부대탑승즉시이동`
- `sammo\\Command\\Nation\\che_발령``event_부대발령즉시집합`
- `sammo\\API\\Troop\\JoinTroop``event_부대탑승즉시이동`
- `sammo\\Command\\Nation\\che_발령``event_부대발령즉시집합`
Static handler names should map to classes in `legacy/hwe/sammo/StaticEvent/`
(class name matches handler key).
+43 -43
View File
@@ -16,32 +16,32 @@ based on `legacy/hwe/sammo/API/Global/ExecuteEngine.php`,
## Global Turn Loop (`TurnExecutionHelper::executeAllCommand`)
1. **Time gate**
- If `now < game_env.turntime`, return without executing.
- If `now < game_env.turntime`, return without executing.
2. **Locking**
- `tryLock()` on `plock` row (`type='GAME'`).
- If locked or `isunited` in `2|3`, return (frozen state).
- `tryLock()` on `plock` row (`type='GAME'`).
- If locked or `isunited` in `2|3`, return (frozen state).
3. **Pre-turn maintenance**
- Cache game env, `checkDelay()`, `updateOnline()`, `CheckOverhead()`.
- Cache game env, `checkDelay()`, `updateOnline()`, `CheckOverhead()`.
4. **Catch-up monthly loop**
- Compute `prevTurn = cutTurn(turntime, turnterm)` and `nextTurn = addTurn(prevTurn)`.
- While `nextTurn <= now`:
- `executeGeneralCommandUntil(nextTurn, limitActionTime, year, month)`
- `updateTraffic()`
- Monthly pipeline:
- `runEventHandler(PreMonth)`
- `preUpdateMonthly()`
- `turnDate(nextTurn)`
- `checkStatistic()` (only if `month == 1`)
- `runEventHandler(Month)`
- `postUpdateMonthly($monthlyRng)`
- Advance `prevTurn`, `nextTurn`, update `game_env.turntime`.
- Compute `prevTurn = cutTurn(turntime, turnterm)` and `nextTurn = addTurn(prevTurn)`.
- While `nextTurn <= now`:
- `executeGeneralCommandUntil(nextTurn, limitActionTime, year, month)`
- `updateTraffic()`
- Monthly pipeline:
- `runEventHandler(PreMonth)`
- `preUpdateMonthly()`
- `turnDate(nextTurn)`
- `checkStatistic()` (only if `month == 1`)
- `runEventHandler(Month)`
- `postUpdateMonthly($monthlyRng)`
- Advance `prevTurn`, `nextTurn`, update `game_env.turntime`.
5. **Current partial month**
- `turnDate(prevTurn)`
- `executeGeneralCommandUntil(now, limitActionTime, year, month)`
- `turnDate(prevTurn)`
- `executeGeneralCommandUntil(now, limitActionTime, year, month)`
6. **Post-turn maintenance**
- `processTournament()`
- `processAuction()`
- Reset cache, `unlock()`.
- `processTournament()`
- `processAuction()`
- Reset cache, `unlock()`.
`limitActionTime` is derived from `max_execution_time` (roughly 2/3 of the
configured limit). If time runs out mid-loop, it returns early and keeps
@@ -52,36 +52,36 @@ configured limit). If time runs out mid-loop, it returns early and keeps
For each general with `turntime < targetDate` (ordered by `turntime, no`):
1. **Load state**
- `General::createObjFromDB()` and `KVStorage` for `game_env`.
- `General::createObjFromDB()` and `KVStorage` for `game_env`.
2. **Nation command (if officer_level >= 5)**
- Pull `nation_turn` row for `turn_idx = 0`.
- Build `NationCommand` with `LastTurn` stored in `nation_env`.
- Pull `nation_turn` row for `turn_idx = 0`.
- Build `NationCommand` with `LastTurn` stored in `nation_env`.
3. **AI/autorun**
- NPCs (`npc >= 2`) or players past `autorun_limit` use `GeneralAI`.
- AI can replace both nation and general commands.
- NPCs (`npc >= 2`) or players past `autorun_limit` use `GeneralAI`.
- AI can replace both nation and general commands.
4. **Preprocess triggers**
- RNG seed: `hiddenSeed + 'preprocess' + year + month + generalID`.
- `preprocessCommand()` merges action triggers + `che_부상경감`, `che_병력군량소모`.
- RNG seed: `hiddenSeed + 'preprocess' + year + month + generalID`.
- `preprocessCommand()` merges action triggers + `che_부상경감`, `che_병력군량소모`.
5. **Blocked users**
- `processBlocked()` consumes `killturn` and logs if `block >= 2`.
- `processBlocked()` consumes `killturn` and logs if `block >= 2`.
6. **Execute nation command**
- RNG seed: `hiddenSeed + 'nationCommand' + year + month + generalID + commandKey`.
- `processNationCommand()` checks conditions, term stack, `run()`.
- If `run()` fails and `getAlternativeCommand()` exists, it retries.
- Updates `nation_env` with `LastTurn` result.
- RNG seed: `hiddenSeed + 'nationCommand' + year + month + generalID + commandKey`.
- `processNationCommand()` checks conditions, term stack, `run()`.
- If `run()` fails and `getAlternativeCommand()` exists, it retries.
- Updates `nation_env` with `LastTurn` result.
7. **Execute general command**
- Load from `general_turn` (`turn_idx = 0`), default to `휴식`.
- RNG seed: `hiddenSeed + 'generalCommand' + year + month + generalID + commandKey`.
- `processCommand()` checks conditions, term stack, `run()`.
- Updates `killturn` based on NPC status, autorun, or `휴식`.
- Load from `general_turn` (`turn_idx = 0`), default to `휴식`.
- RNG seed: `hiddenSeed + 'generalCommand' + year + month + generalID + commandKey`.
- `processCommand()` checks conditions, term stack, `run()`.
- Updates `killturn` based on NPC status, autorun, or `휴식`.
8. **Queue maintenance & turntime**
- `pullNationCommand()` / `pullGeneralCommand()` advance queues.
- `pullNationCommand()` / `pullGeneralCommand()` advance queues.
- `updateTurnTime()` handles deletion/retirement and sets next `turntime`.
- Persist via `General::applyDB()`.
9. **LastTurn persistence**
- General `LastTurn` lives in `general.last_turn` JSON.
- Nation `LastTurn` is stored in `nation_env` under `turn_last_{officer_level}`.
- `LastTurn.term` only advances when the same command + arg repeats across turns.
- General `LastTurn` lives in `general.last_turn` JSON.
- Nation `LastTurn` is stored in `nation_env` under `turn_last_{officer_level}`.
- `LastTurn.term` only advances when the same command + arg repeats across turns.
## Command Semantics (BaseCommand)
@@ -97,8 +97,8 @@ For each general with `turntime < targetDate` (ordered by `turntime, no`):
## Turntime & Lifecycle (`updateTurnTime`)
- **Inactivity**: if `killturn <= 0`:
- NPC-owned characters can detach (owner -> NPC) if `deadyear` not reached.
- Otherwise the general is deleted (`kill()`).
- NPC-owned characters can detach (owner -> NPC) if `deadyear` not reached.
- Otherwise the general is deleted (`kill()`).
- **Retirement**: if `age >= retirementYear` and not NPC, `CheckHall()` and
`rebirth()` are invoked.
- **Scheduling**: next `turntime` is `addTurn(current, turnterm)` with
+8 -7
View File
@@ -11,9 +11,9 @@ exposes trigger hooks. The main reference is `legacy/hwe/sammo/General.php`.
- `penalty` JSON is decoded into `PenaltyKey -> value` map.
- `aux` JSON is lazily decoded and only written when aux values change.
- Rank data is tracked with:
- `rankVarRead` (current read view)
- `rankVarIncrease` (increment queue)
- `rankVarSet` (explicit set queue)
- `rankVarRead` (current read view)
- `rankVarIncrease` (increment queue)
- `rankVarSet` (explicit set queue)
## Action Objects (`iAction`)
@@ -44,6 +44,7 @@ modifiers and triggers.
- Cached by `(statName, injury, action, adjust)` tuple
Convenience wrappers:
- `getLeadership()`, `getStrength()`, `getIntel()`
## Turn Reservation and LastTurn
@@ -83,10 +84,10 @@ Battle triggers:
- `getBattleInitSkillTriggerList()` merges per-action battle-init triggers.
- `getBattlePhaseSkillTriggerList()` includes base phase triggers:
- `che_필살시도`, `che_필살발동`
- `che_회피시도`, `che_회피발동`
- `che_계략시도`, `che_계략발동`, `che_계략실패`
- plus any action-specific additions.
- `che_필살시도`, `che_필살발동`
- `che_회피시도`, `che_회피발동`
- `che_계략시도`, `che_계략발동`, `che_계략실패`
- plus any action-specific additions.
## Rank and Access Log Updates
+3 -3
View File
@@ -21,7 +21,7 @@ They can:
## Item System
- Base class: `BaseItem` implements `iAction` and exposes:
- `getCost()`, `isConsumable()`, `isBuyable()`, `getReqSecu()`.
- `getCost()`, `isConsumable()`, `isBuyable()`, `getReqSecu()`.
- Stat items inherit `BaseStatItem` and apply a fixed stat bonus in
`onCalcStat()` based on class name tokens.
- Item instances live on the general (`General::getItem()`); items can also
@@ -36,8 +36,8 @@ Items are configured via `GameConst::$allItems` and scenario overrides.
(`onCalcDomestic`), e.g., cost/success/score adjustments.
- `ActionSpecialWar/*` injects battle triggers or stat modifiers.
- Weighting and eligibility are managed by `SpecialityHelper`:
- Uses `selectWeight` + stat/arm constraints to choose specials.
- Deterministic RNG affects special selection for NPCs or resets.
- Uses `selectWeight` + stat/arm constraints to choose specials.
- Deterministic RNG affects special selection for NPCs or resets.
## Personalities
+3 -3
View File
@@ -9,9 +9,9 @@ and `legacy/hwe/sammo/VersionGitDynamic.php`.
`ActionLogger` aggregates logs for a general, nation, and global context:
- Buffers multiple log channels:
- General history/action/battle logs
- National history logs
- Global history/action logs
- General history/action/battle logs
- National history logs
- Global history/action logs
- Supports formatted prefixes (`PLAIN`, `YEAR_MONTH`, `NOTICE`, etc.).
- `flush()` emits buffered logs via helper functions
(`pushGeneralHistoryLog`, `pushGlobalHistoryLog`, etc.).
+11 -11
View File
@@ -7,25 +7,25 @@ and event-driven recruitment. References include `legacy/hwe/sammo/AbsGeneralPoo
## Core Abstractions
- `AbsGeneralPool`
- Wraps a `Scenario\GeneralBuilder` and a raw `info` payload.
- Provides `getGeneralBuilder()`, `getInfo()`, and `occupyGeneralName()`.
- Abstract method: `pickGeneralFromPool(...)`.
- Wraps a `Scenario\GeneralBuilder` and a raw `info` payload.
- Provides `getGeneralBuilder()`, `getInfo()`, and `occupyGeneralName()`.
- Abstract method: `pickGeneralFromPool(...)`.
- `AbsFromUserPool`
- Implements reservation logic backed by the `select_pool` table.
- Reserves candidates for a user (`owner`) until `reserved_until`.
- On selection, clears reservation and binds `general_id`.
- Implements reservation logic backed by the `select_pool` table.
- Reserves candidates for a user (`owner`) until `reserved_until`.
- On selection, clears reservation and binds `general_id`.
## Pool Implementations
- `GeneralPool/RandomNameGeneral`
- Generates random names using `GameConst::$randGen*` lists.
- Checks duplicate counts and appends suffixes if needed.
- Inserts reservation rows into `select_pool` for user selection.
- Generates random names using `GameConst::$randGen*` lists.
- Checks duplicate counts and appends suffixes if needed.
- Inserts reservation rows into `select_pool` for user selection.
- `GeneralPool/SPoolUnderU30`
- Loads a curated pool from `GeneralPool/Pool/UnderS30.json`.
- Inserts rows into `select_pool` with pre-defined stats and specials.
- Loads a curated pool from `GeneralPool/Pool/UnderS30.json`.
- Inserts rows into `select_pool` with pre-defined stats and specials.
## Data Model
+6 -6
View File
@@ -122,8 +122,8 @@ Used for base stats and battle-derived parameters. Common `statName` keys:
1. `General::getPreTurnExecuteTriggerList()` merges triggers from all actions.
2. Base triggers are appended:
- `GeneralTrigger/che_부상경감` (priority 10000 / BEGIN)
- `GeneralTrigger/che_병력군량소모` (priority 50000 / FINAL)
- `GeneralTrigger/che_부상경감` (priority 10000 / BEGIN)
- `GeneralTrigger/che_병력군량소모` (priority 50000 / FINAL)
3. `TriggerCaller::fire()` executes them before the command runs.
General triggers use `General::activateSkill()` for logging and gating.
@@ -146,10 +146,10 @@ Every phase:
1. `WarUnit::beginPhase()` clears activated skills and recomputes war power.
2. `General::getBattlePhaseSkillTriggerList()` builds the trigger list.
- Base triggers (always included):
- `che_필살시도`, `che_필살발동`
- `che_회피시도`, `che_회피발동`
- `che_계략시도`, `che_계략발동`, `che_계략실패`
- Base triggers (always included):
- `che_필살시도`, `che_필살발동`
- `che_회피시도`, `che_회피발동`
- `che_계략시도`, `che_계략발동`, `che_계략실패`
3. Attacker/defender trigger lists are merged and fired.
4. Damage is calculated using `getWarPower()` (after trigger multipliers).
+36 -36
View File
@@ -31,9 +31,9 @@ For defender generals:
- Must have rice > crew/100
- `train` and `atmos` must meet `defence_train`
- Battle order uses:
- `totalStat = (realStat + fullStat) / 2`
- `totalCrew = crew / 1_000_000 * (train * atmos) ^ 1.5`
- `totalStat + totalCrew / 100`
- `totalStat = (realStat + fullStat) / 2`
- `totalCrew = crew / 1_000_000 * (train * atmos) ^ 1.5`
- `totalStat + totalCrew / 100`
For cities:
@@ -44,24 +44,24 @@ For cities:
1. Log start, include seed in battle logs.
2. If no defenders remain, set defender = city and switch to siege.
3. **Initial engagement**
- `setOppose()` for attacker/defender
- `addTrain(1)` for both
- Fire battle-init triggers
- `setOppose()` for attacker/defender
- `addTrain(1)` for both
- Fire battle-init triggers
4. **Per phase**
- `beginPhase()` computes war power
- Fire battle-phase triggers
- `calcDamage()` on both sides
- Clamp damage if it exceeds HP ratios
- Apply damage, increase killed/dead counters
- Log phase results
- `beginPhase()` computes war power
- Fire battle-phase triggers
- `calcDamage()` on both sides
- Clamp damage if it exceeds HP ratios
- Apply damage, increase killed/dead counters
- Log phase results
5. **Continuation checks**
- `continueWar()` fails on no rice or HP <= 0
- On retreat/defeat: log, apply win/lose, try wound
- If defender removed, move to next defender (or city siege)
- `continueWar()` fails on no rice or HP <= 0
- On retreat/defeat: log, apply win/lose, try wound
- If defender removed, move to next defender (or city siege)
6. **Finish**
- `logBattleResult()` for last phase if needed
- `finishBattle()` for attacker/defender
- City conflict tracking and history logs
- `logBattleResult()` for last phase if needed
- `finishBattle()` for attacker/defender
- City conflict tracking and history logs
## Post-Battle Updates (`processWar`)
@@ -118,9 +118,9 @@ Triggered when the defender nation owns exactly one city (the captured city):
attacker nation with a random delay (012 turns), gated by
`GameConst::$joinRuinedNPCProp`.
- Attacker reward:
- Half of defender nation gold/rice above base (`GameConst::$basegold`,
`GameConst::$baserice`) plus half of the aggregated general losses.
- Credited to attacker nation and logged to all chiefs (officer level >= 5).
- Half of defender nation gold/rice above base (`GameConst::$basegold`,
`GameConst::$baserice`) plus half of the aggregated general losses.
- Credited to attacker nation and logged to all chiefs (officer level >= 5).
- Runs `EventTarget::DESTROY_NATION` handlers.
### Nation Survives Path
@@ -128,14 +128,14 @@ Triggered when the defender nation owns exactly one city (the captured city):
If the defender nation still has other cities:
- Demotes city officers (태수/군사/종사) to general:
- `officer_level = 1`, `officer_city = 0`.
- `officer_level = 1`, `officer_city = 0`.
- If the city was the capital:
- Picks a new capital via `findNextCapital()` (closest distance, highest pop).
- Logs an emergency relocation message to global and all nation generals.
- Sets `nation.capital` to new city and halves nation gold/rice.
- Marks new capital as supply city; moves chiefs to it.
- Applies 20% morale loss to all generals (`atmos *= 0.8`).
- Refreshes cached nation static info.
- Picks a new capital via `findNextCapital()` (closest distance, highest pop).
- Logs an emergency relocation message to global and all nation generals.
- Sets `nation.capital` to new city and halves nation gold/rice.
- Marks new capital as supply city; moves chiefs to it.
- Applies 20% morale loss to all generals (`atmos *= 0.8`).
- Refreshes cached nation static info.
### Final Ownership + City Reset
@@ -149,8 +149,8 @@ City stats are reset after ownership is settled:
`officer_set = 0`.
- `agri/comm/secu` multiplied by 0.7.
- `def/wall` reset:
- If `level > 3`: both set to `GameConst::$defaultCityWall`.
- Else: set to `def_max/2`, `wall_max/2`.
- If `level > 3`: both set to `GameConst::$defaultCityWall`.
- Else: set to `def_max/2`, `wall_max/2`.
- Frontline status recalculated for all nearby nations (`SetNationFront()`).
### Deterministic RNG
@@ -168,10 +168,10 @@ Used for:
- Train/atmos bonuses depend on city level and attacker/defender role.
- War power:
- Base attack/defence from crew type + tech
- Adjusted by train/atmos, dex (`getDex()`), crew type coefficients
- Experience level scales war power and counter-scales opponent
- `General::getWarPowerMultiplier()` applies special multipliers
- Base attack/defence from crew type + tech
- Adjusted by train/atmos, dex (`getDex()`), crew type coefficients
- Experience level scales war power and counter-scales opponent
- `General::getWarPowerMultiplier()` applies special multipliers
- Rice consumption on kills: proportional to damage, tech cost, unit rice
- Wound chance: 5% unless `부상무효` / `퇴각부상무효` triggered
- `finishBattle()` updates rank stats, rounds values, and checks stat changes
@@ -183,8 +183,8 @@ Used for:
- Computed attack/defence = `(def + wall * 9) / 500 + 200`
- City train/atmos scales with elapsed years since `startYear`
- Siege state:
- Non-siege battle ends after one exchange
- Siege continues until HP <= 0
- Non-siege battle ends after one exchange
- Siege continues until HP <= 0
- `heavyDecreaseWealth()` halves `agri/comm/secu` on supply-based rout
- `addConflict()` records contribution in `city.conflict` JSON
+130 -130
View File
@@ -23,27 +23,27 @@ interact, and where their state lives. The active runtime lives in
- Class: `legacy/hwe/sammo/General.php` (base: `legacy/hwe/sammo/GeneralBase.php`)
- Tables: `general`, `general_turn`, `rank_data`, `general_access_log`
- Interaction highlights:
- Command execution: `legacy/hwe/sammo/Command/*` and
`legacy/hwe/sammo/TurnExecutionHelper.php`
- Turn reservation: `legacy/hwe/sammo/LastTurn.php`
- Combat adapter: `legacy/hwe/sammo/WarUnitGeneral.php`
- Modifiers/triggers via `iAction` modules (items, specials, traits)
- Logging: `legacy/hwe/sammo/ActionLogger.php`
- Command execution: `legacy/hwe/sammo/Command/*` and
`legacy/hwe/sammo/TurnExecutionHelper.php`
- Turn reservation: `legacy/hwe/sammo/LastTurn.php`
- Combat adapter: `legacy/hwe/sammo/WarUnitGeneral.php`
- Modifiers/triggers via `iAction` modules (items, specials, traits)
- Logging: `legacy/hwe/sammo/ActionLogger.php`
- DB fields (schema):
- `general`: no, owner, npcmsg, npc, npc_org, affinity, bornyear, deadyear,
newmsg, picture, imgsvr, name, owner_name, nation, city, troop, leadership,
leadership_exp, strength, strength_exp, intel, intel_exp, injury,
experience, dedication, dex1, dex2, dex3, dex4, dex5, officer_level,
officer_city, permission, gold, rice, crew, crewtype, train, atmos, weapon,
book, horse, item, turntime, recent_war, makelimit, killturn, block,
dedlevel, explevel, age, startage, belong, betray, personal, special,
specage, special2, specage2, defence_train, tnmt, myset, tournament,
newvote, last_turn, aux, penalty
- `general_turn`: id, general_id, turn_idx, action, arg, brief
- `general_access_log`: id, general_id, user_id, last_refresh, refresh,
refresh_total, refresh_score, refresh_score_total
- `rank_data`: id, nation_id, general_id, type, value
- JSON columns: `general.last_turn`, `general.aux`, `general.penalty`
- `general`: no, owner, npcmsg, npc, npc_org, affinity, bornyear, deadyear,
newmsg, picture, imgsvr, name, owner_name, nation, city, troop, leadership,
leadership_exp, strength, strength_exp, intel, intel_exp, injury,
experience, dedication, dex1, dex2, dex3, dex4, dex5, officer_level,
officer_city, permission, gold, rice, crew, crewtype, train, atmos, weapon,
book, horse, item, turntime, recent_war, makelimit, killturn, block,
dedlevel, explevel, age, startage, belong, betray, personal, special,
specage, special2, specage2, defence_train, tnmt, myset, tournament,
newvote, last_turn, aux, penalty
- `general_turn`: id, general_id, turn_idx, action, arg, brief
- `general_access_log`: id, general_id, user_id, last_refresh, refresh,
refresh_total, refresh_score, refresh_score_total
- `rank_data`: id, nation_id, general_id, type, value
- JSON columns: `general.last_turn`, `general.aux`, `general.penalty`
### Nation (국가)
@@ -51,18 +51,18 @@ interact, and where their state lives. The active runtime lives in
- Nation-type actions: `legacy/hwe/sammo/ActionNationType/*`,
base in `legacy/hwe/sammo/BaseNation.php`
- Interaction highlights:
- Nation commands: `legacy/hwe/sammo/Command/Nation/*`
- Diplomacy: `diplomacy`, `ng_diplomacy`, and
`legacy/hwe/sammo/DiplomaticMessage.php`
- Monthly economy updates: `legacy/hwe/sammo/Event/Action/*`
- Nation commands: `legacy/hwe/sammo/Command/Nation/*`
- Diplomacy: `diplomacy`, `ng_diplomacy`, and
`legacy/hwe/sammo/DiplomaticMessage.php`
- Monthly economy updates: `legacy/hwe/sammo/Event/Action/*`
- DB fields (schema):
- `nation`: nation, name, color, capital, capset, gennum, gold, rice, bill,
rate, rate_tmp, secretlimit, chief_set, scout, war, strategic_cmd_limit,
surlimit, tech, power, spy, level, type, aux
- `nation_turn`: id, nation_id, officer_level, turn_idx, action, arg, brief
- `nation_env`: id, namespace, key, value
- JSON columns: `nation.spy`, `nation.aux`, `nation_turn.arg`,
`nation_env.value`
- `nation`: nation, name, color, capital, capset, gennum, gold, rice, bill,
rate, rate_tmp, secretlimit, chief_set, scout, war, strategic_cmd_limit,
surlimit, tech, power, spy, level, type, aux
- `nation_turn`: id, nation_id, officer_level, turn_idx, action, arg, brief
- `nation_env`: id, namespace, key, value
- JSON columns: `nation.spy`, `nation.aux`, `nation_turn.arg`,
`nation_env.value`
### City (도시)
@@ -71,22 +71,22 @@ interact, and where their state lives. The active runtime lives in
`legacy/hwe/sammo/CityInitialDetail.php`
- Live cache: `legacy/hwe/sammo/CityHelper.php`
- Interaction highlights:
- Supply and isolation updates via `Event/Action/UpdateCitySupply`
- Conquest/war defender: `legacy/hwe/sammo/WarUnitCity.php`
- Supply and isolation updates via `Event/Action/UpdateCitySupply`
- Conquest/war defender: `legacy/hwe/sammo/WarUnitCity.php`
- DB fields (schema):
- `city`: city, name, level, nation, supply, front, pop, pop_max, agri,
agri_max, comm, comm_max, secu, secu_max, trust, trade, dead, def, def_max,
wall, wall_max, officer_set, state, region, term, conflict
- JSON columns: `city.conflict`
- `city`: city, name, level, nation, supply, front, pop, pop_max, agri,
agri_max, comm, comm_max, secu, secu_max, trust, trade, dead, def, def_max,
wall, wall_max, officer_set, state, region, term, conflict
- JSON columns: `city.conflict`
### Troop (부대)
- Data rows: `troop`
- Interaction highlights:
- Join/leave flows in `legacy/hwe/sammo/API/Troop/*`
- Static event hooks from `legacy/hwe/sammo/StaticEvent/*`
- Join/leave flows in `legacy/hwe/sammo/API/Troop/*`
- Static event hooks from `legacy/hwe/sammo/StaticEvent/*`
- DB fields (schema):
- `troop`: troop_leader, nation, name
- `troop`: troop_leader, nation, name
### Command / Turn (턴 예약과 실행)
@@ -96,8 +96,8 @@ interact, and where their state lives. The active runtime lives in
- Execution entry: `legacy/hwe/sammo/TurnExecutionHelper.php` and
`legacy/hwe/proc.php`
- DB fields (schema):
- `general_turn`: id, general_id, turn_idx, action, arg, brief
- `nation_turn`: id, nation_id, officer_level, turn_idx, action, arg, brief
- `general_turn`: id, general_id, turn_idx, action, arg, brief
- `nation_turn`: id, nation_id, officer_level, turn_idx, action, arg, brief
### War Units (전투 단위)
@@ -105,8 +105,8 @@ interact, and where their state lives. The active runtime lives in
- General combat: `legacy/hwe/sammo/WarUnitGeneral.php`
- City combat: `legacy/hwe/sammo/WarUnitCity.php`
- Interaction highlights:
- Battle flow and resolution: `legacy/hwe/process_war.php`
- Uses General/Nation/City snapshots for deterministic resolution
- Battle flow and resolution: `legacy/hwe/process_war.php`
- Uses General/Nation/City snapshots for deterministic resolution
## Rule Modules and Modifiers
@@ -114,13 +114,13 @@ interact, and where their state lives. The active runtime lives in
- Interface: `legacy/hwe/sammo/iAction.php`
- Modules:
- Items: `legacy/hwe/sammo/ActionItem/*`
- Domestic specials: `legacy/hwe/sammo/ActionSpecialDomestic/*`
- War specials: `legacy/hwe/sammo/ActionSpecialWar/*`
- Personalities: `legacy/hwe/sammo/ActionPersonality/*`
- Nation types: `legacy/hwe/sammo/ActionNationType/*`
- Crew types: `legacy/hwe/sammo/ActionCrewType/*`
- Scenario effects: `legacy/hwe/sammo/ActionScenarioEffect/*`
- Items: `legacy/hwe/sammo/ActionItem/*`
- Domestic specials: `legacy/hwe/sammo/ActionSpecialDomestic/*`
- War specials: `legacy/hwe/sammo/ActionSpecialWar/*`
- Personalities: `legacy/hwe/sammo/ActionPersonality/*`
- Nation types: `legacy/hwe/sammo/ActionNationType/*`
- Crew types: `legacy/hwe/sammo/ActionCrewType/*`
- Scenario effects: `legacy/hwe/sammo/ActionScenarioEffect/*`
- Applied by `General::getActionList()` and used by triggers, stat calculation,
and combat phases.
@@ -146,8 +146,8 @@ interact, and where their state lives. The active runtime lives in
- Static hooks: `legacy/hwe/sammo/StaticEvent/*`,
wired via `GameConst::$staticEventHandlers`
- DB fields (schema):
- `event`: id, target, priority, condition, action
- JSON columns: `event.condition`, `event.action`
- `event`: id, target, priority, condition, action
- JSON columns: `event.condition`, `event.action`
### Messaging and Diplomacy
@@ -157,11 +157,11 @@ interact, and where their state lives. The active runtime lives in
- Diplomacy wrapper: `legacy/hwe/sammo/DiplomaticMessage.php`
- Table: `message`
- DB fields (schema):
- `message`: id, mailbox, type, src, dest, time, valid_until, message
- `diplomacy`: no, me, you, state, term, dead, showing
- `ng_diplomacy`: no, src_nation_id, dest_nation_id, prev_no, state,
text_brief, text_detail, date, src_signer, dest_signer, aux
- JSON columns: `message.message`, `ng_diplomacy.aux`
- `message`: id, mailbox, type, src, dest, time, valid_until, message
- `diplomacy`: no, me, you, state, term, dead, showing
- `ng_diplomacy`: no, src_nation_id, dest_nation_id, prev_no, state,
text_brief, text_detail, date, src_signer, dest_signer, aux
- JSON columns: `message.message`, `ng_diplomacy.aux`
### Economy and World State
@@ -170,10 +170,10 @@ interact, and where their state lives. The active runtime lives in
- Trade and supply: `RandomizeCityTradeRate`, `UpdateCitySupply`,
`UpdateNationLevel`
- DB fields (schema):
- `nation_env`: id, namespace, key, value
- `statistic`: no, year, month, nation_count, nation_name, nation_hist,
gen_count, personal_hist, special_hist, power_hist, crewtype, etc, aux
- JSON columns: `statistic.aux`, `nation_env.value`
- `nation_env`: id, namespace, key, value
- `statistic`: no, year, month, nation_count, nation_name, nation_hist,
gen_count, personal_hist, special_hist, power_hist, crewtype, etc, aux
- JSON columns: `statistic.aux`, `nation_env.value`
### Auctions, Betting, and Votes
@@ -181,17 +181,17 @@ interact, and where their state lives. The active runtime lives in
- Betting: `legacy/hwe/sammo/Betting.php` and `ng_betting`
- Votes and tournaments: `vote`, `vote_comment`, `tournament`
- DB fields (schema):
- `ng_auction`: id, type, finished, target, host_general_id, req_resource,
open_date, close_date, detail
- `ng_auction_bid`: no, auction_id, owner, general_id, amount, date, aux
- `ng_betting`: id, betting_id, general_id, user_id, betting_type, amount
- `vote`: id, vote_id, general_id, nation_id, selection
- `vote_comment`: id, vote_id, general_id, nation_id, general_name,
nation_name, text, date
- `tournament`: seq, no, npc, name, w, b, h, leadership, strength, intel, lvl,
grp, grp_no, win, draw, lose, gl, prmt
- JSON columns: `ng_auction.detail`, `ng_auction_bid.aux`, `ng_betting.betting_type`,
`vote.selection`
- `ng_auction`: id, type, finished, target, host_general_id, req_resource,
open_date, close_date, detail
- `ng_auction_bid`: no, auction_id, owner, general_id, amount, date, aux
- `ng_betting`: id, betting_id, general_id, user_id, betting_type, amount
- `vote`: id, vote_id, general_id, nation_id, selection
- `vote_comment`: id, vote_id, general_id, nation_id, general_name,
nation_name, text, date
- `tournament`: seq, no, npc, name, w, b, h, leadership, strength, intel, lvl,
grp, grp_no, win, draw, lose, gl, prmt
- JSON columns: `ng_auction.detail`, `ng_auction_bid.aux`, `ng_betting.betting_type`,
`vote.selection`
### Logs and Records
@@ -199,14 +199,14 @@ interact, and where their state lives. The active runtime lives in
- User logs: `legacy/hwe/sammo/UserLogger.php`
- Tables: `ng_history`, `world_history`, `general_record`, `user_record`, `hall`
- DB fields (schema):
- `ng_history`: no, server_id, year, month, map, global_history, global_action,
nations
- `world_history`: id, nation_id, year, month, text
- `general_record`: id, general_id, log_type, year, month, text
- `user_record`: id, user_id, server_id, log_type, year, month, date, text
- `hall`: id, server_id, season, scenario, general_no, type, value, owner, aux
- JSON columns: `ng_history.map`, `ng_history.global_history`,
`ng_history.global_action`, `ng_history.nations`, `hall.aux`
- `ng_history`: no, server_id, year, month, map, global_history, global_action,
nations
- `world_history`: id, nation_id, year, month, text
- `general_record`: id, general_id, log_type, year, month, text
- `user_record`: id, user_id, server_id, log_type, year, month, date, text
- `hall`: id, server_id, season, scenario, general_no, type, value, owner, aux
- JSON columns: `ng_history.map`, `ng_history.global_history`,
`ng_history.global_action`, `ng_history.nations`, `hall.aux`
### Boards, Locks, and Storage
@@ -214,13 +214,13 @@ interact, and where their state lives. The active runtime lives in
- Locks: `plock`
- KV stores: `storage`, `nation_env`
- DB fields (schema):
- `board`: no, nation_no, is_secret, date, general_no, author, author_icon,
title, text
- `comment`: no, nation_no, is_secret, date, document_no, general_no, author,
text
- `plock`: no, type, plock, locktime
- `storage`: id, namespace, key, value
- JSON columns: `storage.value`
- `board`: no, nation_no, is_secret, date, general_no, author, author_icon,
title, text
- `comment`: no, nation_no, is_secret, date, document_no, general_no, author,
text
- `plock`: no, type, plock, locktime
- `storage`: id, namespace, key, value
- JSON columns: `storage.value`
### Archive and Lifecycle Tables
@@ -230,24 +230,24 @@ interact, and where their state lives. The active runtime lives in
- Selection pools: `select_npc_token`, `select_pool`
- Inheritance: `inheritance_result`
- DB fields (schema):
- `ng_games`: id, server_id, date, winner_nation, map, season, scenario,
scenario_name, env
- `ng_old_nations`: id, server_id, nation, data, date
- `ng_old_generals`: id, server_id, general_no, owner, name, last_yearmonth,
turntime, data
- `emperior`: no, server_id, phase, nation_count, nation_name, nation_hist,
gen_count, personal_hist, special_hist, name, type, color, year, month,
power, gennum, citynum, pop, poprate, gold, rice, l12name, l12pic, l11name,
l11pic, l10name, l10pic, l9name, l9pic, l8name, l8pic, l7name, l7pic,
l6name, l6pic, l5name, l5pic, tiger, eagle, gen, history, aux
- `reserved_open`: id, options, date
- `select_npc_token`: id, owner, valid_until, pick_more_from, pick_result,
nonce
- `select_pool`: id, unique_name, owner, general_id, reserved_until, info
- `inheritance_result`: id, server_id, owner, general_id, year, month, value
- JSON columns: `ng_games.env`, `ng_old_nations.data`, `ng_old_generals.data`,
`emperior.history`, `emperior.aux`, `reserved_open.options`,
`select_npc_token.pick_result`, `inheritance_result.value`
- `ng_games`: id, server_id, date, winner_nation, map, season, scenario,
scenario_name, env
- `ng_old_nations`: id, server_id, nation, data, date
- `ng_old_generals`: id, server_id, general_no, owner, name, last_yearmonth,
turntime, data
- `emperior`: no, server_id, phase, nation_count, nation_name, nation_hist,
gen_count, personal_hist, special_hist, name, type, color, year, month,
power, gennum, citynum, pop, poprate, gold, rice, l12name, l12pic, l11name,
l11pic, l10name, l10pic, l9name, l9pic, l8name, l8pic, l7name, l7pic,
l6name, l6pic, l5name, l5pic, tiger, eagle, gen, history, aux
- `reserved_open`: id, options, date
- `select_npc_token`: id, owner, valid_until, pick_more_from, pick_result,
nonce
- `select_pool`: id, unique_name, owner, general_id, reserved_until, info
- `inheritance_result`: id, server_id, owner, general_id, year, month, value
- JSON columns: `ng_games.env`, `ng_old_nations.data`, `ng_old_generals.data`,
`emperior.history`, `emperior.aux`, `reserved_open.options`,
`select_npc_token.pick_result`, `inheritance_result.value`
## Aux and JSON Payloads
@@ -259,17 +259,17 @@ not enforced by DB constraints.
- Storage layer: `legacy/hwe/sammo/LazyVarAndAuxUpdater.php`
- Access pattern: `getAuxVar()` / `setAuxVar()` on `General`
- Literal keys observed in code (not exhaustive):
- `armType`, `autorun_limit`, `inheritBuff`, `inheritRandomUnique`,
`inheritResetSpecialWar`, `inheritResetTurnTime`,
`inheritSpecificSpecialWar`, `last발령`, `max_domestic_critical`,
`movingTargetCityID`, `nextTurnTimeBase`, `pickYearMonth`,
`use_auto_nation_turn`, `use_treatment`
- `armType`, `autorun_limit`, `inheritBuff`, `inheritRandomUnique`,
`inheritResetSpecialWar`, `inheritResetTurnTime`,
`inheritSpecificSpecialWar`, `last발령`, `max_domestic_critical`,
`movingTargetCityID`, `nextTurnTimeBase`, `pickYearMonth`,
`use_auto_nation_turn`, `use_treatment`
- Key sources:
- Turn execution timing: `legacy/hwe/sammo/TurnExecutionHelper.php`
- AI state and movement: `legacy/hwe/sammo/GeneralAI.php`
- Inheritance actions: `legacy/hwe/sammo/API/InheritAction/*`
- Domestic critical resets: `legacy/hwe/sammo/Command/General/*`
- Troop dispatch markers: `legacy/hwe/sammo/Command/Nation/che_발령.php`
- Turn execution timing: `legacy/hwe/sammo/TurnExecutionHelper.php`
- AI state and movement: `legacy/hwe/sammo/GeneralAI.php`
- Inheritance actions: `legacy/hwe/sammo/API/InheritAction/*`
- Domestic critical resets: `legacy/hwe/sammo/Command/General/*`
- Troop dispatch markers: `legacy/hwe/sammo/Command/Nation/che_발령.php`
- Item or trigger-specific keys are stored via constants (e.g.
`ActionItem/event_충차.php`, `WarUnitTrigger/event_충차아이템소모.php`) and are
not visible in literal scans.
@@ -277,10 +277,10 @@ not enforced by DB constraints.
### Nation `aux`
- Enum keys: `legacy/hwe/sammo/Enums/NationAuxKey.php`
- `can_국기변경`, `can_국호변경`, `did_특성초토화`, `can_무작위수도이전`,
`can_대검병사용`, `can_극병사용`, `can_화시병사용`, `can_원융노병사용`,
`can_산저병사용`, `can_상병사용`, `can_음귀병사용`, `can_무희사용`,
`can_화륜차사용`
- `can_국기변경`, `can_국호변경`, `did_특성초토화`, `can_무작위수도이전`,
`can_대검병사용`, `can_극병사용`, `can_화시병사용`, `can_원융노병사용`,
`can_산저병사용`, `can_상병사용`, `can_음귀병사용`, `can_무희사용`,
`can_화륜차사용`
- Used by constraints and nation commands, especially unit unlock logic.
### Other JSON Columns
@@ -372,18 +372,18 @@ This index focuses on primary runtime touch points and omits view-only pages.
## Interaction Sketch
- Turn execution (`TurnExecutionHelper`):
- Reads `general_turn` / `nation_turn`
- Builds General/Nation/City context
- Applies constraints → runs command → triggers → logs
- Runs monthly events and world updates
- Reads `general_turn` / `nation_turn`
- Builds General/Nation/City context
- Applies constraints → runs command → triggers → logs
- Runs monthly events and world updates
- Battle (`process_war.php`):
- Builds `WarUnitGeneral` and `WarUnitCity`
- Resolves phases and damage with triggers
- Applies city conquest and nation collapse rules
- Builds `WarUnitGeneral` and `WarUnitCity`
- Resolves phases and damage with triggers
- Applies city conquest and nation collapse rules
- Diplomacy:
- `Message` delivers a diplomacy request
- `DiplomaticMessage` accepts/rejects
- Nation command executes and updates diplomacy tables
- `Message` delivers a diplomacy request
- `DiplomaticMessage` accepts/rejects
- Nation command executes and updates diplomacy tables
## Entity Relationships (Mermaid ERD)
+8 -8
View File
@@ -43,11 +43,11 @@ Directly stored keys (`storeType = true`):
Computed keys (`storeType = false` or derived):
- `combat`: from `rank_data.warnum` (war engagements) * 5.
- `sabotage`: from `rank_data.firenum` (strategy success) * 20.
- `dex`: sum of arm-type dex, with overflow reduction, * 0.001.
- `combat`: from `rank_data.warnum` (war engagements) \* 5.
- `sabotage`: from `rank_data.firenum` (strategy success) \* 20.
- `dex`: sum of arm-type dex, with overflow reduction, \* 0.001.
- `betting`: `betwin * 10 * (betwingold / betgold)^2`.
- `max_belong`: max of `general.belong` and aux `max_belong`, * 10.
- `max_belong`: max of `general.belong` and aux `max_belong`, \* 10.
Notes:
@@ -60,11 +60,11 @@ Notes:
The system uses a two-step merge:
1. **Merge** (`mergeTotalInheritancePoint`)
- Computes derived keys and stores them into `inheritance_{userID}`.
- Records a snapshot into `inheritance_result`.
- Computes derived keys and stores them into `inheritance_{userID}`.
- Records a snapshot into `inheritance_result`.
2. **Apply** (`applyInheritanceUser`)
- Sums all keys into a total, logs to `user_record`, resets storage to only
`previous` (plus any rebirth-kept keys).
- Sums all keys into a total, logs to `user_record`, resets storage to only
`previous` (plus any rebirth-kept keys).
This merge/apply is triggered in these places:
+7 -5
View File
@@ -10,9 +10,9 @@ rule set, commands, and effects. Core references include
1. Server reset/init calls `ResetHelper::buildScenario()`.
2. `Scenario` loads `scenario_{id}.json` and merges defaults (`default.json`).
3. `Scenario::buildConf()` generates runtime constants:
- `d_setting/GameConst.php` from `GameConstBase + scenario.const/map/stat`
- `d_setting/CityConst.php` from `scenario/map/{mapName}.php`
- `d_setting/GameUnitConst.php` from `scenario/unit/{unitSet}.php`
- `d_setting/GameConst.php` from `GameConstBase + scenario.const/map/stat`
- `d_setting/CityConst.php` from `scenario/map/{mapName}.php`
- `d_setting/GameUnitConst.php` from `scenario/unit/{unitSet}.php`
4. `Scenario::build()` inserts nations, generals, and events into DB and runs
`initialEvents` immediately.
@@ -39,7 +39,7 @@ Notes:
engine currently reads `initialEvents` only.
- `general` rows use the tuple format from `Scenario::generateGeneral()`:
`affinity, name, picture, nationName, city, leadership, strength, intel,
officerLevel, birth, death, ego, char, text`.
officerLevel, birth, death, ego, char, text`.
## How Scenario Chooses Commands and Effects
@@ -77,6 +77,7 @@ These are the map/unit/effect variants referenced by existing scenario files.
Defaults are `mapName=che` and `unitSet=che` when not specified.
Map sets (`scenario/map/*.php`):
- `che` (default)
- `miniche`, `miniche_b`, `miniche_clean`
- `cr`
@@ -85,6 +86,7 @@ Map sets (`scenario/map/*.php`):
- `ludo_rathowm`
Unit sets (`scenario/unit/*.php`):
- `che` (default)
- `che_except_siege`
- `cr`
@@ -94,6 +96,7 @@ Unit sets (`scenario/unit/*.php`):
- `ludo_rathowm`
Scenario effects (`sammo/ActionScenarioEffect/*`):
- `event_StrongAttacker`
- `event_UnlimitedDefenceThresholdChange`
- `event_MoreEffect`
@@ -106,4 +109,3 @@ Scenario events are stored in the `event` table and executed via
Most scenario JSON uses lowercase targets (e.g. `"month"`). The DB enum uses
uppercase values but is case-insensitive, so lowercase targets still match.
+4 -8
View File
@@ -58,16 +58,12 @@ export interface Constraint {
- `ConstraintPlanner` collects requirements across constraints.
- `StateView` loads those requirements (daemon: in-memory, API: DB).
- `test()` returns:
- `allow` if constraint passes.
- `deny` with a stable reason/code for UI.
- `unknown` if required data is missing and `mode === 'precheck'`.
- `allow` if constraint passes.
- `deny` with a stable reason/code for UI.
- `unknown` if required data is missing and `mode === 'precheck'`.
```ts
function evaluateConstraints(
constraints: Constraint[],
ctx: ConstraintContext,
view: StateView,
): ConstraintResult {
function evaluateConstraints(constraints: Constraint[], ctx: ConstraintContext, view: StateView): ConstraintResult {
for (const constraint of constraints) {
const missing = constraint.requires(ctx).filter((req) => !view.has(req));
if (missing.length && ctx.mode === 'precheck') {
+24 -23
View File
@@ -50,16 +50,17 @@ Gateway loads the profile table on boot, then reconciles PM2 to match.
### Boot Reconciliation
1) Load profile rows from DB.
2) List PM2 processes and map `profileName -> running state`.
3) For each profile:
- If desired `RUNNING/PREOPEN/PAUSED/COMPLETED` and any process is missing, start.
- If desired `RESERVED/STOPPED/DISABLED` and any process is running, stop both.
4) Persist errors to DB for audit.
1. Load profile rows from DB.
2. List PM2 processes and map `profileName -> running state`.
3. For each profile:
- If desired `RUNNING/PREOPEN/PAUSED/COMPLETED` and any process is missing, start.
- If desired `RESERVED/STOPPED/DISABLED` and any process is running, stop both.
4. Persist errors to DB for audit.
### Internal Scheduler (Gateway Cron)
Gateway runs a lightweight cron loop (setInterval) that:
- When `RESERVED` and `preopenAt <= now`, queue a build for the reserved commit.
- When build succeeds, status becomes `PREOPEN` (daemon paused).
- When `openAt <= now`, status becomes `RUNNING` and daemon resumes.
@@ -102,15 +103,15 @@ replay while pub/sub keeps live updates simple and low-latency.
### Recommended Split
- Redis Streams:
- API server -> daemon: mutation requests, turn-run commands.
- Daemon -> API server: run status events, job results, error reports.
- Use consumer groups for daemon workers and API server listeners.
- Require `requestId` for correlation and idempotency.
- Ack on success; move failed items to a dead-letter stream after retry.
- API server -> daemon: mutation requests, turn-run commands.
- Daemon -> API server: run status events, job results, error reports.
- Use consumer groups for daemon workers and API server listeners.
- Require `requestId` for correlation and idempotency.
- Ack on success; move failed items to a dead-letter stream after retry.
- Redis pub/sub:
- Daemon -> API server: low-stakes live update signals (run started/ended).
- API server -> frontend: SSE fan-out triggered by pub/sub updates.
- Do not use pub/sub for data that must be replayed or audited.
- Daemon -> API server: low-stakes live update signals (run started/ended).
- API server -> frontend: SSE fan-out triggered by pub/sub updates.
- Do not use pub/sub for data that must be replayed or audited.
### Operational Notes
@@ -158,12 +159,12 @@ also supports local ID/password login for users who cannot use Kakao.
- The turn daemon runs as a single-threaded loop.
- The daemon engine uses in-memory state as the primary working set.
- The daemon waits on two conditions during the event loop.
- Query/command requests from the external API server.
- The scheduled start time of the next turn.
- Query/command requests from the external API server.
- The scheduled start time of the next turn.
- External requests are processed until the next turn start time is reached.
- If no requests arrive, the daemon waits until the next turn start time.
- When the next turn start time arrives, the daemon starts turn processing
immediately even if requests remain queued.
- If no requests arrive, the daemon waits until the next turn start time.
- When the next turn start time arrives, the daemon starts turn processing
immediately even if requests remain queued.
- While the daemon is resolving a turn, the API server queues incoming requests.
Note: the current implementation does not yet process API mutation requests
@@ -203,16 +204,16 @@ export type DaemonEvent =
- API server requests are delivered to the daemon via Redis Streams or
Redis pub/sub.
- Redis Stream mutation requests are rate-limited per user.
- Each user can have up to 30 pending mutation requests.
- Additional requests are rejected once the limit is exceeded.
- Each user can have up to 30 pending mutation requests.
- Additional requests are rejected once the limit is exceeded.
### In-Memory and DBMS Flush
- The daemon processes actions against in-memory state by default.
- DBMS writes are flushed in bulk after turn processing completes.
- Frequently changing "next-turn intent" data is stored separately.
- The API server persists this data in the DBMS.
- The daemon loads only this data when the next turn begins.
- The API server persists this data in the DBMS.
- The daemon loads only this data when the next turn begins.
## Turn Daemon vs API Query Priority (Outline)
+1 -1
View File
@@ -35,7 +35,7 @@ Move items into the main docs once they are finalized.
- Deterministic RNG test harness guidelines
- Output comparison rules (sorting, tolerances, diff granularity)
- Unit test vs simulation test split and responsibilities
- [AI suggestion] Document che_출병 parity gaps vs legacy (city state/term=43, fallback to che_이동 when friendly, nation.war/AllowWar check, post-war static events/unique-item lottery, missing route data when map/diplomacy not provided).
- [AI suggestion] Document che*출병 parity gaps vs legacy (city state/term=43, fallback to che*이동 when friendly, nation.war/AllowWar check, post-war static events/unique-item lottery, missing route data when map/diplomacy not provided).
- [AI suggestion] Verify that "이호경식" followed by "출병" is correctly blocked with the new reserved-turn overlay (diplomacy state sync).
## Trigger System
+9 -2
View File
@@ -5,9 +5,10 @@ The key is to separate "DB behavior emulation" from "state/logic/flow validation
so each layer is tested with the right scope.
## Core Principles
- Prefer Repository/DB Port interfaces over ORM mocks; split implementations:
- InMemory Repository (Fake)
- Real DB Repository (e.g., Prisma)
- InMemory Repository (Fake)
- Real DB Repository (e.g., Prisma)
- Validate domain constraints in the logic layer where possible; DB constraints
act as a secondary safety net.
- Use deterministic seeds for all gameplay-impacting RNG.
@@ -15,6 +16,7 @@ so each layer is tested with the right scope.
## Test Layers
### 1) Constraint Tests
Goal: ensure constraints (unique/foreign key/check) behave consistently in
both InMemory state and the DB.
@@ -24,6 +26,7 @@ both InMemory state and the DB.
- Use real DB only in integration tests.
### 2) Game Logic / Command Tests
Goal: verify state input -> state output and that flush behaves as expected.
- Unit tests: run logic against InMemory state and assert outputs.
@@ -32,6 +35,7 @@ Goal: verify state input -> state output and that flush behaves as expected.
- "Send to DB" behavior is validated via real DB tests.
### 3) Turn Flow Tests
Goal: verify the end-to-end scheduler/turn processing flow.
- Nature: integration/system tests.
@@ -40,16 +44,19 @@ Goal: verify the end-to-end scheduler/turn processing flow.
- Keep a small set of smoke/regression scenarios due to cost.
### 4) Scenario Build Tests
Goal: ensure scenario parsing and DB application are correct.
- Unit tests: parse/validate scenario files (map size, ID collisions, ranges).
- Integration tests: load scenarios into a test DB and verify key tables.
## Result Composition Guidelines
- Unit tests: fast, wide coverage.
- Integration tests: focus on real DB/Redis behavior.
- Smoke tests: minimal coverage of turn flow/build/scenario loading.
## MockDB Conclusion
- "InMemory Repository (Fake)" is more practical than an ORM mock.
- DB-level behavior (constraints/transactions/locks) belongs to integration tests.