Add internal architecture analysis (docs/INTERNALS.md)
This commit is contained in:
38
AGENTS.md
38
AGENTS.md
@@ -4,43 +4,45 @@
|
|||||||
|
|
||||||
Decompilation + modding re-architecture of "Slave Matrix" (Auto Eden). Three .NET 8 projects:
|
Decompilation + modding re-architecture of "Slave Matrix" (Auto Eden). Three .NET 8 projects:
|
||||||
- **SlaveMatrix** (`WinExe`) — game entrypoint `SlaveMatrix.Program.Main` at `SlaveMatrix/SlaveMatrix/GameClasses/Program.cs`
|
- **SlaveMatrix** (`WinExe`) — game entrypoint `SlaveMatrix.Program.Main` at `SlaveMatrix/SlaveMatrix/GameClasses/Program.cs`
|
||||||
- **2DGAMELIB** (class library) — legacy rendering engine (GDI+) + core data types (BodyTemplate, ShapePart, etc.)
|
- **2DGAMELIB** (class library) — legacy GDI+ rendering engine + core data types (BodyTemplate, ShapePart, etc.)
|
||||||
- **SlaveMatrix.Extract** (`Exe`) — asset pipeline CLI; depends on SlaveMatrix for embedded resources
|
- **SlaveMatrix.Extract** (`Exe`) — asset pipeline CLI; depends on SlaveMatrix for embedded resources
|
||||||
|
|
||||||
## Build & Run
|
## Build & Run
|
||||||
|
|
||||||
```
|
```
|
||||||
dotnet build Solution.sln # build all
|
dotnet build Solution.sln # build all
|
||||||
./run.sh # launch game (cds to game_folder/, sets DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1)
|
./run.sh # cd game_folder/ + DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 + dotnet run
|
||||||
dotnet run --project SlaveMatrix.Extract -- --output <path> # asset extraction
|
dotnet run --project SlaveMatrix.Extract -- --output <path> # asset extraction
|
||||||
```
|
```
|
||||||
|
|
||||||
The game **must** run with `game_folder/` as working directory. `launchSettings.json` sets this automatically in VS/Rider.
|
Game **must** run with `game_folder/` as working directory. `launchSettings.json` sets `../game_folder` automatically.
|
||||||
|
|
||||||
## Key Quirks
|
## Key Quirks
|
||||||
|
|
||||||
- **C# 9.0** (`LangVersion` hardcoded in `.csproj`). No `ImplicitUsings` in SlaveMatrix/2DGAMELIB.
|
- **C# 9.0** (`LangVersion` in `.csproj`). No `ImplicitUsings` in SlaveMatrix/2DGAMELIB. Extract has `ImplicitUsings=enable` + `Nullable=enable` — it can use modern C#.
|
||||||
- **x64 only**, `AllowUnsafeBlocks=true`, `InvariantGlobalization=true` (needed on Linux).
|
- **x64 only**, `AllowUnsafeBlocks=true`, `InvariantGlobalization=true` (required on Linux).
|
||||||
- **BinaryFormatter** still in use (legacy saves). Requires `EnableUnsafeBinaryFormatterSerialization=true` and `NoWarn=SYSLIB0011`.
|
- **BinaryFormatter** for legacy saves: `EnableUnsafeBinaryFormatterSerialization=true`, `NoWarn=SYSLIB0011`.
|
||||||
- **`System.Drawing.EnableUnixSupport`** must be set at startup (`runtimeconfig.template.json` + `AppContext.SetSwitch` in `Program.cs`).
|
- **`System.Drawing.EnableUnixSupport`** set at startup (`runtimeconfig.template.json` + `AppContext.SetSwitch` in `Program.cs`). `NoWarn=CA1416` suppresses platform-compat warnings across all projects.
|
||||||
- **Newtonsoft.Json** (not `System.Text.Json`) — used in 2DGAMELIB and Extract.
|
- **Newtonsoft.Json** (not `System.Text.Json`) in 2DGAMELIB and Extract.
|
||||||
- **Empty `RootNamespace`** in SlaveMatrix and 2DGAMELIB — types live in global namespace.
|
- **Empty `RootNamespace`** in SlaveMatrix/2DGAMELIB — types live in global namespace.
|
||||||
- **`GenerateAssemblyInfo=false`** — uses legacy `Properties/AssemblyInfo.cs`.
|
- **`GenerateAssemblyInfo=false`** — legacy `Properties/AssemblyInfo.cs`.
|
||||||
|
- **`global.json`** pins SDK 8.0 with `rollForward=latestMajor, allowPrerelease=true`.
|
||||||
- **No test projects**, no CI/CD, no formatter/linter config.
|
- **No test projects**, no CI/CD, no formatter/linter config.
|
||||||
- **843 BodyPartClasses** (`SlaveMatrix/SlaveMatrix/BodyPartClasses/`). **Do not rename** — C# reflection (`Type.GetType`) resolves joint types by class name at runtime.
|
- **843 BodyPartClasses** (`SlaveMatrix/SlaveMatrix/BodyPartClasses/`). **Do not rename** — C# reflection (`Type.GetType`) resolves joint type names at runtime.
|
||||||
|
|
||||||
## Architecture Notes
|
## Architecture
|
||||||
|
|
||||||
- **BodyTemplate** (13 binary `Obj` resources) → `VariantGrid` → `MorphVariant` → `PartGroup` → `ShapePart` tree with cardinal-spline curves and joint points. Deserialized via `BinaryFormatter` + `RemappedTypeBinder` (type remapping for decompiled names).
|
- **BodyTemplate** (13 binary `Obj` resources) → `VariantGrid` → `MorphVariant` → `PartGroup` → `ShapePart` tree (cardinal-spline curves + joint points). Deserialized via `BinaryFormatter` + `RemappedTypeBinder`.
|
||||||
- **Extract pipeline** (`SlaveMatrix.Extract`) loads the 13 embedded resources, applies `MigrateKeys()` (23-entry runtime KeyMap for Japanese→English), then exports:
|
- **Extract pipeline** loads the 13 embedded resources, applies `MigrateKeys()` (20-entry runtime KeyMap for Japanese→English lookups), then exports:
|
||||||
- Intermediate JSON → `extracted/` (gitignored)
|
- Intermediate JSON → `extracted/` (gitignored)
|
||||||
- SVG + YAML per part → `SlaveMatrix/Assets/Parts/` (checked in)
|
- SVG + YAML per part → `SlaveMatrix/Assets/Parts/` (checked in)
|
||||||
- `Catalog.yaml` index → `SlaveMatrix/Assets/`
|
- `Catalog.yaml` → `SlaveMatrix/Assets/`
|
||||||
- **EnglishNameMap** in `SlaveMatrix.Extract/Program.cs` (179 entries) is for extraction output naming only. It is separate from the smaller runtime `MigrateKeys` KeyMap which only covers keys the old game code actually looks up.
|
- **EnglishNameMap** in `SlaveMatrix.Extract/Program.cs` (~176 entries) is for extraction output naming only, separate from the runtime KeyMap.
|
||||||
- **Phase 0 (extraction) is complete.** Phases 1-5 (engine rewrite) are planned in `PLAN.md`.
|
- **game_folder/ content** is copied to build output via `<Content Include="..\game_folder\**\*" CopyToOutputDirectory="PreserveNewest" />`.
|
||||||
|
- **Phase 0 (extraction) is complete.** Phases 1-5 (engine rewrite, Vulkan/Silk.NET) are planned in `PLAN.md`.
|
||||||
|
|
||||||
## Git
|
## Git
|
||||||
|
|
||||||
- `.gitignore` excludes: `**/bin/`, `**/obj/`, `.vs/`, `.idea/`, `Config.ini`, `game_folder/save/*`, `extracted/`.
|
- `.gitignore` excludes: `**/bin/`, `**/obj/`, `.vs/`, `.idea/`, `Config.ini`, `game_folder/save/*`, `extracted/`.
|
||||||
- `SlaveMatrix/Assets/` (auto-generated SVGs + YAML) **is** checked in.
|
- `SlaveMatrix/Assets/` (auto-generated) **is** checked in.
|
||||||
- `game_folder/` runtime assets are checked in (bgm, text, etc.).
|
- `game_folder/` runtime assets are checked in (bgm, text, etc.).
|
||||||
|
|||||||
672
docs/INTERNALS.md
Normal file
672
docs/INTERNALS.md
Normal file
@@ -0,0 +1,672 @@
|
|||||||
|
# Slave Matrix Internal Architecture
|
||||||
|
|
||||||
|
A detailed technical analysis of how the program works from startup to rendering a character on screen, with focus on asset loading and generalization strategy.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Startup Pipeline
|
||||||
|
|
||||||
|
```
|
||||||
|
Program.Main()
|
||||||
|
│
|
||||||
|
├─ [STATIC INIT] Type initializer for GlobalState (before Main)
|
||||||
|
│ └─ Program static cctor (#1)
|
||||||
|
│ ├─ AppContext.SetSwitch("System.Drawing.EnableUnixSupport", true)
|
||||||
|
│ └─ RemappedTypeBinder.RegisterMapping for 3 game types
|
||||||
|
│ "SlaveMatrix.Ele" → Element
|
||||||
|
│ "SlaveMatrix.EleD" → ElementData
|
||||||
|
│ "SlaveMatrix.EleI" → ElementInstance
|
||||||
|
│
|
||||||
|
├─ GlobalState static cctor (#2)
|
||||||
|
│ ├─ Sets CurrentDirectory = AppContext.BaseDirectory
|
||||||
|
│ ├─ Loads 13 BodyTemplate Obj resources from embedded bytes
|
||||||
|
│ │ Each: byte[] → BinaryFormatter→byte[] → BinaryFormatter→BodyTemplate
|
||||||
|
│ ├─ MigrateKeys() — 20-entry KeyMap renames Japanese→English keys
|
||||||
|
│ ├─ Caches 843 BodyPartClass Type objects for reflection-based Element creation
|
||||||
|
│ ├─ Verifies type resolution (Type.GetType) for all race-part combos
|
||||||
|
│ └─ Sets imiPath, panPath for onomatopoeia/moaning text directories
|
||||||
|
│
|
||||||
|
├─ Main()
|
||||||
|
│ ├─ GlobalState.LoadConfig() — reads game_folder/Config.ini
|
||||||
|
│ │ Booleans: BigWindow, HighQuality, ShowFPS, etc.
|
||||||
|
│ │
|
||||||
|
│ ├─ Create ModeEventDispatcher
|
||||||
|
│ │ ├─ UITitle = game title string
|
||||||
|
│ │ ├─ Unit = scaling factor (2203 HQ / 1101.5 LQ)
|
||||||
|
│ │ ├─ Base = Rectangle(4f, 3f, percent/100f) — aspect ratio + viewport
|
||||||
|
│ │ ├─ DisQuality = 1.0
|
||||||
|
│ │ └─ HitAccuracy = 0.3
|
||||||
|
│ │
|
||||||
|
│ ├─ med.InitializeModes("Start", ModuleRegistry.GetMods)
|
||||||
|
│ │ └─ GetMods callback registers 25+ screen modules via RegisterModule()
|
||||||
|
│ │
|
||||||
|
│ └─ med.Drawing() — creates GLFW window, enters main loop, never returns
|
||||||
|
│
|
||||||
|
└─ [MAIN LOOP] med.Drawing()
|
||||||
|
├─ GlImage.BitmapSetting(Display)
|
||||||
|
│ ├─ GLFW 3.3 + OpenGL 3.3 Core profile window, size = Display bitmap dims
|
||||||
|
│ ├─ Compiles vertex+ fragment shader (simple textured quad)
|
||||||
|
│ ├─ Creates full-screen triangle-strip VAO/VBO
|
||||||
|
│ └─ Sets up callbacks: Click, Move, Leave, Scroll, Resize → Module routing
|
||||||
|
│
|
||||||
|
├─ med.Setting(GlImage)
|
||||||
|
│ ├─ Creates Display Bitmap (Base.LocalWidth * Unit × Base.LocalHeight * Unit px)
|
||||||
|
│ ├─ Creates Hit Bitmap (same × HitAccuracy = 0.3)
|
||||||
|
│ ├─ Creates SceneFader for crossfade transitions
|
||||||
|
│ └─ Calls GetMods(this) → ModuleRegistry populates all Module delegates
|
||||||
|
│
|
||||||
|
└─ while(Drive)
|
||||||
|
├─ FPSF.FPSFixed(action) — fixed 60 fps timestep
|
||||||
|
│ └─ action: Modes[mode].Draw(FPSF) for active screen module
|
||||||
|
├─ baseControl.PollEvents() — GLFW event pump
|
||||||
|
└─ baseControl.SetBitmap(Display)
|
||||||
|
├─ glTexSubImage2D(GDI+ Bitmap → OpenGL texture)
|
||||||
|
├─ glDrawArrays(TriangleStrip, 0, 4)
|
||||||
|
└─ Glfw.SwapBuffers(window)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key files
|
||||||
|
|
||||||
|
| Step | File | Lines |
|
||||||
|
|------|------|-------|
|
||||||
|
| Program.Main | `SlaveMatrix/GameClasses/Program.cs` | 18-43 |
|
||||||
|
| GlobalState static init | `SlaveMatrix/GameClasses/GlobalState.cs` | 1087-1429 |
|
||||||
|
| ModeEventDispatcher ctor | `2DGAMELIB/ModeEventDispatcher.cs` | 78-99 |
|
||||||
|
| Drawing() loop | `2DGAMELIB/ModeEventDispatcher.cs` | 290-325 |
|
||||||
|
| Setting() | `2DGAMELIB/ModeEventDispatcher.cs` | 101-232 |
|
||||||
|
| GlImage.BitmapSetting | `2DGAMELIB/WPFImage.cs` | 146-238 |
|
||||||
|
| GlImage.SetBitmap | `2DGAMELIB/WPFImage.cs` | 99-144 |
|
||||||
|
| ModuleRegistry.GetMods | `SlaveMatrix/GameClasses/ModuleRegistry.cs` | 204-493 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Module System (Screen Management)
|
||||||
|
|
||||||
|
Each "screen" is a `Module` struct — a set of delegate callbacks:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public struct Module {
|
||||||
|
public Action<FpsCounter, ModeEventDispatcher> Draw;
|
||||||
|
public Action<MouseButtons, Vector2D, Color> Down, Up, Move;
|
||||||
|
public Action Leave;
|
||||||
|
public Action<Vector2D> Wheel;
|
||||||
|
public Action Setting;
|
||||||
|
public Action Dispose;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Registered via `ModeEventDispatcher.RegisterModule(string name, Module mod)`. 25 modules include:
|
||||||
|
|
||||||
|
| Module Name | Purpose |
|
||||||
|
|-------------|---------|
|
||||||
|
| `Start` | Initial screen |
|
||||||
|
| `Title` | Title screen |
|
||||||
|
| `Credit` | Credits |
|
||||||
|
| `メインフォーム` (MainForm) | Primary HUD/gameplay |
|
||||||
|
| `対象` (Target) | Character target selection |
|
||||||
|
| `Training` | Training/interaction scene |
|
||||||
|
| `Blessing`, `Office` | Other gameplay scenes |
|
||||||
|
| `Debt` | Debt management |
|
||||||
|
| `SlaveShop`, `ToolShop` | Shop screens |
|
||||||
|
| `PlayerInformation` | Player status |
|
||||||
|
| `OP0`, `OP1`, `説明`, `初事務所` | Event scenes |
|
||||||
|
| `RepaymentEvent1-3` | Story events |
|
||||||
|
|
||||||
|
**Mode switching**: `ModeEventDispatcher.Mode = name` calls `Leave` on the old module and `Setting` on the new module.
|
||||||
|
|
||||||
|
**Module draw delegates** are defined inline in `ModuleRegistry.GetMods()` — typically compositing multiple `RenderArea` layers, characters, and UI elements.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Asset Loading Architecture
|
||||||
|
|
||||||
|
### 3.1 The 13 Binary Resources
|
||||||
|
|
||||||
|
Embedded as `byte[]` in `SlaveMatrix/Properties/Resources.resx`, accessed via `Resources.xxx`:
|
||||||
|
|
||||||
|
| # | Resource Name | Contents (after MigrateKeys) |
|
||||||
|
|---|---------------|------------------------------|
|
||||||
|
| 1 | `Resources.胴体` | Torso, Waist, Neck, Head, Chest, BackHair0/1, SideHair, etc. |
|
||||||
|
| 2 | `Resources.肩左` | Shoulder variants |
|
||||||
|
| 3 | `Resources.腕左` | Arm, UpperArm, LowerArm, + wing/beast/quadruped variants |
|
||||||
|
| 4 | `Resources.脚左` | Leg variants |
|
||||||
|
| 5 | `Resources.尻尾` | Tail variants |
|
||||||
|
| 6 | `Resources.半身` | Half-body composites |
|
||||||
|
| 7 | `Resources.肢左` | Limb variants |
|
||||||
|
| 8 | `Resources.肢中` | Middle limb variants |
|
||||||
|
| 9 | `Resources.性器` | Genital variants |
|
||||||
|
| 10 | `Resources.性器付` | Genitals with attachments |
|
||||||
|
| 11 | `Resources.スタンプ` | Stamp/stencil overlays |
|
||||||
|
| 12 | `Resources.カーソル` | Cursor graphics |
|
||||||
|
| 13 | `Resources.その他` | Miscellaneous |
|
||||||
|
| — | `Resources.タイル` | Tile/background patterns |
|
||||||
|
|
||||||
|
### 3.2 Deserialization Pipeline
|
||||||
|
|
||||||
|
```
|
||||||
|
byte[] raw = Resources.胴体
|
||||||
|
│
|
||||||
|
├─ Serializer.Load<byte[]>(raw) // FIRST deserialization
|
||||||
|
│ └─ new BinaryFormatter { Binder = RemappedTypeBinder }
|
||||||
|
│ .Deserialize(new MemoryStream(raw))
|
||||||
|
│ └─ RemappedTypeBinder translates:
|
||||||
|
│ _2DGAMELIB.Obj → BodyTemplate (outer layer is Obj wrapping a byte[])
|
||||||
|
│ └─ Result: byte[] innerData
|
||||||
|
│
|
||||||
|
└─ innerData.ToDeserialObject<BodyTemplate>() // SECOND deserialization
|
||||||
|
└─ new BinaryFormatter { Binder = RemappedTypeBinder }
|
||||||
|
.Deserialize(new MemoryStream(innerData))
|
||||||
|
└─ RemappedTypeBinder translates:
|
||||||
|
_2DGAMELIB.Obj → BodyTemplate
|
||||||
|
_2DGAMELIB.Difs → VariantGrid
|
||||||
|
_2DGAMELIB.Dif → MorphVariant
|
||||||
|
_2DGAMELIB.Pars → PartGroup
|
||||||
|
_2DGAMELIB.Par → ShapePart
|
||||||
|
_2DGAMELIB.ParT → ShapePartT
|
||||||
|
_2DGAMELIB.Out → CurveOutline
|
||||||
|
_2DGAMELIB.Joi → JointPoint
|
||||||
|
└─ Result: BodyTemplate with full object graph
|
||||||
|
```
|
||||||
|
|
||||||
|
The **double-wrapping** is a legacy artifact: the `.resx` stores the data as `byte[]`, but the original source wrapped a `byte[]` containing a serialized `BodyTemplate` inside another `BinaryFormatter` envelope.
|
||||||
|
|
||||||
|
### 3.3 Post-Deserialization
|
||||||
|
|
||||||
|
1. **`SetDefaultR()`** — initializes all PartGroup runtime fields to defaults
|
||||||
|
2. **`MigrateKeys()`** — replaces 20 Japanese keys in `BodyTemplate.Difss` with English names
|
||||||
|
3. **Type caching** — iterates all `VariantGrid` keys + race suffixes (人/猫/獣/鳥/蜘蛛/蜥蜴/魚/蛙/四足/水棲/半人鮫/竜/植物/巨人/妖精/兎/狐/熊/馬/鹿/狼/河馬/恐/牛/羊/鬼/亜人/機械/蛇/狗/黄金/冥府/冥界/天使/墮天使) and caches `Type.GetType("BodyPartName_RaceSuffix")` for each.
|
||||||
|
|
||||||
|
### 3.4 KeyMap (Runtime)
|
||||||
|
|
||||||
|
Defined in `2DGAMELIB/ObjExtensions.cs` — 20 entries:
|
||||||
|
|
||||||
|
| Japanese | English |
|
||||||
|
|----------|---------|
|
||||||
|
| 咳 → Cough | 腰 → Waist | 胴 → Torso | 首 → Neck |
|
||||||
|
| 頭 → Head | 後髪0 → BackHair0 | 後髪1 → BackHair1 | 横髪 → SideHair |
|
||||||
|
| 脚 → Leg | 腕 → Arm | 肩 → Shoulder | 胸 → Chest |
|
||||||
|
| 下腕 → LowerArm | 上腕 → UpperArm |
|
||||||
|
| 鳥翼上腕/獣翼上腕/四足上腕 → 鳥翼UpperArm/獣翼UpperArm/四足UpperArm |
|
||||||
|
| 鳥翼下腕/獣翼下腕/四足下腕 → 鳥翼LowerArm/獣翼LowerArm/四足LowerArm |
|
||||||
|
|
||||||
|
### 3.5 game_folder Runtime Assets
|
||||||
|
|
||||||
|
Copied to build output via `.csproj`:
|
||||||
|
```xml
|
||||||
|
<Content Include="..\game_folder\**\*" CopyToOutputDirectory="PreserveNewest" LinkBase="" />
|
||||||
|
```
|
||||||
|
|
||||||
|
Loaded at runtime:
|
||||||
|
- **Text**: `GameText` static ctor loads `text/System/Race.txt`, `Attribute.txt`, `Common.txt`, and scene-specific text
|
||||||
|
- **Onomatopoeia/Moaning**: `GlobalState.Set擬音()` → `Imitation.txt`; `Set喘ぎ()` → `Pant/*.txt`
|
||||||
|
- **Config**: `game_folder/Config.ini`
|
||||||
|
- **BGM**: Expected in `bgm/` (currently commented out in code)
|
||||||
|
- **Saves**: `save/*.sav` (BinaryFormatter) or `.json`
|
||||||
|
- **Background images**: *loaded from embedded resources*, not game_folder (e.g., `Resources.dangeon01_ex2`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Data Model
|
||||||
|
|
||||||
|
### 4.1 Hierarchy
|
||||||
|
|
||||||
|
```
|
||||||
|
BodyTemplate [Serializable]
|
||||||
|
└── OrderedDictionary<string, VariantGrid> "Difss"
|
||||||
|
├── Key: "Waist", "Torso", "Neck", "Head", ...
|
||||||
|
│
|
||||||
|
└── VariantGrid [Serializable] (.Difs)
|
||||||
|
├── CountX : int ← morph grid width (ValueX axis)
|
||||||
|
├── CountY : int ← morph grid height (ValueY axis)
|
||||||
|
├── ValueX : double ← current X selection (0..1 → index)
|
||||||
|
├── ValueY : double ← current Y selection (0..1 → index)
|
||||||
|
├── PositionSize : Rectangle ← inherited from parent template
|
||||||
|
├── PositionVector : Vector2D
|
||||||
|
├── AngleBase : double
|
||||||
|
├── SizeBase : double
|
||||||
|
└── List<MorphVariant> difs ← linear array [x * CountY + y]
|
||||||
|
│
|
||||||
|
└── MorphVariant [Serializable] (.Dif)
|
||||||
|
├── Tag : string
|
||||||
|
└── List<PartGroup> parss
|
||||||
|
│
|
||||||
|
└── PartGroup [Serializable] (.Pars)
|
||||||
|
├── Tag : string
|
||||||
|
├── Parent : PartGroup (runtime, non-serialized)
|
||||||
|
└── OrderedDictionary<string, object> pars
|
||||||
|
├── Key: "childPartName"
|
||||||
|
│
|
||||||
|
├── ShapePart [Serializable] (.Par)
|
||||||
|
│ ├── Dra : bool ← visibility
|
||||||
|
│ ├── Closed : bool
|
||||||
|
│ ├── PenWidth : double
|
||||||
|
│ ├── BasePoint : Vector2D ← local origin
|
||||||
|
│ ├── Position : Vector2D ← translation
|
||||||
|
│ ├── Angle : double ← rotation
|
||||||
|
│ ├── Size : double ← uniform scale
|
||||||
|
│ ├── SizeX / SizeY : double ← non-uniform scale
|
||||||
|
│ ├── HitColor : Color ← unique per part
|
||||||
|
│ ├── OP : List<CurveOutline>
|
||||||
|
│ │ ├── ps : List<Vector2D> ← control points
|
||||||
|
│ │ ├── Tension : float ← cardinal spline
|
||||||
|
│ │ └── Outline : bool ← stroke-only?
|
||||||
|
│ ├── JP : List<JointPoint>
|
||||||
|
│ │ └── Joint : Vector2D ← attachment point
|
||||||
|
│ ├── Brush : Brush (runtime, [JsonIgnore])
|
||||||
|
│ └── Pen : Pen (runtime, [JsonIgnore])
|
||||||
|
│
|
||||||
|
├── ShapePartT [Serializable] (.ParT)
|
||||||
|
│ └── extends ShapePart
|
||||||
|
│ ├── Text : string
|
||||||
|
│ ├── FontSize : double
|
||||||
|
│ └── Font : Font (runtime)
|
||||||
|
│
|
||||||
|
└── PartGroup (recursive nesting)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 Coordinate System
|
||||||
|
|
||||||
|
- **Local space**: points in `CurveOutline.ps[]` — normalized 0..1 range
|
||||||
|
- **Part space**: transformed by `BasePoint`, `Position`, `Angle`, `Size/SizeX/SizeY`
|
||||||
|
- **Parent space**: transformed by parent chain (PartGroup hierarchy)
|
||||||
|
- **VariantGrid space**: further transformed by `VariantGrid.PositionVector`, `AngleBase`, `SizeBase`
|
||||||
|
- **Screen space**: multiplied by `Unit` (2203 or 1101.5)
|
||||||
|
|
||||||
|
### 4.3 Joint System
|
||||||
|
|
||||||
|
`SetJoints()` on `BodyTemplate` builds:
|
||||||
|
- **`pj`** (`Dictionary<PartGroup, List<Joint>>`) — joints per PartGroup
|
||||||
|
- **`pr`** (`Dictionary<PartGroup, ShapePart>`) — the root ShapePart for each PartGroup's joints
|
||||||
|
- **`JoinRoot`** — identifies the anchor point in parent space
|
||||||
|
|
||||||
|
`JoinPA()` propagates angle changes through the chain:
|
||||||
|
```
|
||||||
|
Parent.Angle → drives → Child.Position offset via joint point
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.4 Morph Variant Selection
|
||||||
|
|
||||||
|
`VariantGrid.ValueX` (0..1) → `indexX = Clamp((int)(ValueX * (CountX - 1)), 0, CountX - 1)`
|
||||||
|
`VariantGrid.ValueY` (0..1) → `indexY = Clamp((int)(ValueY * (CountY - 1)), 0, CountY - 1)`
|
||||||
|
Current MorphVariant: `difs[indexX * CountY + indexY]`
|
||||||
|
Current PartGroup: `Current.parss[IndexY]` — then draws all children
|
||||||
|
|
||||||
|
This means X axis iterates across `difs[]` while Y iterates within a `MorphVariant.parss[]`. The first entry (Y=0) of the current X gives the active PartGroup tree.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Rendering Pipeline (Per-Frame)
|
||||||
|
|
||||||
|
### 5.1 Compositing Layers
|
||||||
|
|
||||||
|
```
|
||||||
|
[Module.Draw(FPSF)]
|
||||||
|
│
|
||||||
|
├─ Med.HitGraphics.Clear(Color.Transparent)
|
||||||
|
│
|
||||||
|
├─ a.Draw(BasementBackground) ← background layer (embedded bitmap)
|
||||||
|
│ └─ RenderArea.Draw(RenderArea other)
|
||||||
|
│ └─ DisplayGraphics.DrawImage(other.DisplayLayer, ...)
|
||||||
|
│
|
||||||
|
├─ TrainingTarget.Draw(a, FPS) ← character layer
|
||||||
|
│ └─ Character.Draw(Area, FpsCounter)
|
||||||
|
│
|
||||||
|
├─ bs.Draw(a) ← UI button layer
|
||||||
|
├─ dbs.Draw(a) ← bottom button layer
|
||||||
|
├─ ip.Draw(a, FPS) ← info panel layer
|
||||||
|
├─ SaveData.Draw(a) ← save data overlay
|
||||||
|
│
|
||||||
|
└─ Med.Draw(a) ← composite to main display
|
||||||
|
└─ RenderArea.DrawTo(Med.Display, Med.Hit)
|
||||||
|
└─ DisplayGraphics.DrawImage(a.DisplayLayer, 0, 0)
|
||||||
|
└─ HitGraphics.DrawImage(a.HitLayer, 0, 0)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 Character Draw
|
||||||
|
|
||||||
|
```
|
||||||
|
Character.Draw(Area, FPS)
|
||||||
|
├─ Motions.Drive(FPS) — update all animation state
|
||||||
|
│ ├─ Breathing: oscillates Chest/Abdomen size
|
||||||
|
│ ├─ Blinking: triggers Eye morph (Xv/Yv → closed)
|
||||||
|
│ ├─ Tear/Cum/Squirt/Urination: fluid system updates
|
||||||
|
│ ├─ Sway: body angle oscillation
|
||||||
|
│ ├─ Eye tracking: head angle toward cursor
|
||||||
|
│ ├─ Cough/Moan: variant cycling
|
||||||
|
│ └─ User-defined morph motions
|
||||||
|
│
|
||||||
|
└─ Body.描画(Area)
|
||||||
|
├─ 接続PA() — propagate parent angles through joint chains
|
||||||
|
│ └─ For each Element: Element.Body.JoinPA()
|
||||||
|
│ └─ VariantGrid.JoinPA() recurses PartGroups
|
||||||
|
│ └─ ShapePart.Rotate() updates child positions
|
||||||
|
│
|
||||||
|
├─ Color updates: skin/dress/wing membrane/etc.
|
||||||
|
├─ ElementInstance updates
|
||||||
|
│
|
||||||
|
└─ Draw(Area) delegate
|
||||||
|
└─ Elements sorted by 描画前後 (back-to-front)
|
||||||
|
├─ Element.描画0(Are) — main body
|
||||||
|
│ └─ VariantGrid.Draw(Are)
|
||||||
|
│ └─ Are.Draw(Current PartGroup)
|
||||||
|
│ └─ PartGroup.Draw(Unit, Graphics)
|
||||||
|
│ └─ for each child in pars:
|
||||||
|
│ ├─ if ShapePart + Dra:
|
||||||
|
│ │ ShapePart.Draw(Unit, Graphics)
|
||||||
|
│ ├─ if ShapePartT:
|
||||||
|
│ │ ShapePartT.DrawString(Unit, Graphics)
|
||||||
|
│ └─ if PartGroup:
|
||||||
|
│ recursive PartGroup.Draw()
|
||||||
|
│
|
||||||
|
├─ Element.描画1(Are) — overlay layer 1
|
||||||
|
└─ Element.描画2(Are) — overlay layer 2
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.3 ShapePart.Draw()
|
||||||
|
|
||||||
|
```
|
||||||
|
ShapePart.Draw(double Unit, Graphics g)
|
||||||
|
├─ if (Edit flag) → Calculation(Unit):
|
||||||
|
│ ├─ BaseTransform = -BasePoint * Size (shifts to origin)
|
||||||
|
│ ├─ Position += ParentChain offset
|
||||||
|
│ ├─ Angle += ParentChain angle
|
||||||
|
│ ├─ Size *= ParentChain scale
|
||||||
|
│ ├─ For each CurveOutline:
|
||||||
|
│ │ └─ Transform points through: base → scale → rotate → translate
|
||||||
|
│ ├─ Build GraphicsPath.Path via AddCurve(pointArray, tension)
|
||||||
|
│ ├─ Build GraphicsPath.OutlinePath for stroke-only curves
|
||||||
|
│ └─ Clear Edit flag
|
||||||
|
│
|
||||||
|
├─ Brush.Color = resolve runtime color (from Element.ColorSet)
|
||||||
|
├─ g.FillPath(Brush, Path)
|
||||||
|
└─ if (Outline curves exist) → g.DrawPath(Pen, OutlinePath)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.4 Hit Detection
|
||||||
|
|
||||||
|
Rendered on a separate lower-resolution `Hit` Bitmap. Each `ShapePart` has a unique `HitColor`. Module mouse callbacks check the pixel color under the cursor:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
Color hit = Med.Hit.GetPixel((int)(pos.X * HitAccuracy), (int)(pos.Y * HitAccuracy));
|
||||||
|
// match hit to Element via HitColor → find which part was clicked
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.5 RenderArea Hierarchy
|
||||||
|
|
||||||
|
```
|
||||||
|
RenderArea : Rectangle
|
||||||
|
├── DisplayLayer : Bitmap (full-res, visible output)
|
||||||
|
├── HitLayer : Bitmap (low-res, hit detection)
|
||||||
|
├── DisplayGraphics : Graphics (wraps DisplayLayer)
|
||||||
|
├── HitGraphics : Graphics (wraps HitLayer)
|
||||||
|
├── Draw(ShapePart) → renders to both layers
|
||||||
|
├── Draw(PartGroup) → recursive render
|
||||||
|
├── Draw(RenderArea) → composite another area onto this one
|
||||||
|
└── DrawTo(display, hit) → final composite to Med.Display/Med.Hit
|
||||||
|
|
||||||
|
ManagedArea : RenderArea
|
||||||
|
└── Lower-resolution render target for character body (3x3 internal cells)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Character System
|
||||||
|
|
||||||
|
### 6.1 Element Class
|
||||||
|
|
||||||
|
Each `Element` wraps a single `VariantGrid` from a BodyTemplate resource. 843 Element subclasses in `BodyPartClasses/` define concrete body parts with race-specific behavior.
|
||||||
|
|
||||||
|
```
|
||||||
|
Element [Serializable]
|
||||||
|
├── 本体 : VariantGrid ← the template data
|
||||||
|
├── 位置B / 位置C : Vector2D ← position base/contract
|
||||||
|
├── 角度B / 角度C : double ← angle base/contract
|
||||||
|
├── 尺度B / 尺度C : double ← scale base/contract
|
||||||
|
├── Xv / Yv : double ← morph variant selection (drives ValueX/Y)
|
||||||
|
├── 描画前後 : int ← render order (back:0, middle:1, front:2)
|
||||||
|
├── 接続Type : int ← joint connection type
|
||||||
|
├── ColorSlot : int ← which color set to use
|
||||||
|
├── 拡張 : OrderedDictionary ← extension data
|
||||||
|
│
|
||||||
|
├── 描画0(Are) → 本体.Draw(Are) ← main render
|
||||||
|
├── 描画1(Are) → 本体.Draw(Are) ← overlay 1
|
||||||
|
└── 描画2(Are) → 本体.Draw(Are) ← overlay 2
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 Element Construction via Reflection
|
||||||
|
|
||||||
|
In `Body` constructor, Elements are instantiated by `Type.GetType()`:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Simplified:
|
||||||
|
string typeName = $"_{partKey}_{raceSuffix}"; // e.g., "Head_人"
|
||||||
|
Type t = GlobalState.BodyTypeCache[typeName];
|
||||||
|
Element ele = (Element)Activator.CreateInstance(t);
|
||||||
|
ele.本体 = GlobalState.BodyTemplates[resourceName].Difss[partKey];
|
||||||
|
```
|
||||||
|
|
||||||
|
843 files mean 843 `Type` lookups. The reflection names use **original Japanese** keys (pre-MigrateKeys) for the `_人/猫/etc` suffix convention.
|
||||||
|
|
||||||
|
### 6.3 Body Class
|
||||||
|
|
||||||
|
```
|
||||||
|
Body
|
||||||
|
├── Elements : Element[] ← flat array of all parts
|
||||||
|
├── Strongly-typed fields:
|
||||||
|
│ Waist, Torso, Chest, Neck, Head,
|
||||||
|
│ BackHair0, BackHair1, SideHair, BaseHair, FrontHair,
|
||||||
|
│ EyeL, EyeR, Nose, Mouth, EyebrowL, EyebrowR,
|
||||||
|
│ BreastL, BreastR, Belly,
|
||||||
|
│ Genitals, Anus,
|
||||||
|
│ UpperArmL/R, LowerArmL/R, HandL/R,
|
||||||
|
│ ShoulderL/R,
|
||||||
|
│ UpperLegL/R, LowerLegL/R, FootL/R,
|
||||||
|
│ WingL/R, Tail,
|
||||||
|
│ etc. (~80 fields)
|
||||||
|
│
|
||||||
|
├── 接続PA() — joint angle propagation
|
||||||
|
├── 描画(Area) — main render entry
|
||||||
|
└── Draw(Area) delegate — sorted Element rendering
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.4 Character Class
|
||||||
|
|
||||||
|
```
|
||||||
|
Character
|
||||||
|
├── Body : Body
|
||||||
|
├── Motions : Motions ← animation controller
|
||||||
|
├── FluidSystem : CharacterFluidSystem
|
||||||
|
├── CharacterData : CharacterData ← race, colors, stats
|
||||||
|
│
|
||||||
|
├── Draw(Area, FPS) → Body.描画(Area)
|
||||||
|
└── Motions.Drive(FPS) — updates all animation state
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.5 Animation System (Motions)
|
||||||
|
|
||||||
|
The `Motions` class manages a collection of `Motion` objects. Each `Motion` modifies Element properties over time:
|
||||||
|
|
||||||
|
- **Breathing**: oscillates `Chest.尺度C` and `Belly.尺度C` using a sine wave
|
||||||
|
- **Blinking**: transitions `EyeL.Yv` / `EyeR.Yv` to 1 (closed) then back
|
||||||
|
- **Cough**: cycles `Cough.Xv` through morph variants
|
||||||
|
- **Sway**: oscillates body `角度B` (Angle Base)
|
||||||
|
- **Eye tracking**: sets `Head.角度C` toward cursor position
|
||||||
|
- **Fluid drip**: tears, drool, cum, squirt, urination — updates fluid system
|
||||||
|
- **Climax**: full-body spasm (randomized element position/angle jitter)
|
||||||
|
|
||||||
|
Each `Motion` implements `Drive(FPS)` which time-samples a curve/function and applies deltas to `Element.Xv`, `Yv`, `位置B/C`, `角度B/C`, `尺度B/C`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Generalization Strategy
|
||||||
|
|
||||||
|
### 7.1 Replace BinaryFormatter (Data Layer)
|
||||||
|
|
||||||
|
**Current**: `byte[] → BinaryFormatter→byte[] → BinaryFormatter→BodyTemplate`
|
||||||
|
|
||||||
|
**Target**: Load from structured files. Extraction is already done via `SlaveMatrix.Extract`:
|
||||||
|
|
||||||
|
```
|
||||||
|
Embedded .resx bytes ──[Extract CLI]──▶ extracted/*.json (full template data)
|
||||||
|
Assets/Parts/{id}/part.yaml (per-part metadata)
|
||||||
|
Assets/Parts/{id}/x{y}x{y}.svg (variant curves)
|
||||||
|
Assets/Catalog.yaml (runtime index)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Strategy**:
|
||||||
|
1. Parse `Catalog.yaml` → discover all parts
|
||||||
|
2. For each part, load `part.yaml` → get morph dimensions, joints, fields, variant list
|
||||||
|
3. For active morph (Xv/Yv), load corresponding SVG → parse path data → extract control points
|
||||||
|
4. Rebuild `ShapePart`-equivalent geometry from SVG path data
|
||||||
|
|
||||||
|
### 7.2 Replace GDI+ (Rendering Layer)
|
||||||
|
|
||||||
|
**Current**: `System.Drawing.Graphics.FillPath/DrawPath` on GDI+ `GraphicsPath` built via `AddCurve(points, tension)`.
|
||||||
|
|
||||||
|
**Target**: Vulkan/Silk.NET (or any modern GPU API).
|
||||||
|
|
||||||
|
**Strategy**:
|
||||||
|
1. Cardinal spline → cubic Bezier conversion (already done in SVG export — `BuildSvgPath` in Extract)
|
||||||
|
2. Tessellate cubic Bezier curves into triangles (or use GPU path rendering)
|
||||||
|
3. Maintain the same transform chain:
|
||||||
|
```
|
||||||
|
localPoint → baseTransform → scale → rotate → translate → parentTransform → Unit
|
||||||
|
```
|
||||||
|
4. Replace hit-testing: render with unique color IDs to offscreen buffer, sample pixel
|
||||||
|
|
||||||
|
### 7.3 Critical Data to Preserve
|
||||||
|
|
||||||
|
```
|
||||||
|
ShapePart (or equivalent):
|
||||||
|
- CurveOutline[] → Points[] (local coords), Tension, Outline flag
|
||||||
|
- Closed → fill vs stroke
|
||||||
|
- BasePoint → local origin
|
||||||
|
- Position → translation in parent space
|
||||||
|
- Angle → rotation
|
||||||
|
- Size, SizeX, SizeY → scale
|
||||||
|
- Joints → attachment positions in local coords
|
||||||
|
- Dra → visibility
|
||||||
|
- HitColor → unique ID for hit testing
|
||||||
|
|
||||||
|
PartGroup (or equivalent):
|
||||||
|
- Tree hierarchy (ordered children)
|
||||||
|
- Parent reference
|
||||||
|
|
||||||
|
VariantGrid (or equivalent):
|
||||||
|
- CountX, CountY → morph grid dimensions
|
||||||
|
- ValueX, ValueY → current selection → (xIndex, yIndex)
|
||||||
|
- PositionVector, AngleBase, SizeBase → root transform
|
||||||
|
|
||||||
|
MorphVariant (or equivalent):
|
||||||
|
- Per-variant PartGroup tree
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.4 Joint System Generalization
|
||||||
|
|
||||||
|
Current: `SetJoints()` builds proximity-based joint dictionaries. PartGroups have `JointPoint` lists that define connection anchors.
|
||||||
|
|
||||||
|
Target: Explicit joint definitions in YAML (already in extracted `part.yaml`):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
joints:
|
||||||
|
- position: [0.50, 0.47] # pre-computed world-space joint positions
|
||||||
|
- position: [0.48, 0.52]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Strategy**:
|
||||||
|
1. Define explicit parent-child joint connections in a race template YAML
|
||||||
|
2. Match joint positions between adjacent parts (or name them explicitly)
|
||||||
|
3. `JoinPA()` equivalent: when parent rotates, propagate angle through the joint point offset
|
||||||
|
|
||||||
|
### 7.5 Morph Variant Generalization
|
||||||
|
|
||||||
|
Current: Discrete X/Y index into `List<MorphVariant>`. Selection via `(int)(ValueX * (CountX-1))`.
|
||||||
|
|
||||||
|
Target: Support both discrete selection and interpolation:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# interpolate between variant x0y0 and x0y1
|
||||||
|
morph:
|
||||||
|
x: 0.3 # selects x=0, interpolates 30% toward next X
|
||||||
|
y: 0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
**Strategy**:
|
||||||
|
1. Load both adjacent SVG variants
|
||||||
|
2. Interpolate control point positions: `p = lerp(p_a, p_b, t)`
|
||||||
|
3. Rebuild path geometry from interpolated points
|
||||||
|
|
||||||
|
### 7.6 Suggested Implementation Order
|
||||||
|
|
||||||
|
```
|
||||||
|
Phase 1: Data layer
|
||||||
|
├── JSON/YAML deserializer for extracted assets
|
||||||
|
├── BodyTemplate/VariantGrid/MorphVariant ⇒ plain C# records
|
||||||
|
└── replaces BinaryFormatter dependency
|
||||||
|
|
||||||
|
Phase 2: Rendering backend
|
||||||
|
├── Curve tessellator (cardinal spline → bezier → triangles)
|
||||||
|
├── Transform chain (preserve hierarchy logic)
|
||||||
|
├── Hit detection (offscreen ID buffer)
|
||||||
|
└── ShapePart.Draw equivalent in new backend
|
||||||
|
|
||||||
|
Phase 3: Character assembly
|
||||||
|
├── Joint system (explicit YAML connections)
|
||||||
|
├── Element system (part instance with morph state)
|
||||||
|
├── Animation (Motion/Motions port)
|
||||||
|
└── Character.Draw equivalent
|
||||||
|
|
||||||
|
Phase 4: Migration
|
||||||
|
├── Port BodyPartClasses → data-driven definitions
|
||||||
|
├── Port UI system → new rendering backend
|
||||||
|
├── Replace Module system → scene graph
|
||||||
|
└── Wire game logic (training, shops, events)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Appendix: Key File Index
|
||||||
|
|
||||||
|
| File | Role |
|
||||||
|
|------|------|
|
||||||
|
| `SlaveMatrix/GameClasses/Program.cs` | Entry point, static cctor, Main |
|
||||||
|
| `SlaveMatrix/GameClasses/GlobalState.cs` | Global static state, template loading, type cache |
|
||||||
|
| `SlaveMatrix/GameClasses/ModuleRegistry.cs` | Module definitions, UI, game initialization |
|
||||||
|
| `SlaveMatrix/GameClasses/Character.cs` | Character class + animation |
|
||||||
|
| `SlaveMatrix/GameClasses/Body.cs` | Body assembly + rendering |
|
||||||
|
| `SlaveMatrix/GameClasses/Element.cs` | Element base class (VariantGrid wrapper) |
|
||||||
|
| `SlaveMatrix/GameClasses/ElementInstance.cs` | Runtime Element instance |
|
||||||
|
| `SlaveMatrix/GameClasses/ElementData.cs` | Serializable Element data |
|
||||||
|
| `SlaveMatrix/GameClasses/GameText.cs` | Game text loader |
|
||||||
|
| `SlaveMatrix/BodyPartClasses/*.cs` | 843 concrete Element subclasses |
|
||||||
|
| `2DGAMELIB/ModeEventDispatcher.cs` | Main loop, mode switching, GLFW integration |
|
||||||
|
| `2DGAMELIB/WPFImage.cs` | GLFW window, OpenGL texture upload (GlImage) |
|
||||||
|
| `2DGAMELIB/RenderArea.cs` | Offscreen render buffer (display + hit) |
|
||||||
|
| `2DGAMELIB/ManagedArea.cs` | Lower-res render area |
|
||||||
|
| `2DGAMELIB/SceneFader.cs` | Crossfade transition |
|
||||||
|
| `2DGAMELIB/Module.cs` | Module delegate struct |
|
||||||
|
| `2DGAMELIB/BodyTemplate.cs` | Top-level template container |
|
||||||
|
| `2DGAMELIB/VariantGrid.cs` | Morph variant grid |
|
||||||
|
| `2DGAMELIB/MorphVariant.cs` | Morph variant (PartGroup list) |
|
||||||
|
| `2DGAMELIB/PartGroup.cs` | Part group tree node |
|
||||||
|
| `2DGAMELIB/ShapePart.cs` | Drawable shape (curves, joints, transforms) |
|
||||||
|
| `2DGAMELIB/ShapePartT.cs` | Text-rendering shape |
|
||||||
|
| `2DGAMELIB/CurveOutline.cs` | Cardinal spline curve data |
|
||||||
|
| `2DGAMELIB/JointPoint.cs` | Joint anchor point |
|
||||||
|
| `2DGAMELIB/Joints.cs` | Joint connection management |
|
||||||
|
| `2DGAMELIB/OrderedDictionary.cs` | Serializable ordered dictionary |
|
||||||
|
| `2DGAMELIB/Vector2D.cs` | 2D vector struct |
|
||||||
|
| `2DGAMELIB/Serializer.cs` | BinaryFormatter + JSON serialization |
|
||||||
|
| `2DGAMELIB/RemappedTypeBinder.cs` | Legacy type name remapping for BinaryFormatter |
|
||||||
|
| `2DGAMELIB/ObjExtensions.cs` | KeyMap + MigrateKeys |
|
||||||
|
| `2DGAMELIB/GeometryUtils.cs` | ObjLoad/ObjLoadRaw extension methods |
|
||||||
|
| `2DGAMELIB/Motion.cs` | Single animation motion |
|
||||||
|
| `2DGAMELIB/Motions.cs` | Motion collection controller |
|
||||||
|
| `2DGAMELIB/FpsCounter.cs` | Fixed-timestep FPS lock |
|
||||||
|
| `SlaveMatrix.Extract/Program.cs` | Asset extraction CLI (BinaryFormatter→JSON/SVG/YAML) |
|
||||||
|
| `SlaveMatrix/Properties/Resources.resx` | Embedded resource manifest |
|
||||||
|
| `SlaveMatrix/Properties/Resources.Designer.cs` | Strongly-typed resource accessors |
|
||||||
|
| `SlaveMatrix/Resources/*` | Raw binary resource files |
|
||||||
|
| `game_folder/` | Runtime assets (text, bgm, save, config) |
|
||||||
|
| `Assets/Catalog.yaml` | Generated global catalog |
|
||||||
|
| `Assets/Parts/{id}/` | Generated per-part SVG+YAML |
|
||||||
Reference in New Issue
Block a user