Compare commits
428 Commits
e5a75a83b5
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| f89cd1ad63 | |||
| 758ebd19f2 | |||
| 90494232bc | |||
| ecadf3606e | |||
| 4b559ca7fa | |||
| 30c73c9899 | |||
| ffcec649fa | |||
| 3b678e35d1 | |||
| 47e954266c | |||
| 8b4ece766a | |||
| ee68fb5bb0 | |||
| a7f949fad8 | |||
| 9fbf8e8574 | |||
| d61628d359 | |||
| 18da7a7983 | |||
| 695f048c2d | |||
| 6e82d0f128 | |||
| 1390b9ec50 | |||
| cdfc79cd57 | |||
| bfb9ee5bef | |||
| 5297922f99 | |||
| e3a75c33a3 | |||
| 0a040837d9 | |||
| da0d74f841 | |||
| 7f30803862 | |||
| 2fdd535939 | |||
| 1100cbeb08 | |||
| e14f19e4ed | |||
| 1a10444136 | |||
| 4d8fa0f40f | |||
| 9fd4b2d2e3 | |||
| 0def604f62 | |||
| a2e4f16402 | |||
| e8ea5e249d | |||
| 66985c2af6 | |||
| 1ecccb4ae7 | |||
| 985225dbd2 | |||
| f0b7704fc1 | |||
| 8628727bcc | |||
| 7db67e3ccd | |||
| 1847e2d9b2 | |||
| 5e2fd5db22 | |||
| 17200d47ec | |||
| 95d6155086 | |||
| de917f812d | |||
| 8a43ca91da | |||
| fc03d58ee7 | |||
| ead73b427e | |||
| d78049182b | |||
| 5f615e30e2 | |||
| 222ed92807 | |||
| 72750f3647 | |||
| 1291c52346 | |||
| 926733dbef | |||
| d7813f9912 | |||
| e6f351420b | |||
| b4a4560678 | |||
| 1e0b91294a | |||
| 8f8f17bd8f | |||
| 478fd1e5f0 | |||
| 0c1dfd3162 | |||
| 8d2e320d60 | |||
| 7b5e79bcf2 | |||
| 39356e5038 | |||
| 4878e5d8cc | |||
| 5f047ae41b | |||
| d83a377865 | |||
| 8292e26726 | |||
| 07ae56909a | |||
| f33018194f | |||
| a682baa5dc | |||
| 6d0ebde863 | |||
| 4ce87bec5d | |||
| 0cf714dca6 | |||
| fd00ed12d9 | |||
| 74a2106021 | |||
| a2044e20af | |||
| a3d5174b34 | |||
| 4f9be00ff2 | |||
| 24a79a309f | |||
| ba450f16b0 | |||
| 278007f908 | |||
| 16ebf304a5 | |||
| 5b7f7bb69f | |||
| 34531b184f | |||
| f6650a6c70 | |||
| acf295d56c | |||
| 9278c47901 | |||
| b2bf1bf4dd | |||
| 5da6e8f3aa | |||
| 71435a2c91 | |||
| f64e35668d | |||
| ba1651e52c | |||
| f8414a9c33 | |||
| 6344685052 | |||
| b0f1a0840c | |||
| c703bd9b4d | |||
| 96102dc41f | |||
| 8702d5209e | |||
| 74d4824a1c | |||
| bdea6a8c28 | |||
| 4fa0bc85c0 | |||
| db76870d4e | |||
| 491833b025 | |||
| 83de73c2c1 | |||
| b06ad8e8ee | |||
| 578f4416b2 | |||
| bc80b96875 | |||
| f2828deb19 | |||
| b549abc3b3 | |||
| 5b21e7f436 | |||
| b42d5fcf51 | |||
| 43530bee60 | |||
| 9b8884da81 | |||
| 2b3d77c588 | |||
| 3efb6993c7 | |||
| 5f8475d018 | |||
| afac34d7b5 | |||
| a917a6d82b | |||
| 9fba9b5aaa | |||
| 54d9632534 | |||
| c274322887 | |||
| 5900af087e | |||
| b0d3da2f39 | |||
| 6ef0ba48f6 | |||
| 1b1fce3d6e | |||
| c34a1126fb | |||
| 964cf7cc3d | |||
| 098571e9aa | |||
| ea832ad846 | |||
| 4288c4101b | |||
| 2bb7360a47 | |||
| a5388da2cc | |||
| 8ca48eca60 | |||
| eeca77df35 | |||
| 40e351333e | |||
| 0a83dea2d8 | |||
| fbf4d8d02d | |||
| a141939675 | |||
| 42eb33b579 | |||
| 9f7713267c | |||
| bfa86f0f28 | |||
| c5bb8c18a9 | |||
| 2f9c325c96 | |||
| 420cce561c | |||
| d265c8f918 | |||
| 255781d969 | |||
| b904b29503 | |||
| 0435a76fc1 | |||
| d82e98f832 | |||
| eafd6747a7 | |||
| bc266b1885 | |||
| e6a397cc55 | |||
| fcc103227c | |||
| 44878bab9e | |||
| 064d81d424 | |||
| 62187db5dd | |||
| 1a5953050f | |||
| a902cb8bce | |||
| b23dc3868e | |||
| 98ca1668c8 | |||
| 654a49f3a2 | |||
| 3e4619ed2f | |||
| aa872afa7b | |||
| 1eb6622cf5 | |||
| 8309b25ec5 | |||
| 00903f2659 | |||
| f2c470f972 | |||
| 2e8a1ab869 | |||
| 4228f58b09 | |||
| 5e0eca6cdf | |||
| 4da934585c | |||
| 49069a16cf | |||
| bda35eefc7 | |||
| 44010e0fce | |||
| efa32d0a8f | |||
| 7c776864e2 | |||
| 72370aab23 | |||
| 5377112826 | |||
| 8a5b0d4f8d | |||
| 6c35d959ac | |||
| 67d21a9619 | |||
| b1d0af311a | |||
| 5b41eb78a4 | |||
| 3902c9b1ee | |||
| d1e51878c3 | |||
| cc945fce8b | |||
| 9966065409 | |||
| bc9bc78cef | |||
| 9cb5e1abff | |||
| 1fce0b284a | |||
| e269154d17 | |||
| b65d4af1eb | |||
| d5318ac86b | |||
| bd91c67483 | |||
| b43ee02014 | |||
| 6427d23f50 | |||
| b40c8d11d8 | |||
| f9e7bc3603 | |||
| 256433d3f3 | |||
| 05a06644cf | |||
| 709e6f8f99 | |||
| a88c1d344c | |||
| a24f3592c4 | |||
| 3db11f5d82 | |||
| 6e1f1cf990 | |||
| 304b2f3c2a | |||
| 15bc17b351 | |||
| 6f436ef3eb | |||
| cf193bf51a | |||
| 1e87be2cd6 | |||
| 6cc008e894 | |||
| 760b856576 | |||
| 91bbe7d200 | |||
| 989e3fe000 | |||
| 0e064cc1e9 | |||
| de1e69de7d | |||
| a683f186d4 | |||
| a309da2a99 | |||
| 82bf22d4cc | |||
| f36bc0d14e | |||
| 1f0a8099ee | |||
| a5f6a4509d | |||
| d3ae6c1c62 | |||
| 4d3f6fc0af | |||
| a2b8d6bfb9 | |||
| 8f233296af | |||
| 5f89d61a8b | |||
| 8879647b26 | |||
| 7ee323ea8b | |||
| 7cc311ee91 | |||
| 5cde11647f | |||
| 8296775e21 | |||
| d546d62755 | |||
| 8f58a90746 | |||
| 8fa1079548 | |||
| 9e16465218 | |||
| f67471435e | |||
| fd57e0d56d | |||
| afe995a895 | |||
| 6a6b64cbc5 | |||
| b2693be111 | |||
| 675616bf51 | |||
| 1e48fa35b3 | |||
| aaa68ebe07 | |||
| 35dfcbaffe | |||
| 9aa4721790 | |||
| e553ebe666 | |||
| a814bf2c4b | |||
| 9e162d6e2d | |||
| 8baa97bde8 | |||
| 66c1ac8ee1 | |||
| abd6d00052 | |||
| 2cd672b474 | |||
| 56d958fe19 | |||
| 7aed1943b7 | |||
| 9989a61675 | |||
| 2c28935d95 | |||
| b635cb3a63 | |||
| bcdf9457c8 | |||
| 89056e903d | |||
| fc0a96fcb7 | |||
| 7b6e181cb0 | |||
| e1d298f972 | |||
| 811c8ec2ac | |||
| 6b6037739f | |||
| 80c5daabbf | |||
| 6e8d1a88f5 | |||
| 13d1ccb771 | |||
| d0b8fbe091 | |||
| 2c9a1b351e | |||
| 74e3a70a19 | |||
| 15b342972d | |||
| 1925144f85 | |||
| 2a0ec0ef21 | |||
| 65ad2fe854 | |||
| d7e4e88182 | |||
| 52c03b208e | |||
| 76aaafcf1b | |||
| c69a17abe0 | |||
| 1abd9f7987 | |||
| 1624ef6f3b | |||
| 443aaf83d2 | |||
| 67e8b4c848 | |||
| 52d808eacd | |||
| b5a648fc23 | |||
| 62b2193f2e | |||
| 2a42c4a372 | |||
| 42f9143f73 | |||
| 239dd6caf3 | |||
| e4f7ff10d7 | |||
| 6b8db0b871 | |||
| 4c3be95c86 | |||
| 88a9136398 | |||
| 0a96a6955a | |||
| 00844857ee | |||
| 6df3fcf01c | |||
| 2bf5716fce | |||
| 12f3928ab4 | |||
| eb06663758 | |||
| 02aa73dda3 | |||
| 134353d374 | |||
| 013f946c6b | |||
| 8ff50b428d | |||
| 1de8fac893 | |||
| 248677759c | |||
| 2af67b3b2b | |||
| 7890f4c029 | |||
| 32a0b2437b | |||
| dd2a814eeb | |||
| 4a1c66c5fe | |||
| 869e4d88df | |||
| e876a8ce3a | |||
| f88e6ffeeb | |||
| 5921dfbeb8 | |||
| 4b7ad753a4 | |||
| c80020a464 | |||
| 858f9727dd | |||
| 1c2062b8cc | |||
| d4ec8757ce | |||
| 76cb2cd65e | |||
| d2aba643fa | |||
| d783cccea1 | |||
| c4ad8fc068 | |||
| 4ea9bfe14b | |||
| 18729a2047 | |||
| 50462f3d8a | |||
| 2f73910e47 | |||
| 659ae2a60f | |||
| b5facb7763 | |||
| 1762112296 | |||
| 389e7f36cf | |||
| 24e6aca305 | |||
| d1f894a802 | |||
| c5e28062eb | |||
| c7d795f839 | |||
| 2265bd7fa1 | |||
| fb5f0d6d5b | |||
| 569c1d5eb4 | |||
| 066ad6ddca | |||
| 1a15225ff6 | |||
| 33bc98c015 | |||
| 895e521724 | |||
| cd7ae102ca | |||
| d0353fb81f | |||
| 6eea4f9e17 | |||
| 903a06d233 | |||
| c614b10566 | |||
| 6feb252674 | |||
| 428bdc8a2e | |||
| 271a7991d1 | |||
| 59c699c04b | |||
| 0d517617a3 | |||
| d247115ad7 | |||
| 26c084d951 | |||
| 6349be63aa | |||
| 7167ece6a7 | |||
| 76e60d3350 | |||
| e241382d09 | |||
| f4b349532d | |||
| 683ea88271 | |||
| 516348c0ec | |||
| f211a79c82 | |||
| f33a5507db | |||
| 647516d0cd | |||
| f704d0f14e | |||
| f0569d9a53 | |||
| a5c7d96770 | |||
| 423407325d | |||
| ec45438b3c | |||
| 020be477e6 | |||
| 9eef5eb66e | |||
| 185e0f3a94 | |||
| de23829439 | |||
| 4ef3d1811d | |||
| b14b614d94 | |||
| 0cbcf4c70d | |||
| 1583f7ec26 | |||
| da5dd03183 | |||
| c0cbea42a2 | |||
| de6e12c765 | |||
| f38a3c98b1 | |||
| 62e76f7db2 | |||
| ff91680f18 | |||
| b18c44f0a5 | |||
| 124e49b938 | |||
| c9f82708c8 | |||
| f1d101f6a4 | |||
| 5dfbef4f0f | |||
| fbf5cfe19f | |||
| 9eeb12adf9 | |||
| 68c9333b59 | |||
| 8405395d84 | |||
| f310d2978e | |||
| 971539a72f | |||
| 861442e2c1 | |||
| cb4d72ead2 | |||
| 376511dfa9 | |||
| f42628c2e9 | |||
| ace489ed0f | |||
| 4c866f3cd9 | |||
| 5ebc781f81 | |||
| edbc717426 | |||
| 03b59eeafc | |||
| 0400291939 | |||
| 42ce7286f5 | |||
| 444d02367e | |||
| 15975d7f51 | |||
| e5960e150b | |||
| 266b7ddb0c | |||
| e9b6d9c6c0 | |||
| 3be5e85c94 | |||
| 911f45407c | |||
| f42e03a006 | |||
| e6994f92f7 | |||
| 929347c599 | |||
| 8789330a4e | |||
| 9550302137 | |||
| e8ed467cda | |||
| 9206018fbe | |||
| 788167b1ae | |||
| 14449373e0 | |||
|
|
27818e92c7 | ||
|
|
1299d718e2 | ||
|
|
913b4f1721 | ||
|
|
a9926feea3 | ||
|
|
8eab9a75ac | ||
|
|
8c397cbc09 |
20
.claude/settings.json
Normal file
20
.claude/settings.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/claude-code-settings.json",
|
||||
"permissions": {
|
||||
"deny": [
|
||||
"Read(./ui/*.ui)",
|
||||
"Read(./map/*.map)",
|
||||
"Read(./RootDesk/MyDesk/*.codeblock)",
|
||||
"Edit(./ui/*.ui)",
|
||||
"Edit(./map/*.map)",
|
||||
"Edit(./RootDesk/MyDesk/*.codeblock)",
|
||||
"Edit(./Global/common.gamelogic)",
|
||||
"Edit(./Global/SectorConfig.config)",
|
||||
"Write(./ui/*.ui)",
|
||||
"Write(./map/*.map)",
|
||||
"Write(./RootDesk/MyDesk/*.codeblock)",
|
||||
"Write(./Global/common.gamelogic)",
|
||||
"Write(./Global/SectorConfig.config)"
|
||||
]
|
||||
}
|
||||
}
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -4,8 +4,11 @@
|
||||
# Codex CLI 로컬 설정 — Authorization Bearer 토큰 포함
|
||||
.codex/
|
||||
.agents/
|
||||
# Claude Code 로컬 설정
|
||||
.claude/
|
||||
# Claude Code 로컬 설정 — 단, 팀 공유 하네스 설정(settings.json)은 커밋 (RULES.md 참조)
|
||||
.claude/*
|
||||
!.claude/settings.json
|
||||
# 개인 스킬(superpowers) 브레인스토밍/계획 산출물 — 로컬 전용, 협업 공유 X (프로젝트 설계 문서 docs/*.md 는 추적 유지)
|
||||
docs/superpowers/
|
||||
|
||||
# === OS / 에디터 잡파일 ===
|
||||
Thumbs.db
|
||||
@@ -22,3 +25,5 @@ AGENTS.md
|
||||
Environment/
|
||||
McpScreenshots/
|
||||
*.log
|
||||
# 메이커가 재편(reorg) 중 부모를 잃은 엔티티를 모아두는 임시 폴더 (잡파일)
|
||||
Mislocated/
|
||||
|
||||
7
CLAUDE.md
Normal file
7
CLAUDE.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# SlayMaple — CLAUDE.md
|
||||
|
||||
MapleStory Worlds 기반 Slay the Spire 풍 덱빌더. 게임 전체가 데이터(`data/*.json`) + 생성기(`tools/`) 단일 소스이고, `ui/DefaultGroup.ui`(~7.1MB)·codeblock·map 파일은 **생성 산출물**이다.
|
||||
|
||||
@RULES.md
|
||||
|
||||
위 RULES.md의 하네스 규칙(산출물 Read/Edit 금지·카운트 검증·gitea-pr.mjs PR 절차)을 항상 따른다. `.claude/settings.json`이 산출물에 대한 Read/Edit/Write를 도구 수준에서 차단한다.
|
||||
@@ -23,7 +23,6 @@
|
||||
"MOD.Core.SpriteRendererComponent",
|
||||
"MOD.Core.RigidbodyComponent",
|
||||
"MOD.Core.MovementComponent",
|
||||
"MOD.Core.AIChaseComponent",
|
||||
"MOD.Core.StateComponent",
|
||||
"MOD.Core.HitComponent",
|
||||
"MOD.Core.DamageSkinSpawnerComponent",
|
||||
@@ -150,4 +149,4 @@
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 1.0
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
@@ -39,7 +39,7 @@
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 1.0
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
@@ -48,7 +48,7 @@
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 1.0
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
@@ -57,7 +57,7 @@
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 1.0
|
||||
"Value": 1
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
@@ -118,7 +118,7 @@
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 0.0
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
@@ -129,8 +129,8 @@
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0,
|
||||
"y": 0.0
|
||||
"x": 0,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -185,7 +185,7 @@
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0,
|
||||
"x": 0,
|
||||
"y": 0.35
|
||||
}
|
||||
},
|
||||
@@ -198,7 +198,7 @@
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0,
|
||||
"x": 0,
|
||||
"y": 0.35
|
||||
}
|
||||
},
|
||||
@@ -218,7 +218,7 @@
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 500.0
|
||||
"Value": 500
|
||||
},
|
||||
{
|
||||
"TargetType": "script.PlayerHit",
|
||||
@@ -254,7 +254,7 @@
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0,
|
||||
"x": 0,
|
||||
"y": 0.35
|
||||
}
|
||||
},
|
||||
@@ -265,7 +265,7 @@
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 1.0
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.MovementComponent",
|
||||
@@ -274,7 +274,7 @@
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 1.0
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.PlayerComponent",
|
||||
@@ -302,4 +302,4 @@
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
"MOD.Core.SpriteRendererComponent",
|
||||
"MOD.Core.RigidbodyComponent",
|
||||
"MOD.Core.MovementComponent",
|
||||
"MOD.Core.AIWanderComponent",
|
||||
"MOD.Core.StateComponent",
|
||||
"MOD.Core.HitComponent",
|
||||
"MOD.Core.DamageSkinSpawnerComponent",
|
||||
@@ -141,4 +140,4 @@
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,12 +24,7 @@
|
||||
"map://map03",
|
||||
"map://map04",
|
||||
"map://map05",
|
||||
"map://map06",
|
||||
"map://map07",
|
||||
"map://map08",
|
||||
"map://map09",
|
||||
"map://map10",
|
||||
"map://map11"
|
||||
"map://lobby"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
142
Global/UIButton.model
Normal file
142
Global/UIButton.model
Normal file
@@ -0,0 +1,142 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "model://uibutton",
|
||||
"ContentType": "x-mod/model",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"Version": 1,
|
||||
"Name": "UIButton",
|
||||
"BaseModelId": null,
|
||||
"Id": "uibutton",
|
||||
"Components": [
|
||||
"MOD.Core.UITransformComponent",
|
||||
"MOD.Core.SpriteGUIRendererComponent"
|
||||
],
|
||||
"Properties": [
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "RectSize",
|
||||
"DisplayName": "RectSize",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.UITransformComponent, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "RectSize"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODDataRef, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "ImageRUID",
|
||||
"DisplayName": "ImageRUID",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteGUIRendererComponent, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "ImageRUID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODColor, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "Color",
|
||||
"DisplayName": "Color",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteGUIRendererComponent, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "Color"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Values": [
|
||||
{
|
||||
"TargetType": "MOD.Core.UITransformComponent",
|
||||
"Name": "anchoredPosition",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0,
|
||||
"y": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.UITransformComponent",
|
||||
"Name": "RectSize",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 200.0,
|
||||
"y": 75.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.UITransformComponent",
|
||||
"Name": "AlignmentOption",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.AlignmentType, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteGUIRendererComponent",
|
||||
"Name": "ImageRUID",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODDataRef, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODDataRef, MOD.Core",
|
||||
"DataId": "cc3457b8e97b3e14f9d5c39ccdd640bf"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteGUIRendererComponent",
|
||||
"Name": "Color",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODColor, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODColor, MOD.Core",
|
||||
"r": 1.0,
|
||||
"g": 1.0,
|
||||
"b": 1.0,
|
||||
"a": 1.0
|
||||
}
|
||||
}
|
||||
],
|
||||
"EventLinks": [],
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
205
README.md
205
README.md
@@ -1,7 +1,7 @@
|
||||
# SlayMaple
|
||||
|
||||
[MapleStory Worlds(MSW)](https://maplestoryworlds.nexon.com/) 기반으로 제작하는 **Slay the Spire 풍 덱빌더 로그라이크** 월드.
|
||||
턴제 카드 전투, 덱 구성, 보상 선택, 맵 노드 진행을 메이플 월드 위에서 구현하는 것을 목표로 합니다.
|
||||
로비 마을에서 NPC와 상호작용해 런을 시작하고, 턴제 카드 전투·덱 구성·보상 선택·절차 생성 맵 진행·전직·영혼 메타 성장을 메이플 월드 위에서 구현합니다.
|
||||
|
||||
> 이 저장소는 MSW **로컬 워크스페이스(Local Workspace)** 데이터를 git으로 형상관리하기 위한 것입니다.
|
||||
> 공동작업자는 이 저장소를 통해 월드 데이터를 주고받습니다. (클라우드 공동제작 모드 미사용)
|
||||
@@ -32,9 +32,10 @@ git push
|
||||
```bash
|
||||
git pull
|
||||
```
|
||||
받아온 뒤, 메이커에서 **로컬 워크스페이스를 다시 로드(reload)** 해야 새 codeblock/모델 파일이 에디터 상태로 반영됩니다.
|
||||
받아온 뒤, 메이커에서 **로컬 워크스페이스를 다시 로드(reload)** 해야 새 codeblock/모델/맵 파일이 에디터 상태로 반영됩니다.
|
||||
|
||||
> 💡 같은 파일을 동시에 수정하면 git 충돌이 날 수 있으니, **서로 다른 맵/codeblock/UI를 나눠서** 작업하는 것을 권장합니다.
|
||||
> ⚠️ git pull 후 reload를 빠뜨리면 메이커의 stale 상태가 디스크를 덮어쓸 수 있습니다. 재생성 후에도 reload → 빌드 콘솔 0 에러 확인.
|
||||
|
||||
---
|
||||
|
||||
@@ -42,71 +43,178 @@ git pull
|
||||
|
||||
```
|
||||
slaymaple/
|
||||
├── data/ # 게임 데이터 단일 소스 (생성기가 읽어 주입). 맵은 정적 데이터 없음(절차 생성)
|
||||
│ ├── cards.json # 카드 166장(1~3차 전직 계열별 + 저주) + 클래스별 시작 덱
|
||||
│ ├── enemies.json # 적 18종(일반/정예/보스, 디버프 인텐트 + 외형 appearance)
|
||||
│ ├── encounters.json # 맵별 몬스터 로스터(map01~05 × combat/elite/boss)
|
||||
│ ├── potions.json # 물약 6종 + 드랍률·슬롯·상점가
|
||||
│ ├── relics.json # 유물 19종(StS 효과 × 메이플 장비) + 시작 유물 + 풀
|
||||
│ ├── cardframes.json # 커스텀 카드 프레임 3종(전사/마법사/도적 × normal/unique/legend) + 보상 등급 가중치
|
||||
│ ├── characters.json # 클래스별 초상화 RUID
|
||||
│ ├── cards.xlsx # cards.json 왕복 편집용 엑셀(excel_to_cards.bat / cards_to_excel.bat)
|
||||
│ └── camera.json # 맵별 카메라 설정값(줌·오프셋·고정 영역)
|
||||
├── Global/ # 월드 전역 설정 · 공용 모델 · 게임로직
|
||||
│ ├── common.gamelogic # SlayCardCatalog / SlayRunState / SlayCombatManager 부착 지점
|
||||
│ ├── Player.model # 플레이어 모델
|
||||
│ ├── *.model # 몬스터 등 공용 모델
|
||||
│ ├── common.gamelogic # SlayDeckController 부착 지점 (산출물)
|
||||
│ ├── DefaultPlayer.model # 플레이어 모델 (턴전투용 이동 정지 freeze 적용)
|
||||
│ ├── ChaseMonster.model · MoveMonster.model # 몬스터 공용 모델
|
||||
│ ├── SectorConfig.config # 섹터/맵 등록 (lobby + map01~05 = 6 entries)
|
||||
│ ├── WorldConfig.config # 월드 설정
|
||||
│ └── ...
|
||||
├── RootDesk/
|
||||
│ └── MyDesk/ # 작업용 책상 — codeblock(스크립트)·모델·타일셋
|
||||
│ ├── Monster.codeblock
|
||||
│ ├── MonsterAttack.codeblock
|
||||
│ ├── PlayerAttack.codeblock
|
||||
│ ├── PlayerHit.codeblock
|
||||
│ ├── UIPopup.codeblock
|
||||
│ ├── UIToast.codeblock
|
||||
│ └── RectTileData_Henesys.tileset
|
||||
├── map/
|
||||
│ └── map01.map # 메인 맵
|
||||
├── ui/ # UI 그룹 (Default / Popup / Toast)
|
||||
│ └── MyDesk/ # 작업용 책상 — codeblock(스크립트)·타일셋
|
||||
│ ├── SlayDeckController.codeblock # 게임 전체 컨트롤러 (★산출물, 직접 편집 금지)
|
||||
│ ├── Monster.codeblock · MonsterAttack.codeblock # 필드 액션 몬스터 (카드 전투와 별개)
|
||||
│ ├── PlayerAttack.codeblock · PlayerHit.codeblock · UIPopup.codeblock · UIToast.codeblock
|
||||
│ ├── CombatMonster.codeblock # 맵 몬스터 EnemyId 마커 + /common 자기등록
|
||||
│ ├── MapCamera.codeblock # 맵별 카메라 적용
|
||||
│ ├── PlayerLock.codeblock # 전투맵 플레이어 입력·이동 잠금
|
||||
│ ├── LobbyNpc.codeblock # 로비 NPC 상호작용(근접·클릭)
|
||||
│ ├── LobbyMobility.codeblock # 로비 이동·공격 해제 + 카메라 추종
|
||||
│ └── Models/Monsters/ # 적 종별 모델 <enemyId>.model (산출물 — 외형·EnemyId 베이크)
|
||||
├── map/ # 맵 6종 (산출물)
|
||||
│ ├── lobby.map # 로비 허브 맵 (마을 배경, NPC 4종, 전투 없음)
|
||||
│ └── map01.map ~ map05.map # 5막 전투/맵 노드 (공식 배경 + STS풍 우측 배치)
|
||||
├── tools/ # 결정적 생성기·도구 (주체별 폴더, 단일 소스)
|
||||
│ ├── deck/ # gen-slaydeck.mjs(★컨트롤러+common 생성 오케스트레이터) · cb/(codeblock Lua 메서드 20모듈: boot·screens·combat·hand·npc·navigation·layout·shop·reward·soul 등) · lib/(공유 상수·데이터·헬퍼) · legacy/(옛 UI emit 휴면)
|
||||
│ ├── map/ # gen-maps.mjs(맵 배경/타일) · gen-lobby-map.mjs(로비 맵+NPC) · gen-map-encounters.mjs(encounters.json 로스터 기반 종별 모델 인스턴스 배치) · rogue-map.mjs(절차 생성 JS 미러)+test
|
||||
│ ├── camera/ # gen-camera.mjs(맵별 고정 카메라 codeblock)
|
||||
│ ├── player/ # gen-player-lock.mjs(전투맵 입력 잠금) · freeze-turn-player.mjs(모델 이동 정지) · gen-lobby-npc.mjs(LobbyNpc·LobbyMobility codeblock)
|
||||
│ ├── monster/ # gen-monster-models.mjs(적 종별 .model) · gen-combat-monster.mjs(자기등록 codeblock) · freeze-turn-monsters.mjs(레거시 AI 정지) · lib/monster-model.mjs(공용 빌더)+test
|
||||
│ ├── balance/ # sim-balance.mjs(전투 밸런스 몬테카를로 시뮬) · sim-balance.test.mjs
|
||||
│ ├── verify/ # count·uimap·cbgap(카운트/UIGroup 매핑/재연결 GAP) · cardkinds(카드 kind↔효과) · cbprops(미선언 self 대입) · cbset(메서드 집합 무손실) · diffcheck(바이트동일)
|
||||
│ └── git/ # gitea-pr.mjs(UTF-8 안전 PR 생성/수정/머지 — RULES.md 참조)
|
||||
├── ui/ # UIGroup 7종 — 메이커 저작(Default/Select/Lobby/Run/Deck/Popup/Toast)
|
||||
├── docs/
|
||||
│ └── slaymaple_basic_framework.md # 전투 프레임워크 설계 문서
|
||||
│ ├── slaymaple_basic_framework.md # 전투 프레임워크 설계 문서
|
||||
│ ├── ui-generation-structure.md # UI 생성 구조 문서
|
||||
│ └── superpowers/specs|plans/ # 각 기능 설계·구현 계획 문서(P1~P15)
|
||||
├── RULES.md # 협업·AI 에이전트 하네스 규칙 (토큰 가드·검증·PR 절차)
|
||||
├── CLAUDE.md # Claude Code 자동 로드 (RULES.md 임포트)
|
||||
└── README.md
|
||||
```
|
||||
|
||||
> ⚠️ **`map/*.map` · `SlayDeckController.codeblock` · `Global/common.gamelogic`는 생성 산출물**입니다 — 직접 편집하면 재생성 때 사라집니다. 게임 로직 변경은 `data/*.json`·`tools/`의 생성기를 고쳐 재생성하세요. **`ui/*.ui`는 메이커 저작**(생성기 미생성)이라 메이커에서만 편집합니다(자세한 규칙은 [`RULES.md`](RULES.md)).
|
||||
> `.mcp.json`, `.codex/` 는 **Authorization 토큰이 포함**되어 있어 git에서 제외됩니다(`.gitignore`). 각자 로컬에서 직접 구성하세요.
|
||||
|
||||
---
|
||||
|
||||
## 직업 컨셉
|
||||
|
||||
3직업 모두 Slay the Spire 2 차용 + 메이플 IP 재해석. 카드 덱 상세 설계는 [`docs/deck-concept.md`](docs/deck-concept.md) 참조.
|
||||
|
||||
- **⚔️ 전사 (탱커, Ironclad 차용, HP80)** — 2차 3종. **파이터**: 공격을 *연속*으로 내면 콤보가 쌓이고(비공격 카드 시 리셋) 콤보로 데미지 증가 = 브루저(콤보 어택·버서크·라이징 어택). **페이지**: 썬더/블리자드 **속성 차지** + 파워 가드. **스피어맨**: 피어스·아이언 월·하이퍼 바디 유지/관통형.
|
||||
- **🗡️ 도적 (단검·독, Silent 차용, HP70)** — 표창 난사 / 독 / 교활·버림. **2차 어쌔신**(표창·독 압박·빠른 마무리)·**시프**(단검·드로우·연계) → **3차 헤르밋**(어쌔신 심화)·**시프 마스터**(시프 심화). 도적 계열만 132장(Silent 완역 포트 + 공식 스킬 아이콘).
|
||||
- **🔮 법사 (약체·게이지, Defect 차용, HP70)** — 2차 3종. **위자드(불·독)**: 독을 묻히고 *독 걸린 적에 불 카드 → 추가 데미지*(독뎀 시너지). **위자드(썬·콜)**: 오브로 썬더(다중 공격)·콜드(빙결=취약+피해), 오브 획득·다중 소모 운용. **클레릭**: 오브 없이 회복·버프 + 언데드엔 힐로 공격하는 보조 힐러.
|
||||
|
||||
## 게임 프레임워크 현황
|
||||
|
||||
`Global/common.gamelogic`의 `/common` 엔티티에 부착된 세 컴포넌트가 전투의 핵심입니다.
|
||||
**StS2풍 덱빌더 로그라이크가 end-to-end로 완성**됐고, 이제 **로비 마을을 기점으로 반복 런**이 돕니다 (게임 시작 시 MainMenu 없이 바로 로비로 진입):
|
||||
|
||||
| 컴포넌트 | 역할 |
|
||||
|---|---|
|
||||
| `SlayCardCatalog` | 카드 데이터, 시작 덱 구성, 보상 풀, 카드 복제 정의 |
|
||||
| `SlayRunState` | HP·골드·층수·덱·유물·카드 보상 등 런(run) 영속 데이터 관리 |
|
||||
| `SlayCombatManager` | 턴 진행, 드로우/버림/소멸 더미, 에너지, 적 의도, 방어도, 데미지, 승패 처리 |
|
||||
|
||||
### 프로토타입 흐름
|
||||
1. `SlayRunState`가 HP 80 · 10장 시작 덱으로 새 런 시작
|
||||
2. `SlayCombatManager`가 데모 전투 자동 시작
|
||||
3. 매 플레이어 턴: 에너지 3 회복, 방어도 초기화, 적 의도 갱신, 5장 드로우
|
||||
4. 카드 사용 시 에너지 소모 → 데미지/방어/드로우/에너지/상태이상 적용 → 버림 또는 소멸
|
||||
5. 턴 종료 시 손패 버림, 적 의도 실행, 상태이상 처리, 다음 턴 시작
|
||||
6. 전투 승리 시 잔여 HP 저장, 골드 15 지급, 카드 보상 3종 생성
|
||||
|
||||
### 유용한 스크립트 호출
|
||||
`/common` 엔티티에 붙은 스크립트에서:
|
||||
```lua
|
||||
self.Entity.SlayCombatManager:PlayCard(1, 1)
|
||||
self.Entity.SlayCombatManager:EndPlayerTurn()
|
||||
self.Entity.SlayCombatManager:DebugPlayFirstPlayable()
|
||||
self.Entity.SlayRunState:PickReward(1)
|
||||
self.Entity.SlayCombatManager:StartCombat("elite")
|
||||
```
|
||||
로비 맵(NPC 4종) → 모험가 NPC → 캐릭터 선택(전사/도적/마법사) → 절차 생성 맵(5막)
|
||||
→ 전투/엘리트/상점/휴식/유물 방 → 보상·전직·덱 성장 → 보스 → 다음 막
|
||||
→ 런 클리어(승천 해금) → 로비 복귀(영혼 정산) → 다음 런 …
|
||||
```
|
||||
|
||||
상세 설계는 [`docs/slaymaple_basic_framework.md`](docs/slaymaple_basic_framework.md) 참조.
|
||||
게임 전체는 `/common` 엔티티에 부착된 **`SlayDeckController` 단일 컴포넌트**로 동작합니다. **UI는 메이커 저작**(7개 UIGroup: Default/Select/Lobby/Run/Deck/Popup/Toast)이고, 컨트롤러가 엔티티 경로(`/ui/<UIGroup>/<Hud>/...`)로 내용을 런타임 주입합니다. 생성기 `tools/deck/gen-slaydeck.mjs`는 **`SlayDeckController.codeblock` + `common.gamelogic`만 생성**(`.ui` 미접근, 결정적 출력 — `RULES.md` 참조). 게임 데이터는 **`data/*.json`**, 맵 구조는 **런타임 절차 생성**(`GenerateMap` Lua ↔ `tools/map/rogue-map.mjs` JS 미러).
|
||||
|
||||
### 구현된 기능 (배포 퀄리티 P1~P15+, PR #34~#104)
|
||||
|
||||
| 영역 | 내용 |
|
||||
|---|---|
|
||||
| **로비 마을** | 전용 물리 맵 `lobby.map`(마을 배경). **NPC 4종 월드 엔티티** — 모험가(런 시작)·사서(카드 도감)·상인(영혼 상점)·안내원(게시판). 근접 시 머리 위 마크 + `↑`키 **또는 직접 클릭**으로 상호작용. **이동·공격 모션은 로비 맵에서만** 풀림(전투맵은 잠금), 카메라는 로비에서 **플레이어 추종**(전투맵은 고정) |
|
||||
| **캐릭터·전직** | 시작 시 **전사(HP80)/도적(HP70)/마법사(HP70)** 3종 선택(**초상화·직업 설명·선택 테두리 강조** 캐릭터 선택 UI), 클래스별 시작 덱. 보스 클리어 시 [유물] vs [**전직**] — 전사→파이터/페이지/스피어맨, 법사→위자드(불·독)/위자드(썬·콜)/클레릭 (2차 3종씩), **도적→어쌔신·시프(2차) → 헤르밋·시프 마스터(3차)**. 전직 시 대표 카드 지급, 전용 카드는 해당 계열 풀만 획득 |
|
||||
| **카드 전투** | 에너지 3·드로우·**드래그 사용**(공격=적에 드롭, 스킬/파워=위로 스윕). 카드 **166장** — kind **Attack(59)/Skill(74)/Power(31)/Status(2)**. kind↔효과 정합성 정적 검증(`cardkinds.mjs`). 메커니즘: 다단히트·방어 무시·자가 디버프·드로·회복·**전체 공격(AoE)**·**독(DoT)**·**retain**(턴 종료 손패 유지)·**sly discard**(버림 트리거) |
|
||||
| **도적 카드 공용 효과** | 카드 효과를 **카드명 하드코딩 대신 `data/cards.json` 공용 필드**로 표현(재사용). **불가침**·**x-cost**(에너지 비례 피해/약화)·드로우 수 비례 데미지·**다음 스킬 반복**·**처치 보상/반복**·카드 설명 **키워드 하이라이트**·드로우 연동(`drawSkillBlock`·`drawPoison`)·독 버스트·랜덤 타깃 등. **Lua + JS 미러 양쪽 구현**. 필드 사전 [`docs/card-effect-fields.md`](docs/card-effect-fields.md) |
|
||||
| **버프/디버프** | StS 표준 — **힘**(+N 영구)·**약화**(주는 피해 −25%)·**취약**(받는 피해 +50%)·**독**(매 행동 틱). 양방향(적 디버프 인텐트 포함), 인텐트는 최종 예상치 표시 |
|
||||
| **전투 연출** | 공격 이펙트·**몬스터 데미지 팝업(자릿수 스킨)**·드래그 타깃 마커·적 개별 차례·**공격/피격/독뎀 모션**(아바타 상태 전이·몬스터 hit 클립·런지/넉백) |
|
||||
| **절차 생성 맵** | 막 시작마다 **경로 생성**(런마다 다름, **가로 진행**). 층 규칙: 1~2층 전투만 → 3층~ 상점/휴식 → 4층~ 엘리트/**유물 방** → 보스 수렴. 점선 경로·상태 4단·층 카운터. 노드 타입별 **몬스터 랜덤 구성**(일반 1~3 / 엘리트 / 보스) + intent 랜덤 행동 |
|
||||
| **몬스터 종별 모델** | 적 종별 전용 `.model`(프리팹) — 외형(stand/hit/die)·EnemyId 베이크, 태생 AI-free. 맵 배치는 **`data/encounters.json` 맵별 로스터**대로 해당 모델 인스턴스 생성(외형=정체성 고정). 능력치·행동은 `enemies.json`, 외형은 `appearance`, 배치는 `encounters.json`로 관심사 분리 |
|
||||
| **유물 19종 / 물약 6종** | 유물: StS 효과 × 메이플 장비 외형, TopBar 아이콘 + 마우스오버 툴팁, 8종 훅. 물약: 승리 40% 드랍·상점·슬롯 메뉴. 보물 방=상자 연출 → 유물+메소 |
|
||||
| **카드 프레임·등급** | 커스텀 프레임 3종(전사/마법사/도적 × normal/unique/legend), 카드 5개 사이트 통합 레이아웃. 보상 등급 가중 추첨 70/25/5 |
|
||||
| **영혼(Soul) 메타 성장** | 승천과 별개의 영구 강화 화폐. 2차 전직 상태로 보스 클리어 시 적립 → 로비 영혼 상점 4종 해금(시작 메소 +60·HP +15·덱 정제·시작 유물 +1). **UserDataStorage 영구 저장** |
|
||||
| **승천(Ascension)** | A1~A10 누적 모디파이어(적 강화·시작 HP 감소·보상 감소). UserDataStorage 유저별 영구 저장, 런 클리어 시 다음 단계 해금 |
|
||||
| **멀티 act** | **5막** 진행(보스 클리어→다음 막 텔레포트, 맵·인카운터 변경, 적 스케일 `1+(막-1)*0.45`), 5막 클리어 시 런 종료 |
|
||||
| **경제** | 화폐 표기 **메소**(코인 아이콘), 카드/유물/물약 메소 가격. 내부 식별자는 Gold 유지 |
|
||||
| **밸런스 시뮬** | `tools/balance/sim-balance.mjs` — 전투 규칙 JS 미러(몬테카를로) + `tools/map/rogue-map.mjs`(맵 생성 미러) + node 단위테스트(현 97종) |
|
||||
|
||||
> ⚠️ 수치(적 스탯·경제·승천 배율)는 1차 조정 상태입니다. 정밀 밸런싱은 `sim-balance.mjs`로 검증하며 진행합니다.
|
||||
> ℹ️ 도적 계열 카드 132장은 STS Silent 완역 포트 + **공식 스킬 아이콘 적용 완료**, rogue 1차 + 어쌔신/시프(2차) + 헤르밋/시프 마스터(3차)로 재편. 남은 작업은 카드명 메이플 재서사·멀티플레이어 전제 카드 싱글 정리 — [`docs/deck-concept.md`](docs/deck-concept.md)·[`docs/bandit-card-audit.md`](docs/bandit-card-audit.md) 참조.
|
||||
|
||||
### 유용한 스크립트 호출
|
||||
`/common` 엔티티(또는 Play Test 컨텍스트)에서:
|
||||
```lua
|
||||
local c = _EntityService:GetEntityByPath("/common").SlayDeckController
|
||||
-- 로비
|
||||
c:OnLobbyNpcInteract("run") -- 모험가(런 시작) / "codex"(도감) / "shop"(영혼상점) / "board"(게시판)
|
||||
c:ShowLobby() -- 로비 맵 복귀 + 상태 초기화
|
||||
-- 런
|
||||
c:SelectClass("warrior") -- "warrior" / "rogue" / "magician"
|
||||
c:StartNewGame() -- 캐릭터 선택 → 런 시작(map01 텔레포트)
|
||||
c:PickNode("r1c2") -- 맵 노드 선택(절차 생성 그리드 id) / "boss"
|
||||
c:PlayCard(1) -- 손패 slot 카드 사용
|
||||
c:EndPlayerTurn() -- 턴 종료 → 적 턴 → 다음 턴
|
||||
c:PickReward(1) -- 보상 카드 1택(0=건너뛰기)
|
||||
c:BuyCard(1) / c:BuyRelic() / c:BuyPotion() -- 상점 구매(메소)
|
||||
c:SetJob("fighter") -- 전직 (보스 보상 화면) — 2차: fighter/page/spearman·firepoison/icelightning/cleric·assassin/thief, 3차: hermit/thiefmaster
|
||||
c:AdjustAscension(1) -- 메뉴에서 승천 단계 +1
|
||||
```
|
||||
|
||||
밸런스 검증: `node tools/balance/sim-balance.mjs [N] [--seed S]` · 테스트: `node --test tools/balance/sim-balance.test.mjs tools/map/rogue-map.test.mjs`.
|
||||
상세 설계는 [`docs/slaymaple_basic_framework.md`](docs/slaymaple_basic_framework.md) 및 `docs/superpowers/specs/` 참조.
|
||||
|
||||
### 디버그 단축키
|
||||
|
||||
개발·QA용 키보드 단축키. **전투 중**(런 활성 + 전투 진행 중)에만 동작합니다.
|
||||
|
||||
| 단축키 | 기능 |
|
||||
|---|---|
|
||||
| **Ctrl + Shift + C** | **카드 picker** — 직업 전체 카드 패널을 띄우고, 카드를 클릭하면 **즉시 손패에 추가**. 상단 탭(전사/도적/마법사)으로 직업별 카드 풀 전환. 카드 효과·메커니즘 즉석 테스트용 |
|
||||
| **Ctrl + Shift + E** | **전체 회복 치트** — 체력·에너지를 최대치로 회복 |
|
||||
|
||||
> 카드 picker는 메이커 저작 UI `DeckUIGroup/DeckAllHud`(120 슬롯 그리드 + 직업 탭 3종)를 사용하고, 컨트롤러가 런타임에 카드 비주얼·버튼을 바인딩합니다. 구현: 키 바인딩 `tools/deck/cb/boot.mjs`, picker 로직 `tools/deck/cb/deckview.mjs`(`OpenDebugCardPicker`/`OnAllDeckCardButton`), 버튼 바인딩 `tools/deck/cb/deckturn.mjs`(`BindButtons`). 옛 picker UI 생성기 `tools/deck/legacy/hud/deckall.mjs`는 UI 메이커-저작 전환 후 **휴면**(Maker UI가 대체).
|
||||
|
||||
### 산출물 재생성
|
||||
```bash
|
||||
node tools/deck/gen-slaydeck.mjs # 컨트롤러+common (UI는 메이커 저작 — 미생성)
|
||||
node tools/map/gen-maps.mjs # map01~05 배경/타일
|
||||
node tools/map/gen-lobby-map.mjs # 로비 맵 + NPC 배치
|
||||
node tools/player/gen-lobby-npc.mjs # 로비 codeblock(LobbyNpc·LobbyMobility)
|
||||
node tools/camera/gen-camera.mjs # 맵별 카메라
|
||||
node tools/player/gen-player-lock.mjs # 전투맵 입력 잠금
|
||||
node tools/monster/gen-monster-models.mjs # 적 종별 모델 .model (외형=enemies.json appearance)
|
||||
node tools/monster/gen-combat-monster.mjs # 자기등록 마커 codeblock
|
||||
node tools/map/gen-map-encounters.mjs # encounters.json 로스터 기반 맵 몬스터 배치
|
||||
```
|
||||
> 산출물 검증은 내용 출력 없이 카운트만: `node tools/verify/count.mjs <ui|cb|common> <regex>...`. 정적 가드 — 카드 kind↔효과 `cardkinds.mjs` · 미선언 self 대입 `cbprops.mjs` · UI 경로 재연결 GAP `cbgap.mjs` · 리팩터 바이트동일 `diffcheck.mjs` (자세한 가드는 [`RULES.md`](RULES.md)).
|
||||
|
||||
---
|
||||
|
||||
## 다음 구현 단계
|
||||
- [ ] HP·방어도·에너지·적 의도·손패 5버튼을 렌더링하는 전투 UI
|
||||
- [ ] 전투/엘리트/상점/휴식/이벤트/보스 노드를 가진 맵 노드 UI
|
||||
- [ ] `OnCombatStart` / `OnCardPlayed` / `OnTurnStart` / `OnCombatReward` 훅을 가진 유물 시스템
|
||||
- [ ] 적 행동 패턴을 데이터로 정의 (현재 단순 의도 패턴 → 무브셋)
|
||||
- [ ] 런 루프 완성 후 저장/불러오기
|
||||
## 아키텍처 메모
|
||||
|
||||
현재 게임 전체 로직이 `SlayDeckController` 단일 codeblock에 모여 있습니다. 초기 설계의 3분할(`SlayCardCatalog`/`SlayRunState`/`SlayCombatManager`)은 **기능적으로 모두 구현**됐으나 아직 한 컴포넌트 안에 있습니다. 맵 NPC·카메라·입력 잠금 등 **맵 단위 동작은 별도 codeblock**(LobbyNpc/LobbyMobility/MapCamera/PlayerLock/CombatMonster)으로 분리해 각 맵 루트/엔티티에 부착합니다. 카드/적/맵/유물/프레임/카메라 데이터는 `data/*.json`로 외부화돼 있습니다. **2026-06-17**: UI를 단일 `DefaultGroup`에서 7개 UIGroup(Select/Lobby/Run/Deck 등)으로 분리해 **메이커 저작으로 전환** — 생성기는 더 이상 `.ui`를 만들지 않고, 컨트롤러가 새 UIGroup 경로로 재연결됨(옛 UI emit `hud/*`·`gen-cardhand`는 `tools/deck/legacy/` 휴면). 재연결 무결성은 `tools/verify/cbgap.mjs`(GAP 0)로 검증.
|
||||
|
||||
> ⚠️ **전투 규칙과 맵 생성은 Lua(gen-slaydeck 내장)와 JS 미러(sim-balance/rogue-map)로 이중 구현**입니다. 한쪽을 고치면 반드시 다른 쪽도 동기화하고 테스트하세요(`RULES.md` §6).
|
||||
> ⚠️ **카드 `kind`는 효과와 반드시 일치**해야 합니다 — 데미지=`Attack`, 방어/유틸=`Skill`, 지속효과=`Power`. 안 맞으면 런타임 에러 없이 *사용 불가/무효과 死카드*가 됩니다(2026-06-30 Defend·Rage 사고). 새 효과 필드는 `docs/card-effect-fields.md` 등록 + Lua/JS 양쪽 핸들러 구현. 정적 검증 `node tools/verify/cardkinds.mjs`(`RULES.md` §9). cb Lua 지역변수는 의미명 사용(`RULES.md` §8).
|
||||
|
||||
---
|
||||
|
||||
## 향후 개선 계획 (후속 후보)
|
||||
- [x] 전투 루프 · 런 루프 · 절차 생성 맵 · 상점/휴식/유물 방 · 유물 19종 · 물약 · 버프/디버프 · Power · 전직(전사/법사/도적 2차) · 승천+개인 저장 · 전투 모션 · 커스텀 프레임 · **반복 런·로비 맵·NPC·영혼·메소·카메라 추종 (P1~P15 완료)**
|
||||
- [x] **UI 메이커-저작 전환** — 단일 DefaultGroup → 7개 UIGroup 분리, 생성기 UI 저작 폐기(`tools/deck/legacy/`), 컨트롤러 경로 재연결(cbgap GAP 0) (2026-06-17)
|
||||
- [x] **시작 로비 직행 · 캐릭터 선택 UI · 디버그 치트 · map01 로스터 (2026-06-18)** — 게임 시작 시 MainMenu 없이 곧장 로비 진입(MainMenu는 추후 싱글/멀티/종료 메뉴로 재지정); 캐릭터 선택 화면 초상화·직업 설명·선택 테두리·Art 클리핑(MaskComponent) 배선; 디버그 단축키 Ctrl+Shift+C(카드 picker)·Ctrl+Shift+E(체력+에너지 전체 회복); map01 몬스터 18종 로스터(랜덤 행동)
|
||||
- [x] **컨트롤러 관심사별 모듈 분리 · 코드 규칙 (2026-06-26, #94)** — SlayDeckController를 `cb/*.mjs` 20모듈로 분리(런타임은 단일 codeblock 유지), 변수명 의미화, 검증 `cbset.mjs`(집합 무손실)·`cbprops.mjs`(미선언 self)
|
||||
- [x] **도적 계열 대개편 + 3차 전직 · 카드 공용 효과 (2026-06-23~30, #82~#99)** — Silent 포트를 rogue 1차 + 어쌔신/시프(2차) + 헤르밋/시프 마스터(3차)로 재편, 카드 효과를 카드명 하드코딩 대신 `cards.json` 공용 필드로(`docs/card-effect-fields.md`), 카드 **166장**
|
||||
- [x] **코드리뷰 버그수정 + kind↔효과 규칙 (2026-06-29~30, #96·#102)** — 게임버그 6·시뮬 충실도 3·설명 2 수정(Defend kind Attack→Skill·Rage Power→Attack 포함), kind↔효과 정적 검증 `cardkinds.mjs`, 카드 왕복 편집 엑셀(#93)
|
||||
- [ ] **도적 카드명 재서사·설명 한글화** — Silent 직역 카드명을 어쌔신/시프 메이플 스킬명으로 재서사(아이콘은 적용 완료), 2·3차 전직 설명 한글화
|
||||
- [ ] **런 이어하기** — 진행 중 런 직렬화 저장(UserDataStorage 확장, 메뉴 "이어하기" 활성화)
|
||||
- [ ] **카드 제거/업그레이드** — 상점 카드 제거 슬롯, 휴식 노드에서 카드 강화
|
||||
- [ ] **이벤트 노드(?)** — 랜덤 텍스트 이벤트(선택지·리스크/리워드)
|
||||
- [ ] **3차 전직 — 전사·법사 확장** (도적은 완료: 헤르밋·시프 마스터), 후반 막 보상으로
|
||||
- [ ] **궁수 등 추가 클래스** — 캐릭터 선택 슬롯 확장
|
||||
- [ ] **정밀 밸런싱** — 첫 인카운터 승률 완화·직업별 카드 효율 튜닝(`sim-balance.mjs` 리포트 기반)
|
||||
- [ ] **상점 보장 규칙** — 막당 상점 최소 1회 등장
|
||||
- [ ] **연출 보강** — 사운드(타격·획득), 맵 화면에 유물/물약 표시
|
||||
|
||||
---
|
||||
|
||||
@@ -117,4 +225,5 @@ self.Entity.SlayCombatManager:StartCombat("elite")
|
||||
```
|
||||
2. MSW Maker에서 이 폴더를 **로컬 워크스페이스 경로**로 지정해 월드 열기
|
||||
3. `.mcp.json` / `.codex/` 는 git에 없으므로, 본인 토큰으로 직접 생성 (MCP·Codex 사용 시)
|
||||
4. 작업 전 항상 `git pull`, 작업 후 `git add/commit/push`
|
||||
4. 작업 전 항상 `git pull` + 메이커 reload, 작업 후 `git add/commit/push`
|
||||
5. **AI 에이전트(Claude Code 등)로 작업한다면 [`RULES.md`](RULES.md) 필독** — 생성 산출물 접근 금지(토큰 가드)·검증 절차·PR 도구(`tools/git/gitea-pr.mjs`) 규칙. Claude Code는 `CLAUDE.md`가 자동 적용
|
||||
|
||||
107
RULES.md
Normal file
107
RULES.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# RULES.md — SlayMaple 하네스 엔지니어링 규칙
|
||||
|
||||
AI 에이전트(Claude Code 등)와 협업자가 이 저장소에서 **토큰을 낭비하지 않고 안전하게** 작업하기 위한 공용 규칙.
|
||||
Claude Code는 `CLAUDE.md`가 이 파일을 임포트하므로 자동 적용된다. 다른 도구(Codex 등)를 쓰면 세션 시작 시 이 파일을 읽혀라.
|
||||
|
||||
---
|
||||
|
||||
## 1. 생성 산출물은 읽지도, 고치지도 않는다 (가장 중요)
|
||||
|
||||
이 저장소의 큰 파일들은 전부 **생성기 산출물**이다. 직접 읽으면 토큰이 증발하고, 직접 고치면 다음 재생성 때 사라진다.
|
||||
|
||||
| 산출물 (절대 Read/Edit 금지) | 크기 | 단일 소스 (여기만 편집) | 재생성 명령 |
|
||||
|---|---|---|---|
|
||||
| `ui/*.ui` (Default·Select·Lobby·Run·Deck·Popup·Toast UIGroup 7종) | 9KB~4.5MB | **메이커 저작 (생성기 미생성, 2026-06-17~)** — 메이커에서 시각 편집 | (없음) |
|
||||
| `RootDesk/MyDesk/SlayDeckController.codeblock` | ~270KB | `data/*.json` + `tools/deck/`(`gen-slaydeck.mjs`+`lib/`+`cb/`) | `node tools/deck/gen-slaydeck.mjs` |
|
||||
| `Global/common.gamelogic` | ~1KB | 〃 | 〃 |
|
||||
| `map/map01.map`~`map05.map`, `map/lobby.map` | 각 ~210KB | `tools/map/`·`tools/monster/`·`tools/camera/`·`tools/player/` (↓ 보조 생성기) | 해당 생성기 |
|
||||
| `RootDesk/MyDesk/CombatMonster.codeblock` | ~2KB | `tools/monster/gen-combat-monster.mjs` | `node tools/monster/gen-combat-monster.mjs` |
|
||||
| `RootDesk/MyDesk/Models/Monsters/<enemyId>.model` (적 종별, 최대 18종) | 각 ~5KB | `data/enemies.json`(`appearance`) + `tools/monster/gen-monster-models.mjs` | `node tools/monster/gen-monster-models.mjs` |
|
||||
| `RootDesk/MyDesk/PlayerLock.codeblock` | ~2KB | `tools/player/gen-player-lock.mjs` | `node tools/player/gen-player-lock.mjs` |
|
||||
| `RootDesk/MyDesk/MapCamera.codeblock` | ~2KB | `tools/camera/gen-camera.mjs` (값: `data/camera.json`) | `node tools/camera/gen-camera.mjs` |
|
||||
| `RootDesk/MyDesk/LobbyNpc.codeblock`·`LobbyMobility.codeblock` | 각 ~2-3KB | `tools/player/gen-lobby-npc.mjs` | `node tools/player/gen-lobby-npc.mjs` |
|
||||
| `Global/SectorConfig.config` | ~1KB | `tools/map/gen-maps.mjs`·`gen-lobby-map.mjs` (패치) | 해당 생성기 |
|
||||
|
||||
- `.claude/settings.json`의 permissions.deny가 위 파일의 Read/Edit/Write 도구 사용을 차단한다 (이 저장소를 열면 자동 적용). deny는 **glob** — `ui/*.ui`·`map/*.map`·`RootDesk/MyDesk/*.codeblock`·`Global/common.gamelogic`·`Global/SectorConfig.config`. 따라서 **메이커 저작 codeblock/UI**(`Monster`·`MonsterAttack`·`PlayerAttack`·`PlayerHit`·`UIPopup`·`UIToast`.codeblock, **그리고 모든 `ui/*.ui`** — UI는 6개 UIGroup으로 메이커 저작)**도** Read/Edit 금지 — 이들은 생성기가 없으니 **메이커에서** 편집한다(텍스트 도구로 X). codeblock은 한 줄짜리 JSON이라 Read 시 토큰 폭발.
|
||||
- **게임 로직 수정** = `tools/deck/gen-slaydeck.mjs`(오케스트레이터) + `tools/deck/cb/*.mjs`(codeblock Lua) 또는 `data/*.json`(데이터) 수정 → 재생성(`SlayDeckController.codeblock`+`common.gamelogic`만, **`.ui` 미접근**) → 통째로 커밋. **UI 수정 = 메이커에서**(생성기는 UI를 안 만든다).
|
||||
- **codeblock 메서드(Lua)는 관심사별 모듈** `tools/deck/cb/*.mjs`(boot·screens·npc·navigation·layout·combat·hand·deckview·items·map·shop 등 20종 — 화면전환=`screens`·NPC=`npc`·포지션=`navigation`(월드 텔레포트)/`layout`(UI 슬롯 배치). 새 메서드는 관심사에 맞는 모듈에 작성하고, 한 모듈이 비대해지면 분할한다. 횡단 관심사를 한 모듈에 몰아넣지 않는다). **공유분**: 상수·데이터·lua 테이블 = `tools/deck/lib/{ui-helpers,data,codeblock}.mjs`(cb가 import — `MAX_MONSTERS`=4 등). prop 103개는 오케스트레이터 `writeCodeblocks`에 유지. 특정 메서드 수정은 `cb/<name>.mjs`만(의존: orchestrator→cb→lib 단방향). **cb 모듈은 원본 메서드 순서 보존이 바이트동일 조건**. **UI emit(옛 `hud/*.mjs` 15종·`gen-cardhand.mjs`)은 `tools/deck/legacy/`로 이관 — 휴면(생성기 미사용)**: UI가 메이커 저작이라 생성기가 안 만든다. (롤백용 `legacy/upsert-ui.mjs`는 직접 실행 시에만 옛 `DefaultGroup.ui`를 재생성.)
|
||||
- 리팩터 시 **출력 바이트-동일 검증**: `node tools/deck/gen-slaydeck.mjs` 후 `node tools/verify/diffcheck.mjs [ref]`(워킹트리 vs ref(기본 HEAD) 줄바꿈 정규화 비교 — 산출물 경로를 명령줄에 노출 안 해 deny 회피). 산출물 ` M`은 보통 autocrlf churn이니 `git checkout --`로 복원.
|
||||
- **UI 전면 메이커 저작 (2026-06-17~)**: 단일 `DefaultGroup`을 7개 UIGroup으로 분리 — `DefaultGroup`(MainMenu+월드조작), `SelectUIGroup`(charselect/job), `LobbyUIGroup`(lobby/board/soulshop), `RunUIGroup`(combat/map/shop/rest/treasure/reward/cardhand/deck), `DeckUIGroup`(덱 도감), `PopupGroup`·`ToastGroup`. 컨트롤러(`cb/*.mjs`)는 엔티티 **경로**(`/ui/<UIGroup>/<Hud>/...`)로 텍스트·이미지·표시숨김·상태기반 위치/크기/색을 **런타임 주입**(레이아웃=메이커, 내용=컨트롤러 — 메이커가 이 경로 유지 필수). 몬스터 슬롯 = `RunUIGroup/CombatHud/MonsterStatus{1..4}`(자식 Name·Hp·Intent·HpBarFill·Buffs·BlockBadge·TargetMarker; TargetFrame 없음). **부트 흐름**: `OnBeginPlay`→MainMenu→(`MainMenu/NewGameButton`)→로비→run NPC(`OnLobbyNpcInteract` id=="run")→charselect→런. **재연결 검증**: `node tools/verify/cbgap.mjs`(cb 참조 경로↔.ui GAP 0이어야) + 재생성 후 `git status -- ui/` 변경 0(생성기 .ui 미접근 증명). 섹션→UIGroup 일괄 remap 마이그레이션은 `tools/deck/reconnect-ui-paths.mjs`(멱등). UIGroup별 .ui 분포 확인은 `tools/verify/uimap.mjs`.
|
||||
- **머지 충돌(gen-slaydeck.mjs)**: 다른 브랜치가 단일체를 수정해 충돌나면, 그쪽 버전(`git checkout --theirs tools/deck/gen-slaydeck.mjs`)을 취해 **콘텐츠 마커 기반으로 재모듈화**(라인인덱스 X — 줄 추가에 안전·export 이름 자동 파생·`const x=[]` 직전 전문 상수 walk-back 포함) 후 `node tools/verify/diffcheck.mjs origin/main`으로 ui·codeblock 바이트-동일 확인(손실 0 증명). codeblock 메서드·patchCommon은 오케스트레이터 잔류라 그쪽 변경은 자동 보존됨.
|
||||
- **보조 생성기**(각자 자기 산출물의 단일 소스 — 위 표의 메인 `gen-slaydeck.mjs` 외):
|
||||
- `tools/camera/gen-camera.mjs` → `MapCamera.codeblock` + map01~05 카메라 부착 (값 `data/camera.json`)
|
||||
- `tools/map/gen-maps.mjs` → `map02~05` + `Global/SectorConfig.config` (map01 템플릿 클론)
|
||||
- `tools/map/gen-lobby-map.mjs` → `map/lobby.map` + `SectorConfig.config`
|
||||
- `tools/monster/gen-monster-models.mjs` → `Models/Monsters/<enemyId>.model`(적 종별, 외형·EnemyId 베이크·태생 AI-free). 단일 소스 `data/enemies.json`의 `appearance`. `appearance` 미보유 적은 스킵(메이커 저작 모델 `HolodragonKing`·`Model_monster-43`은 예외 — 이 생성기 대상 아님). 공용 빌더 `tools/monster/lib/monster-model.mjs`.
|
||||
- `tools/map/gen-map-encounters.mjs` → map01~05에 `data/encounters.json` 로스터 기반 **종별 모델 인스턴스** 배치(외형=정체성 고정). 준비도 가드: 로스터에 `appearance` 미보유 적이 있는 맵은 재생성 스킵(기존 보존). 값 검증 `node --test tools/monster/monster-model.test.mjs`.
|
||||
- `tools/monster/gen-combat-monster.mjs` → `CombatMonster.codeblock`(자기등록 마커)만 생성. 맵 부착값(EnemyId/Group)은 `gen-map-encounters.mjs`가 인스턴스에 직접 기록.
|
||||
- `tools/monster/freeze-turn-monsters.mjs` → 레거시 공용 몬스터 `.model`·맵 AI 컴포넌트 제거(신규 종별 모델은 태생 frozen — 대상 아님).
|
||||
- `tools/player/gen-player-lock.mjs` → `PlayerLock.codeblock` + map01~05 부착
|
||||
- `tools/player/gen-lobby-npc.mjs` → `LobbyNpc.codeblock`·`LobbyMobility.codeblock`
|
||||
- `tools/player/freeze-turn-player.mjs` → `Global/DefaultPlayer.model` 이동 0 고정
|
||||
- (옛 `tools/deck/gen-cardhand.mjs`·`hud/*.mjs`는 `tools/deck/legacy/`로 이관 — 휴면, UI 메이커 저작 전환)
|
||||
|
||||
## 2. 산출물 검증은 카운트로, 내용 출력 금지
|
||||
|
||||
재생성 결과 확인이 필요하면 **본문을 출력하지 말고** 존재/개수만 확인한다:
|
||||
|
||||
```bash
|
||||
node tools/deck/gen-slaydeck.mjs # 성공 메시지 1줄
|
||||
grep -c "TreasureHud" ui/DefaultGroup.ui # 개수만
|
||||
grep -c "CalcPlayerAttack" RootDesk/MyDesk/SlayDeckController.codeblock
|
||||
```
|
||||
|
||||
- ⚠️ codeblock은 **한 줄이 수만 자**(JSON 직렬화)다. `grep`(내용 출력)·`sed -n` 광역 범위 출력은 한 줄만 걸려도 토큰 폭발 → `grep -c`/`grep -l`/`grep -o '짧은패턴'`만 사용.
|
||||
- Claude Code의 Grep 도구를 산출물에 쓸 때는 `output_mode: count` 또는 `files_with_matches`만. content 모드 금지.
|
||||
- 진짜 내용 확인이 필요하면 좁은 `grep -o` 또는 `python`으로 슬라이스해서 **수 줄 이내**로.
|
||||
|
||||
## 3. 탐색 규칙
|
||||
|
||||
- 코드 탐색은 `tools/`·`data/`·`docs/`만 대상으로. 저장소 전체 grep은 산출물이 걸리므로 경로를 지정한다.
|
||||
- `git add -A` 전 `git status --short`로 산출물 외 의도치 않은 변경이 없는지 확인 (산출물 diff는 보지 않는다 — 생성기가 결정적이므로 소스 리뷰로 충분).
|
||||
- 게임 동작 확인은 메이커 플레이테스트(스크린샷·로그)로 한다. 산출물 정독으로 동작을 추론하지 않는다.
|
||||
|
||||
## 4. Git/PR 절차
|
||||
|
||||
- 브랜치 → 커밋(기능 단위) → push → **PR은 반드시 `node tools/git/gitea-pr.mjs`로** (인라인 `curl -d` 한글 본문은 Windows에서 CP949로 깨짐 — PR #34~41 사고).
|
||||
- 제목/본문은 UTF-8 spec JSON 파일로 작성 후 `create <spec.json>` / `merge <번호>`.
|
||||
- PR 제목과 본문은 한국어로 작성한다.
|
||||
- 산출물 재생성 커밋은 소스 변경 커밋과 분리하거나, 메시지에 "산출물 재생성"을 명시.
|
||||
- **PR 머지 후 브랜치 삭제**: 머지된 `feature/*`·`docs/*` 브랜치는 로컬·원격 모두 삭제한다. 삭제 전 `git merge-base --is-ancestor origin/<브랜치> origin/main`로 완전 머지 확인(종료코드 0=완전 머지 → 삭제 가능). main에 없는 커밋이 남은 브랜치와 `codex/*` 등 작업 중 브랜치는 보존한다.
|
||||
- **⚠️ main 머지 충돌 시 "머지 전체 revert" 금지 (타인 작업 유실 방지)**: 작업 브랜치에 `git merge main`(또는 origin/main) 했다가 충돌·문제가 나도 **그 머지 커밋을 통째로 `git revert` 하지 말 것.** main에 먼저 들어간 타인의 작업이 collateral로 전부 사라진다. 대신 **소스 충돌만 해소**하고 산출물(codeblock 등)은 **재생성**한다. 충돌이 산출물뿐이면 `git checkout --theirs`/재생성으로 끝. (2026-06-30 사고: codex `#98/#99`가 main 머지 후 그 머지를 revert해 `#96`의 버그수정 11개를 전부 날림 → 다시 재통합해야 했다. 복구는 `git diff <pre-merge> <내브랜치> -- <소스> | git apply --3way` 로 소스만 재적용 후 재생성하면 codex 변경과 충돌 없이 양립.)
|
||||
|
||||
## 5. 메이커(MSW) 연동 주의
|
||||
|
||||
- git pull 후 메이커에서 **로컬 워크스페이스 reload** 필수 (안 하면 메이커의 stale 상태가 디스크를 덮어씀).
|
||||
- 재생성 후 메이커가 켜져 있으면 refresh → 빌드 콘솔 0 에러 확인.
|
||||
- 카드/유물/물약 이미지는 **공식 maplestory 리소스 RUID**만 (계정 업로드 리소스는 로컬 워크스페이스에서 흰 박스).
|
||||
|
||||
## 6. 밸런스·맵 규칙 동기화
|
||||
|
||||
전투 규칙과 맵 생성은 Lua(생성기 내장)와 JS가 **이중 구현**이다. 한쪽을 고치면 반드시 다른 쪽도:
|
||||
|
||||
| 영역 | Lua (gen-slaydeck.mjs 내) | JS 미러 | 테스트 |
|
||||
|---|---|---|---|
|
||||
| 전투 규칙 | PlayCard·CalcPlayerAttack 등 | `tools/balance/sim-balance.mjs` | `node --test tools/balance/sim-balance.test.mjs` |
|
||||
| 맵 생성 | GenerateMap | `tools/map/rogue-map.mjs` | `node --test tools/map/rogue-map.test.mjs` |
|
||||
|
||||
## 7. UI 숫자 표기
|
||||
|
||||
- UI 텍스트에서는 정수값인 숫자에 `.0`을 붙이지 않는다. `1.0/1.0`이 아니라 `1/1`처럼 표시한다.
|
||||
- 생성기 내 Lua UI 코드에서 number 또는 숫자 문자열을 텍스트에 붙일 때는 `FormatNumber` 같은 포맷 헬퍼를 우선 사용한다.
|
||||
- 소수부가 플레이어에게 의미 있을 때만 소수점 표기를 유지한다.
|
||||
|
||||
## 8. codeblock 변수명
|
||||
|
||||
- cb(`tools/deck/cb/*.mjs`)의 Lua 지역변수는 **의미가 드러나는 이름**으로 작성한다(`e`→`entity`, `n`→`count`, `m`→`monster`, `lp`→`localPlayer`, `s`→`soulPoints`, `tr`→`transform`). `a`/`b`/`c` 같은 무의미 단일문자 변수는 금지.
|
||||
- 단, 순수 반복 인덱스 `i`/`j`/`r`/`c`는 관용상 허용한다.
|
||||
- 새 cb 메서드를 작성하거나 기존 메서드를 손댈 때 이 규칙을 적용한다(대규모 일괄 개명은 별도 작업으로).
|
||||
|
||||
## 9. 카드 데이터 규칙 (kind ↔ 효과 일치)
|
||||
|
||||
새 카드를 추가/수정할 때 `data/cards.json`의 `kind`는 카드의 효과·사용 메커니즘과 **반드시 일치**해야 한다. 안 맞으면 카드가 **사용 불가**거나 **재생 시 아무 효과 없는 死카드**가 된다(런타임 에러도 안 나고 sim 테스트도 못 잡음 — 정적 검증 필수).
|
||||
|
||||
- **`ResolveCardDrop` 사용 라우팅이 kind별로 다름**: `Attack`=몬스터 위에 드롭(`FindMonsterAtTouch>0` 필요)·`Skill`/`Power`=위로 스윕(`ui.y>-180`)·`Status`=unplayable. → **block·디버프·드로우 등 유틸만 있고 데미지가 없는 카드를 `Attack`으로 두면 위로 스윕으로 사용할 수 없다**(2026-06-30 아이언 바디 사고: block만 있는 방어카드가 Attack이라 전사 시작덱 4장이 먹통 → Skill로 수정).
|
||||
- **`PlayCard`의 `Power` 분기는 PlayerPowers 등록만 하고 `damage`/`aoe`를 무시**한다. → 데미지 카드=`Attack`, 방어/유틸=`Skill`, 지속효과=`Power`(단 `powerEffect` 또는 지속/온플레이 power 필드 — `turnStart*`·`dex`·`thorns`·`intangible`·`attackPoison`·`drawDamage`·`shivX`·`cardPlayed*` 등 — 이 있어야 함). Power인데 power 효과 필드가 없으면 死카드(2026-06-30 분노 사고: `damage:4/aoe`만 있어 Power 분기서 무시됨 → kind Power→Attack으로 기능화).
|
||||
- 새 효과 필드는 `docs/card-effect-fields.md` 사전에 등록하고 Lua(`tools/deck/cb/*.mjs`) + JS 미러(`tools/balance/sim-balance.mjs`) **양쪽에 핸들러 구현**(§6). 한쪽만 있으면 게임↔시뮬 드리프트.
|
||||
- **검증: `node tools/verify/cardkinds.mjs`** — kind↔효과 위반(Attack-무데미지 / Power-무효과 / 미지원 kind)을 정적 검출(이상 0 = exit 0). 카드 추가/수정 후 반드시 실행. (관련 가드: 미선언 `self.X` = `cbprops.mjs`, UI 경로 = `cbgap.mjs`, 이중구현 = `sim-balance.test.mjs`.)
|
||||
23
RootDesk/MyDesk/01_blue_background_clean_1920x1080.sprite
Normal file
23
RootDesk/MyDesk/01_blue_background_clean_1920x1080.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://ac448e909f89464898708ce232ab8b51",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/ac448e909f89464898708ce232ab8b51/639173152021849887",
|
||||
"upload_hash": "CCC4771B9353971748EF9BEE32D57F15090CE62C4BA6446B11E7842FC7AFDF1F",
|
||||
"name": "01_blue_background_clean_1920x1080",
|
||||
"resource_guid": "ac448e909f89464898708ce232ab8b51",
|
||||
"resource_version": "6a32dd82c325482f6e2bb455"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/Character select bg.sprite
Normal file
23
RootDesk/MyDesk/Character select bg.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://7629b520ced54d508b040681d06741fb",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/7629b520ced54d508b040681d06741fb/639172208899179951",
|
||||
"upload_hash": "C84DCE36101CF3F05E74F93F9B416E7D08D8B78B699E22CF8A6784994115DDAE",
|
||||
"name": "Character select bg",
|
||||
"resource_guid": "7629b520ced54d508b040681d06741fb",
|
||||
"resource_version": "6a316d1a3d5de2eb0c7d345b"
|
||||
}
|
||||
}
|
||||
}
|
||||
74
RootDesk/MyDesk/CombatMonster.codeblock
Normal file
74
RootDesk/MyDesk/CombatMonster.codeblock
Normal file
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "codeblock://combatmonster",
|
||||
"ContentType": "x-mod/codeblock",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"CoreVersion": {
|
||||
"Major": 0,
|
||||
"Minor": 2
|
||||
},
|
||||
"ScriptVersion": {
|
||||
"Major": 1,
|
||||
"Minor": 0
|
||||
},
|
||||
"Description": "",
|
||||
"Id": "CombatMonster",
|
||||
"Language": 1,
|
||||
"Name": "CombatMonster",
|
||||
"Type": 1,
|
||||
"Source": 0,
|
||||
"Target": null,
|
||||
"Properties": [
|
||||
{
|
||||
"Type": "string",
|
||||
"DefaultValue": "\"\"",
|
||||
"SyncDirection": 0,
|
||||
"Attributes": [],
|
||||
"Name": "EnemyId"
|
||||
},
|
||||
{
|
||||
"Type": "string",
|
||||
"DefaultValue": "\"combat\"",
|
||||
"SyncDirection": 0,
|
||||
"Attributes": [],
|
||||
"Name": "Group"
|
||||
},
|
||||
{
|
||||
"Type": "number",
|
||||
"DefaultValue": "0",
|
||||
"SyncDirection": 0,
|
||||
"Attributes": [],
|
||||
"Name": "RegTries"
|
||||
}
|
||||
],
|
||||
"Methods": [
|
||||
{
|
||||
"Return": {
|
||||
"Type": "void",
|
||||
"DefaultValue": null,
|
||||
"SyncDirection": 0,
|
||||
"Attributes": [],
|
||||
"Name": null
|
||||
},
|
||||
"Arguments": [],
|
||||
"Code": "self.RegTries = 0\nlocal eventId = 0\nlocal function reg()\n\tself.RegTries = self.RegTries + 1\n\tlocal c = _EntityService:GetEntityByPath(\"/common\")\n\tif c ~= nil and c.SlayDeckController ~= nil then\n\t\tlocal mapName = \"\"\n\t\tif self.Entity.CurrentMapName ~= nil then\n\t\t\tmapName = self.Entity.CurrentMapName\n\t\tend\n\t\tc.SlayDeckController:RegisterMonster(self.Entity, self.EnemyId, self.Group, mapName)\n\t\t_TimerService:ClearTimer(eventId)\n\telseif self.RegTries > 50 then\n\t\t_TimerService:ClearTimer(eventId)\n\tend\nend\neventId = _TimerService:SetTimerRepeat(reg, 0.1)",
|
||||
"Scope": 2,
|
||||
"ExecSpace": 6,
|
||||
"Attributes": [],
|
||||
"Name": "OnBeginPlay"
|
||||
}
|
||||
],
|
||||
"EntityEventHandlers": []
|
||||
}
|
||||
}
|
||||
}
|
||||
60
RootDesk/MyDesk/LobbyMobility.codeblock
Normal file
60
RootDesk/MyDesk/LobbyMobility.codeblock
Normal file
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "codeblock://lobbymobility",
|
||||
"ContentType": "x-mod/codeblock",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"CoreVersion": {
|
||||
"Major": 0,
|
||||
"Minor": 2
|
||||
},
|
||||
"ScriptVersion": {
|
||||
"Major": 1,
|
||||
"Minor": 0
|
||||
},
|
||||
"Description": "",
|
||||
"Id": "LobbyMobility",
|
||||
"Language": 1,
|
||||
"Name": "LobbyMobility",
|
||||
"Type": 1,
|
||||
"Source": 0,
|
||||
"Target": null,
|
||||
"Properties": [
|
||||
{
|
||||
"Type": "number",
|
||||
"DefaultValue": "0",
|
||||
"SyncDirection": 0,
|
||||
"Attributes": [],
|
||||
"Name": "Tries"
|
||||
}
|
||||
],
|
||||
"Methods": [
|
||||
{
|
||||
"Return": {
|
||||
"Type": "void",
|
||||
"DefaultValue": null,
|
||||
"SyncDirection": 0,
|
||||
"Attributes": [],
|
||||
"Name": null
|
||||
},
|
||||
"Arguments": [],
|
||||
"Code": "self.Tries = 0\nlocal eventId = 0\nlocal function apply()\n\tself.Tries = self.Tries + 1\n\tlocal lp = _UserService.LocalPlayer\n\tif lp ~= nil and lp.PlayerControllerComponent ~= nil then\n\t\tlocal pc = lp.PlayerControllerComponent\n\t\tpc.Enable = true\n\t\tpc.FixedLookAt = 0\n\t\tlocal rb = lp.RigidbodyComponent\n\t\tif rb ~= nil then rb.WalkAcceleration = 0.7 end\n\t\tlocal mv = lp.MovementComponent\n\t\tif mv ~= nil then\n\t\t\tmv.InputSpeed = 1.4\n\t\t\tmv.JumpForce = 1.23\n\t\tend\n\t\tlocal cam = lp.CameraComponent\n\t\tif cam == nil then cam = _CameraService:GetCurrentCameraComponent() end\n\t\tif cam ~= nil then\n\t\t\tcam.ZoomRatio = 90\n\t\t\tcam.ConfineCameraArea = false\n\t\t\tcam.ScreenOffset = Vector2(0.5, 0.5)\n\t\t\tcam.CameraOffset = Vector2(0, 0)\n\t\tend\n\t\t_TimerService:ClearTimer(eventId)\n\telseif self.Tries > 50 then\n\t\t_TimerService:ClearTimer(eventId)\n\tend\nend\neventId = _TimerService:SetTimerRepeat(apply, 0.1)",
|
||||
"Scope": 2,
|
||||
"ExecSpace": 6,
|
||||
"Attributes": [],
|
||||
"Name": "OnBeginPlay"
|
||||
}
|
||||
],
|
||||
"EntityEventHandlers": []
|
||||
}
|
||||
}
|
||||
}
|
||||
89
RootDesk/MyDesk/LobbyNpc.codeblock
Normal file
89
RootDesk/MyDesk/LobbyNpc.codeblock
Normal file
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "codeblock://lobbynpc",
|
||||
"ContentType": "x-mod/codeblock",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"CoreVersion": {
|
||||
"Major": 0,
|
||||
"Minor": 2
|
||||
},
|
||||
"ScriptVersion": {
|
||||
"Major": 1,
|
||||
"Minor": 0
|
||||
},
|
||||
"Description": "",
|
||||
"Id": "LobbyNpc",
|
||||
"Language": 1,
|
||||
"Name": "LobbyNpc",
|
||||
"Type": 1,
|
||||
"Source": 0,
|
||||
"Target": null,
|
||||
"Properties": [
|
||||
{
|
||||
"Type": "string",
|
||||
"DefaultValue": "\"\"",
|
||||
"SyncDirection": 0,
|
||||
"Attributes": [],
|
||||
"Name": "NpcId"
|
||||
},
|
||||
{
|
||||
"Type": "string",
|
||||
"DefaultValue": "\"\"",
|
||||
"SyncDirection": 0,
|
||||
"Attributes": [],
|
||||
"Name": "MarkName"
|
||||
},
|
||||
{
|
||||
"Type": "boolean",
|
||||
"DefaultValue": "false",
|
||||
"SyncDirection": 0,
|
||||
"Attributes": [],
|
||||
"Name": "InRange"
|
||||
}
|
||||
],
|
||||
"Methods": [
|
||||
{
|
||||
"Return": {
|
||||
"Type": "void",
|
||||
"DefaultValue": null,
|
||||
"SyncDirection": 0,
|
||||
"Attributes": [],
|
||||
"Name": null
|
||||
},
|
||||
"Arguments": [],
|
||||
"Code": "self.InRange = false\nlocal mark = _EntityService:GetEntityByPath(\"/maps/lobby/\" .. self.MarkName)\nif mark ~= nil then mark:SetVisible(false) end\nself.Entity:ConnectEvent(TouchEvent, function(e)\n\tself:Interact()\nend)\n_InputService:ConnectEvent(KeyDownEvent, function(e)\n\tif self.InRange and e.key == KeyboardKey.UpArrow then\n\t\tself:Interact()\n\tend\nend)\nlocal eventId = 0\nlocal function tick()\n\tlocal lp = _UserService.LocalPlayer\n\tif lp == nil then return end\n\tif mark == nil then mark = _EntityService:GetEntityByPath(\"/maps/lobby/\" .. self.MarkName) end\n\tlocal a = lp.TransformComponent.WorldPosition\n\tlocal b = self.Entity.TransformComponent.WorldPosition\n\tlocal d = Vector2.Distance(Vector2(a.x, a.y), Vector2(b.x, b.y))\n\tlocal near = d < 1.2\n\tif near ~= self.InRange then\n\t\tself.InRange = near\n\t\tif mark ~= nil then mark:SetVisible(near) end\n\tend\nend\neventId = _TimerService:SetTimerRepeat(tick, 0.15)",
|
||||
"Scope": 2,
|
||||
"ExecSpace": 6,
|
||||
"Attributes": [],
|
||||
"Name": "OnBeginPlay"
|
||||
},
|
||||
{
|
||||
"Return": {
|
||||
"Type": "void",
|
||||
"DefaultValue": null,
|
||||
"SyncDirection": 0,
|
||||
"Attributes": [],
|
||||
"Name": null
|
||||
},
|
||||
"Arguments": [],
|
||||
"Code": "local c = _EntityService:GetEntityByPath(\"/common\")\nif c ~= nil and c.SlayDeckController ~= nil then\n\tc.SlayDeckController:OnLobbyNpcInteract(self.NpcId)\nend",
|
||||
"Scope": 2,
|
||||
"ExecSpace": 6,
|
||||
"Attributes": [],
|
||||
"Name": "Interact"
|
||||
}
|
||||
],
|
||||
"EntityEventHandlers": []
|
||||
}
|
||||
}
|
||||
}
|
||||
60
RootDesk/MyDesk/MapCamera.codeblock
Normal file
60
RootDesk/MyDesk/MapCamera.codeblock
Normal file
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "codeblock://mapcamera",
|
||||
"ContentType": "x-mod/codeblock",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"CoreVersion": {
|
||||
"Major": 0,
|
||||
"Minor": 2
|
||||
},
|
||||
"ScriptVersion": {
|
||||
"Major": 1,
|
||||
"Minor": 0
|
||||
},
|
||||
"Description": "",
|
||||
"Id": "MapCamera",
|
||||
"Language": 1,
|
||||
"Name": "MapCamera",
|
||||
"Type": 1,
|
||||
"Source": 0,
|
||||
"Target": null,
|
||||
"Properties": [
|
||||
{
|
||||
"Type": "number",
|
||||
"DefaultValue": "0",
|
||||
"SyncDirection": 0,
|
||||
"Attributes": [],
|
||||
"Name": "CamTries"
|
||||
}
|
||||
],
|
||||
"Methods": [
|
||||
{
|
||||
"Return": {
|
||||
"Type": "void",
|
||||
"DefaultValue": null,
|
||||
"SyncDirection": 0,
|
||||
"Attributes": [],
|
||||
"Name": null
|
||||
},
|
||||
"Arguments": [],
|
||||
"Code": "self.CamTries = 0\nlocal eventId = 0\nlocal function apply()\n\tself.CamTries = self.CamTries + 1\n\tlocal cam = nil\n\tlocal lp = _UserService.LocalPlayer\n\tif lp ~= nil then\n\t\tcam = lp.CameraComponent\n\tend\n\tif cam == nil then\n\t\tcam = _CameraService:GetCurrentCameraComponent()\n\tend\n\tif cam ~= nil then\n\t\tcam.ZoomRatio = 90\n\t\tcam.ScreenOffset = Vector2(0.5, 0.655)\n\t\tcam.ConfineCameraArea = true\n\t\tcam.CameraOffset = Vector2(1.5, -0.83)\n\tend\n\tif cam ~= nil then\n\t\t_TimerService:ClearTimer(eventId)\n\telseif self.CamTries > 30 then\n\t\t_TimerService:ClearTimer(eventId)\n\tend\nend\neventId = _TimerService:SetTimerRepeat(apply, 0.1)",
|
||||
"Scope": 2,
|
||||
"ExecSpace": 6,
|
||||
"Attributes": [],
|
||||
"Name": "OnBeginPlay"
|
||||
}
|
||||
],
|
||||
"EntityEventHandlers": []
|
||||
}
|
||||
}
|
||||
}
|
||||
36
RootDesk/MyDesk/MapleTree.codeblock
Normal file
36
RootDesk/MyDesk/MapleTree.codeblock
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "codeblock://4a220aa8-e014-4c7b-8234-fff8c5c66686",
|
||||
"ContentType": "x-mod/codeblock",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"CoreVersion": {
|
||||
"Major": 0,
|
||||
"Minor": 2
|
||||
},
|
||||
"ScriptVersion": {
|
||||
"Major": 1,
|
||||
"Minor": 1
|
||||
},
|
||||
"Description": "",
|
||||
"Id": "4a220aa8-e014-4c7b-8234-fff8c5c66686",
|
||||
"Language": 1,
|
||||
"Name": "MapleTree",
|
||||
"Type": 1,
|
||||
"Source": 0,
|
||||
"Target": null,
|
||||
"Properties": [],
|
||||
"Methods": [],
|
||||
"EntityEventHandlers": []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@
|
||||
"MOD.Core.SpriteRendererComponent",
|
||||
"MOD.Core.RigidbodyComponent",
|
||||
"MOD.Core.MovementComponent",
|
||||
"MOD.Core.AIWanderComponent",
|
||||
"MOD.Core.StateComponent",
|
||||
"MOD.Core.HitComponent",
|
||||
"MOD.Core.DamageSkinSpawnerComponent",
|
||||
@@ -57,10 +56,10 @@
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODQuaternion, MOD.Core",
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0,
|
||||
"w": 1.0
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -186,8 +185,8 @@
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0,
|
||||
"y": 0.0
|
||||
"x": 0,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -199,8 +198,8 @@
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0,
|
||||
"y": 0.0
|
||||
"x": 0,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -219,7 +218,7 @@
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 1.0
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.MovementComponent",
|
||||
@@ -228,7 +227,7 @@
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 0.0
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.MovementComponent",
|
||||
@@ -239,24 +238,6 @@
|
||||
},
|
||||
"Value": true
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.AIWanderComponent",
|
||||
"Name": "IsLegacy",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": false
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.AIWanderComponent",
|
||||
"Name": "Enable",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": true
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.StateComponent",
|
||||
"Name": "IsLegacy",
|
||||
@@ -376,8 +357,8 @@
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0,
|
||||
"y": 0.0
|
||||
"x": 0,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -389,8 +370,8 @@
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0,
|
||||
"y": 0.0
|
||||
"x": 0,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -402,8 +383,8 @@
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0,
|
||||
"y": 0.0
|
||||
"x": 0,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -424,8 +405,8 @@
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0,
|
||||
"y": 0.0
|
||||
"x": 0,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -460,4 +441,4 @@
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
231
RootDesk/MyDesk/Models/Monsters/blue_mushroom.model
Normal file
231
RootDesk/MyDesk/Models/Monsters/blue_mushroom.model
Normal file
@@ -0,0 +1,231 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "model://monster-blue_mushroom",
|
||||
"ContentType": "x-mod/model",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.3.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"Version": 1,
|
||||
"Name": "blue_mushroom",
|
||||
"BaseModelId": null,
|
||||
"Id": "monster-blue_mushroom",
|
||||
"Components": [
|
||||
"MOD.Core.TransformComponent",
|
||||
"MOD.Core.StateAnimationComponent",
|
||||
"MOD.Core.SpriteRendererComponent",
|
||||
"MOD.Core.RigidbodyComponent",
|
||||
"MOD.Core.MovementComponent",
|
||||
"MOD.Core.StateComponent",
|
||||
"MOD.Core.HitComponent",
|
||||
"MOD.Core.DamageSkinSpawnerComponent",
|
||||
"script.Monster",
|
||||
"script.MonsterAttack",
|
||||
"MOD.Core.KinematicbodyComponent",
|
||||
"MOD.Core.SideviewbodyComponent",
|
||||
"MOD.Core.DamageSkinSettingComponent",
|
||||
"script.CombatMonster"
|
||||
],
|
||||
"Properties": [
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "speed",
|
||||
"DisplayName": "speed",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "InputSpeed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "jumpForce",
|
||||
"DisplayName": "jumpForce",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "JumpForce"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "actionSheet",
|
||||
"DisplayName": "actionSheet",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.StateAnimationComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "ActionSheet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "renderguid",
|
||||
"DisplayName": "renderguid",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "SpriteRUID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.RenderSettingType, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "renderSetting",
|
||||
"DisplayName": "renderSetting",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "RenderSetting"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Values": [
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "OrderInLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 2
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
"Name": "renderguid",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "null"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "CollisionGroup",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.Physics.CollisionGroup, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.Physics.CollisionGroup, MOD.Core",
|
||||
"Id": "8992acd1e8cd45838db6f10a7b41df09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SpriteRUID",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "1a176a7afb114fe7aef2bc58ef2d945b"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SortingLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "MapLayer0"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.StateAnimationComponent",
|
||||
"Name": "ActionSheet",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"stand": "1a176a7afb114fe7aef2bc58ef2d945b",
|
||||
"move": "8239541953a6457fbe6d35e17f19f0f8",
|
||||
"hit": "7b405108d05741699893a4dc3d715165",
|
||||
"jump": "a7ea0755262242199ae50ab6a3387034",
|
||||
"die": "9e74e807797d442f9c938ca64aa9f4cd"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "BoxSize",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.63,
|
||||
"y": 0.58
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "ColliderOffset",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0449999869,
|
||||
"y": 0.29
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.MovementComponent",
|
||||
"Name": "InputSpeed",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "script.CombatMonster",
|
||||
"Name": "EnemyId",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "blue_mushroom"
|
||||
}
|
||||
],
|
||||
"EventLinks": [],
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
231
RootDesk/MyDesk/Models/Monsters/dile.model
Normal file
231
RootDesk/MyDesk/Models/Monsters/dile.model
Normal file
@@ -0,0 +1,231 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "model://monster-dile",
|
||||
"ContentType": "x-mod/model",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.3.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"Version": 1,
|
||||
"Name": "dile",
|
||||
"BaseModelId": null,
|
||||
"Id": "monster-dile",
|
||||
"Components": [
|
||||
"MOD.Core.TransformComponent",
|
||||
"MOD.Core.StateAnimationComponent",
|
||||
"MOD.Core.SpriteRendererComponent",
|
||||
"MOD.Core.RigidbodyComponent",
|
||||
"MOD.Core.MovementComponent",
|
||||
"MOD.Core.StateComponent",
|
||||
"MOD.Core.HitComponent",
|
||||
"MOD.Core.DamageSkinSpawnerComponent",
|
||||
"script.Monster",
|
||||
"script.MonsterAttack",
|
||||
"MOD.Core.KinematicbodyComponent",
|
||||
"MOD.Core.SideviewbodyComponent",
|
||||
"MOD.Core.DamageSkinSettingComponent",
|
||||
"script.CombatMonster"
|
||||
],
|
||||
"Properties": [
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "speed",
|
||||
"DisplayName": "speed",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "InputSpeed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "jumpForce",
|
||||
"DisplayName": "jumpForce",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "JumpForce"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "actionSheet",
|
||||
"DisplayName": "actionSheet",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.StateAnimationComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "ActionSheet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "renderguid",
|
||||
"DisplayName": "renderguid",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "SpriteRUID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.RenderSettingType, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "renderSetting",
|
||||
"DisplayName": "renderSetting",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "RenderSetting"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Values": [
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "OrderInLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 2
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
"Name": "renderguid",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "null"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "CollisionGroup",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.Physics.CollisionGroup, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.Physics.CollisionGroup, MOD.Core",
|
||||
"Id": "8992acd1e8cd45838db6f10a7b41df09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SpriteRUID",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "68070c6f4abe40658899a208ddaf4081"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SortingLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "MapLayer0"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.StateAnimationComponent",
|
||||
"Name": "ActionSheet",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"move": "426ba2c6fa2d4cdd92bcb0bb37861dcc",
|
||||
"stand": "68070c6f4abe40658899a208ddaf4081",
|
||||
"skill": "4ba2cdc2f11746afa0f542293b0618d5",
|
||||
"hit": "172640e6d4ce444aa1dfbd9bd9523eb1",
|
||||
"die": "5d50d9aa34c745b9b8932c15da919927"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "BoxSize",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 2.2,
|
||||
"y": 1.51
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "ColliderOffset",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": -0.220000029,
|
||||
"y": 0.755
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.MovementComponent",
|
||||
"Name": "InputSpeed",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "script.CombatMonster",
|
||||
"Name": "EnemyId",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "dile"
|
||||
}
|
||||
],
|
||||
"EventLinks": [],
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
229
RootDesk/MyDesk/Models/Monsters/green_mushroom.model
Normal file
229
RootDesk/MyDesk/Models/Monsters/green_mushroom.model
Normal file
@@ -0,0 +1,229 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "model://monster-green_mushroom",
|
||||
"ContentType": "x-mod/model",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.3.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"Version": 1,
|
||||
"Name": "green_mushroom",
|
||||
"BaseModelId": null,
|
||||
"Id": "monster-green_mushroom",
|
||||
"Components": [
|
||||
"MOD.Core.TransformComponent",
|
||||
"MOD.Core.StateAnimationComponent",
|
||||
"MOD.Core.SpriteRendererComponent",
|
||||
"MOD.Core.RigidbodyComponent",
|
||||
"MOD.Core.MovementComponent",
|
||||
"MOD.Core.StateComponent",
|
||||
"MOD.Core.HitComponent",
|
||||
"MOD.Core.DamageSkinSpawnerComponent",
|
||||
"script.Monster",
|
||||
"script.MonsterAttack",
|
||||
"MOD.Core.KinematicbodyComponent",
|
||||
"MOD.Core.SideviewbodyComponent",
|
||||
"MOD.Core.DamageSkinSettingComponent",
|
||||
"script.CombatMonster"
|
||||
],
|
||||
"Properties": [
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "speed",
|
||||
"DisplayName": "speed",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "InputSpeed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "jumpForce",
|
||||
"DisplayName": "jumpForce",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "JumpForce"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "actionSheet",
|
||||
"DisplayName": "actionSheet",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.StateAnimationComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "ActionSheet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "renderguid",
|
||||
"DisplayName": "renderguid",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "SpriteRUID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.RenderSettingType, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "renderSetting",
|
||||
"DisplayName": "renderSetting",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "RenderSetting"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Values": [
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "OrderInLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 2
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
"Name": "renderguid",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "null"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "CollisionGroup",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.Physics.CollisionGroup, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.Physics.CollisionGroup, MOD.Core",
|
||||
"Id": "8992acd1e8cd45838db6f10a7b41df09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SpriteRUID",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "f86992ba9c41487c8480fcb893fcbda6"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SortingLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "MapLayer0"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.StateAnimationComponent",
|
||||
"Name": "ActionSheet",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"stand": "f86992ba9c41487c8480fcb893fcbda6",
|
||||
"hit": "d305b942b1704c8084548108ff3b7a6b",
|
||||
"die": "5a563e5fd98c4132b61057dc6bb8aaf2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "BoxSize",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.63,
|
||||
"y": 0.58
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "ColliderOffset",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.00999999,
|
||||
"y": 0.26
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.MovementComponent",
|
||||
"Name": "InputSpeed",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "script.CombatMonster",
|
||||
"Name": "EnemyId",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "green_mushroom"
|
||||
}
|
||||
],
|
||||
"EventLinks": [],
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
229
RootDesk/MyDesk/Models/Monsters/junior_bugi.model
Normal file
229
RootDesk/MyDesk/Models/Monsters/junior_bugi.model
Normal file
@@ -0,0 +1,229 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "model://monster-junior_bugi",
|
||||
"ContentType": "x-mod/model",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.3.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"Version": 1,
|
||||
"Name": "junior_bugi",
|
||||
"BaseModelId": null,
|
||||
"Id": "monster-junior_bugi",
|
||||
"Components": [
|
||||
"MOD.Core.TransformComponent",
|
||||
"MOD.Core.StateAnimationComponent",
|
||||
"MOD.Core.SpriteRendererComponent",
|
||||
"MOD.Core.RigidbodyComponent",
|
||||
"MOD.Core.MovementComponent",
|
||||
"MOD.Core.StateComponent",
|
||||
"MOD.Core.HitComponent",
|
||||
"MOD.Core.DamageSkinSpawnerComponent",
|
||||
"script.Monster",
|
||||
"script.MonsterAttack",
|
||||
"MOD.Core.KinematicbodyComponent",
|
||||
"MOD.Core.SideviewbodyComponent",
|
||||
"MOD.Core.DamageSkinSettingComponent",
|
||||
"script.CombatMonster"
|
||||
],
|
||||
"Properties": [
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "speed",
|
||||
"DisplayName": "speed",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "InputSpeed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "jumpForce",
|
||||
"DisplayName": "jumpForce",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "JumpForce"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "actionSheet",
|
||||
"DisplayName": "actionSheet",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.StateAnimationComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "ActionSheet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "renderguid",
|
||||
"DisplayName": "renderguid",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "SpriteRUID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.RenderSettingType, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "renderSetting",
|
||||
"DisplayName": "renderSetting",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "RenderSetting"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Values": [
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "OrderInLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 2
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
"Name": "renderguid",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "null"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "CollisionGroup",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.Physics.CollisionGroup, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.Physics.CollisionGroup, MOD.Core",
|
||||
"Id": "8992acd1e8cd45838db6f10a7b41df09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SpriteRUID",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "a2204a21d88942b281d2cac6053ffbaa"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SortingLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "MapLayer0"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.StateAnimationComponent",
|
||||
"Name": "ActionSheet",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"stand": "a2204a21d88942b281d2cac6053ffbaa",
|
||||
"hit": "afc08936b8a64b26bc3dd8c03ead1f26",
|
||||
"die": "fc1c6d9ba9bc413ab53b6dbfae3ac45b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "BoxSize",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.63,
|
||||
"y": 0.58
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "ColliderOffset",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0449999869,
|
||||
"y": 0.29
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.MovementComponent",
|
||||
"Name": "InputSpeed",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "script.CombatMonster",
|
||||
"Name": "EnemyId",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "junior_bugi"
|
||||
}
|
||||
],
|
||||
"EventLinks": [],
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
229
RootDesk/MyDesk/Models/Monsters/junior_neki.model
Normal file
229
RootDesk/MyDesk/Models/Monsters/junior_neki.model
Normal file
@@ -0,0 +1,229 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "model://monster-junior_neki",
|
||||
"ContentType": "x-mod/model",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.3.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"Version": 1,
|
||||
"Name": "junior_neki",
|
||||
"BaseModelId": null,
|
||||
"Id": "monster-junior_neki",
|
||||
"Components": [
|
||||
"MOD.Core.TransformComponent",
|
||||
"MOD.Core.StateAnimationComponent",
|
||||
"MOD.Core.SpriteRendererComponent",
|
||||
"MOD.Core.RigidbodyComponent",
|
||||
"MOD.Core.MovementComponent",
|
||||
"MOD.Core.StateComponent",
|
||||
"MOD.Core.HitComponent",
|
||||
"MOD.Core.DamageSkinSpawnerComponent",
|
||||
"script.Monster",
|
||||
"script.MonsterAttack",
|
||||
"MOD.Core.KinematicbodyComponent",
|
||||
"MOD.Core.SideviewbodyComponent",
|
||||
"MOD.Core.DamageSkinSettingComponent",
|
||||
"script.CombatMonster"
|
||||
],
|
||||
"Properties": [
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "speed",
|
||||
"DisplayName": "speed",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "InputSpeed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "jumpForce",
|
||||
"DisplayName": "jumpForce",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "JumpForce"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "actionSheet",
|
||||
"DisplayName": "actionSheet",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.StateAnimationComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "ActionSheet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "renderguid",
|
||||
"DisplayName": "renderguid",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "SpriteRUID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.RenderSettingType, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "renderSetting",
|
||||
"DisplayName": "renderSetting",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "RenderSetting"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Values": [
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "OrderInLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 2
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
"Name": "renderguid",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "null"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "CollisionGroup",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.Physics.CollisionGroup, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.Physics.CollisionGroup, MOD.Core",
|
||||
"Id": "8992acd1e8cd45838db6f10a7b41df09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SpriteRUID",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "48c10437ae8344a9b2a1d3f36185728f"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SortingLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "MapLayer0"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.StateAnimationComponent",
|
||||
"Name": "ActionSheet",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"stand": "48c10437ae8344a9b2a1d3f36185728f",
|
||||
"hit": "9044063647854f5e9128efcf80e909be",
|
||||
"die": "f414577d18c94cc387c275df4abdbc3b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "BoxSize",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.63,
|
||||
"y": 0.58
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "ColliderOffset",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0449999869,
|
||||
"y": 0.29
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.MovementComponent",
|
||||
"Name": "InputSpeed",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "script.CombatMonster",
|
||||
"Name": "EnemyId",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "junior_neki"
|
||||
}
|
||||
],
|
||||
"EventLinks": [],
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
229
RootDesk/MyDesk/Models/Monsters/kapa_drake.model
Normal file
229
RootDesk/MyDesk/Models/Monsters/kapa_drake.model
Normal file
@@ -0,0 +1,229 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "model://monster-kapa_drake",
|
||||
"ContentType": "x-mod/model",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.3.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"Version": 1,
|
||||
"Name": "kapa_drake",
|
||||
"BaseModelId": null,
|
||||
"Id": "monster-kapa_drake",
|
||||
"Components": [
|
||||
"MOD.Core.TransformComponent",
|
||||
"MOD.Core.StateAnimationComponent",
|
||||
"MOD.Core.SpriteRendererComponent",
|
||||
"MOD.Core.RigidbodyComponent",
|
||||
"MOD.Core.MovementComponent",
|
||||
"MOD.Core.StateComponent",
|
||||
"MOD.Core.HitComponent",
|
||||
"MOD.Core.DamageSkinSpawnerComponent",
|
||||
"script.Monster",
|
||||
"script.MonsterAttack",
|
||||
"MOD.Core.KinematicbodyComponent",
|
||||
"MOD.Core.SideviewbodyComponent",
|
||||
"MOD.Core.DamageSkinSettingComponent",
|
||||
"script.CombatMonster"
|
||||
],
|
||||
"Properties": [
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "speed",
|
||||
"DisplayName": "speed",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "InputSpeed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "jumpForce",
|
||||
"DisplayName": "jumpForce",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "JumpForce"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "actionSheet",
|
||||
"DisplayName": "actionSheet",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.StateAnimationComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "ActionSheet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "renderguid",
|
||||
"DisplayName": "renderguid",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "SpriteRUID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.RenderSettingType, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "renderSetting",
|
||||
"DisplayName": "renderSetting",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "RenderSetting"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Values": [
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "OrderInLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 2
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
"Name": "renderguid",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "null"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "CollisionGroup",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.Physics.CollisionGroup, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.Physics.CollisionGroup, MOD.Core",
|
||||
"Id": "8992acd1e8cd45838db6f10a7b41df09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SpriteRUID",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "4ca39dbfa1c6492283ba8bd352d12b0a"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SortingLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "MapLayer0"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.StateAnimationComponent",
|
||||
"Name": "ActionSheet",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"stand": "4ca39dbfa1c6492283ba8bd352d12b0a",
|
||||
"hit": "7ac78511036e4ebe988b97c35fc275d1",
|
||||
"die": "740f3f2b2e7a4b71bec5eac84e8539f9"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "BoxSize",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.63,
|
||||
"y": 0.58
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "ColliderOffset",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0449999869,
|
||||
"y": 0.29
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.MovementComponent",
|
||||
"Name": "InputSpeed",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "script.CombatMonster",
|
||||
"Name": "EnemyId",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "kapa_drake"
|
||||
}
|
||||
],
|
||||
"EventLinks": [],
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
233
RootDesk/MyDesk/Models/Monsters/king_slime.model
Normal file
233
RootDesk/MyDesk/Models/Monsters/king_slime.model
Normal file
@@ -0,0 +1,233 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "model://monster-king_slime",
|
||||
"ContentType": "x-mod/model",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.3.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"Version": 1,
|
||||
"Name": "king_slime",
|
||||
"BaseModelId": null,
|
||||
"Id": "monster-king_slime",
|
||||
"Components": [
|
||||
"MOD.Core.TransformComponent",
|
||||
"MOD.Core.StateAnimationComponent",
|
||||
"MOD.Core.SpriteRendererComponent",
|
||||
"MOD.Core.RigidbodyComponent",
|
||||
"MOD.Core.MovementComponent",
|
||||
"MOD.Core.StateComponent",
|
||||
"MOD.Core.HitComponent",
|
||||
"MOD.Core.DamageSkinSpawnerComponent",
|
||||
"script.Monster",
|
||||
"script.MonsterAttack",
|
||||
"MOD.Core.KinematicbodyComponent",
|
||||
"MOD.Core.SideviewbodyComponent",
|
||||
"MOD.Core.DamageSkinSettingComponent",
|
||||
"script.CombatMonster"
|
||||
],
|
||||
"Properties": [
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "speed",
|
||||
"DisplayName": "speed",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "InputSpeed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "jumpForce",
|
||||
"DisplayName": "jumpForce",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "JumpForce"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "actionSheet",
|
||||
"DisplayName": "actionSheet",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.StateAnimationComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "ActionSheet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "renderguid",
|
||||
"DisplayName": "renderguid",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "SpriteRUID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.RenderSettingType, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "renderSetting",
|
||||
"DisplayName": "renderSetting",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "RenderSetting"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Values": [
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "OrderInLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 2
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
"Name": "renderguid",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "null"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "CollisionGroup",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.Physics.CollisionGroup, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.Physics.CollisionGroup, MOD.Core",
|
||||
"Id": "8992acd1e8cd45838db6f10a7b41df09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SpriteRUID",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "dd9de73d580240faab8cad03b587013b"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SortingLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "MapLayer0"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.StateAnimationComponent",
|
||||
"Name": "ActionSheet",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"move": "873425127b75475b9944dc86bf77f885",
|
||||
"stand": "dd9de73d580240faab8cad03b587013b",
|
||||
"jump": "6a2b983b7a31417ca19c29c3d1d00817",
|
||||
"attack": "a34d1146057443fd8b578dafeb7c2ed1",
|
||||
"skill": "0b0bb78f0ca44526bad6d994bb16f973",
|
||||
"hit": "d2de42d3233b42a58d9799d5e762a19c",
|
||||
"die": "5bd3969c3bcb4df2bd79c2b940ee03dc"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "BoxSize",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 2.19,
|
||||
"y": 1.39
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "ColliderOffset",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.335000038,
|
||||
"y": 0.695
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.MovementComponent",
|
||||
"Name": "InputSpeed",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "script.CombatMonster",
|
||||
"Name": "EnemyId",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "king_slime"
|
||||
}
|
||||
],
|
||||
"EventLinks": [],
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
231
RootDesk/MyDesk/Models/Monsters/mano.model
Normal file
231
RootDesk/MyDesk/Models/Monsters/mano.model
Normal file
@@ -0,0 +1,231 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "model://monster-mano",
|
||||
"ContentType": "x-mod/model",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.3.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"Version": 1,
|
||||
"Name": "mano",
|
||||
"BaseModelId": null,
|
||||
"Id": "monster-mano",
|
||||
"Components": [
|
||||
"MOD.Core.TransformComponent",
|
||||
"MOD.Core.StateAnimationComponent",
|
||||
"MOD.Core.SpriteRendererComponent",
|
||||
"MOD.Core.RigidbodyComponent",
|
||||
"MOD.Core.MovementComponent",
|
||||
"MOD.Core.StateComponent",
|
||||
"MOD.Core.HitComponent",
|
||||
"MOD.Core.DamageSkinSpawnerComponent",
|
||||
"script.Monster",
|
||||
"script.MonsterAttack",
|
||||
"MOD.Core.KinematicbodyComponent",
|
||||
"MOD.Core.SideviewbodyComponent",
|
||||
"MOD.Core.DamageSkinSettingComponent",
|
||||
"script.CombatMonster"
|
||||
],
|
||||
"Properties": [
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "speed",
|
||||
"DisplayName": "speed",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "InputSpeed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "jumpForce",
|
||||
"DisplayName": "jumpForce",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "JumpForce"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "actionSheet",
|
||||
"DisplayName": "actionSheet",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.StateAnimationComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "ActionSheet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "renderguid",
|
||||
"DisplayName": "renderguid",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "SpriteRUID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.RenderSettingType, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "renderSetting",
|
||||
"DisplayName": "renderSetting",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "RenderSetting"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Values": [
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "OrderInLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 2
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
"Name": "renderguid",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "null"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "CollisionGroup",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.Physics.CollisionGroup, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.Physics.CollisionGroup, MOD.Core",
|
||||
"Id": "8992acd1e8cd45838db6f10a7b41df09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SpriteRUID",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "e035bb90c053401b88de2159dfa230eb"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SortingLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "MapLayer0"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.StateAnimationComponent",
|
||||
"Name": "ActionSheet",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"move": "3dcd0dc63d2d491b9b8d39b3b9d0a214",
|
||||
"stand": "e035bb90c053401b88de2159dfa230eb",
|
||||
"skill": "c05453dd21fd4ed581d193930ab4c331",
|
||||
"hit": "452cb740ddcb4837a46b75d7935e2ffc",
|
||||
"die": "f430051f6fc34f2eb56fe5e62b346eac"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "BoxSize",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 1.05,
|
||||
"y": 0.95
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "ColliderOffset",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.004999995,
|
||||
"y": 0.475
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.MovementComponent",
|
||||
"Name": "InputSpeed",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "script.CombatMonster",
|
||||
"Name": "EnemyId",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "mano"
|
||||
}
|
||||
],
|
||||
"EventLinks": [],
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
230
RootDesk/MyDesk/Models/Monsters/modified_snail.model
Normal file
230
RootDesk/MyDesk/Models/Monsters/modified_snail.model
Normal file
@@ -0,0 +1,230 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "model://monster-modified_snail",
|
||||
"ContentType": "x-mod/model",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.3.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"Version": 1,
|
||||
"Name": "modified_snail",
|
||||
"BaseModelId": null,
|
||||
"Id": "monster-modified_snail",
|
||||
"Components": [
|
||||
"MOD.Core.TransformComponent",
|
||||
"MOD.Core.StateAnimationComponent",
|
||||
"MOD.Core.SpriteRendererComponent",
|
||||
"MOD.Core.RigidbodyComponent",
|
||||
"MOD.Core.MovementComponent",
|
||||
"MOD.Core.StateComponent",
|
||||
"MOD.Core.HitComponent",
|
||||
"MOD.Core.DamageSkinSpawnerComponent",
|
||||
"script.Monster",
|
||||
"script.MonsterAttack",
|
||||
"MOD.Core.KinematicbodyComponent",
|
||||
"MOD.Core.SideviewbodyComponent",
|
||||
"MOD.Core.DamageSkinSettingComponent",
|
||||
"script.CombatMonster"
|
||||
],
|
||||
"Properties": [
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "speed",
|
||||
"DisplayName": "speed",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "InputSpeed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "jumpForce",
|
||||
"DisplayName": "jumpForce",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "JumpForce"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "actionSheet",
|
||||
"DisplayName": "actionSheet",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.StateAnimationComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "ActionSheet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "renderguid",
|
||||
"DisplayName": "renderguid",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "SpriteRUID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.RenderSettingType, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "renderSetting",
|
||||
"DisplayName": "renderSetting",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "RenderSetting"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Values": [
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "OrderInLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 2
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
"Name": "renderguid",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "null"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "CollisionGroup",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.Physics.CollisionGroup, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.Physics.CollisionGroup, MOD.Core",
|
||||
"Id": "8992acd1e8cd45838db6f10a7b41df09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SpriteRUID",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "17b55730c26f4fd6b8fcfa288da388de"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SortingLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "MapLayer0"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.StateAnimationComponent",
|
||||
"Name": "ActionSheet",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"stand": "17b55730c26f4fd6b8fcfa288da388de",
|
||||
"move": "f40108c8b0b84696a67337b801201f7d",
|
||||
"hit": "eac48e84a9fc4580a4018de5cf52ddb3",
|
||||
"die": "51c2f4b59a2c413db26035aa57002fc8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "BoxSize",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.75,
|
||||
"y": 0.68
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "ColliderOffset",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0449999869,
|
||||
"y": 0.29
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.MovementComponent",
|
||||
"Name": "InputSpeed",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "script.CombatMonster",
|
||||
"Name": "EnemyId",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "modified_snail"
|
||||
}
|
||||
],
|
||||
"EventLinks": [],
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
232
RootDesk/MyDesk/Models/Monsters/mushmom.model
Normal file
232
RootDesk/MyDesk/Models/Monsters/mushmom.model
Normal file
@@ -0,0 +1,232 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "model://monster-mushmom",
|
||||
"ContentType": "x-mod/model",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.3.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"Version": 1,
|
||||
"Name": "mushmom",
|
||||
"BaseModelId": null,
|
||||
"Id": "monster-mushmom",
|
||||
"Components": [
|
||||
"MOD.Core.TransformComponent",
|
||||
"MOD.Core.StateAnimationComponent",
|
||||
"MOD.Core.SpriteRendererComponent",
|
||||
"MOD.Core.RigidbodyComponent",
|
||||
"MOD.Core.MovementComponent",
|
||||
"MOD.Core.StateComponent",
|
||||
"MOD.Core.HitComponent",
|
||||
"MOD.Core.DamageSkinSpawnerComponent",
|
||||
"script.Monster",
|
||||
"script.MonsterAttack",
|
||||
"MOD.Core.KinematicbodyComponent",
|
||||
"MOD.Core.SideviewbodyComponent",
|
||||
"MOD.Core.DamageSkinSettingComponent",
|
||||
"script.CombatMonster"
|
||||
],
|
||||
"Properties": [
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "speed",
|
||||
"DisplayName": "speed",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "InputSpeed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "jumpForce",
|
||||
"DisplayName": "jumpForce",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "JumpForce"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "actionSheet",
|
||||
"DisplayName": "actionSheet",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.StateAnimationComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "ActionSheet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "renderguid",
|
||||
"DisplayName": "renderguid",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "SpriteRUID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.RenderSettingType, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "renderSetting",
|
||||
"DisplayName": "renderSetting",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "RenderSetting"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Values": [
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "OrderInLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 2
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
"Name": "renderguid",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "null"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "CollisionGroup",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.Physics.CollisionGroup, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.Physics.CollisionGroup, MOD.Core",
|
||||
"Id": "8992acd1e8cd45838db6f10a7b41df09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SpriteRUID",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "23c38ef3acad4a30ad59120bb939b008"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SortingLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "MapLayer0"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.StateAnimationComponent",
|
||||
"Name": "ActionSheet",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"stand": "23c38ef3acad4a30ad59120bb939b008",
|
||||
"move": "24d8a3a75f96406ba690ed42d7250b8f",
|
||||
"hit": "c826e36ee89c48bca6aab856aa773f38",
|
||||
"attack": "4d7465e950144dc59c263aad01b14e14",
|
||||
"jump": "b7ddbda71a294141ba134249fc34c7da",
|
||||
"die": "f50664a4524147399359cb90a6f3e80c"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "BoxSize",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 1.2,
|
||||
"y": 1.1
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "ColliderOffset",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.02,
|
||||
"y": 0.55
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.MovementComponent",
|
||||
"Name": "InputSpeed",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "script.CombatMonster",
|
||||
"Name": "EnemyId",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "mushmom"
|
||||
}
|
||||
],
|
||||
"EventLinks": [],
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
229
RootDesk/MyDesk/Models/Monsters/octopus.model
Normal file
229
RootDesk/MyDesk/Models/Monsters/octopus.model
Normal file
@@ -0,0 +1,229 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "model://monster-octopus",
|
||||
"ContentType": "x-mod/model",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.3.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"Version": 1,
|
||||
"Name": "octopus",
|
||||
"BaseModelId": null,
|
||||
"Id": "monster-octopus",
|
||||
"Components": [
|
||||
"MOD.Core.TransformComponent",
|
||||
"MOD.Core.StateAnimationComponent",
|
||||
"MOD.Core.SpriteRendererComponent",
|
||||
"MOD.Core.RigidbodyComponent",
|
||||
"MOD.Core.MovementComponent",
|
||||
"MOD.Core.StateComponent",
|
||||
"MOD.Core.HitComponent",
|
||||
"MOD.Core.DamageSkinSpawnerComponent",
|
||||
"script.Monster",
|
||||
"script.MonsterAttack",
|
||||
"MOD.Core.KinematicbodyComponent",
|
||||
"MOD.Core.SideviewbodyComponent",
|
||||
"MOD.Core.DamageSkinSettingComponent",
|
||||
"script.CombatMonster"
|
||||
],
|
||||
"Properties": [
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "speed",
|
||||
"DisplayName": "speed",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "InputSpeed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "jumpForce",
|
||||
"DisplayName": "jumpForce",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "JumpForce"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "actionSheet",
|
||||
"DisplayName": "actionSheet",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.StateAnimationComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "ActionSheet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "renderguid",
|
||||
"DisplayName": "renderguid",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "SpriteRUID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.RenderSettingType, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "renderSetting",
|
||||
"DisplayName": "renderSetting",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "RenderSetting"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Values": [
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "OrderInLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 2
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
"Name": "renderguid",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "null"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "CollisionGroup",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.Physics.CollisionGroup, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.Physics.CollisionGroup, MOD.Core",
|
||||
"Id": "8992acd1e8cd45838db6f10a7b41df09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SpriteRUID",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "d8f014043ce8418f96700c2b6c9ebf6c"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SortingLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "MapLayer0"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.StateAnimationComponent",
|
||||
"Name": "ActionSheet",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"stand": "d8f014043ce8418f96700c2b6c9ebf6c",
|
||||
"hit": "c3cf643b618346c7bfa6574187b396f9",
|
||||
"die": "a88d9b3d60f941e4890dc89a6ccaa8ee"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "BoxSize",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.63,
|
||||
"y": 0.58
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "ColliderOffset",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0449999869,
|
||||
"y": 0.29
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.MovementComponent",
|
||||
"Name": "InputSpeed",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "script.CombatMonster",
|
||||
"Name": "EnemyId",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "octopus"
|
||||
}
|
||||
],
|
||||
"EventLinks": [],
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
231
RootDesk/MyDesk/Models/Monsters/orange_mushroom.model
Normal file
231
RootDesk/MyDesk/Models/Monsters/orange_mushroom.model
Normal file
@@ -0,0 +1,231 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "model://monster-orange_mushroom",
|
||||
"ContentType": "x-mod/model",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.3.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"Version": 1,
|
||||
"Name": "orange_mushroom",
|
||||
"BaseModelId": null,
|
||||
"Id": "monster-orange_mushroom",
|
||||
"Components": [
|
||||
"MOD.Core.TransformComponent",
|
||||
"MOD.Core.StateAnimationComponent",
|
||||
"MOD.Core.SpriteRendererComponent",
|
||||
"MOD.Core.RigidbodyComponent",
|
||||
"MOD.Core.MovementComponent",
|
||||
"MOD.Core.StateComponent",
|
||||
"MOD.Core.HitComponent",
|
||||
"MOD.Core.DamageSkinSpawnerComponent",
|
||||
"script.Monster",
|
||||
"script.MonsterAttack",
|
||||
"MOD.Core.KinematicbodyComponent",
|
||||
"MOD.Core.SideviewbodyComponent",
|
||||
"MOD.Core.DamageSkinSettingComponent",
|
||||
"script.CombatMonster"
|
||||
],
|
||||
"Properties": [
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "speed",
|
||||
"DisplayName": "speed",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "InputSpeed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "jumpForce",
|
||||
"DisplayName": "jumpForce",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "JumpForce"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "actionSheet",
|
||||
"DisplayName": "actionSheet",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.StateAnimationComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "ActionSheet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "renderguid",
|
||||
"DisplayName": "renderguid",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "SpriteRUID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.RenderSettingType, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "renderSetting",
|
||||
"DisplayName": "renderSetting",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "RenderSetting"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Values": [
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "OrderInLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 2
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
"Name": "renderguid",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "null"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "CollisionGroup",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.Physics.CollisionGroup, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.Physics.CollisionGroup, MOD.Core",
|
||||
"Id": "8992acd1e8cd45838db6f10a7b41df09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SpriteRUID",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "6d381bea1bcb4504b518a1fbfa0904ac"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SortingLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "MapLayer0"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.StateAnimationComponent",
|
||||
"Name": "ActionSheet",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"move": "573fe938562a4abf91eebf951f21afd5",
|
||||
"stand": "6d381bea1bcb4504b518a1fbfa0904ac",
|
||||
"jump": "59823e146a034e48b8667ebb6f0724b1",
|
||||
"hit": "642ece38d8d449b29ce4479100e37a54",
|
||||
"die": "3c99d6b9b89b4295a9c2749eb02e28e9"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "BoxSize",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.63,
|
||||
"y": 0.58
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "ColliderOffset",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0449999869,
|
||||
"y": 0.29
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.MovementComponent",
|
||||
"Name": "InputSpeed",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "script.CombatMonster",
|
||||
"Name": "EnemyId",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "orange_mushroom"
|
||||
}
|
||||
],
|
||||
"EventLinks": [],
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
231
RootDesk/MyDesk/Models/Monsters/pig.model
Normal file
231
RootDesk/MyDesk/Models/Monsters/pig.model
Normal file
@@ -0,0 +1,231 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "model://monster-pig",
|
||||
"ContentType": "x-mod/model",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.3.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"Version": 1,
|
||||
"Name": "pig",
|
||||
"BaseModelId": null,
|
||||
"Id": "monster-pig",
|
||||
"Components": [
|
||||
"MOD.Core.TransformComponent",
|
||||
"MOD.Core.StateAnimationComponent",
|
||||
"MOD.Core.SpriteRendererComponent",
|
||||
"MOD.Core.RigidbodyComponent",
|
||||
"MOD.Core.MovementComponent",
|
||||
"MOD.Core.StateComponent",
|
||||
"MOD.Core.HitComponent",
|
||||
"MOD.Core.DamageSkinSpawnerComponent",
|
||||
"script.Monster",
|
||||
"script.MonsterAttack",
|
||||
"MOD.Core.KinematicbodyComponent",
|
||||
"MOD.Core.SideviewbodyComponent",
|
||||
"MOD.Core.DamageSkinSettingComponent",
|
||||
"script.CombatMonster"
|
||||
],
|
||||
"Properties": [
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "speed",
|
||||
"DisplayName": "speed",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "InputSpeed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "jumpForce",
|
||||
"DisplayName": "jumpForce",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "JumpForce"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "actionSheet",
|
||||
"DisplayName": "actionSheet",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.StateAnimationComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "ActionSheet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "renderguid",
|
||||
"DisplayName": "renderguid",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "SpriteRUID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.RenderSettingType, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "renderSetting",
|
||||
"DisplayName": "renderSetting",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "RenderSetting"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Values": [
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "OrderInLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 2
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
"Name": "renderguid",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "null"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "CollisionGroup",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.Physics.CollisionGroup, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.Physics.CollisionGroup, MOD.Core",
|
||||
"Id": "8992acd1e8cd45838db6f10a7b41df09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SpriteRUID",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "528a8638b12f41b8b5781a05360d2949"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SortingLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "MapLayer0"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.StateAnimationComponent",
|
||||
"Name": "ActionSheet",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"stand": "528a8638b12f41b8b5781a05360d2949",
|
||||
"move": "8baad61512be4b33b2a0879fec7a266e",
|
||||
"hit": "60e42a918a0342478903cc71adba1dc5",
|
||||
"jump": "c9e27ce6f8344aefba169c5ca6571def",
|
||||
"die": "0644beff80a44ec7acc011ea0961df57"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "BoxSize",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.63,
|
||||
"y": 0.58
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "ColliderOffset",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0449999869,
|
||||
"y": 0.29
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.MovementComponent",
|
||||
"Name": "InputSpeed",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "script.CombatMonster",
|
||||
"Name": "EnemyId",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "pig"
|
||||
}
|
||||
],
|
||||
"EventLinks": [],
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
230
RootDesk/MyDesk/Models/Monsters/red_snail.model
Normal file
230
RootDesk/MyDesk/Models/Monsters/red_snail.model
Normal file
@@ -0,0 +1,230 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "model://monster-red_snail",
|
||||
"ContentType": "x-mod/model",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.3.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"Version": 1,
|
||||
"Name": "red_snail",
|
||||
"BaseModelId": null,
|
||||
"Id": "monster-red_snail",
|
||||
"Components": [
|
||||
"MOD.Core.TransformComponent",
|
||||
"MOD.Core.StateAnimationComponent",
|
||||
"MOD.Core.SpriteRendererComponent",
|
||||
"MOD.Core.RigidbodyComponent",
|
||||
"MOD.Core.MovementComponent",
|
||||
"MOD.Core.StateComponent",
|
||||
"MOD.Core.HitComponent",
|
||||
"MOD.Core.DamageSkinSpawnerComponent",
|
||||
"script.Monster",
|
||||
"script.MonsterAttack",
|
||||
"MOD.Core.KinematicbodyComponent",
|
||||
"MOD.Core.SideviewbodyComponent",
|
||||
"MOD.Core.DamageSkinSettingComponent",
|
||||
"script.CombatMonster"
|
||||
],
|
||||
"Properties": [
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "speed",
|
||||
"DisplayName": "speed",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "InputSpeed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "jumpForce",
|
||||
"DisplayName": "jumpForce",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "JumpForce"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "actionSheet",
|
||||
"DisplayName": "actionSheet",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.StateAnimationComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "ActionSheet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "renderguid",
|
||||
"DisplayName": "renderguid",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "SpriteRUID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.RenderSettingType, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "renderSetting",
|
||||
"DisplayName": "renderSetting",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "RenderSetting"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Values": [
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "OrderInLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 2
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
"Name": "renderguid",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "null"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "CollisionGroup",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.Physics.CollisionGroup, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.Physics.CollisionGroup, MOD.Core",
|
||||
"Id": "8992acd1e8cd45838db6f10a7b41df09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SpriteRUID",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "17b55730c26f4fd6b8fcfa288da388de"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SortingLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "MapLayer0"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.StateAnimationComponent",
|
||||
"Name": "ActionSheet",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"stand": "17b55730c26f4fd6b8fcfa288da388de",
|
||||
"move": "f40108c8b0b84696a67337b801201f7d",
|
||||
"hit": "eac48e84a9fc4580a4018de5cf52ddb3",
|
||||
"die": "51c2f4b59a2c413db26035aa57002fc8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "BoxSize",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.63,
|
||||
"y": 0.58
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "ColliderOffset",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0449999869,
|
||||
"y": 0.29
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.MovementComponent",
|
||||
"Name": "InputSpeed",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "script.CombatMonster",
|
||||
"Name": "EnemyId",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "red_snail"
|
||||
}
|
||||
],
|
||||
"EventLinks": [],
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
231
RootDesk/MyDesk/Models/Monsters/slime.model
Normal file
231
RootDesk/MyDesk/Models/Monsters/slime.model
Normal file
@@ -0,0 +1,231 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "model://monster-slime",
|
||||
"ContentType": "x-mod/model",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.3.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"Version": 1,
|
||||
"Name": "slime",
|
||||
"BaseModelId": null,
|
||||
"Id": "monster-slime",
|
||||
"Components": [
|
||||
"MOD.Core.TransformComponent",
|
||||
"MOD.Core.StateAnimationComponent",
|
||||
"MOD.Core.SpriteRendererComponent",
|
||||
"MOD.Core.RigidbodyComponent",
|
||||
"MOD.Core.MovementComponent",
|
||||
"MOD.Core.StateComponent",
|
||||
"MOD.Core.HitComponent",
|
||||
"MOD.Core.DamageSkinSpawnerComponent",
|
||||
"script.Monster",
|
||||
"script.MonsterAttack",
|
||||
"MOD.Core.KinematicbodyComponent",
|
||||
"MOD.Core.SideviewbodyComponent",
|
||||
"MOD.Core.DamageSkinSettingComponent",
|
||||
"script.CombatMonster"
|
||||
],
|
||||
"Properties": [
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "speed",
|
||||
"DisplayName": "speed",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "InputSpeed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "jumpForce",
|
||||
"DisplayName": "jumpForce",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "JumpForce"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "actionSheet",
|
||||
"DisplayName": "actionSheet",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.StateAnimationComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "ActionSheet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "renderguid",
|
||||
"DisplayName": "renderguid",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "SpriteRUID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.RenderSettingType, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "renderSetting",
|
||||
"DisplayName": "renderSetting",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "RenderSetting"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Values": [
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "OrderInLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 2
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
"Name": "renderguid",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "null"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "CollisionGroup",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.Physics.CollisionGroup, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.Physics.CollisionGroup, MOD.Core",
|
||||
"Id": "8992acd1e8cd45838db6f10a7b41df09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SpriteRUID",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "50faf654ee5d479cb2958edce9feaef0"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SortingLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "MapLayer0"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.StateAnimationComponent",
|
||||
"Name": "ActionSheet",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"stand": "50faf654ee5d479cb2958edce9feaef0",
|
||||
"move": "dc932872543f4a02bf41e977ab79e5ad",
|
||||
"hit": "61c27025a8f14c478f30ede1b49758bc",
|
||||
"jump": "8b89d86b1a9c4c4288650614c6f30e67",
|
||||
"die": "31ecb6c7cbc24599881f00cb01599f09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "BoxSize",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.63,
|
||||
"y": 0.58
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "ColliderOffset",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0449999869,
|
||||
"y": 0.29
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.MovementComponent",
|
||||
"Name": "InputSpeed",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "script.CombatMonster",
|
||||
"Name": "EnemyId",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "slime"
|
||||
}
|
||||
],
|
||||
"EventLinks": [],
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
231
RootDesk/MyDesk/Models/Monsters/slime_boss.model
Normal file
231
RootDesk/MyDesk/Models/Monsters/slime_boss.model
Normal file
@@ -0,0 +1,231 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "model://monster-slime_boss",
|
||||
"ContentType": "x-mod/model",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.3.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"Version": 1,
|
||||
"Name": "slime_boss",
|
||||
"BaseModelId": null,
|
||||
"Id": "monster-slime_boss",
|
||||
"Components": [
|
||||
"MOD.Core.TransformComponent",
|
||||
"MOD.Core.StateAnimationComponent",
|
||||
"MOD.Core.SpriteRendererComponent",
|
||||
"MOD.Core.RigidbodyComponent",
|
||||
"MOD.Core.MovementComponent",
|
||||
"MOD.Core.StateComponent",
|
||||
"MOD.Core.HitComponent",
|
||||
"MOD.Core.DamageSkinSpawnerComponent",
|
||||
"script.Monster",
|
||||
"script.MonsterAttack",
|
||||
"MOD.Core.KinematicbodyComponent",
|
||||
"MOD.Core.SideviewbodyComponent",
|
||||
"MOD.Core.DamageSkinSettingComponent",
|
||||
"script.CombatMonster"
|
||||
],
|
||||
"Properties": [
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "speed",
|
||||
"DisplayName": "speed",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "InputSpeed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "jumpForce",
|
||||
"DisplayName": "jumpForce",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "JumpForce"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "actionSheet",
|
||||
"DisplayName": "actionSheet",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.StateAnimationComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "ActionSheet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "renderguid",
|
||||
"DisplayName": "renderguid",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "SpriteRUID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.RenderSettingType, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "renderSetting",
|
||||
"DisplayName": "renderSetting",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "RenderSetting"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Values": [
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "OrderInLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 2
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
"Name": "renderguid",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "null"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "CollisionGroup",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.Physics.CollisionGroup, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.Physics.CollisionGroup, MOD.Core",
|
||||
"Id": "8992acd1e8cd45838db6f10a7b41df09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SpriteRUID",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "50faf654ee5d479cb2958edce9feaef0"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SortingLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "MapLayer0"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.StateAnimationComponent",
|
||||
"Name": "ActionSheet",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"stand": "50faf654ee5d479cb2958edce9feaef0",
|
||||
"move": "dc932872543f4a02bf41e977ab79e5ad",
|
||||
"hit": "61c27025a8f14c478f30ede1b49758bc",
|
||||
"jump": "8b89d86b1a9c4c4288650614c6f30e67",
|
||||
"die": "31ecb6c7cbc24599881f00cb01599f09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "BoxSize",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 1.2,
|
||||
"y": 1.1
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "ColliderOffset",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0449999869,
|
||||
"y": 0.4
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.MovementComponent",
|
||||
"Name": "InputSpeed",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "script.CombatMonster",
|
||||
"Name": "EnemyId",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "slime_boss"
|
||||
}
|
||||
],
|
||||
"EventLinks": [],
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
231
RootDesk/MyDesk/Models/Monsters/slime_elite.model
Normal file
231
RootDesk/MyDesk/Models/Monsters/slime_elite.model
Normal file
@@ -0,0 +1,231 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "model://monster-slime_elite",
|
||||
"ContentType": "x-mod/model",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.3.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"Version": 1,
|
||||
"Name": "slime_elite",
|
||||
"BaseModelId": null,
|
||||
"Id": "monster-slime_elite",
|
||||
"Components": [
|
||||
"MOD.Core.TransformComponent",
|
||||
"MOD.Core.StateAnimationComponent",
|
||||
"MOD.Core.SpriteRendererComponent",
|
||||
"MOD.Core.RigidbodyComponent",
|
||||
"MOD.Core.MovementComponent",
|
||||
"MOD.Core.StateComponent",
|
||||
"MOD.Core.HitComponent",
|
||||
"MOD.Core.DamageSkinSpawnerComponent",
|
||||
"script.Monster",
|
||||
"script.MonsterAttack",
|
||||
"MOD.Core.KinematicbodyComponent",
|
||||
"MOD.Core.SideviewbodyComponent",
|
||||
"MOD.Core.DamageSkinSettingComponent",
|
||||
"script.CombatMonster"
|
||||
],
|
||||
"Properties": [
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "speed",
|
||||
"DisplayName": "speed",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "InputSpeed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "jumpForce",
|
||||
"DisplayName": "jumpForce",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "JumpForce"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "actionSheet",
|
||||
"DisplayName": "actionSheet",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.StateAnimationComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "ActionSheet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "renderguid",
|
||||
"DisplayName": "renderguid",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "SpriteRUID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.RenderSettingType, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "renderSetting",
|
||||
"DisplayName": "renderSetting",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "RenderSetting"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Values": [
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "OrderInLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 2
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
"Name": "renderguid",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "null"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "CollisionGroup",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.Physics.CollisionGroup, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.Physics.CollisionGroup, MOD.Core",
|
||||
"Id": "8992acd1e8cd45838db6f10a7b41df09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SpriteRUID",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "50faf654ee5d479cb2958edce9feaef0"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SortingLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "MapLayer0"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.StateAnimationComponent",
|
||||
"Name": "ActionSheet",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"stand": "50faf654ee5d479cb2958edce9feaef0",
|
||||
"move": "dc932872543f4a02bf41e977ab79e5ad",
|
||||
"hit": "61c27025a8f14c478f30ede1b49758bc",
|
||||
"jump": "8b89d86b1a9c4c4288650614c6f30e67",
|
||||
"die": "31ecb6c7cbc24599881f00cb01599f09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "BoxSize",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.85,
|
||||
"y": 0.78
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "ColliderOffset",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0449999869,
|
||||
"y": 0.29
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.MovementComponent",
|
||||
"Name": "InputSpeed",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "script.CombatMonster",
|
||||
"Name": "EnemyId",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "slime_elite"
|
||||
}
|
||||
],
|
||||
"EventLinks": [],
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
230
RootDesk/MyDesk/Models/Monsters/stump.model
Normal file
230
RootDesk/MyDesk/Models/Monsters/stump.model
Normal file
@@ -0,0 +1,230 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "model://monster-stump",
|
||||
"ContentType": "x-mod/model",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.3.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"Version": 1,
|
||||
"Name": "stump",
|
||||
"BaseModelId": null,
|
||||
"Id": "monster-stump",
|
||||
"Components": [
|
||||
"MOD.Core.TransformComponent",
|
||||
"MOD.Core.StateAnimationComponent",
|
||||
"MOD.Core.SpriteRendererComponent",
|
||||
"MOD.Core.RigidbodyComponent",
|
||||
"MOD.Core.MovementComponent",
|
||||
"MOD.Core.StateComponent",
|
||||
"MOD.Core.HitComponent",
|
||||
"MOD.Core.DamageSkinSpawnerComponent",
|
||||
"script.Monster",
|
||||
"script.MonsterAttack",
|
||||
"MOD.Core.KinematicbodyComponent",
|
||||
"MOD.Core.SideviewbodyComponent",
|
||||
"MOD.Core.DamageSkinSettingComponent",
|
||||
"script.CombatMonster"
|
||||
],
|
||||
"Properties": [
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "speed",
|
||||
"DisplayName": "speed",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "InputSpeed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "jumpForce",
|
||||
"DisplayName": "jumpForce",
|
||||
"ShowInInspector": true,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MovementComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "JumpForce"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "actionSheet",
|
||||
"DisplayName": "actionSheet",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.StateAnimationComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "ActionSheet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Name": "renderguid",
|
||||
"DisplayName": "renderguid",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "SpriteRUID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.RenderSettingType, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Name": "renderSetting",
|
||||
"DisplayName": "renderSetting",
|
||||
"ShowInInspector": false,
|
||||
"Link": {
|
||||
"Target": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.SpriteRendererComponent, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Property": "RenderSetting"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Values": [
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "OrderInLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 2
|
||||
},
|
||||
{
|
||||
"TargetType": null,
|
||||
"Name": "renderguid",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "null"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "CollisionGroup",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.Physics.CollisionGroup, MOD.Core, Version=26.3.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.Physics.CollisionGroup, MOD.Core",
|
||||
"Id": "8992acd1e8cd45838db6f10a7b41df09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SpriteRUID",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "ed3908e24d694bb786023fc1ed073489"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.SpriteRendererComponent",
|
||||
"Name": "SortingLayer",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "MapLayer0"
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.StateAnimationComponent",
|
||||
"Name": "ActionSheet",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODSyncDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"stand": "ed3908e24d694bb786023fc1ed073489",
|
||||
"move": "9a4cad470f304753885e06c043156efb",
|
||||
"hit": "4763c9bebc9245998c9c499b6316aa9f",
|
||||
"die": "b168793b92a844a3a3a6f4ce647a14d2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "BoxSize",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.63,
|
||||
"y": 0.58
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.HitComponent",
|
||||
"Name": "ColliderOffset",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
},
|
||||
"Value": {
|
||||
"$type": "MOD.Core.MODVector2, MOD.Core",
|
||||
"x": 0.0449999869,
|
||||
"y": 0.29
|
||||
}
|
||||
},
|
||||
{
|
||||
"TargetType": "MOD.Core.MovementComponent",
|
||||
"Name": "InputSpeed",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": 0
|
||||
},
|
||||
{
|
||||
"TargetType": "script.CombatMonster",
|
||||
"Name": "EnemyId",
|
||||
"ValueType": {
|
||||
"$type": "MODNativeType",
|
||||
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
||||
},
|
||||
"Value": "stump"
|
||||
}
|
||||
],
|
||||
"EventLinks": [],
|
||||
"Children": []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,7 @@
|
||||
"Name": null
|
||||
},
|
||||
"Arguments": [],
|
||||
"Code": "local monster = self.Entity.Monster\nif not monster then\n\treturn\nend\n\nself.Shape = BoxShape(Vector2.zero, Vector2.one, 0)\n\n-- sprite 사이즈를 가져와 공격 영역으로 사용한다\n_ResourceService:PreloadAsync({self.Entity.SpriteRendererComponent.SpriteRUID}, function()\n\tlocal clip = _ResourceService:LoadAnimationClipAndWait(self.Entity.SpriteRendererComponent.SpriteRUID)\n\tlocal firstFrameSprite = clip.Frames[1].FrameSprite\n\tlocal firstSpriteSizeInPixel = Vector2(firstFrameSprite.Width, firstFrameSprite.Height)\n\tlocal ppu = firstFrameSprite.PixelPerUnit\n\n\tself.SpriteSize = firstSpriteSizeInPixel / ppu\n\tself.PositionOffset = (firstSpriteSizeInPixel / 2 - firstFrameSprite.PivotPixel:ToVector2()) / ppu\n\t\n\t_TimerService:SetTimerRepeat(function() \n\t\tif monster.IsDead == false then\n\t\t\tself:AttackNear()\n\t\tend\n\tend, self.AttackInterval)\nend)",
|
||||
"Code": "local monster = self.Entity.Monster\nif not monster then\n\treturn\nend\n\nself.Shape = BoxShape(Vector2.zero, Vector2.one, 0)\n\n-- sprite 사이즈를 가져와 공격 영역으로 사용한다\n_ResourceService:PreloadAsync({self.Entity.SpriteRendererComponent.SpriteRUID}, function()\n\tif _ResourceService:GetTypeAndWait(self.Entity.SpriteRendererComponent.SpriteRUID) ~= ResourceType.AnimationClip then\n\t\treturn\n\tend\n\tlocal clip = _ResourceService:LoadAnimationClipAndWait(self.Entity.SpriteRendererComponent.SpriteRUID)\n\tif clip == nil then\n\t\treturn\n\tend\n\tlocal firstFrameSprite = clip.Frames[1].FrameSprite\n\tlocal firstSpriteSizeInPixel = Vector2(firstFrameSprite.Width, firstFrameSprite.Height)\n\tlocal ppu = firstFrameSprite.PixelPerUnit\n\n\tself.SpriteSize = firstSpriteSizeInPixel / ppu\n\tself.PositionOffset = (firstSpriteSizeInPixel / 2 - firstFrameSprite.PivotPixel:ToVector2()) / ppu\n\t\n\t_TimerService:SetTimerRepeat(function() \n\t\tif monster.IsDead == false then\n\t\t\tself:AttackNear()\n\t\tend\n\tend, self.AttackInterval)\nend)",
|
||||
"Scope": 2,
|
||||
"ExecSpace": 1,
|
||||
"Attributes": [],
|
||||
|
||||
60
RootDesk/MyDesk/PlayerLock.codeblock
Normal file
60
RootDesk/MyDesk/PlayerLock.codeblock
Normal file
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "codeblock://playerlock",
|
||||
"ContentType": "x-mod/codeblock",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"CoreVersion": {
|
||||
"Major": 0,
|
||||
"Minor": 2
|
||||
},
|
||||
"ScriptVersion": {
|
||||
"Major": 1,
|
||||
"Minor": 0
|
||||
},
|
||||
"Description": "",
|
||||
"Id": "PlayerLock",
|
||||
"Language": 1,
|
||||
"Name": "PlayerLock",
|
||||
"Type": 1,
|
||||
"Source": 0,
|
||||
"Target": null,
|
||||
"Properties": [
|
||||
{
|
||||
"Type": "number",
|
||||
"DefaultValue": "0",
|
||||
"SyncDirection": 0,
|
||||
"Attributes": [],
|
||||
"Name": "LockTries"
|
||||
}
|
||||
],
|
||||
"Methods": [
|
||||
{
|
||||
"Return": {
|
||||
"Type": "void",
|
||||
"DefaultValue": null,
|
||||
"SyncDirection": 0,
|
||||
"Attributes": [],
|
||||
"Name": null
|
||||
},
|
||||
"Arguments": [],
|
||||
"Code": "self.LockTries = 0\nlocal eventId = 0\nlocal function apply()\n\tself.LockTries = self.LockTries + 1\n\tlocal pc = nil\n\tlocal lp = _UserService.LocalPlayer\n\tif lp ~= nil then\n\t\tpc = lp.PlayerControllerComponent\n\tend\n\tif pc ~= nil then\n\t\tpc.LookDirectionX = 1\n\t\tpc.FixedLookAt = true\n\t\tpc.Enable = false\n\tend\n\tif lp ~= nil then\n\t\tif lp.RigidbodyComponent ~= nil then lp.RigidbodyComponent.WalkAcceleration = 0 end\n\t\tif lp.MovementComponent ~= nil then lp.MovementComponent.InputSpeed = 0; lp.MovementComponent.JumpForce = 0 end\n\tend\n\tif pc ~= nil then\n\t\t_TimerService:ClearTimer(eventId)\n\telseif self.LockTries > 30 then\n\t\t_TimerService:ClearTimer(eventId)\n\tend\nend\neventId = _TimerService:SetTimerRepeat(apply, 0.1)",
|
||||
"Scope": 2,
|
||||
"ExecSpace": 6,
|
||||
"Attributes": [],
|
||||
"Name": "OnBeginPlay"
|
||||
}
|
||||
],
|
||||
"EntityEventHandlers": []
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
23
RootDesk/MyDesk/Thumnail.sprite
Normal file
23
RootDesk/MyDesk/Thumnail.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://41ad73da083d41b0ae30bf7b86794376",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/41ad73da083d41b0ae30bf7b86794376/639172145413258274",
|
||||
"upload_hash": "CFC620F96E1621FEE5594456FC8A4157BC6EF0D3E7661C5543293200FD364A85",
|
||||
"name": "Thumnail",
|
||||
"resource_guid": "41ad73da083d41b0ae30bf7b86794376",
|
||||
"resource_version": "6a31544d335c959bb11f45eb"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/archmage(fire_poison).sprite
Normal file
23
RootDesk/MyDesk/archmage(fire_poison).sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://2d659478140c4b1c8f37febbb61bdaa0",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/2d659478140c4b1c8f37febbb61bdaa0/639171361295360405",
|
||||
"upload_hash": "13E6D3B629261148095059F7C1D8EDC012C7A60422FC769ECB574A7C5A75759E",
|
||||
"name": "archmage(fire_poison)",
|
||||
"resource_guid": "2d659478140c4b1c8f37febbb61bdaa0",
|
||||
"resource_version": "6a3022013e53f03801a4ac58"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/archmage(thun_cold).sprite
Normal file
23
RootDesk/MyDesk/archmage(thun_cold).sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://ddd0a729328f452b9ff0802ab1f6f579",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/ddd0a729328f452b9ff0802ab1f6f579/639171361295458274",
|
||||
"upload_hash": "7B874B7774FA37E570B16E369112EC467EEA5EC1395A32E4DF01FB6165062E67",
|
||||
"name": "archmage(thun_cold)",
|
||||
"resource_guid": "ddd0a729328f452b9ff0802ab1f6f579",
|
||||
"resource_version": "6a3022012c6a274be88a0819"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/bandit.sprite
Normal file
23
RootDesk/MyDesk/bandit.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://efa920e58d31426486ef974106e7dc8b",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/efa920e58d31426486ef974106e7dc8b/639171361295547945",
|
||||
"upload_hash": "C4B0469A46B70C2356DC8B0F99D36FD9480BFDA5832E251960DD1FF30C201B7F",
|
||||
"name": "bandit",
|
||||
"resource_guid": "efa920e58d31426486ef974106e7dc8b",
|
||||
"resource_version": "6a302201a81bed5f59770e23"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/bandit_legend.sprite
Normal file
23
RootDesk/MyDesk/bandit_legend.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://c357d2daf31a489d95b8fa47e50dd879",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/c357d2daf31a489d95b8fa47e50dd879/639168711224334184",
|
||||
"upload_hash": "A1639991C7A8CB1025C97E6BE93F618088971DC609F03106C50D8B6EA145F6A2",
|
||||
"name": "bandit_legend",
|
||||
"resource_guid": "c357d2daf31a489d95b8fa47e50dd879",
|
||||
"resource_version": "6a2c16d2cc7e89479f128ffb"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/bandit_normal.sprite
Normal file
23
RootDesk/MyDesk/bandit_normal.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://9487b06867bc46269ed1d855420f457f",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/9487b06867bc46269ed1d855420f457f/639168711224307886",
|
||||
"upload_hash": "FD9F2140D16EFC7A77F0B09CA230702CA6CA25E3178AAF6ACD63EF07D5C2C83E",
|
||||
"name": "bandit_normal",
|
||||
"resource_guid": "9487b06867bc46269ed1d855420f457f",
|
||||
"resource_version": "6a2c16d23d5de2eb0c7d16a2"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/bandit_unique.sprite
Normal file
23
RootDesk/MyDesk/bandit_unique.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://b3081fb2fb1445fa90b12b01481a78ef",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/b3081fb2fb1445fa90b12b01481a78ef/639168711224396185",
|
||||
"upload_hash": "1F5218270148F873D060223A617DFC184AEEE61344D26C85705E40EECC086D5E",
|
||||
"name": "bandit_unique",
|
||||
"resource_guid": "b3081fb2fb1445fa90b12b01481a78ef",
|
||||
"resource_version": "6a2c16d21a7908d59b5dc059"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/bowmaster.sprite
Normal file
23
RootDesk/MyDesk/bowmaster.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://93e615d645e948f5a76656bfdd9dce15",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/93e615d645e948f5a76656bfdd9dce15/639171361295501823",
|
||||
"upload_hash": "A5A1263A9E1F5D58B0F985AC58DE7DDE1EA13E0CC5CEE56FC34C3FD792A8D39E",
|
||||
"name": "bowmaster",
|
||||
"resource_guid": "93e615d645e948f5a76656bfdd9dce15",
|
||||
"resource_version": "6a302201a0766b148f66ec2c"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/cleric.sprite
Normal file
23
RootDesk/MyDesk/cleric.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://3c752ffcd4984dcb9f04baab06544f02",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/3c752ffcd4984dcb9f04baab06544f02/639171361295496567",
|
||||
"upload_hash": "ED1ABC3DBCB04FCBD365BAD0408C8CA1D2458FB337DBDEB4A7EC5DF1BAC32496",
|
||||
"name": "cleric",
|
||||
"resource_guid": "3c752ffcd4984dcb9f04baab06544f02",
|
||||
"resource_version": "6a302201d03493c632770e41"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/darkknight.sprite
Normal file
23
RootDesk/MyDesk/darkknight.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://e207e6839a4a4bd0aab681bd296a609a",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/e207e6839a4a4bd0aab681bd296a609a/639171361295401732",
|
||||
"upload_hash": "D5931943781C46611D43595594234BFB133F8BCCAA920C13BCA7E2F8AC9C09D0",
|
||||
"name": "darkknight",
|
||||
"resource_guid": "e207e6839a4a4bd0aab681bd296a609a",
|
||||
"resource_version": "6a302201644d4c175c75d435"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/hero.sprite
Normal file
23
RootDesk/MyDesk/hero.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://0efbf37bb7414aea82b257781068372b",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/0efbf37bb7414aea82b257781068372b/639171361295431877",
|
||||
"upload_hash": "DE22318162E1F93E7B90A0B6A59BE31FC76DEEC122914979915D6D575A4D99B5",
|
||||
"name": "hero",
|
||||
"resource_guid": "0efbf37bb7414aea82b257781068372b",
|
||||
"resource_version": "6a302201c377d9630d82c463"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/hunter.sprite
Normal file
23
RootDesk/MyDesk/hunter.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://fd460e6ee38a40e3b6b05580d00773b6",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/fd460e6ee38a40e3b6b05580d00773b6/639171361295408991",
|
||||
"upload_hash": "B832DE85217EA6544BA4477F0DBA5D2148E4E392A170944336E0DA74E8D41961",
|
||||
"name": "hunter",
|
||||
"resource_guid": "fd460e6ee38a40e3b6b05580d00773b6",
|
||||
"resource_version": "6a3022013d5de2eb0c7d2a51"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/mage.sprite
Normal file
23
RootDesk/MyDesk/mage.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://3b9ea1f066a744bb859df47fef817277",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/3b9ea1f066a744bb859df47fef817277/639171361295599801",
|
||||
"upload_hash": "770C9D83241F8CE2C0EA8B742887D8351A580E2DE15A6380380653855A974F14",
|
||||
"name": "mage",
|
||||
"resource_guid": "3b9ea1f066a744bb859df47fef817277",
|
||||
"resource_version": "6a302201cc7e89479f12a1ac"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/mage_legend.sprite
Normal file
23
RootDesk/MyDesk/mage_legend.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://cff71f2e472041ce80c6fbd296f42e2d",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/cff71f2e472041ce80c6fbd296f42e2d/639168711224422293",
|
||||
"upload_hash": "040CA63C5D3D52E1661F3A008D229A182B1A7C09D919BB74E23D1305B1AA56A7",
|
||||
"name": "mage_legend",
|
||||
"resource_guid": "cff71f2e472041ce80c6fbd296f42e2d",
|
||||
"resource_version": "6a2c16d2a0766b148f66d799"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/mage_normal.sprite
Normal file
23
RootDesk/MyDesk/mage_normal.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://d788d09f6f50467ebc67f01dec45f9e2",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/d788d09f6f50467ebc67f01dec45f9e2/639168711224258598",
|
||||
"upload_hash": "5D91E6E333564F15A76554AE07402DC0ED39ABC02439F65C2CCB818C71FB6994",
|
||||
"name": "mage_normal",
|
||||
"resource_guid": "d788d09f6f50467ebc67f01dec45f9e2",
|
||||
"resource_version": "6a2c16d2e75b0d4ccdfcee8b"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/mage_unique.sprite
Normal file
23
RootDesk/MyDesk/mage_unique.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://f5def2e8022b4e59a17d3c16414034fe",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/f5def2e8022b4e59a17d3c16414034fe/639168711224271729",
|
||||
"upload_hash": "B07075BE5F13B1D7BFB4AE36F4C47D97A10A3CC7EF763F02DD2A11C2AFC61E67",
|
||||
"name": "mage_unique",
|
||||
"resource_guid": "f5def2e8022b4e59a17d3c16414034fe",
|
||||
"resource_version": "6a2c16d2e75b0d4ccdfcee8a"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/nightlord.sprite
Normal file
23
RootDesk/MyDesk/nightlord.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://994c64290e6d4248bd60aba03a595f72",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/994c64290e6d4248bd60aba03a595f72/639171361295422460",
|
||||
"upload_hash": "A0B7146F6D8E9D72CEACDB7E21A2CD6EEBD4135554D3965B4E1C06BC98FC1211",
|
||||
"name": "nightlord",
|
||||
"resource_guid": "994c64290e6d4248bd60aba03a595f72",
|
||||
"resource_version": "6a30220139613d284615a1e1"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/palladin.sprite
Normal file
23
RootDesk/MyDesk/palladin.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://415d423954764b659574fe829f9aff52",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/415d423954764b659574fe829f9aff52/639171361329450040",
|
||||
"upload_hash": "EB94BA457C18E04D87964201DD50042695A3C7F93651CBC8ED9509BDDE07F331",
|
||||
"name": "palladin",
|
||||
"resource_guid": "415d423954764b659574fe829f9aff52",
|
||||
"resource_version": "6a302205a0766b148f66ec2d"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/pirate.sprite
Normal file
23
RootDesk/MyDesk/pirate.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://94d6417c55da48e9861964c405991219",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/94d6417c55da48e9861964c405991219/639171361329410075",
|
||||
"upload_hash": "EBEC4B43CDBB1759C0BC92C051252A7DF84E6B89C9C56ECBFEFF543A0E260889",
|
||||
"name": "pirate",
|
||||
"resource_guid": "94d6417c55da48e9861964c405991219",
|
||||
"resource_version": "6a3022052c6a274be88a081a"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/restBgImage.sprite
Normal file
23
RootDesk/MyDesk/restBgImage.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://477679b832b44e099a30e4905078dbcb",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/477679b832b44e099a30e4905078dbcb/639172226341792721",
|
||||
"upload_hash": "3E30B07C24C4BC4E373CDEA653035146D2F50ACC6484F6E9DA34E6179BB38F15",
|
||||
"name": "restBgImage",
|
||||
"resource_guid": "477679b832b44e099a30e4905078dbcb",
|
||||
"resource_version": "6a3173ea002bbe95706406b6"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/shadower.sprite
Normal file
23
RootDesk/MyDesk/shadower.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://06c1060586e3457f897f2c596eb5cd71",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/06c1060586e3457f897f2c596eb5cd71/639171361329387468",
|
||||
"upload_hash": "42EE2E63508762E86391486107A23322E11038070F48851D03FE91255CF41596",
|
||||
"name": "shadower",
|
||||
"resource_guid": "06c1060586e3457f897f2c596eb5cd71",
|
||||
"resource_version": "6a302205a81bed5f59770e24"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/shopBgImage.sprite
Normal file
23
RootDesk/MyDesk/shopBgImage.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://28f3b10ac0334fbfbf29677bf963c57a",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/28f3b10ac0334fbfbf29677bf963c57a/639172222414073214",
|
||||
"upload_hash": "01BE0B58F480BA86DA1D18BFE25C01E1B27219A14FE2DCD73456A7A48553CF15",
|
||||
"name": "shopBgImage",
|
||||
"resource_guid": "28f3b10ac0334fbfbf29677bf963c57a",
|
||||
"resource_version": "6a3172612c6a274be88a130e"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/singung.sprite
Normal file
23
RootDesk/MyDesk/singung.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://b2e099f2e5334705af122e3f88840ba7",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/b2e099f2e5334705af122e3f88840ba7/639171361329408374",
|
||||
"upload_hash": "2AB66279D07E64A17CD2AD05BB03F732632805B0076278EC94F51C0227341CC5",
|
||||
"name": "singung",
|
||||
"resource_guid": "b2e099f2e5334705af122e3f88840ba7",
|
||||
"resource_version": "6a302204c377d9630d82c464"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/treasureBgImage.sprite
Normal file
23
RootDesk/MyDesk/treasureBgImage.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://dd6193fd37da4b12bcdbcdcf2fbe8e40",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/dd6193fd37da4b12bcdbcdcf2fbe8e40/639172228832890845",
|
||||
"upload_hash": "3EDD046B291806637ADD12A77BF94CF00BDD9F4F9912C132B14323D9DE5F297C",
|
||||
"name": "treasureBgImage",
|
||||
"resource_guid": "dd6193fd37da4b12bcdbcdcf2fbe8e40",
|
||||
"resource_version": "6a3174e32a2802c06419f288"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/warior_legend.sprite
Normal file
23
RootDesk/MyDesk/warior_legend.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://6d741a60c60743cb98ee740a1e2dbfed",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/6d741a60c60743cb98ee740a1e2dbfed/639168711258121433",
|
||||
"upload_hash": "80E2D8FFC8E56ECE4694BED5A387413F49633841DD37B7AA9FA9AC713962602C",
|
||||
"name": "warior_legend",
|
||||
"resource_guid": "6d741a60c60743cb98ee740a1e2dbfed",
|
||||
"resource_version": "6a2c16d59c3c6c308bfd4bf8"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/warior_normal.sprite
Normal file
23
RootDesk/MyDesk/warior_normal.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://4bb57ef88ef449fdaf958f6cf37fe44b",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/4bb57ef88ef449fdaf958f6cf37fe44b/639168711258172386",
|
||||
"upload_hash": "2832F3B6C4C9530DE69DF061E590FD314D8646BE6BDB65931AFBE68D38DBB0ED",
|
||||
"name": "warior_normal",
|
||||
"resource_guid": "4bb57ef88ef449fdaf958f6cf37fe44b",
|
||||
"resource_version": "6a2c16d53d5de2eb0c7d16a3"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/warior_unique.sprite
Normal file
23
RootDesk/MyDesk/warior_unique.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://4f71c124c8bc4e13b5e9fad392995f68",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/4f71c124c8bc4e13b5e9fad392995f68/639168711257524633",
|
||||
"upload_hash": "F27B2D713455FB18C10E924D381C4582E5E039D5DCCA9CECD2FB0D8E9A4C8135",
|
||||
"name": "warior_unique",
|
||||
"resource_guid": "4f71c124c8bc4e13b5e9fad392995f68",
|
||||
"resource_version": "6a2c16d5a43134e1b8a34b65"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RootDesk/MyDesk/warrior.sprite
Normal file
23
RootDesk/MyDesk/warrior.sprite
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "sprite://28c88fdc5ab44f34a8b3fc1e19d4ce78",
|
||||
"ContentType": "x-mod/sprite",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "0.1.0.0",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/28c88fdc5ab44f34a8b3fc1e19d4ce78/639171361330139251",
|
||||
"upload_hash": "2929F452FBB26215631886FFB430EE6035D55EB42B1770E880C1B0A34D97BDA0",
|
||||
"name": "warrior",
|
||||
"resource_guid": "28c88fdc5ab44f34a8b3fc1e19d4ce78",
|
||||
"resource_version": "6a30220539613d284615a1e2"
|
||||
}
|
||||
}
|
||||
}
|
||||
7
cards_to_excel.bat
Normal file
7
cards_to_excel.bat
Normal file
@@ -0,0 +1,7 @@
|
||||
@echo off
|
||||
setlocal
|
||||
chcp 65001 >nul
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0tools\cards\cards_excel.ps1" export
|
||||
echo.
|
||||
echo Press any key to close this window.
|
||||
pause >nul
|
||||
8
data/camera.json
Normal file
8
data/camera.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"zoomRatio": 90,
|
||||
"screenOffsetX": 0.5,
|
||||
"screenOffsetY": 0.655,
|
||||
"confineCameraArea": true,
|
||||
"cameraOffsetX": 1.5,
|
||||
"cameraOffsetY": -0.83
|
||||
}
|
||||
43
data/cardframes.json
Normal file
43
data/cardframes.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"frames": {
|
||||
"warrior": {
|
||||
"normal": "4bb57ef88ef449fdaf958f6cf37fe44b",
|
||||
"unique": "4f71c124c8bc4e13b5e9fad392995f68",
|
||||
"legend": "6d741a60c60743cb98ee740a1e2dbfed"
|
||||
},
|
||||
"magician": {
|
||||
"normal": "d788d09f6f50467ebc67f01dec45f9e2",
|
||||
"unique": "f5def2e8022b4e59a17d3c16414034fe",
|
||||
"legend": "cff71f2e472041ce80c6fbd296f42e2d"
|
||||
},
|
||||
"rogue": {
|
||||
"normal": "9487b06867bc46269ed1d855420f457f",
|
||||
"unique": "b3081fb2fb1445fa90b12b01481a78ef",
|
||||
"legend": "c357d2daf31a489d95b8fa47e50dd879"
|
||||
}
|
||||
},
|
||||
"classToFrame": {
|
||||
"warrior": "warrior",
|
||||
"fighter": "warrior",
|
||||
"crusader": "warrior",
|
||||
"page": "warrior",
|
||||
"knight": "warrior",
|
||||
"spearman": "warrior",
|
||||
"magician": "magician",
|
||||
"firepoison": "magician",
|
||||
"icelightning": "magician",
|
||||
"cleric": "magician",
|
||||
"curse": "rogue",
|
||||
"shiv": "rogue",
|
||||
"rogue": "rogue",
|
||||
"assassin": "rogue",
|
||||
"hermit": "rogue",
|
||||
"thief": "rogue",
|
||||
"thiefmaster": "rogue"
|
||||
},
|
||||
"rewardWeights": {
|
||||
"normal": 70,
|
||||
"unique": 25,
|
||||
"legend": 5
|
||||
}
|
||||
}
|
||||
3039
data/cards.json
Normal file
3039
data/cards.json
Normal file
File diff suppressed because it is too large
Load Diff
BIN
data/cards.xlsx
Normal file
BIN
data/cards.xlsx
Normal file
Binary file not shown.
7
data/characters.json
Normal file
7
data/characters.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"portraits": {
|
||||
"warrior": "28c88fdc5ab44f34a8b3fc1e19d4ce78",
|
||||
"magician": "3b9ea1f066a744bb859df47fef817277",
|
||||
"rogue": "efa920e58d31426486ef974106e7dc8b"
|
||||
}
|
||||
}
|
||||
7
data/encounters.json
Normal file
7
data/encounters.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"map01": { "combat": ["junior_bugi", "kapa_drake", "junior_neki", "octopus", "green_mushroom", "orange_mushroom"], "elite": ["dile", "mano"], "boss": ["king_slime"] },
|
||||
"map02": { "combat": ["pig", "green_mushroom", "blue_mushroom", "orange_mushroom", "slime"], "elite": ["mushmom"], "boss": ["slime_boss"] },
|
||||
"map03": { "combat": ["octopus", "junior_neki", "junior_bugi", "slime"], "elite": ["slime_elite"], "boss": ["king_slime"] },
|
||||
"map04": { "combat": ["kapa_drake", "junior_neki", "junior_bugi", "stump"], "elite": ["dile"], "boss": ["mushmom"] },
|
||||
"map05": { "combat": ["kapa_drake", "octopus", "junior_bugi", "junior_neki"], "elite": ["dile", "slime_elite"], "boss": ["king_slime"] }
|
||||
}
|
||||
203
data/enemies.json
Normal file
203
data/enemies.json
Normal file
@@ -0,0 +1,203 @@
|
||||
{
|
||||
"enemies": {
|
||||
"slime": {
|
||||
"name": "슬라임",
|
||||
"maxHp": 45,
|
||||
"intents": [
|
||||
{ "kind": "Attack", "value": 10 },
|
||||
{ "kind": "Attack", "value": 6 },
|
||||
{ "kind": "Defend", "value": 8 }
|
||||
],
|
||||
"appearance": { "sheet": { "stand": "50faf654ee5d479cb2958edce9feaef0", "move": "dc932872543f4a02bf41e977ab79e5ad", "hit": "61c27025a8f14c478f30ede1b49758bc", "jump": "8b89d86b1a9c4c4288650614c6f30e67", "die": "31ecb6c7cbc24599881f00cb01599f09" }, "box": { "x": 0.63, "y": 0.58 }, "off": { "x": 0.0449999869, "y": 0.29 } }
|
||||
},
|
||||
"slime_elite": {
|
||||
"name": "정예 슬라임",
|
||||
"maxHp": 70,
|
||||
"intents": [
|
||||
{ "kind": "Attack", "value": 14 },
|
||||
{ "kind": "Attack", "value": 8 },
|
||||
{ "kind": "Defend", "value": 10 },
|
||||
{ "kind": "Debuff", "effect": "weak", "value": 1 }
|
||||
],
|
||||
"appearance": { "sheet": { "stand": "50faf654ee5d479cb2958edce9feaef0", "move": "dc932872543f4a02bf41e977ab79e5ad", "hit": "61c27025a8f14c478f30ede1b49758bc", "jump": "8b89d86b1a9c4c4288650614c6f30e67", "die": "31ecb6c7cbc24599881f00cb01599f09" }, "box": { "x": 0.85, "y": 0.78 }, "off": { "x": 0.0449999869, "y": 0.29 } }
|
||||
},
|
||||
"slime_boss": {
|
||||
"name": "슬라임 킹",
|
||||
"maxHp": 120,
|
||||
"intents": [
|
||||
{ "kind": "Attack", "value": 18 },
|
||||
{ "kind": "Defend", "value": 12 },
|
||||
{ "kind": "Debuff", "effect": "vuln", "value": 2 },
|
||||
{ "kind": "Attack", "value": 10 },
|
||||
{ "kind": "Attack", "value": 22 }
|
||||
],
|
||||
"appearance": { "sheet": { "stand": "50faf654ee5d479cb2958edce9feaef0", "move": "dc932872543f4a02bf41e977ab79e5ad", "hit": "61c27025a8f14c478f30ede1b49758bc", "jump": "8b89d86b1a9c4c4288650614c6f30e67", "die": "31ecb6c7cbc24599881f00cb01599f09" }, "box": { "x": 1.2, "y": 1.1 }, "off": { "x": 0.0449999869, "y": 0.4 } }
|
||||
},
|
||||
"orange_mushroom": {
|
||||
"name": "주황버섯",
|
||||
"maxHp": 16,
|
||||
"intents": [
|
||||
{ "kind": "Attack", "value": 5 },
|
||||
{ "kind": "Attack", "value": 5 },
|
||||
{ "kind": "Defend", "value": 4 },
|
||||
{ "kind": "Attack", "value": 8 }
|
||||
],
|
||||
"appearance": { "sheet": { "move": "573fe938562a4abf91eebf951f21afd5", "stand": "6d381bea1bcb4504b518a1fbfa0904ac", "jump": "59823e146a034e48b8667ebb6f0724b1", "hit": "642ece38d8d449b29ce4479100e37a54", "die": "3c99d6b9b89b4295a9c2749eb02e28e9" }, "box": { "x": 0.63, "y": 0.58 }, "off": { "x": 0.0449999869, "y": 0.29 } }
|
||||
},
|
||||
"blue_mushroom": {
|
||||
"name": "파란버섯",
|
||||
"maxHp": 22,
|
||||
"intents": [
|
||||
{ "kind": "Attack", "value": 4 },
|
||||
{ "kind": "Attack", "value": 4 },
|
||||
{ "kind": "Attack", "value": 10 },
|
||||
{ "kind": "AddCard", "card": "Wound", "count": 1 }
|
||||
],
|
||||
"appearance": { "sheet": { "stand": "1a176a7afb114fe7aef2bc58ef2d945b", "move": "8239541953a6457fbe6d35e17f19f0f8", "hit": "7b405108d05741699893a4dc3d715165", "jump": "a7ea0755262242199ae50ab6a3387034", "die": "9e74e807797d442f9c938ca64aa9f4cd" }, "box": { "x": 0.63, "y": 0.58 }, "off": { "x": 0.0449999869, "y": 0.29 } }
|
||||
},
|
||||
"pig": {
|
||||
"name": "돼지",
|
||||
"maxHp": 18,
|
||||
"intents": [
|
||||
{ "kind": "Attack", "value": 6 },
|
||||
{ "kind": "Attack", "value": 6 },
|
||||
{ "kind": "Defend", "value": 5 }
|
||||
],
|
||||
"appearance": { "sheet": { "stand": "528a8638b12f41b8b5781a05360d2949", "move": "8baad61512be4b33b2a0879fec7a266e", "hit": "60e42a918a0342478903cc71adba1dc5", "jump": "c9e27ce6f8344aefba169c5ca6571def", "die": "0644beff80a44ec7acc011ea0961df57" }, "box": { "x": 0.63, "y": 0.58 }, "off": { "x": 0.0449999869, "y": 0.29 } }
|
||||
},
|
||||
"green_mushroom": {
|
||||
"name": "초록버섯",
|
||||
"maxHp": 20,
|
||||
"intents": [
|
||||
{ "kind": "Attack", "value": 7 },
|
||||
{ "kind": "Defend", "value": 3 },
|
||||
{ "kind": "Attack", "value": 9 }
|
||||
],
|
||||
"appearance": { "sheet": { "stand": "f86992ba9c41487c8480fcb893fcbda6", "hit": "d305b942b1704c8084548108ff3b7a6b", "die": "5a563e5fd98c4132b61057dc6bb8aaf2" }, "box": { "x": 0.63, "y": 0.58 }, "off": { "x": 0.00999999, "y": 0.26 } }
|
||||
},
|
||||
"red_snail": {
|
||||
"name": "빨간 달팽이",
|
||||
"maxHp": 14,
|
||||
"intents": [
|
||||
{ "kind": "Attack", "value": 5 },
|
||||
{ "kind": "Defend", "value": 6 },
|
||||
{ "kind": "Attack", "value": 7 }
|
||||
],
|
||||
"appearance": { "sheet": { "stand": "17b55730c26f4fd6b8fcfa288da388de", "move": "f40108c8b0b84696a67337b801201f7d", "hit": "eac48e84a9fc4580a4018de5cf52ddb3", "die": "51c2f4b59a2c413db26035aa57002fc8" }, "box": { "x": 0.63, "y": 0.58 }, "off": { "x": 0.0449999869, "y": 0.29 } }
|
||||
},
|
||||
"stump": {
|
||||
"name": "나무토막",
|
||||
"maxHp": 19,
|
||||
"intents": [
|
||||
{ "kind": "Defend", "value": 5 },
|
||||
{ "kind": "Attack", "value": 8 },
|
||||
{ "kind": "Attack", "value": 6 }
|
||||
],
|
||||
"appearance": { "sheet": { "stand": "ed3908e24d694bb786023fc1ed073489", "move": "9a4cad470f304753885e06c043156efb", "hit": "4763c9bebc9245998c9c499b6316aa9f", "die": "b168793b92a844a3a3a6f4ce647a14d2" }, "box": { "x": 0.63, "y": 0.58 }, "off": { "x": 0.0449999869, "y": 0.29 } }
|
||||
},
|
||||
"mushmom": {
|
||||
"name": "머쉬맘",
|
||||
"maxHp": 75,
|
||||
"intents": [
|
||||
{ "kind": "Defend", "value": 10 },
|
||||
{ "kind": "Debuff", "effect": "weak", "value": 2 },
|
||||
{ "kind": "Attack", "value": 16 },
|
||||
{ "kind": "Attack", "value": 9 },
|
||||
{ "kind": "Defend", "value": 6 },
|
||||
{ "kind": "AddCard", "card": "Burn", "count": 1 }
|
||||
],
|
||||
"appearance": { "sheet": { "stand": "23c38ef3acad4a30ad59120bb939b008", "move": "24d8a3a75f96406ba690ed42d7250b8f", "hit": "c826e36ee89c48bca6aab856aa773f38", "attack": "4d7465e950144dc59c263aad01b14e14", "jump": "b7ddbda71a294141ba134249fc34c7da", "die": "f50664a4524147399359cb90a6f3e80c" }, "box": { "x": 1.2, "y": 1.1 }, "off": { "x": 0.02, "y": 0.55 } }
|
||||
},
|
||||
"modified_snail": {
|
||||
"name": "변형된 달팽이",
|
||||
"maxHp": 60,
|
||||
"intents": [
|
||||
{ "kind": "Attack", "value": 12 },
|
||||
{ "kind": "Defend", "value": 8 },
|
||||
{ "kind": "Attack", "value": 7 },
|
||||
{ "kind": "Attack", "value": 14 },
|
||||
{ "kind": "Debuff", "effect": "weak", "value": 1 }
|
||||
],
|
||||
"appearance": { "sheet": { "stand": "17b55730c26f4fd6b8fcfa288da388de", "move": "f40108c8b0b84696a67337b801201f7d", "hit": "eac48e84a9fc4580a4018de5cf52ddb3", "die": "51c2f4b59a2c413db26035aa57002fc8" }, "box": { "x": 0.75, "y": 0.68 }, "off": { "x": 0.0449999869, "y": 0.29 } }
|
||||
},
|
||||
"king_slime": {
|
||||
"name": "킹 슬라임",
|
||||
"maxHp": 130,
|
||||
"intents": [
|
||||
{ "kind": "Attack", "value": 18 },
|
||||
{ "kind": "Defend", "value": 14 },
|
||||
{ "kind": "Debuff", "effect": "vuln", "value": 2 },
|
||||
{ "kind": "Attack", "value": 12 },
|
||||
{ "kind": "Attack", "value": 24 }
|
||||
],
|
||||
"appearance": { "sheet": { "move": "873425127b75475b9944dc86bf77f885", "stand": "dd9de73d580240faab8cad03b587013b", "jump": "6a2b983b7a31417ca19c29c3d1d00817", "attack": "a34d1146057443fd8b578dafeb7c2ed1", "skill": "0b0bb78f0ca44526bad6d994bb16f973", "hit": "d2de42d3233b42a58d9799d5e762a19c", "die": "5bd3969c3bcb4df2bd79c2b940ee03dc" }, "box": { "x": 2.19, "y": 1.39 }, "off": { "x": 0.335000038, "y": 0.695 } }
|
||||
},
|
||||
"octopus": {
|
||||
"name": "문어",
|
||||
"maxHp": 15,
|
||||
"intents": [
|
||||
{ "kind": "Attack", "value": 5 },
|
||||
{ "kind": "Attack", "value": 6 },
|
||||
{ "kind": "Defend", "value": 4 }
|
||||
],
|
||||
"appearance": { "sheet": { "stand": "d8f014043ce8418f96700c2b6c9ebf6c", "hit": "c3cf643b618346c7bfa6574187b396f9", "die": "a88d9b3d60f941e4890dc89a6ccaa8ee" }, "box": { "x": 0.63, "y": 0.58 }, "off": { "x": 0.0449999869, "y": 0.29 } }
|
||||
},
|
||||
"kapa_drake": {
|
||||
"name": "카파 드레이크",
|
||||
"maxHp": 24,
|
||||
"intents": [
|
||||
{ "kind": "Attack", "value": 9 },
|
||||
{ "kind": "Attack", "value": 6 },
|
||||
{ "kind": "Defend", "value": 6 },
|
||||
{ "kind": "Attack", "value": 11 }
|
||||
],
|
||||
"appearance": { "sheet": { "stand": "4ca39dbfa1c6492283ba8bd352d12b0a", "hit": "7ac78511036e4ebe988b97c35fc275d1", "die": "740f3f2b2e7a4b71bec5eac84e8539f9" }, "box": { "x": 0.63, "y": 0.58 }, "off": { "x": 0.0449999869, "y": 0.29 } }
|
||||
},
|
||||
"junior_neki": {
|
||||
"name": "주니어 네키",
|
||||
"maxHp": 18,
|
||||
"intents": [
|
||||
{ "kind": "Attack", "value": 6 },
|
||||
{ "kind": "Attack", "value": 8 },
|
||||
{ "kind": "Debuff", "effect": "weak", "value": 1 }
|
||||
],
|
||||
"appearance": { "sheet": { "stand": "48c10437ae8344a9b2a1d3f36185728f", "hit": "9044063647854f5e9128efcf80e909be", "die": "f414577d18c94cc387c275df4abdbc3b" }, "box": { "x": 0.63, "y": 0.58 }, "off": { "x": 0.0449999869, "y": 0.29 } }
|
||||
},
|
||||
"junior_bugi": {
|
||||
"name": "주니어 부기",
|
||||
"maxHp": 20,
|
||||
"intents": [
|
||||
{ "kind": "Attack", "value": 7 },
|
||||
{ "kind": "Defend", "value": 5 },
|
||||
{ "kind": "Attack", "value": 9 }
|
||||
],
|
||||
"appearance": { "sheet": { "stand": "a2204a21d88942b281d2cac6053ffbaa", "hit": "afc08936b8a64b26bc3dd8c03ead1f26", "die": "fc1c6d9ba9bc413ab53b6dbfae3ac45b" }, "box": { "x": 0.63, "y": 0.58 }, "off": { "x": 0.0449999869, "y": 0.29 } }
|
||||
},
|
||||
"dile": {
|
||||
"name": "다일",
|
||||
"maxHp": 65,
|
||||
"intents": [
|
||||
{ "kind": "Attack", "value": 13 },
|
||||
{ "kind": "Defend", "value": 9 },
|
||||
{ "kind": "Attack", "value": 8 },
|
||||
{ "kind": "Attack", "value": 16 },
|
||||
{ "kind": "Debuff", "effect": "weak", "value": 1 }
|
||||
],
|
||||
"appearance": { "sheet": { "move": "426ba2c6fa2d4cdd92bcb0bb37861dcc", "stand": "68070c6f4abe40658899a208ddaf4081", "skill": "4ba2cdc2f11746afa0f542293b0618d5", "hit": "172640e6d4ce444aa1dfbd9bd9523eb1", "die": "5d50d9aa34c745b9b8932c15da919927" }, "box": { "x": 2.2, "y": 1.51 }, "off": { "x": -0.220000029, "y": 0.755 } }
|
||||
},
|
||||
"mano": {
|
||||
"name": "마노",
|
||||
"maxHp": 80,
|
||||
"intents": [
|
||||
{ "kind": "Defend", "value": 12 },
|
||||
{ "kind": "Attack", "value": 14 },
|
||||
{ "kind": "Debuff", "effect": "vuln", "value": 1 },
|
||||
{ "kind": "Attack", "value": 10 },
|
||||
{ "kind": "AddCard", "card": "Wound", "count": 1 }
|
||||
],
|
||||
"appearance": { "sheet": { "move": "3dcd0dc63d2d491b9b8d39b3b9d0a214", "stand": "e035bb90c053401b88de2159dfa230eb", "skill": "c05453dd21fd4ed581d193930ab4c331", "hit": "452cb740ddcb4837a46b75d7935e2ffc", "die": "f430051f6fc34f2eb56fe5e62b346eac" }, "box": { "x": 1.05, "y": 0.95 }, "off": { "x": 0.004999995, "y": 0.475 } }
|
||||
}
|
||||
},
|
||||
"activeEnemy": "slime",
|
||||
"simEncounter": ["orange_mushroom", "orange_mushroom", "blue_mushroom"]
|
||||
}
|
||||
11
data/nodeicons.json
Normal file
11
data/nodeicons.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"icons": {
|
||||
"combat": "f98db6823e894a4f90308d61f75894ac",
|
||||
"elite": "793ed8a757534b89a82f460747d2df24",
|
||||
"boss": "423056cdbbc04f4da131b9721c404d96",
|
||||
"shop": "da37e1fac55d455b9ade08569f09f798",
|
||||
"rest": "b86c1b0568bd45f3ae4a4b97e1b4a594",
|
||||
"treasure": "f8a6d58e20f54e2ca899485055df1ce4"
|
||||
},
|
||||
"background": "ef89906dd9844fcbaafc0b2313812eca"
|
||||
}
|
||||
14
data/potions.json
Normal file
14
data/potions.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"potions": {
|
||||
"redPotion": { "name": "빨간 포션", "desc": "HP 20 회복", "effect": "heal", "value": 20, "icon": "393e2a0d8da544899eaa8b22c97f832b" },
|
||||
"firebomb": { "name": "화염병", "desc": "적에게 피해 20", "effect": "damage", "value": 20, "icon": "7ddb464c2574456289a4eb72ce86f193" },
|
||||
"warriorElixir": { "name": "전사의 물약", "desc": "힘 +2", "effect": "strength", "value": 2, "icon": "7cfbd410581e4073815daaf5f3e6c72f" },
|
||||
"guardPotion": { "name": "수호의 물약", "desc": "방어도 +12", "effect": "block", "value": 12, "icon": "8f8402dfa0f746e18bf606ed74302c0a" },
|
||||
"manaElixir": { "name": "마나 엘릭서", "desc": "에너지 +2", "effect": "energy", "value": 2, "icon": "ec2778c366f6477ab0f8e7f06bcd73f4" },
|
||||
"cursedVial": { "name": "저주의 병", "desc": "적에게 약화 3", "effect": "weak", "value": 3, "icon": "a9a2763fdb6849dcba3028c737487680" }
|
||||
},
|
||||
"dropChance": 0.4,
|
||||
"baseSlots": 3,
|
||||
"beltSlots": 5,
|
||||
"shopPrice": 20
|
||||
}
|
||||
30
data/relics.json
Normal file
30
data/relics.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"relics": {
|
||||
"ironHeart": { "name": "강철 심장", "desc": "전투 시작 시 방어도 +6", "hook": "combatStart", "effect": "block", "value": 6, "icon": "e555b3a62f3c49dbb2c53784e6bd481f" },
|
||||
"energyCore": { "name": "에너지 코어", "desc": "턴 시작 시 에너지 +1", "hook": "turnStart", "effect": "energy", "value": 1, "icon": "a41014f28b47434ab9f49ef104523862" },
|
||||
"vampire": { "name": "흡혈 송곳니", "desc": "공격 카드 사용 시 HP +1", "hook": "cardPlayed", "effect": "healOnAttack", "value": 1, "icon": "ed64cde7e6c44b9e99502847e54f04e9" },
|
||||
"goldIdol": { "name": "황금 우상", "desc": "전투 승리 시 메소 +10", "hook": "combatReward", "effect": "gold", "value": 10, "icon": "03bb05c92b8f45edb0f3dad2e118fd5a" },
|
||||
"potionBelt": { "name": "장인의 벨트", "desc": "물약 슬롯이 5칸으로 늘어난다", "hook": "passive", "effect": "potionSlots", "value": 5, "icon": "36725b4566ac40d4902e2ab2113c2096" },
|
||||
"burningBlood": { "name": "자쿰의 투구", "desc": "전투 승리 시 HP 6 회복", "hook": "combatEnd", "effect": "healOnWin", "value": 6, "icon": "07f994825ce34131b419d43e890c878d" },
|
||||
"vajra": { "name": "미스릴 해머", "desc": "전투 시작 시 힘 +1", "hook": "combatStart", "effect": "strength", "value": 1, "icon": "59d2579d46dc41d590a9e6b141ad458b" },
|
||||
"anchor": { "name": "메이플 실드", "desc": "첫 턴 방어도 +10", "hook": "combatStart", "effect": "block", "value": 10, "icon": "6349413e08cc49848862591863d056a0" },
|
||||
"bagOfPrep": { "name": "모험가의 배낭", "desc": "첫 턴 드로우 +2", "hook": "combatStart", "effect": "draw", "value": 2, "icon": "77b240cb8af245b4801a714380267ae9" },
|
||||
"bloodVial": { "name": "피의 목걸이", "desc": "전투 시작 시 HP 2 회복", "hook": "combatStart", "effect": "heal", "value": 2, "icon": "c782e949506a42c49eb139c7e65527d7" },
|
||||
"bronzeScales": { "name": "브론즈 체인메일", "desc": "피격 시 공격자에게 3 반사", "hook": "onPlayerDamaged", "effect": "thorns", "value": 3, "icon": "87272346b145412391622cf803f888d1" },
|
||||
"strawberry": { "name": "건강의 반지", "desc": "획득 시 최대 HP +7", "hook": "passive", "effect": "maxHp", "value": 7, "icon": "58f643e29c354c2783a5ce9a72ec155c" },
|
||||
"penNib": { "name": "황금 깃펜", "desc": "10번째 공격마다 피해 2배", "hook": "attackCalc", "effect": "penNib", "value": 10, "icon": "4d38d721cc064d14b31b9e9a92754139" },
|
||||
"boot": { "name": "브론즈 부츠", "desc": "5 미만 공격 피해가 5로", "hook": "attackCalc", "effect": "boot", "value": 5, "icon": "d572b3aa4dac4162aa0d9e551b055dce" },
|
||||
"akabeko": { "name": "황소 투구", "desc": "전투 첫 공격 피해 +8", "hook": "attackCalc", "effect": "akabeko", "value": 8, "icon": "eb3330a6e2274eff958639f8792119d3" },
|
||||
"centennialPuzzle": { "name": "백년의 부적", "desc": "전투 첫 피격 시 드로우 3", "hook": "onPlayerDamaged", "effect": "firstLossDraw", "value": 3, "icon": "cfe5ed6556b944fc83ab58b774bb2b73" },
|
||||
"meatOnBone": { "name": "고기 망치", "desc": "승리 시 HP 50% 이하면 12 회복", "hook": "combatEnd", "effect": "healIfLow", "value": 12, "icon": "a93e8e87f184411c98c96b877d9f8b10" },
|
||||
"selfFormingClay": { "name": "점토 갑옷", "desc": "피해를 받으면 다음 턴 방어 +3", "hook": "onPlayerDamaged", "effect": "clayBlock", "value": 3, "icon": "bb446793c5204d5db7d33563fe79f648" },
|
||||
"championBelt": { "name": "챔피언 벨트", "desc": "취약 부여 시 약화 1 추가", "hook": "cardDebuff", "effect": "vulnAddsWeak", "value": 1, "icon": "7ca8c63026034113a561d6adf679fed2" }
|
||||
},
|
||||
"startingRelic": "ironHeart",
|
||||
"relicPool": [
|
||||
"energyCore", "vampire", "goldIdol",
|
||||
"potionBelt", "burningBlood", "vajra", "anchor", "bagOfPrep", "bloodVial",
|
||||
"bronzeScales", "strawberry", "penNib", "boot", "akabeko",
|
||||
"centennialPuzzle", "meatOnBone", "selfFormingClay", "championBelt"
|
||||
]
|
||||
}
|
||||
7
docs/attack-poison.md
Normal file
7
docs/attack-poison.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# 공격 중독
|
||||
|
||||
`attackPoison`은 전투 동안 유지되는 공용 카드 효과 필드입니다.
|
||||
|
||||
- 공격 카드가 실제 체력 피해를 주면 대상에게 지정된 수치만큼 중독을 부여합니다.
|
||||
- 광역 공격은 피해를 받은 각 적에게 중독을 부여합니다.
|
||||
- 현재 Thief Master와 Hermit의 `베놈`이 이 효과를 사용합니다.
|
||||
72
docs/bandit-card-audit.md
Normal file
72
docs/bandit-card-audit.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# 도적 카드 구성 및 밸런스 기록
|
||||
|
||||
도적 계보의 카드 역할, 직업 이동 금지 대상, 공용 효과 필드를 정리합니다.
|
||||
|
||||
## 직업별 컨셉
|
||||
|
||||
- `rogue`: 시작 카드, 1차 스킬, 기초 공격·회피·방어
|
||||
- `thief`: 단검 난타, 교활, 버리기, 중독의 시작
|
||||
- `thiefmaster`: 교활·버리기 연계 완성, 광역 난타, 중독 증폭
|
||||
- `assassin`: 표창 생성, 표창 연속 공격, 표창 비용·피해 보조
|
||||
- `hermit`: 표창 보존·광역화·지속 생성 등 표창 빌드 완성
|
||||
|
||||
Rogue 단계에서도 분기 방향을 미리 경험할 수 있도록 약한 입문 카드를 유지합니다.
|
||||
|
||||
- 중독: `PoisonedStab`
|
||||
- 표창: `LeadingStrike`
|
||||
- 교활: `Untouchable`
|
||||
|
||||
## 스킬 카드 고정
|
||||
|
||||
실제 직업 스킬을 바탕으로 추가한 아래 카드는 다른 차수나 계열로 이동하지 않습니다.
|
||||
|
||||
- Rogue: `DoubleStab`, `LuckySeven`, `Haste`, `DarkSight`, `FlashJump`, `NimbleBody`
|
||||
- Thief: `SavageBlow` 포함 9장
|
||||
- Thief Master: `EdgeCarnival` 포함 11장
|
||||
- Assassin: `ShurikenBurst` 포함 10장
|
||||
- Hermit: `TripleThrow` 포함 9장
|
||||
|
||||
나머지 비스킬 카드는 컨셉에 맞춰 상위 직업으로 이동할 수 있습니다. 상위 직업은 하위 직업 카드를 함께 사용하므로, 이동은 해당 분기의 보상 풀을 제한하는 역할을 합니다.
|
||||
|
||||
## 공용 효과 필드
|
||||
|
||||
- `poison`, `innate`, `playableWhenDrawPileEmpty`
|
||||
- `retain`, `sly`, `discard`, `discardAll`, `addShiv`, `addShivPerDiscard`, `turnStartShiv`, `retainOne`
|
||||
- `turnStartDraw`, `turnStartDiscard`
|
||||
- `nextTurnBlock`, `nextTurnDraw`, `nextTurnKeepBlock`, `nextTurnAttackMultiplier`, `nextTurnCopies`, `nextTurnSelectHandCard`
|
||||
- `damagePerOtherHandCard`, `damagePerAttackPlayedThisTurn`, `damagePerDiscardedThisTurn`, `damagePerSkillInHand`, `otherHandAtLeast`, `bonusHitsWhenOtherHandAtLeast`
|
||||
- `gainEnergy`, `drawUntilHandSize`, `drawPerDiscarded`, `cardPlayedBlock`, `blockGainMultiplier`, `blockPerDamageDealtThisTurn`, `nextSkillCostZero`, `skillCostReductionThisTurn`
|
||||
- `firstCardDamageBonus`
|
||||
- `drawDamage`, `drawPoison`, `shivDamageBonus`, `firstShivDamageBonus`, `shivRetain`, `shivAoe`, `attackDamageVsWeakMultiplier`, `poisonHits`, `poisonRandomTargets`, `skillSlyOnPlay`, `extraPoisonTicks`, `poisonApplicationBurstEvery`, `poisonApplicationBurstDamage`
|
||||
|
||||
## 중복 제거 및 보정
|
||||
|
||||
- 삭제: `Mirage`, `Accuracy`, `PhantomBlades`, `Adrenaline`, `Afterimage`, `Accelerant`, `Envenom`, `Tracking`
|
||||
- 이유: 상위 직업 스킬 카드와 효과가 같거나, 비용 대비 열세라 별도 선택지가 되지 못함
|
||||
- `Anticipate`: 턴 종료 시 얻은 민첩을 잃도록 실제 효과와 설명을 일치시킴
|
||||
- `Backstab`, `Assassinate`, `TheHunt`, `PiercingWail`: 설명에 있던 소멸을 실제 필드에 반영
|
||||
- 2차 지급: Thief `DaggerAcceleration`, Assassin `JavelinAcceleration`
|
||||
- 3차 지급: Thief Master `Venom`, Hermit `SpiritJavelin`
|
||||
|
||||
## 카드 효율 검증
|
||||
|
||||
`node tools/balance/card-efficiency.mjs --runs 1000`으로 도적 계열 카드 전체를 검증합니다.
|
||||
|
||||
- 각 직업의 기준 덱에서 같은 종류의 카드 한 장을 교체하고 동일 시드로 반복 전투합니다.
|
||||
- 승률, 승리 시 체력, 전투 턴을 합친 점수를 같은 직업·희귀도 중앙값과 비교합니다.
|
||||
- 0코스트 에너지 생성, 재사용 가능한 영구 능력치, 저비용 2배 증폭처럼 자동 플레이가 놓치기 쉬운 구조도 별도로 검사합니다.
|
||||
- 교활, 조건부 중독, 카드 보존처럼 플레이 순서 의존성이 큰 효과는 자동 시뮬레이션 하위권만으로 상향하지 않습니다.
|
||||
|
||||
2026-07-01 검증 결과 구조적 위험은 0장입니다. 주요 조정은 `독맥 터뜨리기`, `메아리 칼자국`, `소리 없는 제압`, `그리드`, `그림자 속도전`, `스틸`, 두 계열의 `피지컬 트레이닝`, `마크 오브 어쌔신`, `자벨린 액셀레이션`, `비장의 패`에 반영했습니다.
|
||||
|
||||
비스킬 카드 78장의 메이플풍 표시 이름은 `docs/rogue-card-names.md`에서 관리합니다. 메이플 원본 스킬 카드 45장의 이름은 변경하지 않습니다.
|
||||
|
||||
## 5섹션 캠페인 검증
|
||||
|
||||
`node tools/balance/rogue-campaign.mjs --runs 5000 --reward-min 5`로 전체 런을 검증합니다.
|
||||
|
||||
- 섹션마다 일반전 4회, 엘리트 1회, 보스 1회를 진행합니다.
|
||||
- 1섹션은 Rogue, 2섹션은 2차 직업, 3~5섹션은 3차 직업 카드 풀을 사용합니다.
|
||||
- 실제 카드 보상 확률, 전직 지급 카드, 시작·획득 유물, 체력 유지와 휴식 회복을 반영합니다.
|
||||
- 몬스터 배율은 `1.00 → 1.075 → 1.15 → 1.30 → 1.45`이며 런타임과 시뮬레이터가 같은 공용 상수를 사용합니다.
|
||||
- 5,000회 결과: Thief Master 완주 2.9%, Hermit 완주 3.6%. 자동 플레이와 일부 공격형 유물 미구현을 감안한 보수적 결과입니다.
|
||||
164
docs/card-effect-fields.md
Normal file
164
docs/card-effect-fields.md
Normal file
@@ -0,0 +1,164 @@
|
||||
# Card Effect Fields
|
||||
|
||||
This file tracks the shared data fields used by `data/cards.json`.
|
||||
The goal is to keep card behavior reusable instead of hardcoding one-off card names.
|
||||
|
||||
## Damage
|
||||
|
||||
- `damage`: base attack damage
|
||||
- `damageFromCurrentBlock`: add current block times this value to attack damage
|
||||
- `damageNameMatch`: substring to match against owned card names
|
||||
- `damagePerOwnedNameMatch`: bonus damage per owned card whose name matches `damageNameMatch`
|
||||
- `damagePerOtherHandCard`: bonus damage per other card in hand
|
||||
- `damagePerAttackPlayedThisTurn`: bonus damage per attack played this turn
|
||||
- `damagePerDiscardedThisTurn`: bonus damage per card discarded this turn
|
||||
- `damagePerSkillInHand`: bonus damage per skill card in hand
|
||||
- `damagePerCardDrawnThisCombat`: bonus damage per card drawn this combat
|
||||
- `damagePerCombo`: bonus base damage per current Combo
|
||||
- `damagePerHolyCharge`: bonus base damage per current Holy Charge
|
||||
- `attackDamagePerCombo`: Power field that adds base damage per current Combo to all Attacks
|
||||
- `attackPlayedDamage`: Power field that deals extra damage after an Attack card is played
|
||||
- `damagePerTurn`: damage applied at turn start
|
||||
- `cardPlayedDamage`: damage when the card is played
|
||||
- `cardPlayedRandomDamage`: random damage when the card is played
|
||||
- `drawOnExhaust`: draw when a card is exhausted
|
||||
- `rewardOnKill`: gain bonus reward screens when the card kills
|
||||
- `maxHpOnKill`: gain max HP when the attack kills
|
||||
- `drawNameMatchAutoPlay`: auto-play drawn cards whose names contain this substring
|
||||
- `randomTargetEachHit`: choose a random alive enemy for each hit
|
||||
- `repeatOnKill`: repeat the attack when it kills at least one enemy
|
||||
- `firstCardDamageBonus`: bonus damage for the first card played this turn
|
||||
- `drawDamage`: damage dealt when a card is drawn
|
||||
- `blockPerDamageDealtThisTurn`: gain block equal to damage dealt this turn
|
||||
- `shivDamageBonus`: bonus damage for all Shivs
|
||||
- `firstShivDamageBonus`: bonus damage for the first Shiv each turn
|
||||
- `attackDamageVsWeakMultiplier`: multiplier when the attack hits Weak targets
|
||||
- `useAllEnergy`: treat the card as spending all available energy
|
||||
- `xDamagePerEnergy`: scale attack damage by energy spent
|
||||
- `xWeakPerEnergy`: scale Weak applied by energy spent
|
||||
|
||||
## Block and utility
|
||||
|
||||
- `block`: gain block
|
||||
- `cardPlayedBlock`: gain block whenever a card is played
|
||||
- `blockGainMultiplier`: multiplier for block gained this turn
|
||||
- `hits`: multi-hit count
|
||||
- `aoe`: hit all enemies
|
||||
- `pierce`: ignore block
|
||||
- `draw`: draw cards immediately
|
||||
- `drawUntilHandSize`: draw until hand reaches a target size
|
||||
- `drawSkillBlock`: gain block for each Skill drawn
|
||||
- `drawPoison`: apply poison when a card is drawn
|
||||
- `exhaustHandNonAttack`: exhaust every non-Attack card in hand
|
||||
- `exhaustHandAll`: exhaust every card in hand
|
||||
- `drawPerExhausted`: draw cards equal to exhausted cards
|
||||
- `blockPerExhaustedCard`: gain block for each card exhausted by the current effect
|
||||
- `addRandomCardCount`: add random cards to hand
|
||||
- `addRandomCardPerExhausted`: add random cards equal to exhausted cards
|
||||
- `addRandomCardKind`: filter random added cards by kind
|
||||
- `addRandomCardSameClass`: restrict random added cards to the source card class
|
||||
- `addedCardsCostZeroThisTurn`: cards added by this effect cost 0 this turn
|
||||
- `playTopDrawPileCount`: play cards from the top of the draw pile
|
||||
- `playTopDrawPileCountPerEnergy`: play cards from the top of the draw pile per energy spent
|
||||
- `handCostZeroThisTurn`: make hand cards cost 0 this turn
|
||||
- `drawDisabledThisTurn`: disable draw for the rest of the turn
|
||||
- `heal`: heal immediately
|
||||
- `gainEnergy`: gain energy immediately
|
||||
- `strength`: gain Strength
|
||||
- `dex`: gain Dexterity
|
||||
- `thorns`: gain Thorns
|
||||
- `selfVuln`: apply Vulnerable to self
|
||||
- `extraPoisonTicks`: add extra poison ticks at enemy turn start
|
||||
- `comboGain`: gain Combo when the card resolves; Attack cards gain it after dealing damage
|
||||
- `comboOnAttack`: Power field that gains Combo whenever an Attack card is played
|
||||
- `comboMax`: Power field that raises the maximum Combo above the default 5
|
||||
- `attackWeak`: Power field that applies Weak after an Attack card is played
|
||||
- `removePlayerDebuffs`: remove player Weak and Vulnerable
|
||||
- `holyForce`: marks a card as Holy Force for Holy Charge Power triggers
|
||||
- `holyChargeGain`: gain Holy Charge directly
|
||||
- `holyChargeOnHolyForce`: Power field that gains Holy Charge after a Holy Force card
|
||||
- `holyChargeMax`: Power field that raises the maximum Holy Charge above the default 3
|
||||
- `blockPerHolyCharge`: gain additional block per current Holy Charge
|
||||
- `healPerHolyCharge`: heal additional HP per current Holy Charge
|
||||
- `holyChargeSpendAll`: consume all Holy Charge after resolving the card
|
||||
- `damageTakenReduction`: Power field that reduces incoming damage; total reduction is capped at 75%
|
||||
- `blockOnDamaged`: Power field that grants block after taking HP damage
|
||||
- `strengthOnDamagedOnce`: Power field that grants Strength on the first HP damage each combat
|
||||
|
||||
## Status
|
||||
|
||||
- `weak`: apply Weak
|
||||
- `vuln`: apply Vulnerable
|
||||
- `poison`: apply Poison
|
||||
- `poisonHits`: apply poison multiple times
|
||||
- `poisonRandomTargets`: spread poison applications across random alive enemies
|
||||
- `poisonIfTargetPoisoned`: apply poison only if the target is already poisoned
|
||||
- `poisonApplicationBurstEvery`: trigger a burst every N poison applications
|
||||
- `poisonApplicationBurstDamage`: burst damage when the poison application threshold is reached
|
||||
- `skillSlyOnPlay`: make a played Skill card count as sly when it is later discarded
|
||||
- `turnHandSlyCount`: mark up to N other Skill cards in hand as sly for this turn
|
||||
- `attackPoison`: apply poison when attack damage is dealt
|
||||
- `intangible`: reduce incoming damage to 1 for the duration
|
||||
- `endTurnDexLoss`: lose Dexterity at end of turn
|
||||
- `combatCostReductionOnPlay`: reduce this card's cost each time it is played this combat
|
||||
- `enemyStrengthLossThisTurn`: reduce enemy Strength for the rest of the turn
|
||||
- `affectsAllEnemies`: apply the card's debuffs to every alive enemy
|
||||
- `removeEnemyBlock`: clear enemy block when the card resolves
|
||||
- `removeEnemyArtifact`: consume enemy Artifact when the card resolves
|
||||
|
||||
`poison` deals damage at enemy turn start and then decreases by 1.
|
||||
|
||||
## Shivs and discard
|
||||
|
||||
- `discard`: discard a chosen number of cards from hand
|
||||
- `discardAll`: discard the whole hand
|
||||
- `drawPerDiscarded`: draw one extra card per discarded card
|
||||
- `addShiv`: create Shiv cards
|
||||
- `addShivPerDiscard`: create one Shiv per discarded card
|
||||
- `shivRetain`: Shiv cards are retained at end of turn
|
||||
- `shivAoe`: Shiv cards hit all enemies for the turn
|
||||
- `sly`: trigger on discard
|
||||
- `retain`: keep the card at end of turn
|
||||
|
||||
## Powers and turn effects
|
||||
|
||||
- `powerEffect: "strengthPerTurn"`
|
||||
- `powerEffect: "energyPerTurn"`
|
||||
- `powerEffect: "blockPerTurn"`
|
||||
- `powerEffect: "poisonPerTurn"`
|
||||
- `powerEffect: "damagePerTurn"`
|
||||
- `powerEffect: "healPerTurn"`
|
||||
- `powerEffect: "retainOne"`
|
||||
- `powerEffect: "keepBlock"`
|
||||
- `turnStartShiv`: create Shivs at turn start
|
||||
- `turnStartDraw`: draw cards at turn start
|
||||
- `turnStartDiscard`: discard cards at turn start
|
||||
|
||||
## Next turn planning
|
||||
|
||||
- `nextTurnBlock`: gain block next turn
|
||||
- `nextTurnDraw`: draw extra cards next turn
|
||||
- `nextTurnKeepBlock`: keep block next turn
|
||||
- `nextTurnAttackMultiplier`: attack multiplier next turn
|
||||
- `nextTurnCopies`: copy a chosen card next turn
|
||||
- `nextTurnSelectHandCard`: choose a card from the current hand for next turn copies
|
||||
- `nextTurnSelectPrompt`: prompt text for selection UI
|
||||
- `nextSkillRepeatCount`: repeat the next Skill's effect
|
||||
- `nextSkillCostZero`: make the next Skill cost 0
|
||||
- `skillCostReductionThisTurn`: reduce Skill costs this turn
|
||||
|
||||
## Misc
|
||||
|
||||
- `innate`: place the card in the opening hand
|
||||
- `playableWhenDrawPileEmpty`: only playable when the draw pile is empty
|
||||
- `exhaust`: exhaust after use
|
||||
- `unplayable`: cannot be played
|
||||
- `curse`: curse card
|
||||
- `token`: token card
|
||||
- `endTurnDamage`: damage if the card remains in hand at end of turn
|
||||
|
||||
## Rules
|
||||
|
||||
- Prefer shared fields over card-specific branches.
|
||||
- Reuse the same field name for the same behavior.
|
||||
- Add a new shared field before adding more special-case card logic.
|
||||
5
docs/card-play-damage.md
Normal file
5
docs/card-play-damage.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# 카드 사용 시 피해
|
||||
|
||||
`cardPlayedDamage`는 카드를 사용할 때마다 현재 대상에게 체력을 직접 깎는 공용 효과입니다. 방어도는 무시하고, 같은 필드를 다른 카드에도 그대로 붙여 재사용할 수 있습니다.
|
||||
|
||||
`cardPlayedRandomDamage`는 같은 시점에 살아 있는 적 하나를 랜덤으로 골라 체력을 직접 깎습니다. `Strangle`과 `SerpentForm` 같은 카드가 이 계열을 씁니다.
|
||||
39
docs/codex-workflow.md
Normal file
39
docs/codex-workflow.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Codex Workflow
|
||||
|
||||
이 저장소에서 작업할 때는 토큰과 변경량을 아끼는 쪽을 기본으로 둔다.
|
||||
|
||||
## 작업 원칙
|
||||
|
||||
- 이미 확인한 사실은 다시 읽지 않는다.
|
||||
- 같은 내용을 통째로 지우고 새로 쓰지 않는다.
|
||||
- 수정은 가능한 한 `apply_patch`로 섹션 단위만 한다.
|
||||
- 문서는 전체 재작성보다 부분 수정으로 유지한다.
|
||||
- 카드 구현은 한 번에 하나씩, 공용 필드 우선으로 넣는다.
|
||||
- 새 기능은 `데이터 1곳 + 런타임 1곳 + 테스트 1곳` 순서로 맞춘다.
|
||||
|
||||
## 읽기 원칙
|
||||
|
||||
- 파일은 필요한 것만 읽는다.
|
||||
- 비슷한 파일은 병렬로 한 번에 확인한다.
|
||||
- 같은 정보를 여러 번 요약하지 않는다.
|
||||
|
||||
## 쓰기 원칙
|
||||
|
||||
- 공용으로 표현 가능한 효과는 카드 전용 분기로 만들지 않는다.
|
||||
- 같은 의미의 효과는 같은 필드 이름을 쓴다.
|
||||
- 문서는 카드별 상태표와 공용 필드 사전을 분리해서 유지한다.
|
||||
- 카드 `kind`는 효과와 맞춘다 — 데미지 카드=`Attack`, block·유틸만 있으면=`Skill`, 지속효과=`Power`(`powerEffect` 또는 power 필드 필수). 안 맞으면 사용 불가/死카드가 된다(Power 분기는 damage/aoe 무시, Attack은 몬스터 드롭 라우팅).
|
||||
- 새 효과 필드는 Lua(`cb/*.mjs`)와 JS 미러(`tools/balance/sim-balance.mjs`) 양쪽에 구현한다(한쪽만 = 게임↔시뮬 드리프트).
|
||||
|
||||
## 응답 원칙
|
||||
|
||||
- 중간 보고는 짧게 한다.
|
||||
- 바뀐 점과 남은 점만 말한다.
|
||||
- 불필요한 재설명은 줄인다.
|
||||
|
||||
## 검증·통합 원칙
|
||||
|
||||
- 카드/cb 변경 후 검증 스위트를 돌린다: `node tools/verify/cardkinds.mjs`(kind↔효과)·`cbprops.mjs`(미선언 `self.X` 필드)·`cbgap.mjs`(UI 경로) + `node --test tools/balance/sim-balance.test.mjs`(이중구현 미러). 이상 0을 확인한 뒤 산출물을 갱신한다.
|
||||
- 작업 브랜치에 `main`을 머지했다가 충돌·문제가 나도 그 머지 커밋을 통째로 `git revert`하지 않는다 — main에 먼저 들어간 타인 작업이 collateral로 사라진다(2026-06-30 `#98/#99`가 `#96` 11개 수정을 이렇게 날린 사고). 소스 충돌만 해소하고 산출물(codeblock 등)은 재생성한다.
|
||||
- 하네스 규칙의 최종 권위는 `RULES.md`(§1 산출물 읽기/수정 금지·§4 git/PR·§6 이중구현 동기화·§9 카드 kind)이고, codex 전용 하드룰은 `docs/codex-working-rules.md`다. 작업 전 둘 다 따른다.
|
||||
|
||||
11
docs/codex-working-rules.md
Normal file
11
docs/codex-working-rules.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Codex Working Rules
|
||||
|
||||
1. 사용자가 특정 클래스만 수정하라고 했으면 그 클래스 외의 데이터, 설명문, 밸런스 문구는 건드리지 않는다.
|
||||
2. 기존 한글 텍스트는 요청이 없으면 임의로 바꾸지 않는다.
|
||||
3. 전직 구조를 바꿀 때는 실제 직업명만 사용한다. 임의의 내부 분류명이나 새 직업명을 사용자-facing 구조에 추가하지 않는다.
|
||||
4. 대량 치환 전에 수정 대상 파일과 범위를 먼저 확인하고, 원본 문자열이 깨진 상태면 치환 작업을 진행하지 않는다.
|
||||
5. 생성기 파일을 크게 수정할 때는 `node --check`와 생성기 실행으로 문법을 먼저 검증한 뒤 산출물을 갱신한다.
|
||||
6. 작업 브랜치에 `main`을 머지했다가 충돌·문제가 나도 **그 머지 커밋을 통째로 `git revert`하지 않는다** — main에 먼저 들어간 타인 작업이 collateral로 사라진다(2026-06-30 `#98/#99`가 `#96`의 버그수정 11개를 이렇게 전부 날림). 소스 충돌만 해소하고 산출물(codeblock 등)은 재생성한다. (RULES §4)
|
||||
7. 카드 `kind`는 효과와 일치시킨다 — 데미지 카드=`Attack`, block·유틸만 있으면=`Skill`, 지속효과=`Power`(`powerEffect` 또는 power 필드 필수). 안 맞으면 사용 불가/死카드가 된다(2026-06-30 아이언 바디=Attack인데 block만, 분노=Power인데 damage만 → 둘 다 먹통). 카드 추가/수정 후 `node tools/verify/cardkinds.mjs`로 검증(이상 0 = exit 0). (RULES §9)
|
||||
8. 카드/cb 변경 후 검증 스위트를 돌린다: `node tools/verify/cardkinds.mjs`(kind↔효과)·`cbprops.mjs`(미선언 `self.X` 필드)·`cbgap.mjs`(UI 경로) + `node --test tools/balance/sim-balance.test.mjs`(이중구현 미러). 새 효과 필드는 Lua(`cb/*.mjs`)와 JS 미러(`tools/balance/sim-balance.mjs`) **양쪽**에 구현(한쪽만 = 게임↔시뮬 드리프트). (RULES §6)
|
||||
9. 하네스 규칙의 권위는 `RULES.md`다 — 작업 전 RULES.md(§1 산출물 읽기/수정 금지·§4 git/PR·§6 이중구현 동기화·§9 카드 kind)를 읽고 따른다.
|
||||
86
docs/deck-concept.md
Normal file
86
docs/deck-concept.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# SlayMaple 덱 컨셉 & 직업 스킬셋
|
||||
|
||||
> SlayMaple 카드 덱의 **직업별 컨셉 · 메이플 스킬셋 · Slay the Spire 2 차용 매칭** 설계 문서.
|
||||
> 원칙: 카드 한 장 = **STS2 메커니즘(뼈대) + 메이플 스킬(외형)**. STS 고유 *표현*(카드명·아트·UI)은 모방 금지, *메커니즘*만 차용(IP 해석 심사 대비).
|
||||
> 수치(데미지·코스트·등급)는 `tools/balance/sim-balance.mjs`로 검증. 본 문서는 *어떤 스킬을 어떤 카드로* 만들지의 설계도.
|
||||
|
||||
기준: 메이플 = **클래식(빅뱅 이전)** 스킬 외형, STS = **Slay the Spire 2**.
|
||||
|
||||
---
|
||||
|
||||
## 직업 ↔ STS2 매칭 요약
|
||||
|
||||
| 직업 | 컨셉 | STS2 차용 |
|
||||
|---|---|---|
|
||||
| ⚔️ 전사 | 단단한 탱커/브루저 | The Ironclad (힘·방어·소멸) |
|
||||
| 🗡️ 도적 | 단검 난사 / 독 | The Silent (표창·독·교활) |
|
||||
| 🔮 법사 | 약체 + 게이지 운용 | The Defect (오브·집중) |
|
||||
|
||||
---
|
||||
|
||||
## ⚔️ 전사 (Warrior) — HP80 · 탱커
|
||||
|
||||
방어를 쌓고 버티다 역공하는 브루저. Ironclad의 두 축을 2차 전직으로 분화.
|
||||
|
||||
### 파이터 — 콤보 브루저형 탱커
|
||||
- **콤보 규칙**: 공격 카드를 **연속으로** 사용하면 콤보가 쌓인다. **방어·파워 등 비공격(Skill/Power) 카드를 사용하면 콤보가 리셋(0)** 된다.
|
||||
- 콤보가 쌓일수록 **데미지 증가 버프(힘 계열)** 를 받는다 → 방어를 포기하고 공격을 몰아칠수록 강해지는 리스크/리워드.
|
||||
- 차용: Ironclad 힘 스택/Demon Form + 콤보. 메이플 외형: 콤보 어택·분노·브랜디시.
|
||||
|
||||
### 페이지 — 방어 축적 → 바디 슬램 카운터
|
||||
- **위협**(전체 적 약화+취약 디버프)로 버티며 **방어도를 축적**(아이언 월 등 + 방어 유지 retain).
|
||||
- **바디 슬램**: 현재 방어도에 비례한 피해로 카운터. 파워 가드(반사) 보조.
|
||||
- 차용: Ironclad 방어 빌드(Barricade+Entrench→Body Slam). 메이플 외형: 위협·아이언 월·파워 가드.
|
||||
|
||||
### 스피어맨 — 유지/리치형
|
||||
- 하이퍼 바디(최대 HP↑)·아이언 월(방어 유지)·창 리치 광역. 공격 스케일(파이터)·방어 카운터(페이지)와 구분되는 지속 탱.
|
||||
|
||||
---
|
||||
|
||||
## 🗡️ 도적 (Thief) — HP70 · 단검/독
|
||||
|
||||
Slay the Spire *Silent* 차용. **형(codex)이 Silent 88장 완역 포트 + 스킬 아이콘 적용 완료.** 3대 아키타입:
|
||||
|
||||
- **표창(Shiv) 난사**: 0코스트 표창 토큰 대량 생성 → 공격마다 연계. (정밀=표창 피해↑, 칼날 부채=표창 전체화)
|
||||
- **독(Poison)**: 중독 중첩 → 매턴 틱뎀. (유독 가스·발병·촉진제·독 바르기)
|
||||
- **교활(Sly)·버림(discard)**: 버려질 때 무료 발동, 얇은 덱 빠른 순환.
|
||||
|
||||
### 2차 갈래
|
||||
- **어쌔신** — 표창 난사 + 크리 / 흡혈(드레인) 중심.
|
||||
- **시프** — 단검 난타(새비지 블로우 = 다단히트) + 독 / 버림 중심.
|
||||
|
||||
> 남은 작업: 카드명이 STS 직역(무력화·배신·아드레날린 등) → **어쌔신/시프 메이플 스킬명으로 재서사** + 멀티플레이어 전제 카드(측면 공격·비열함·추적) 싱글 출품용 정리.
|
||||
|
||||
---
|
||||
|
||||
## 🔮 법사 (Magician) — HP70 · 약체/게이지
|
||||
|
||||
몸은 약하나 **게이지(오브) 운용**으로 다중 공격·화력 집중. **오브 메커니즘은 위자드(불/독·썬/콜)에만 적용**, 클레릭은 별도 보조 컨셉.
|
||||
|
||||
### 위자드(불/독) — 독 + 불 시너지
|
||||
- **독을 묻히는 스킬**(포이즌 브레스 등)으로 대상에 독을 부여(독뎀 = DoT).
|
||||
- **독이 묻은 적에게 불 카드(파이어 애로우 등)를 쓰면 추가 데미지** — *독뎀 상수* 보너스(독 스택/상수 비례).
|
||||
- 즉 "독 깔기 → 불로 폭발"의 2단 콤보. 불·독 오브로 운용.
|
||||
|
||||
### 위자드(썬/콜) — 오브 운용(썬더 다중 / 콜드 빙결)
|
||||
- **오브로 썬더·콜드를 보유**. **썬더 = 다중 공격 특화**(AoE·다단). **콜드 = 빙결 부여**(빙결 = *취약과 동일 효과* 를 데미지와 함께).
|
||||
- **오브를 사용하는 만큼 오브를 획득하거나 다중 소모**하는 방식 — 오브 수급/소비 운용이 핵심.
|
||||
|
||||
### 클레릭 — 보조(회복·버프) · 오브 없음
|
||||
- **회복 스킬**(힐)과 **각종 버프**(블레스 등) 중심의 서포트.
|
||||
- **언데드 계열 몬스터에게는 힐로 공격** 가능 — 보조 힐러 정체성.
|
||||
|
||||
> ⚠️ 위자드 오브/게이지·전사 콤보 스택·바디 슬램·독뎀 시너지는 **신규 메커니즘** — `tools/deck/gen-slaydeck.mjs`(전투 규칙) + `tools/balance/sim-balance.mjs`(JS 미러) 양쪽 구현·동기화 필요.
|
||||
|
||||
---
|
||||
|
||||
## 차용 경계 (IP 심사 대비)
|
||||
|
||||
- 차용 OK = **메커니즘**(콤보 스택·방어→피해 전환·독+불 시너지·오브 게이지·빙결=취약 등 시스템).
|
||||
- 모방 금지 = STS 고유 **표현**(카드명·아트·UI 직접 사용).
|
||||
- 만점 루트 = STS2 메커니즘을 **메이플 스킬·외형으로 완전 재서사화**.
|
||||
|
||||
## 참고
|
||||
|
||||
- 카드 데이터 단일 소스: `data/cards.json` (현 122장: 전사 18·마법사 14·도적 88 + Shiv·저주)
|
||||
- 메이플 스킬 외형 매핑·STS2 캐릭터 상세는 박재오 개인 위키 `프로젝트-메이플-덱빌딩-스킬구성` / `프로젝트-메이플-STS2-차용-덱컨셉` 참조.
|
||||
8
docs/draw-count.md
Normal file
8
docs/draw-count.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# 전투 드로우 누적
|
||||
|
||||
`damagePerCardDrawnThisCombat`은 이번 전투 동안 실제로 뽑힌 카드 수를 기준으로 공격력을 올리는 공용 필드입니다.
|
||||
|
||||
적용 예시:
|
||||
|
||||
- `Murder`: 이번 전투 동안 뽑은 카드 1장당 피해량이 1 증가
|
||||
|
||||
22
docs/draw-skill-block.md
Normal file
22
docs/draw-skill-block.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# 드로우 연동 효과
|
||||
|
||||
드로우 결과를 받아 후속 효과를 처리하는 공용 패턴을 정리합니다.
|
||||
|
||||
## 현재 구현
|
||||
|
||||
- `draw`: 카드를 뽑음
|
||||
- `drawUntilHandSize`: 손패가 지정 수치가 될 때까지 뽑음
|
||||
- `drawSkillBlock`: 이번 카드로 뽑힌 카드 중 스킬 카드마다 방어도를 얻음
|
||||
|
||||
## 동작 방식
|
||||
|
||||
- 드로우 함수는 이번에 뽑힌 카드 ID 목록을 반환합니다.
|
||||
- 카드 효과는 그 목록을 보고 조건을 판정합니다.
|
||||
- 그래서 `EscapePlan` 같은 카드뿐 아니라, 나중에 같은 규칙이 필요한 카드에도 같은 필드를 붙이면 됩니다.
|
||||
|
||||
## 예시
|
||||
|
||||
- `EscapePlan`
|
||||
- `draw = 1`
|
||||
- `drawSkillBlock = 3`
|
||||
|
||||
5
docs/intangible.md
Normal file
5
docs/intangible.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# 불가침
|
||||
|
||||
`intangible`는 카드를 사용할 때 플레이어에게 불가침 수치를 부여하는 공용 필드입니다. 불가침이 남아 있는 동안 받는 피해는 1로 줄어들고, 턴이 끝날 때 1씩 감소합니다.
|
||||
|
||||
`endTurnDexLoss`는 그 카드가 활성화된 동안 매 턴 종료 시 민첩을 잃게 만드는 공용 필드입니다. `WraithForm` 같은 카드가 이 조합을 사용합니다.
|
||||
12
docs/next-skill-repeat.md
Normal file
12
docs/next-skill-repeat.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Next Skill Repeat
|
||||
|
||||
`nextSkillRepeatCount`는 다음에 사용하는 스킬 카드의 효과를 추가 횟수만큼 다시 적용하는 공용 필드입니다.
|
||||
|
||||
현재 구현은 카드가 발동할 때 이 수치를 전역 상태에 누적해 두고, 다음 스킬 카드가 실제로 사용되면 그 효과를 같은 카드에 대해 다시 한 번 이상 적용합니다. 카드 종류는 고정하지 않았기 때문에, 같은 필드를 다른 카드에도 그대로 붙일 수 있습니다.
|
||||
|
||||
예시:
|
||||
|
||||
- `Burst`
|
||||
- `nextSkillRepeatCount = 1`
|
||||
- 다음 스킬을 한 번 더 적용
|
||||
|
||||
5
docs/reward-on-kill.md
Normal file
5
docs/reward-on-kill.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# 처치 보상
|
||||
|
||||
`rewardOnKill`은 해당 카드가 적을 처치했을 때 전투 보상 화면을 한 번 더 이어서 보여주는 공용 필드입니다. 현재 보상 UI는 3장 선택을 유지하고, 보상 화면만 추가로 한 번 더 열립니다.
|
||||
|
||||
`TheHunt`는 이 규칙을 사용합니다. 같은 패턴이 필요한 다른 카드에도 그대로 붙일 수 있습니다.
|
||||
97
docs/rogue-card-names.md
Normal file
97
docs/rogue-card-names.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# 도적 비스킬 카드 이름
|
||||
|
||||
메이플스토리 원본 스킬을 바탕으로 만든 카드 45장은 이름을 고정합니다.
|
||||
아래 78장은 다른 직업의 스킬명을 점유하지 않도록 도적 계열의 독자적인 이름으로 변경했습니다.
|
||||
|
||||
## Rogue
|
||||
|
||||
- `Neutralize`: 무력화 -> 커닝식 견제
|
||||
- `SilentStrike`: 타격 -> 초보 도적의 칼끝
|
||||
- `Survivor`: 생존자 -> 골목길 생존술
|
||||
- `SilentDefend`: 수비 -> 낡은 가죽 방패
|
||||
- `Slice`: 칼질 -> 짧은 단검질
|
||||
- `PoisonedStab`: 독 찌르기 -> 초록 독단검
|
||||
- `SuckerPunch`: 불의의 일격 -> 골목 기습
|
||||
- `LeadingStrike`: 선제 타격 -> 초보 표창 던지기
|
||||
- `Anticipate`: 예측 -> 럭키 예감
|
||||
- `Deflect`: 튕겨내기 -> 단검 쳐내기
|
||||
- `Backflip`: 공중제비 -> 커닝 곡예
|
||||
- `DodgeAndRoll`: 구르기 -> 골목 구르기
|
||||
- `Untouchable`: 범접 불가 -> 연막 속 숨기
|
||||
- `Backstab`: 배신 -> 그림자 등찌르기
|
||||
- `EscapePlan`: 탈출구 -> 비상용 연막탄
|
||||
|
||||
## Thief
|
||||
|
||||
- `DaggerSpray`: 단검 분사 -> 단검비
|
||||
- `DaggerThrow`: 단검 투척 -> 비도 투척
|
||||
- `FollowThrough`: 완수 -> 연달아 찌르기
|
||||
- `FlickFlack`: 재주넘기 -> 커닝 난무
|
||||
- `Prepared`: 예비 -> 비장의 패
|
||||
- `PiercingWail`: 귀를 찢는 비명 -> 골목의 살기
|
||||
- `DeadlyPoison`: 맹독 -> 맹독 조제
|
||||
- `Snakebite`: 뱀 물기 -> 독니 단검
|
||||
- `PreciseCut`: 정밀한 베기 -> 급소 절개
|
||||
- `Finisher`: 마무리 -> 마지막 칼끝
|
||||
- `MementoMori`: 메멘토 모리 -> 사신의 장부
|
||||
- `Strangle`: 목 조르기 -> 그림자 올가미
|
||||
- `Dash`: 돌진 -> 뒷골목 돌파
|
||||
- `CalculatedGamble`: 계산된 도박 -> 메소 건 승부
|
||||
- `Expose`: 들춰내기 -> 약점 들추기
|
||||
- `Acrobatics`: 곡예 -> 지붕 위 곡예
|
||||
- `HandTrick`: 손기술 -> 재빠른 손놀림
|
||||
- `Expertise`: 전문성 -> 노련한 단검술
|
||||
- `BubbleBubble`: 차오르는 독 -> 독액 농축
|
||||
- `Blur`: 흐릿함 -> 흐린 잔영
|
||||
- `LegSweep`: 다리 걸기 -> 발목 베기
|
||||
- `Reflex`: 반사신경 -> 찰나의 반응
|
||||
- `Tactician`: 전략가 -> 골목길 책략
|
||||
- `WellLaidPlans`: 괜찮은 전략 -> 빈틈없는 작전
|
||||
- `Footwork`: 발놀림 -> 사뿐한 발놀림
|
||||
- `NoxiousFumes`: 유독 가스 -> 숨막히는 독연기
|
||||
|
||||
## Thief Master
|
||||
|
||||
- `BouncingFlask`: 탄성 플라스크 -> 통통 독병
|
||||
- `Haze`: 아지랑이 -> 보랏빛 독연기
|
||||
- `Outbreak`: 발병 -> 독맥 터뜨리기
|
||||
- `Speedster`: 스피드스터 -> 그림자 속도전
|
||||
- `GrandFinale`: 대단원의 막 -> 커닝의 대단원
|
||||
- `Assassinate`: 암살 -> 어둠 속 급소
|
||||
- `EchoingSlash`: 메아리 참격 -> 메아리 칼자국
|
||||
- `Murder`: 살해 -> 쌓여가는 살의
|
||||
- `Malaise`: 불쾌 -> 기운 빼는 독
|
||||
- `ShadowStep`: 그림자 걸음 -> 그림자 발자국
|
||||
- `Shadowmeld`: 그림자 은신 -> 연막 속 은신
|
||||
- `CorrosiveWave`: 부식성 파도 -> 부식 독물결
|
||||
- `Burst`: 폭주 -> 연속 술수
|
||||
- `KnifeTrap`: 칼날 함정 -> 숨은 칼날덫
|
||||
- `BulletTime`: 불릿 타임 -> 멈춘 듯한 순간
|
||||
- `Nightmare`: 악몽 -> 검은 꿈
|
||||
- `ToolsOfTheTrade`: 작업 도구 -> 도적의 연장통
|
||||
- `MasterPlanner`: 설계의 대가 -> 작전의 달인
|
||||
- `SerpentForm`: 구렁이의 형상 -> 독사의 몸놀림
|
||||
- `Abrasive`: 연마 -> 거친 숫돌질
|
||||
- `Suppress`: 진압 -> 소리 없는 제압
|
||||
- `WraithForm`: 유령의 형상 -> 유령 같은 몸놀림
|
||||
|
||||
## Assassin
|
||||
|
||||
- `Ricochet`: 도탄 -> 통통 튀는 표창
|
||||
- `BladeDance`: 검무 -> 표창 별무리
|
||||
- `CloakAndDagger`: 망토와 단검 -> 망토 속 별
|
||||
- `Skewer`: 꼬챙이 -> 꿰뚫는 표창
|
||||
- `Flechettes`: 프레췌 -> 표창 셈법
|
||||
- `Pounce`: 덮치기 -> 어둠을 가르는 도약
|
||||
- `Predator`: 천적 -> 표창 끝의 추격
|
||||
- `Pinpoint`: 정밀 사격 -> 한 점 겨냥
|
||||
- `HiddenDaggers`: 숨겨진 표창 -> 숨겨둔 표창
|
||||
- `UpMySleeve`: 비책 -> 소매 속 표창
|
||||
- `InfiniteBlades`: 무한의 검날 -> 끝없는 표창통
|
||||
- `TheHunt`: 사냥 -> 커닝 현상금
|
||||
- `StormOfSteel`: 강철의 폭풍 -> 쇠별 폭풍
|
||||
|
||||
## Hermit
|
||||
|
||||
- `BladeOfInk`: 잉크 칼날 -> 먹빛 표창
|
||||
- `FanOfKnives`: 칼날 부채 -> 사방 표창비
|
||||
@@ -1,42 +1,84 @@
|
||||
# SlayMaple Basic Framework
|
||||
|
||||
This project now has a small deckbuilder roguelike foundation inspired by turn-based card combat games.
|
||||
This project has a working single-combat deckbuilder loop inspired by turn-based
|
||||
card combat games. Card play is wired to real combat state (enemy/player HP,
|
||||
block, enemy intent, win/lose).
|
||||
|
||||
## Components
|
||||
## Current Components (implemented)
|
||||
|
||||
- `SlayCardCatalog`: Defines card data, starter deck composition, reward pool, and card cloning.
|
||||
- `SlayRunState`: Owns persistent run data such as HP, gold, floor, deck, relics, and card rewards.
|
||||
- `SlayCombatManager`: Runs combat turns, draw/discard/exhaust piles, energy, enemy intents, block, damage, victory, and defeat.
|
||||
- `SlayDeckController`: The single combat component, attached to the `/common`
|
||||
entity in `Global/common.gamelogic`. Handles the card-hand UI (draw/discard/
|
||||
reshuffle, energy, card-click play), card effects (damage/block), enemy
|
||||
HP/block/intent, turn flow, and victory/defeat.
|
||||
- `Monster.codeblock`: A separate field-action monster system (HP, hit event,
|
||||
respawn). **Not** part of the card combat.
|
||||
|
||||
All three components are attached to the `/common` entity in `Global/common.gamelogic`.
|
||||
All card/deck/combat artifacts (`ui/DefaultGroup.ui`,
|
||||
`RootDesk/MyDesk/SlayDeckController.codeblock`, `Global/common.gamelogic`) are
|
||||
**generated from a single source, `tools/gen-slaydeck.mjs`** (deterministic
|
||||
output — do not hand-edit; change the generator and re-run `node
|
||||
tools/gen-slaydeck.mjs`).
|
||||
|
||||
If the Maker session was already open before these files were added, reopen or reload the local workspace so the new codeblock files are imported into the editor state.
|
||||
If the Maker session was already open before these files changed, reload the
|
||||
local workspace so the regenerated codeblock/UI files are imported into the
|
||||
editor state.
|
||||
|
||||
## Prototype Flow
|
||||
## Implemented Combat Loop
|
||||
|
||||
1. `SlayRunState` starts a new run with 80 HP and a 10-card starter deck.
|
||||
2. `SlayCombatManager` starts a demo combat automatically.
|
||||
3. Each player turn refreshes energy to 3, clears block, rolls enemy intent, and draws 5 cards.
|
||||
4. Playing a card spends energy, applies damage/block/draw/energy/status effects, then sends the card to discard or exhaust.
|
||||
5. Ending the turn discards the hand, resolves enemy intent, ticks statuses, and starts the next turn.
|
||||
6. Winning combat stores the remaining HP back into the run, grants 15 gold, and generates 3 card reward options.
|
||||
1. `StartCombat` initializes player (HP 80, Block 0) and a single enemy
|
||||
(HP 45, Block 0) with a deterministic 3-step intent cycle
|
||||
(Attack 10 → Attack 6 → Defend 8), then starts the first player turn.
|
||||
2. Each player turn refreshes energy to 3, resets player block, and draws 5
|
||||
cards. The enemy's upcoming intent is shown in advance.
|
||||
3. Cards: Strike (damage 6), Defend (block 5), Bash (damage 10). Each card has
|
||||
numeric `damage`/`block` fields; starter deck is 10 cards.
|
||||
4. Playing a card spends energy: `Attack` reduces enemy HP (block absorbs
|
||||
first); `Skill` adds player block. The card moves to the discard pile.
|
||||
5. Ending the turn discards the hand, runs the enemy turn (executes the current
|
||||
intent — attack the player or gain block), advances the intent index, then
|
||||
starts the next player turn.
|
||||
6. Enemy HP 0 → victory; player HP 0 → defeat. On combat end, input is locked
|
||||
and a result text is shown (combat-reward hook reserved for the roguelike
|
||||
meta — see Planned below).
|
||||
|
||||
> Player HP (80), enemy HP (45), and intent values (10/6/8) are temporary
|
||||
> placeholders for loop verification. They will move to per-character /
|
||||
> per-enemy data (see "Data externalization" under Planned).
|
||||
|
||||
## Useful Script Calls
|
||||
|
||||
From a script attached to the same `/common` entity:
|
||||
From the `/common` entity (or a Play Test context):
|
||||
|
||||
```lua
|
||||
self.Entity.SlayCombatManager:PlayCard(1, 1)
|
||||
self.Entity.SlayCombatManager:EndPlayerTurn()
|
||||
self.Entity.SlayCombatManager:DebugPlayFirstPlayable()
|
||||
self.Entity.SlayRunState:PickReward(1)
|
||||
self.Entity.SlayCombatManager:StartCombat("elite")
|
||||
local c = _EntityService:GetEntityByPath("/common").SlayDeckController
|
||||
c:PlayCard(1) -- play the hand card in the given slot
|
||||
c:EndPlayerTurn() -- end turn → enemy turn → next turn
|
||||
c:StartCombat() -- restart combat (reset state)
|
||||
```
|
||||
|
||||
## Planned (not yet implemented) — Target Architecture
|
||||
|
||||
The originally envisioned component split for the full roguelike. Currently
|
||||
**none of these exist**; `SlayDeckController` covers only the card combat above.
|
||||
|
||||
- `SlayCardCatalog`: Card data, starter deck composition, reward pool, card cloning.
|
||||
- `SlayRunState`: Persistent run data — HP, gold, floor, deck, relics, card rewards.
|
||||
- `SlayCombatManager`: Turn flow, draw/discard/exhaust piles, energy, enemy
|
||||
intents, block, damage, victory, and defeat (the role currently played by
|
||||
`SlayDeckController`).
|
||||
|
||||
## Next Implementation Steps
|
||||
|
||||
- Add a combat UI that renders HP, block, energy, enemy intent, and 5 hand-card buttons.
|
||||
- Add a map node UI with combat, elite, shop, rest, event, and boss node types.
|
||||
- Add relic definitions and hooks such as `OnCombatStart`, `OnCardPlayed`, `OnTurnStart`, and `OnCombatReward`.
|
||||
- Add enemy move sets as data instead of the current simple intent pattern.
|
||||
- Add save/load once the run loop is playable end to end.
|
||||
- [x] Combat UI rendering HP, block, energy, enemy intent, and hand cards
|
||||
(done — `SlayDeckController` + CombatHud).
|
||||
- [x] Card play wired to real damage/block/intent/win-lose (done).
|
||||
- [ ] Externalize card/enemy data to `data/cards.json` / `data/enemies.json`,
|
||||
injected by the generator.
|
||||
- [ ] Monte-Carlo balance simulator `tools/sim-balance.mjs` (requires the data
|
||||
externalization above).
|
||||
- [ ] Map node UI with combat, elite, shop, rest, event, and boss node types.
|
||||
- [ ] Relic definitions and hooks (`OnCombatStart`, `OnCardPlayed`,
|
||||
`OnTurnStart`, `OnCombatReward`).
|
||||
- [ ] Enemy move sets as data instead of the current deterministic intent cycle.
|
||||
- [ ] Run persistence (HP/gold/floor/deck/relics) + save/load once the loop is
|
||||
playable end to end.
|
||||
|
||||
475
docs/superpowers/plans/2026-06-09-balance-simulator.md
Normal file
475
docs/superpowers/plans/2026-06-09-balance-simulator.md
Normal file
@@ -0,0 +1,475 @@
|
||||
# AI 전투 시뮬레이터 (TODO F) Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** `data/*.json`을 입력으로 전투를 몬테카를로 N회 시뮬레이션해 승률·턴·OP 카드 리포트를 출력하는 오프라인 CLI `tools/sim-balance.mjs`.
|
||||
|
||||
**Architecture:** 순수 함수(PRNG·applyDamage·chooseAction·simulateCombat·runBatch)로 분리해 `node:test`로 단위 테스트. CLI main은 직접 실행 시에만 동작. 전투 규칙은 gen-slaydeck.mjs의 Lua를 JS로 미러, 데이터는 D의 JSON 공유.
|
||||
|
||||
**Tech Stack:** Node.js ESM, `node:test`+`node:assert`. 검증은 단위 테스트 + CLI 실행 + 결정성 + 데이터 반영.
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
- Create: `tools/sim-balance.mjs` — 시뮬레이터(엔진·정책·집계·리포트·CLI). 순수 함수 export.
|
||||
- Create: `tools/sim-balance.test.mjs` — 단위 테스트(node:test).
|
||||
|
||||
전투 규칙은 `tools/gen-slaydeck.mjs` Lua와 중복 → 파일 상단 동기화 주석.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: PRNG·applyDamage·loadData (기반 순수 함수)
|
||||
|
||||
**Files:**
|
||||
- Create: `tools/sim-balance.mjs`
|
||||
- Create: `tools/sim-balance.test.mjs`
|
||||
|
||||
- [ ] **Step 1: 테스트 작성 `tools/sim-balance.test.mjs`**
|
||||
|
||||
```js
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { mulberry32, applyDamage } from './sim-balance.mjs';
|
||||
|
||||
test('applyDamage: 방어 우선 차감 후 hp', () => {
|
||||
assert.deepEqual(applyDamage(80, 0, 10), { hp: 70, block: 0 });
|
||||
assert.deepEqual(applyDamage(80, 5, 10), { hp: 75, block: 0 });
|
||||
assert.deepEqual(applyDamage(80, 12, 10), { hp: 80, block: 2 });
|
||||
assert.deepEqual(applyDamage(3, 0, 10), { hp: 0, block: 0 });
|
||||
});
|
||||
|
||||
test('mulberry32: 동일 시드 동일 수열', () => {
|
||||
const a = mulberry32(1), b = mulberry32(1);
|
||||
assert.equal(a(), b());
|
||||
assert.equal(a(), b());
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 테스트 실패 확인**
|
||||
|
||||
Run: `node --test tools/sim-balance.test.mjs`
|
||||
Expected: FAIL (`Cannot find module './sim-balance.mjs'` 또는 export 없음)
|
||||
|
||||
- [ ] **Step 3: `tools/sim-balance.mjs` 작성(기반부)**
|
||||
|
||||
```js
|
||||
// AI 전투 밸런스 시뮬레이터 — 오프라인 몬테카를로.
|
||||
// ⚠️ 전투 규칙은 tools/gen-slaydeck.mjs 의 Lua(SlayDeckController)와 동기화 유지할 것.
|
||||
import { readFileSync } from 'node:fs';
|
||||
|
||||
export const PLAYER_HP = 80; // 데이터 미포함 placeholder (codeblock과 일치)
|
||||
export const ENERGY = 3;
|
||||
export const HAND_SIZE = 5;
|
||||
export const MAX_TURNS = 100;
|
||||
|
||||
export function mulberry32(seed) {
|
||||
let a = seed >>> 0;
|
||||
return function () {
|
||||
a |= 0; a = (a + 0x6D2B79F5) | 0;
|
||||
let t = Math.imul(a ^ (a >>> 15), 1 | a);
|
||||
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
||||
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
||||
};
|
||||
}
|
||||
|
||||
export function shuffle(arr, rng) {
|
||||
const a = arr.slice();
|
||||
for (let i = a.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(rng() * (i + 1));
|
||||
[a[i], a[j]] = [a[j], a[i]];
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
// 방어 우선 차감 후 hp 적용 → { hp, block }
|
||||
export function applyDamage(hp, block, amount) {
|
||||
let dmg = amount;
|
||||
if (block > 0) {
|
||||
const absorbed = Math.min(block, dmg);
|
||||
block -= absorbed;
|
||||
dmg -= absorbed;
|
||||
}
|
||||
hp -= dmg;
|
||||
if (hp < 0) hp = 0;
|
||||
return { hp, block };
|
||||
}
|
||||
|
||||
export function loadData() {
|
||||
const cardsData = JSON.parse(readFileSync('data/cards.json', 'utf8'));
|
||||
const enemiesData = JSON.parse(readFileSync('data/enemies.json', 'utf8'));
|
||||
const enemy = enemiesData.enemies[enemiesData.activeEnemy];
|
||||
if (!enemy) throw new Error(`activeEnemy 없음: ${enemiesData.activeEnemy}`);
|
||||
return { cards: cardsData.cards, starterDeck: cardsData.starterDeck, enemy };
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 테스트 통과 확인**
|
||||
|
||||
Run: `node --test tools/sim-balance.test.mjs`
|
||||
Expected: PASS (2 tests)
|
||||
|
||||
- [ ] **Step 5: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/sim-balance.mjs tools/sim-balance.test.mjs
|
||||
git commit -m "sim-balance(F): PRNG·applyDamage·loadData 기반 함수 + 테스트"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: chooseAction 정책 (휴리스틱 A)
|
||||
|
||||
**Files:**
|
||||
- Modify: `tools/sim-balance.mjs`, `tools/sim-balance.test.mjs`
|
||||
|
||||
- [ ] **Step 1: 테스트 추가 (test.mjs 하단)**
|
||||
|
||||
```js
|
||||
import { chooseAction } from './sim-balance.mjs';
|
||||
|
||||
const CARDS = {
|
||||
Strike: { name: '타격', cost: 1, kind: 'Attack', damage: 6 },
|
||||
Defend: { name: '방어', cost: 1, kind: 'Skill', block: 5 },
|
||||
Bash: { name: '강타', cost: 2, kind: 'Attack', damage: 10 },
|
||||
};
|
||||
|
||||
test('chooseAction: 치사 가능하면 공격 선택', () => {
|
||||
// 적 hp 5, block 0, 손패 Strike(6) → 공격(인덱스 0)
|
||||
const idx = chooseAction(['Strike', 'Defend'], CARDS, 3, 5, 0, { kind: 'Attack', value: 10 });
|
||||
assert.equal(idx, 0);
|
||||
});
|
||||
|
||||
test('chooseAction: 치사 불가 + 적 공격 의도면 방어 선택', () => {
|
||||
// 적 hp 40(이번 턴 못 죽임), 의도 공격 → Defend(인덱스 1)
|
||||
const idx = chooseAction(['Strike', 'Defend'], CARDS, 3, 40, 0, { kind: 'Attack', value: 10 });
|
||||
assert.equal(idx, 1);
|
||||
});
|
||||
|
||||
test('chooseAction: 적 방어 의도면 공격 우선', () => {
|
||||
const idx = chooseAction(['Defend', 'Strike'], CARDS, 3, 40, 0, { kind: 'Defend', value: 8 });
|
||||
assert.equal(idx, 1);
|
||||
});
|
||||
|
||||
test('chooseAction: 사용 가능 카드 없으면 -1', () => {
|
||||
const idx = chooseAction(['Bash'], CARDS, 1, 40, 0, { kind: 'Attack', value: 10 });
|
||||
assert.equal(idx, -1);
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 테스트 실패 확인**
|
||||
|
||||
Run: `node --test tools/sim-balance.test.mjs`
|
||||
Expected: FAIL (`chooseAction is not a function`)
|
||||
|
||||
- [ ] **Step 3: 구현 추가 (sim-balance.mjs)**
|
||||
|
||||
```js
|
||||
// 손패에서 다음에 낼 카드의 인덱스 반환(-1=턴 종료). hand=카드 id 배열.
|
||||
export function chooseAction(hand, cards, energy, enemyHp, enemyBlock, enemyIntent) {
|
||||
const entries = hand.map((id, i) => ({ id, i })).filter((x) => cards[x.id].cost <= energy);
|
||||
const attacks = entries.filter((x) => cards[x.id].kind === 'Attack');
|
||||
const skills = entries.filter((x) => cards[x.id].kind === 'Skill');
|
||||
const dmgEff = (x) => (cards[x.id].damage || 0) / cards[x.id].cost;
|
||||
const blkEff = (x) => (cards[x.id].block || 0) / cards[x.id].cost;
|
||||
const bestBy = (list, fn) => list.slice().sort((a, b) => fn(b) - fn(a))[0];
|
||||
|
||||
// 1) 치사: 에너지 한도 내 효율순 공격 데미지 합 >= 적 유효 hp?
|
||||
let e = energy, lethalDmg = 0;
|
||||
for (const x of attacks.slice().sort((a, b) => dmgEff(b) - dmgEff(a))) {
|
||||
if (cards[x.id].cost <= e) { e -= cards[x.id].cost; lethalDmg += cards[x.id].damage || 0; }
|
||||
}
|
||||
if (attacks.length && lethalDmg >= enemyHp + enemyBlock) return bestBy(attacks, dmgEff).i;
|
||||
|
||||
// 2) 적 공격 의도면 방어 우선
|
||||
if (enemyIntent && enemyIntent.kind === 'Attack' && skills.length) return bestBy(skills, blkEff).i;
|
||||
|
||||
// 3) 공격 우선, 없으면 스킬, 없으면 종료
|
||||
if (attacks.length) return bestBy(attacks, dmgEff).i;
|
||||
if (skills.length) return bestBy(skills, blkEff).i;
|
||||
return -1;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 테스트 통과 확인**
|
||||
|
||||
Run: `node --test tools/sim-balance.test.mjs`
|
||||
Expected: PASS (6 tests)
|
||||
|
||||
- [ ] **Step 5: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/sim-balance.mjs tools/sim-balance.test.mjs
|
||||
git commit -m "sim-balance(F): 플레이어 휴리스틱 정책 chooseAction + 테스트"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: simulateCombat 엔진
|
||||
|
||||
**Files:**
|
||||
- Modify: `tools/sim-balance.mjs`, `tools/sim-balance.test.mjs`
|
||||
|
||||
- [ ] **Step 1: 테스트 추가**
|
||||
|
||||
```js
|
||||
import { simulateCombat, mulberry32 as m32 } from './sim-balance.mjs';
|
||||
|
||||
const DATA = {
|
||||
cards: {
|
||||
Strike: { name: '타격', cost: 1, kind: 'Attack', damage: 6 },
|
||||
Defend: { name: '방어', cost: 1, kind: 'Skill', block: 5 },
|
||||
Bash: { name: '강타', cost: 2, kind: 'Attack', damage: 10 },
|
||||
},
|
||||
starterDeck: ['Strike','Strike','Strike','Strike','Strike','Defend','Defend','Defend','Defend','Bash'],
|
||||
enemy: { name: '슬라임', maxHp: 45, intents: [
|
||||
{ kind: 'Attack', value: 10 }, { kind: 'Attack', value: 6 }, { kind: 'Defend', value: 8 },
|
||||
] },
|
||||
};
|
||||
|
||||
test('simulateCombat: 결정적 결과(동일 시드)', () => {
|
||||
const r1 = simulateCombat(DATA, m32(1));
|
||||
const r2 = simulateCombat(DATA, m32(1));
|
||||
assert.deepEqual(r1, r2);
|
||||
assert.equal(typeof r1.win, 'boolean');
|
||||
assert.ok(r1.turns >= 1);
|
||||
});
|
||||
|
||||
test('simulateCombat: 약한 적이면 대체로 승리', () => {
|
||||
let wins = 0;
|
||||
for (let i = 0; i < 50; i++) if (simulateCombat(DATA, m32(i + 1)).win) wins++;
|
||||
assert.ok(wins >= 40, `예상 승리 다수, 실제 ${wins}/50`);
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 테스트 실패 확인**
|
||||
|
||||
Run: `node --test tools/sim-balance.test.mjs`
|
||||
Expected: FAIL (`simulateCombat is not a function`)
|
||||
|
||||
- [ ] **Step 3: 구현 추가 (sim-balance.mjs)**
|
||||
|
||||
```js
|
||||
function bump(s, cost, dmg, blk) {
|
||||
s = s || { plays: 0, energy: 0, damage: 0, block: 0 };
|
||||
s.plays++; s.energy += cost; s.damage += dmg; s.block += blk;
|
||||
return s;
|
||||
}
|
||||
|
||||
// 단일 전투 시뮬. stats(선택): {cardId: {plays,energy,damage,block}} 누적.
|
||||
// 반환: { win, turns, playerHpRemaining, draw? }
|
||||
export function simulateCombat(data, rng, stats) {
|
||||
const { cards, starterDeck, enemy } = data;
|
||||
let drawPile = shuffle(starterDeck, rng);
|
||||
let discard = [];
|
||||
let hand = [];
|
||||
let pHp = PLAYER_HP, pBlock = 0;
|
||||
let eHp = enemy.maxHp, eBlock = 0, intentIdx = 0;
|
||||
let turns = 0;
|
||||
|
||||
function draw(n) {
|
||||
for (let k = 0; k < n; k++) {
|
||||
if (drawPile.length === 0) { drawPile = shuffle(discard, rng); discard = []; }
|
||||
if (drawPile.length === 0) break;
|
||||
hand.push(drawPile.pop());
|
||||
}
|
||||
}
|
||||
|
||||
while (turns < MAX_TURNS) {
|
||||
turns++;
|
||||
let energy = ENERGY; pBlock = 0; hand = []; draw(HAND_SIZE);
|
||||
while (true) {
|
||||
const intent = enemy.intents[intentIdx];
|
||||
const idx = chooseAction(hand, cards, energy, eHp, eBlock, intent);
|
||||
if (idx < 0) break;
|
||||
const id = hand[idx], c = cards[id];
|
||||
energy -= c.cost;
|
||||
if (c.kind === 'Attack') {
|
||||
const r = applyDamage(eHp, eBlock, c.damage || 0); eHp = r.hp; eBlock = r.block;
|
||||
if (stats) stats[id] = bump(stats[id], c.cost, c.damage || 0, 0);
|
||||
} else {
|
||||
pBlock += c.block || 0;
|
||||
if (stats) stats[id] = bump(stats[id], c.cost, 0, c.block || 0);
|
||||
}
|
||||
hand.splice(idx, 1); discard.push(id);
|
||||
if (eHp <= 0) return { win: true, turns, playerHpRemaining: pHp };
|
||||
}
|
||||
discard.push(...hand); hand = [];
|
||||
eBlock = 0;
|
||||
const intent = enemy.intents[intentIdx];
|
||||
if (intent.kind === 'Attack') { const r = applyDamage(pHp, pBlock, intent.value); pHp = r.hp; pBlock = r.block; }
|
||||
else if (intent.kind === 'Defend') { eBlock += intent.value; }
|
||||
intentIdx = (intentIdx + 1) % enemy.intents.length;
|
||||
if (pHp <= 0) return { win: false, turns, playerHpRemaining: 0 };
|
||||
}
|
||||
return { win: false, turns, playerHpRemaining: pHp, draw: true };
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 테스트 통과 확인**
|
||||
|
||||
Run: `node --test tools/sim-balance.test.mjs`
|
||||
Expected: PASS (8 tests)
|
||||
|
||||
- [ ] **Step 5: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/sim-balance.mjs tools/sim-balance.test.mjs
|
||||
git commit -m "sim-balance(F): 단일 전투 시뮬 엔진 simulateCombat + 테스트"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: runBatch·리포트·OP 탐지·CLI
|
||||
|
||||
**Files:**
|
||||
- Modify: `tools/sim-balance.mjs`, `tools/sim-balance.test.mjs`
|
||||
|
||||
- [ ] **Step 1: 테스트 추가**
|
||||
|
||||
```js
|
||||
import { runBatch } from './sim-balance.mjs';
|
||||
|
||||
test('runBatch: 집계 필드·승률 범위', () => {
|
||||
const r = runBatch(100, 1);
|
||||
assert.equal(r.N, 100);
|
||||
assert.ok(r.winRate >= 0 && r.winRate <= 1);
|
||||
assert.ok(r.avgTurns > 0);
|
||||
assert.ok(r.cardStats.Strike.plays > 0);
|
||||
});
|
||||
|
||||
test('runBatch: 동일 시드 동일 결과', () => {
|
||||
assert.deepEqual(runBatch(100, 7), runBatch(100, 7));
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 테스트 실패 확인**
|
||||
|
||||
Run: `node --test tools/sim-balance.test.mjs`
|
||||
Expected: FAIL (`runBatch is not a function`)
|
||||
|
||||
- [ ] **Step 3: 구현 추가 (sim-balance.mjs)**
|
||||
|
||||
```js
|
||||
function mean(a) { return a.length ? a.reduce((s, x) => s + x, 0) / a.length : 0; }
|
||||
function median(a) {
|
||||
if (!a.length) return 0;
|
||||
const s = a.slice().sort((x, y) => x - y), m = Math.floor(s.length / 2);
|
||||
return s.length % 2 ? s[m] : (s[m - 1] + s[m]) / 2;
|
||||
}
|
||||
|
||||
export function runBatch(N, seed) {
|
||||
const data = loadData();
|
||||
const rng = mulberry32(seed);
|
||||
const cardStats = {};
|
||||
let wins = 0, draws = 0;
|
||||
const turnsArr = [], hpArr = [];
|
||||
for (let i = 0; i < N; i++) {
|
||||
const r = simulateCombat(data, rng, cardStats);
|
||||
if (r.draw) draws++;
|
||||
if (r.win) { wins++; hpArr.push(r.playerHpRemaining); }
|
||||
turnsArr.push(r.turns);
|
||||
}
|
||||
return {
|
||||
N, wins, draws, losses: N - wins - draws,
|
||||
winRate: wins / N,
|
||||
avgTurns: mean(turnsArr), medianTurns: median(turnsArr),
|
||||
avgHpOnWin: mean(hpArr),
|
||||
cardStats, cards: data.cards, enemy: data.enemy, seed,
|
||||
};
|
||||
}
|
||||
|
||||
export function formatReport(r) {
|
||||
const L = [];
|
||||
L.push(`=== 밸런스 시뮬레이션 (적: ${r.enemy.name} HP ${r.enemy.maxHp}) ===`);
|
||||
L.push(`시뮬 ${r.N}회 (seed=${r.seed})`);
|
||||
L.push(`승률: ${(r.winRate * 100).toFixed(1)}% (승 ${r.wins} / 패 ${r.losses}${r.draws ? ` / 무 ${r.draws}` : ''})`);
|
||||
L.push(`평균 턴: ${r.avgTurns.toFixed(2)} 중앙값 턴: ${r.medianTurns}`);
|
||||
L.push(`승리 시 평균 잔여 HP: ${r.avgHpOnWin.toFixed(1)} / ${PLAYER_HP}`);
|
||||
if (r.draws) L.push(`⚠️ 무승부 ${r.draws}건 (턴 상한 ${MAX_TURNS} 초과)`);
|
||||
L.push('');
|
||||
L.push('카드별:');
|
||||
// 효율 계산 + kind별 중앙값으로 OP 플래그
|
||||
const rows = Object.entries(r.cardStats).map(([id, s]) => {
|
||||
const kind = r.cards[id].kind;
|
||||
const eff = kind === 'Attack' ? s.damage / s.energy : s.block / s.energy;
|
||||
return { id, name: r.cards[id].name, kind, plays: s.plays, eff };
|
||||
});
|
||||
for (const kind of ['Attack', 'Skill']) {
|
||||
const kr = rows.filter((x) => x.kind === kind);
|
||||
if (!kr.length) continue;
|
||||
const med = median(kr.map((x) => x.eff));
|
||||
for (const x of kr) {
|
||||
const op = med > 0 && x.eff >= med * 1.5 ? ' ⚠️ OP 의심' : '';
|
||||
const unit = kind === 'Attack' ? '뎀/E' : '블록/E';
|
||||
L.push(` ${x.name}(${id2(x.id)}): 사용 ${x.plays}, 효율 ${x.eff.toFixed(2)} ${unit}${op}`);
|
||||
}
|
||||
}
|
||||
const sorted = rows.slice().sort((a, b) => b.plays - a.plays);
|
||||
if (sorted.length) L.push(`최다 사용: ${sorted[0].name} / 최소 사용: ${sorted[sorted.length - 1].name}`);
|
||||
return L.join('\n');
|
||||
}
|
||||
function id2(id) { return id; }
|
||||
|
||||
function main() {
|
||||
const args = process.argv.slice(2);
|
||||
let N = 2000, seed = 1;
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === '--seed') seed = parseInt(args[++i], 10);
|
||||
else if (/^\d+$/.test(args[i])) N = parseInt(args[i], 10);
|
||||
}
|
||||
console.log(formatReport(runBatch(N, seed)));
|
||||
}
|
||||
|
||||
if (process.argv[1] && process.argv[1].endsWith('sim-balance.mjs')) main();
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 테스트 통과 확인**
|
||||
|
||||
Run: `node --test tools/sim-balance.test.mjs`
|
||||
Expected: PASS (10 tests)
|
||||
|
||||
- [ ] **Step 5: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/sim-balance.mjs tools/sim-balance.test.mjs
|
||||
git commit -m "sim-balance(F): runBatch·리포트·OP 탐지·CLI + 테스트"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: 검증 (CLI 실행·결정성·데이터 반영)
|
||||
|
||||
**Files:** 없음(실행 검증)
|
||||
|
||||
- [ ] **Step 1: 전체 테스트**
|
||||
|
||||
Run: `node --test tools/sim-balance.test.mjs`
|
||||
Expected: PASS (10 tests, 0 fail)
|
||||
|
||||
- [ ] **Step 2: CLI 실행 (기본)**
|
||||
|
||||
Run: `node tools/sim-balance.mjs 2000`
|
||||
Expected: 승률·평균턴·승리시 잔여HP·카드별 효율 리포트 출력.
|
||||
|
||||
- [ ] **Step 3: 결정성 (동일 시드 동일 출력)**
|
||||
|
||||
Run: `node tools/sim-balance.mjs 500 --seed 3 > /tmp/r1.txt && node tools/sim-balance.mjs 500 --seed 3 > /tmp/r2.txt && diff /tmp/r1.txt /tmp/r2.txt && echo DETERMINISTIC`
|
||||
Expected: `DETERMINISTIC`
|
||||
|
||||
- [ ] **Step 4: 데이터 반영 (강타 데미지↑ → 승률·턴 변동)**
|
||||
|
||||
Run: `node tools/sim-balance.mjs 1000 --seed 1 | grep 승률` (기준값 기록) → `data/cards.json`에서 Bash.damage 10→20으로 임시 변경 → `node tools/sim-balance.mjs 1000 --seed 1 | grep 승률`(변동 확인) → `git checkout -- data/cards.json`(원복).
|
||||
Expected: 두 승률/턴 수치가 다름(데이터 반영). 원복 후 기준 복귀.
|
||||
|
||||
- [ ] **Step 5: 최종 커밋(있다면 없음 — 검증 전용)**
|
||||
|
||||
검증 전용 태스크. 변경 없음. `git status`로 `data/cards.json` 원복 확인.
|
||||
|
||||
---
|
||||
|
||||
## Self-Review
|
||||
|
||||
- **Spec coverage:** PRNG·applyDamage·loadData(Task1), 정책(Task2), 엔진(Task3), 집계·리포트·OP·CLI(Task4), 검증·데이터반영(Task5). 스펙 전 항목 매핑.
|
||||
- **Placeholder scan:** 모든 단계 실제 코드/명령. 동기화 주석은 의도된 문서.
|
||||
- **Type consistency:** `mulberry32/shuffle/applyDamage/loadData/chooseAction/simulateCombat/runBatch/formatReport` 시그니처가 정의(Task1·2·3·4)와 사용(테스트·CLI)에서 일치. `cardStats` 형태 `{plays,energy,damage,block}`가 `bump`·`runBatch`·`formatReport`에서 일치. 카드 필드 `kind/damage/block/cost`가 데이터·정책·엔진에서 일치.
|
||||
341
docs/superpowers/plans/2026-06-09-data-externalization.md
Normal file
341
docs/superpowers/plans/2026-06-09-data-externalization.md
Normal file
@@ -0,0 +1,341 @@
|
||||
# 카드/적 데이터 외부화 (TODO D) Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 카드·적 데이터를 `data/cards.json`·`data/enemies.json`로 분리하고, `gen-slaydeck.mjs`가 읽어 codeblock·UI에 주입한다(데이터만 바꿔 재생성하면 반영).
|
||||
|
||||
**Architecture:** 신규 JSON 2개가 데이터 단일 소스. 생성기는 상단에서 JSON을 로드·검증하고, Lua 직렬화 헬퍼로 `self.Cards`/`self.DrawPile`/적 상태를 만들어 `StartCombat`에 주입한다. DeckHud 카드 미리보기·CombatHud 초기 텍스트도 동일 데이터에서 파생.
|
||||
|
||||
**Tech Stack:** Node.js ESM 생성기, JSON 데이터, MSW Lua codeblock/UI JSON. 검증은 `node --check`+재생성+sha1 결정성+데이터변경 반영 확인+메이커 Play.
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
- Create: `data/cards.json` — 카드 정의(`cards`) + 시작 덱(`starterDeck`).
|
||||
- Create: `data/enemies.json` — 적 정의(`enemies`) + 활성 적(`activeEnemy`).
|
||||
- Modify: `tools/gen-slaydeck.mjs` — JSON 로드·검증·Lua 직렬화 헬퍼, `StartCombat`/`upsertUi`/속성 데이터화.
|
||||
|
||||
검증 한계: MSW Lua 단위 테스트 러너 없음 → 자동 검증은 생성기 문법·재생성·결정성·데이터 반영·JSON 유효성. 실제 동작은 메이커 Play(사용자).
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 데이터 파일 생성
|
||||
|
||||
**Files:**
|
||||
- Create: `data/cards.json`
|
||||
- Create: `data/enemies.json`
|
||||
|
||||
- [ ] **Step 1: `data/cards.json` 작성**
|
||||
|
||||
```json
|
||||
{
|
||||
"cards": {
|
||||
"Strike": { "name": "타격", "cost": 1, "kind": "Attack", "damage": 6, "desc": "피해 6" },
|
||||
"Defend": { "name": "방어", "cost": 1, "kind": "Skill", "block": 5, "desc": "방어도 5" },
|
||||
"Bash": { "name": "강타", "cost": 2, "kind": "Attack", "damage": 10, "desc": "피해 10" }
|
||||
},
|
||||
"starterDeck": ["Strike", "Strike", "Strike", "Strike", "Strike", "Defend", "Defend", "Defend", "Defend", "Bash"]
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: `data/enemies.json` 작성**
|
||||
|
||||
```json
|
||||
{
|
||||
"enemies": {
|
||||
"slime": {
|
||||
"name": "슬라임",
|
||||
"maxHp": 45,
|
||||
"intents": [
|
||||
{ "kind": "Attack", "value": 10 },
|
||||
{ "kind": "Attack", "value": 6 },
|
||||
{ "kind": "Defend", "value": 8 }
|
||||
]
|
||||
}
|
||||
},
|
||||
"activeEnemy": "slime"
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: JSON 유효성 확인**
|
||||
|
||||
Run: `node -e "JSON.parse(require('fs').readFileSync('data/cards.json','utf8')); JSON.parse(require('fs').readFileSync('data/enemies.json','utf8')); console.log('JSON OK')"`
|
||||
Expected: `JSON OK`
|
||||
|
||||
- [ ] **Step 4: 커밋**
|
||||
|
||||
```bash
|
||||
git add data/cards.json data/enemies.json
|
||||
git commit -m "data(D): 카드/적 데이터 JSON 외부화 파일 추가"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: 생성기에 JSON 로드·검증·Lua 직렬화 헬퍼 추가
|
||||
|
||||
**Files:**
|
||||
- Modify: `tools/gen-slaydeck.mjs` (상단 import 직후)
|
||||
|
||||
- [ ] **Step 1: 파일 상단 `import { readFileSync, writeFileSync } from 'node:fs';` 바로 다음에 추가**
|
||||
|
||||
```js
|
||||
const CARDS = JSON.parse(readFileSync('data/cards.json', 'utf8'));
|
||||
const ENEMIES = JSON.parse(readFileSync('data/enemies.json', 'utf8'));
|
||||
|
||||
// 검증 (fail-fast): 잘못된 데이터면 생성 중단
|
||||
for (const id of CARDS.starterDeck) {
|
||||
if (!CARDS.cards[id]) {
|
||||
throw new Error(`[gen-slaydeck] starterDeck에 없는 카드 id 참조: ${id}`);
|
||||
}
|
||||
}
|
||||
if (!ENEMIES.enemies[ENEMIES.activeEnemy]) {
|
||||
throw new Error(`[gen-slaydeck] activeEnemy가 enemies에 없음: ${ENEMIES.activeEnemy}`);
|
||||
}
|
||||
const ACTIVE_ENEMY = ENEMIES.enemies[ENEMIES.activeEnemy];
|
||||
|
||||
// Lua 직렬화 헬퍼
|
||||
function luaStr(s) {
|
||||
return '"' + String(s).replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
|
||||
}
|
||||
function luaCardsTable(cards) {
|
||||
const lines = Object.entries(cards).map(([id, c]) => {
|
||||
const fields = [`name = ${luaStr(c.name)}`, `cost = ${c.cost}`, `desc = ${luaStr(c.desc)}`, `kind = ${luaStr(c.kind)}`];
|
||||
if (c.damage != null) fields.push(`damage = ${c.damage}`);
|
||||
if (c.block != null) fields.push(`block = ${c.block}`);
|
||||
return `\t${id} = { ${fields.join(', ')} },`;
|
||||
});
|
||||
return `self.Cards = {\n${lines.join('\n')}\n}`;
|
||||
}
|
||||
function luaDeckTable(deck) {
|
||||
return `self.DrawPile = { ${deck.map(luaStr).join(', ')} }`;
|
||||
}
|
||||
function luaIntentsTable(intents) {
|
||||
const lines = intents.map((it) => `\t{ kind = ${luaStr(it.kind)}, value = ${it.value} },`);
|
||||
return `self.EnemyIntents = {\n${lines.join('\n')}\n}`;
|
||||
}
|
||||
function intentText(it) {
|
||||
if (it.kind === 'Attack') return `의도: 공격 ${it.value}`;
|
||||
if (it.kind === 'Defend') return `의도: 방어 ${it.value}`;
|
||||
return '';
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 문법 검사**
|
||||
|
||||
Run: `node --check tools/gen-slaydeck.mjs`
|
||||
Expected: 오류 없음
|
||||
|
||||
- [ ] **Step 3: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/gen-slaydeck.mjs
|
||||
git commit -m "gen-slaydeck(D): JSON 로드·검증·Lua 직렬화 헬퍼 추가"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: StartCombat·EnemyMaxHp 속성을 데이터에서 생성
|
||||
|
||||
**Files:**
|
||||
- Modify: `tools/gen-slaydeck.mjs` (`prop('number', 'EnemyMaxHp', ...)`, `method('StartCombat', ...)`)
|
||||
|
||||
- [ ] **Step 1: EnemyMaxHp 속성 기본값을 데이터로**
|
||||
|
||||
`prop('number', 'EnemyMaxHp', '45'),` 를 아래로 교체:
|
||||
|
||||
```js
|
||||
prop('number', 'EnemyMaxHp', String(ACTIVE_ENEMY.maxHp)),
|
||||
```
|
||||
|
||||
- [ ] **Step 2: `StartCombat` 메서드 본문을 데이터 주입형으로 교체**
|
||||
|
||||
기존 `method('StartCombat', \`...\`)` 호출 전체(아래 "현재" 블록)를 "신규"로 교체.
|
||||
|
||||
현재(교체 대상):
|
||||
```
|
||||
self.MaxEnergy = 3
|
||||
self.Turn = 0
|
||||
self.PlayerMaxHp = 80
|
||||
self.PlayerHp = self.PlayerMaxHp
|
||||
self.PlayerBlock = 0
|
||||
self.EnemyName = "슬라임"
|
||||
self.EnemyMaxHp = 45
|
||||
self.EnemyHp = self.EnemyMaxHp
|
||||
self.EnemyBlock = 0
|
||||
self.EnemyIntents = {
|
||||
{ kind = "Attack", value = 10 },
|
||||
{ kind = "Attack", value = 6 },
|
||||
{ kind = "Defend", value = 8 },
|
||||
}
|
||||
self.EnemyIntentIndex = 1
|
||||
self.CombatOver = false
|
||||
self.DiscardPile = {}
|
||||
self.Hand = {}
|
||||
self.Cards = {
|
||||
Strike = { name = "타격", cost = 1, desc = "피해 6", kind = "Attack", damage = 6 },
|
||||
Defend = { name = "방어", cost = 1, desc = "방어도 5", kind = "Skill", block = 5 },
|
||||
Bash = { name = "강타", cost = 2, desc = "피해 10", kind = "Attack", damage = 10 },
|
||||
}
|
||||
self.DrawPile = { "Strike", "Strike", "Strike", "Strike", "Strike", "Defend", "Defend", "Defend", "Defend", "Bash" }
|
||||
self:Shuffle(self.DrawPile)
|
||||
self:BindButtons()
|
||||
self:RenderCombat()
|
||||
self:StartPlayerTurn()
|
||||
```
|
||||
|
||||
신규 — `method('StartCombat', ...)`의 코드 인자를 템플릿으로 생성:
|
||||
```js
|
||||
method('StartCombat', `self.MaxEnergy = 3
|
||||
self.Turn = 0
|
||||
self.PlayerMaxHp = 80
|
||||
self.PlayerHp = self.PlayerMaxHp
|
||||
self.PlayerBlock = 0
|
||||
self.EnemyName = ${luaStr(ACTIVE_ENEMY.name)}
|
||||
self.EnemyMaxHp = ${ACTIVE_ENEMY.maxHp}
|
||||
self.EnemyHp = self.EnemyMaxHp
|
||||
self.EnemyBlock = 0
|
||||
${luaIntentsTable(ACTIVE_ENEMY.intents)}
|
||||
self.EnemyIntentIndex = 1
|
||||
self.CombatOver = false
|
||||
self.DiscardPile = {}
|
||||
self.Hand = {}
|
||||
${luaCardsTable(CARDS.cards)}
|
||||
${luaDeckTable(CARDS.starterDeck)}
|
||||
self:Shuffle(self.DrawPile)
|
||||
self:BindButtons()
|
||||
self:RenderCombat()
|
||||
self:StartPlayerTurn()`),
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 문법 검사**
|
||||
|
||||
Run: `node --check tools/gen-slaydeck.mjs`
|
||||
Expected: 오류 없음
|
||||
|
||||
- [ ] **Step 4: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/gen-slaydeck.mjs
|
||||
git commit -m "gen-slaydeck(D): StartCombat·EnemyMaxHp를 데이터에서 생성"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: DeckHud 카드 미리보기·CombatHud 초기 텍스트를 데이터에서 파생
|
||||
|
||||
**Files:**
|
||||
- Modify: `tools/gen-slaydeck.mjs` (`upsertUi`의 `cards` 배열, `enemyTexts` 초기값)
|
||||
|
||||
- [ ] **Step 1: `upsertUi`의 카드 미리보기 배열을 데이터 파생으로 교체**
|
||||
|
||||
기존:
|
||||
```js
|
||||
const cards = [
|
||||
{ name: '타격', cost: '1', desc: '피해 6', tint: ATTACK },
|
||||
{ name: '타격', cost: '1', desc: '피해 6', tint: ATTACK },
|
||||
{ name: '방어', cost: '1', desc: '방어도 5', tint: DEFEND },
|
||||
{ name: '방어', cost: '1', desc: '방어도 5', tint: DEFEND },
|
||||
{ name: '강타', cost: '2', desc: '피해 10', tint: ATTACK },
|
||||
];
|
||||
```
|
||||
교체:
|
||||
```js
|
||||
const cards = CARDS.starterDeck.slice(0, 5).map((id) => {
|
||||
const c = CARDS.cards[id];
|
||||
return { name: c.name, cost: String(c.cost), desc: c.desc, tint: c.kind === 'Attack' ? ATTACK : DEFEND };
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: CombatHud `enemyTexts` 초기값을 데이터에서 파생**
|
||||
|
||||
기존:
|
||||
```js
|
||||
const enemyTexts = [
|
||||
['EnemyName', { x: 0, y: 358 }, { x: 360, y: 44 }, '슬라임', 28, true, GOLD],
|
||||
['EnemyHp', { x: 0, y: 316 }, { x: 360, y: 40 }, 'HP 45/45', 24, true, { r: 1, g: 1, b: 1, a: 1 }],
|
||||
['EnemyBlock', { x: 0, y: 280 }, { x: 360, y: 36 }, '방어 0', 20, false, { r: 0.6, g: 0.8, b: 1, a: 1 }],
|
||||
['EnemyIntent', { x: 0, y: 244 }, { x: 360, y: 38 }, '의도: 공격 10', 22, true, { r: 1, g: 0.72, b: 0.5, a: 1 }],
|
||||
];
|
||||
```
|
||||
교체:
|
||||
```js
|
||||
const enemyTexts = [
|
||||
['EnemyName', { x: 0, y: 358 }, { x: 360, y: 44 }, ACTIVE_ENEMY.name, 28, true, GOLD],
|
||||
['EnemyHp', { x: 0, y: 316 }, { x: 360, y: 40 }, `HP ${ACTIVE_ENEMY.maxHp}/${ACTIVE_ENEMY.maxHp}`, 24, true, { r: 1, g: 1, b: 1, a: 1 }],
|
||||
['EnemyBlock', { x: 0, y: 280 }, { x: 360, y: 36 }, '방어 0', 20, false, { r: 0.6, g: 0.8, b: 1, a: 1 }],
|
||||
['EnemyIntent', { x: 0, y: 244 }, { x: 360, y: 38 }, intentText(ACTIVE_ENEMY.intents[0]), 22, true, { r: 1, g: 0.72, b: 0.5, a: 1 }],
|
||||
];
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 문법 검사**
|
||||
|
||||
Run: `node --check tools/gen-slaydeck.mjs`
|
||||
Expected: 오류 없음
|
||||
|
||||
- [ ] **Step 4: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/gen-slaydeck.mjs
|
||||
git commit -m "gen-slaydeck(D): 카드 미리보기·CombatHud 초기 텍스트 데이터 파생"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: 재생성 + 검증
|
||||
|
||||
**Files:** 생성물 3종 (생성기 실행 결과)
|
||||
|
||||
- [ ] **Step 1: 생성기 실행**
|
||||
|
||||
Run: `node tools/gen-slaydeck.mjs`
|
||||
Expected: `Slay deck UI and combat codeblocks generated.`
|
||||
|
||||
- [ ] **Step 2: 생성물이 B와 동치인지 — codeblock에 데이터 값이 반영됐는지 확인**
|
||||
|
||||
Run: `node -e "const j=JSON.parse(require('fs').readFileSync('RootDesk/MyDesk/SlayDeckController.codeblock','utf8')); const sc=j.ContentProto.Json.Methods.find(m=>m.Name==='StartCombat').Code; console.log(/Strike = { name = \"타격\".*damage = 6/.test(sc) && /슬라임/.test(sc) && /value = 10/.test(sc) ? 'DATA INJECTED OK' : 'MISMATCH')"`
|
||||
Expected: `DATA INJECTED OK`
|
||||
|
||||
- [ ] **Step 3: 결정성 확인**
|
||||
|
||||
Run: `node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/a.sha && node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/b.sha && diff /tmp/a.sha /tmp/b.sha && echo DETERMINISTIC`
|
||||
Expected: `DETERMINISTIC`
|
||||
|
||||
- [ ] **Step 4: 데이터 변경이 반영되는지 확인 (D의 핵심 검증)**
|
||||
|
||||
Run: `node -e "const fs=require('fs'); const f='data/cards.json'; const o=JSON.parse(fs.readFileSync(f,'utf8')); o.cards.Strike.damage=9; o.cards.Strike.desc='피해 9'; fs.writeFileSync(f, JSON.stringify(o,null,2));" && node tools/gen-slaydeck.mjs >/dev/null && node -e "const j=JSON.parse(require('fs').readFileSync('RootDesk/MyDesk/SlayDeckController.codeblock','utf8')); const sc=j.ContentProto.Json.Methods.find(m=>m.Name==='StartCombat').Code; console.log(/Strike = { name = \"타격\", cost = 1, desc = \"피해 9\", kind = \"Attack\", damage = 9/.test(sc) ? 'CHANGE REFLECTED' : 'NOT REFLECTED')"`
|
||||
Expected: `CHANGE REFLECTED`
|
||||
|
||||
- [ ] **Step 5: 변경 되돌리고 재생성 (원복)**
|
||||
|
||||
Run: `git checkout -- data/cards.json && node tools/gen-slaydeck.mjs >/dev/null && echo reverted`
|
||||
Expected: `reverted`
|
||||
|
||||
- [ ] **Step 6: 잘못된 데이터 fail-fast 확인**
|
||||
|
||||
Run: `node -e "const fs=require('fs'); const o=JSON.parse(fs.readFileSync('data/enemies.json','utf8')); o.activeEnemy='nope'; fs.writeFileSync('/tmp/bad-enemies.json', JSON.stringify(o));" && cp data/enemies.json /tmp/enemies.bak && cp /tmp/bad-enemies.json data/enemies.json; node tools/gen-slaydeck.mjs; echo "exit=$?"; cp /tmp/enemies.bak data/enemies.json`
|
||||
Expected: 에러 메시지 `activeEnemy가 enemies에 없음: nope` + `exit=1`, 이후 원복
|
||||
|
||||
- [ ] **Step 7: 최종 재생성 + git status 확인**
|
||||
|
||||
Run: `node tools/gen-slaydeck.mjs >/dev/null; git checkout -- Global/common.gamelogic 2>/dev/null; git status --short`
|
||||
Expected: `data/*.json`, `tools/gen-slaydeck.mjs`, `ui/DefaultGroup.ui`, `RootDesk/MyDesk/SlayDeckController.codeblock`만 변경(내용 동일한 common 제외).
|
||||
|
||||
- [ ] **Step 8: 생성물 커밋**
|
||||
|
||||
```bash
|
||||
git add ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock
|
||||
git commit -m "재생성(D): 데이터 기반 카드/적 주입 반영"
|
||||
```
|
||||
|
||||
- [ ] **Step 9: 메이커 Play 수동 검증 (사용자)**
|
||||
|
||||
메이커 reload→Play: 기존 B 동작과 동일(데이터 동치라 회귀 없음). 적 슬라임 HP 45·의도 공격10, 카드 3종 효과 정상.
|
||||
|
||||
---
|
||||
|
||||
## Self-Review
|
||||
|
||||
- **Spec coverage:** cards.json/enemies.json 생성(Task1), 로드·검증·직렬화(Task2), StartCombat·속성 데이터화(Task3), UI 파생(Task4), 검증·데이터변경 반영(Task5). 스펙 전 항목 매핑됨.
|
||||
- **Placeholder scan:** 모든 단계 실제 코드/명령 포함. "TODO(E)"류 미래 훅은 본 작업 범위 아님.
|
||||
- **Type consistency:** `luaStr`/`luaCardsTable`/`luaDeckTable`/`luaIntentsTable`/`intentText`/`ACTIVE_ENEMY`/`CARDS`/`ENEMIES` 명칭이 정의부(Task2)와 사용부(Task3·4)에서 일치. 카드 필드(`name/cost/kind/damage/block/desc`)가 데이터(Task1)·직렬화(Task2)·검증(Task5)에서 일치.
|
||||
206
docs/superpowers/plans/2026-06-09-floors.md
Normal file
206
docs/superpowers/plans/2026-06-09-floors.md
Normal file
@@ -0,0 +1,206 @@
|
||||
# 다음 층 / 멀티 act (TODO E6a) Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 보스 클리어 시 즉시 종료 대신 다음 막으로 진행(적 스케일), 최종 막 보스에서 진짜 런 클리어.
|
||||
|
||||
**Architecture:** `Floor`를 막 카운터로 재정의(1..ACT_COUNT). StartCombat에서 적을 막 배율로 스케일, CheckCombatEnd 보스 승리 시 다음 막(같은 맵 재사용)으로. 모두 `gen-slaydeck.mjs`에서 생성.
|
||||
|
||||
**Tech Stack:** Node.js ESM 생성기, MSW Lua codeblock. 검증은 node --check+재생성+결정성+메이커 Play.
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
- Modify: `tools/gen-slaydeck.mjs` — ACT_COUNT 상수, StartRun(Floor=1·RunLength=ACT_COUNT), StartCombat(Floor 제거·적 스케일), CheckCombatEnd(보스 다음 막), RenderRun(막 라벨).
|
||||
|
||||
검증: MSW Lua 단위테스트 불가 → 생성기 문법·재생성·결정성·메이커 Play.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: ACT_COUNT 상수 + StartRun
|
||||
|
||||
**Files:** Modify `tools/gen-slaydeck.mjs`
|
||||
|
||||
- [ ] **Step 1: ACT_COUNT 상수** — `const RELIC_PRICE = 60;` 다음에:
|
||||
|
||||
```js
|
||||
const ACT_COUNT = 3;
|
||||
```
|
||||
|
||||
- [ ] **Step 2: StartRun의 Floor·RunLength 변경** — StartRun 코드에서 아래 두 줄을 교체:
|
||||
|
||||
기존:
|
||||
```
|
||||
self.Floor = 0
|
||||
self.RunLength = ${MAX_ROW}
|
||||
```
|
||||
신규:
|
||||
```
|
||||
self.Floor = 1
|
||||
self.RunLength = ${ACT_COUNT}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 문법 검사**
|
||||
|
||||
Run: `node --check tools/gen-slaydeck.mjs`
|
||||
Expected: 오류 없음
|
||||
|
||||
- [ ] **Step 4: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/gen-slaydeck.mjs
|
||||
git commit -m "gen-slaydeck(E6a): ACT_COUNT·StartRun 막 카운터 초기화"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: StartCombat — Floor 제거 + 적 막 스케일
|
||||
|
||||
**Files:** Modify `tools/gen-slaydeck.mjs`
|
||||
|
||||
- [ ] **Step 1: Floor=node.row 블록 제거 + 적 스케일 적용** — StartCombat 코드의 아래 블록을 교체:
|
||||
|
||||
기존:
|
||||
```
|
||||
local node = self.MapNodes[self.CurrentNodeId]
|
||||
if node ~= nil then
|
||||
self.Floor = node.row
|
||||
end
|
||||
local enemy = self.Enemies[self.CurrentEnemyId]
|
||||
self.PlayerBlock = 0
|
||||
self.EnemyName = enemy.name
|
||||
self.EnemyMaxHp = enemy.maxHp
|
||||
self.EnemyHp = self.EnemyMaxHp
|
||||
self.EnemyBlock = 0
|
||||
self.EnemyIntents = enemy.intents
|
||||
self.EnemyIntentIndex = 1
|
||||
```
|
||||
신규:
|
||||
```
|
||||
local enemy = self.Enemies[self.CurrentEnemyId]
|
||||
local mult = 1 + (self.Floor - 1) * 0.6
|
||||
self.PlayerBlock = 0
|
||||
self.EnemyName = enemy.name
|
||||
self.EnemyMaxHp = math.floor(enemy.maxHp * mult)
|
||||
self.EnemyHp = self.EnemyMaxHp
|
||||
self.EnemyBlock = 0
|
||||
self.EnemyIntents = {}
|
||||
for i = 1, #enemy.intents do
|
||||
self.EnemyIntents[i] = { kind = enemy.intents[i].kind, value = math.floor(enemy.intents[i].value * mult) }
|
||||
end
|
||||
self.EnemyIntentIndex = 1
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 문법 검사**
|
||||
|
||||
Run: `node --check tools/gen-slaydeck.mjs`
|
||||
Expected: 오류 없음
|
||||
|
||||
- [ ] **Step 3: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/gen-slaydeck.mjs
|
||||
git commit -m "gen-slaydeck(E6a): StartCombat 적 막 스케일·Floor 제거"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: CheckCombatEnd 보스 다음 막 + RenderRun 막 라벨
|
||||
|
||||
**Files:** Modify `tools/gen-slaydeck.mjs`
|
||||
|
||||
- [ ] **Step 1: CheckCombatEnd 보스 분기 교체** — 아래 블록을 교체:
|
||||
|
||||
기존:
|
||||
```
|
||||
if node ~= nil and node.type == "boss" then
|
||||
self:ShowResult("런 클리어!")
|
||||
self.RunActive = false
|
||||
else
|
||||
self:OfferReward()
|
||||
end
|
||||
```
|
||||
신규:
|
||||
```
|
||||
if node ~= nil and node.type == "boss" then
|
||||
if self.Floor < self.RunLength then
|
||||
self.Floor = self.Floor + 1
|
||||
self.CurrentNodeId = ""
|
||||
self.CurrentEnemyId = ""
|
||||
self:RenderRun()
|
||||
self:ShowMap()
|
||||
else
|
||||
self:ShowResult("런 클리어!")
|
||||
self.RunActive = false
|
||||
end
|
||||
else
|
||||
self:OfferReward()
|
||||
end
|
||||
```
|
||||
|
||||
- [ ] **Step 2: RenderRun 막 라벨** — RenderRun의 Floor 텍스트 줄을 교체:
|
||||
|
||||
기존:
|
||||
```
|
||||
self:SetText("/ui/DefaultGroup/CombatHud/Floor", "층 " .. string.format("%d", self.Floor) .. "/" .. string.format("%d", self.RunLength))
|
||||
```
|
||||
신규:
|
||||
```
|
||||
self:SetText("/ui/DefaultGroup/CombatHud/Floor", "막 " .. string.format("%d", self.Floor) .. "/" .. string.format("%d", self.RunLength))
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 문법 검사**
|
||||
|
||||
Run: `node --check tools/gen-slaydeck.mjs`
|
||||
Expected: 오류 없음
|
||||
|
||||
- [ ] **Step 4: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/gen-slaydeck.mjs
|
||||
git commit -m "gen-slaydeck(E6a): 보스 승리 다음 막 진행·막 라벨"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: 재생성 + 검증
|
||||
|
||||
**Files:** 생성물
|
||||
|
||||
- [ ] **Step 1: 생성**
|
||||
|
||||
Run: `node tools/gen-slaydeck.mjs`
|
||||
Expected: `Slay deck UI and combat codeblocks generated.`
|
||||
|
||||
- [ ] **Step 2: 스케일·막 진행 코드 확인**
|
||||
|
||||
Run: `node -e "const j=JSON.parse(require('fs').readFileSync('RootDesk/MyDesk/SlayDeckController.codeblock','utf8')); const sc=j.ContentProto.Json.Methods.find(m=>m.Name==='StartCombat').Code; console.log(/mult = 1 \+ \(self.Floor - 1\) \* 0.6/.test(sc)&&/math.floor\(enemy.maxHp \* mult\)/.test(sc)?'SCALE OK':'NO SCALE'); const cc=j.ContentProto.Json.Methods.find(m=>m.Name==='CheckCombatEnd').Code; console.log(/self.Floor = self.Floor \+ 1/.test(cc)?'NEXT-ACT OK':'NO NEXT-ACT')"`
|
||||
Expected: `SCALE OK` / `NEXT-ACT OK`
|
||||
|
||||
- [ ] **Step 3: 결정성**
|
||||
|
||||
Run: `node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/a.sha && node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/b.sha && diff /tmp/a.sha /tmp/b.sha && echo DETERMINISTIC`
|
||||
Expected: `DETERMINISTIC`
|
||||
|
||||
- [ ] **Step 4: git status**
|
||||
|
||||
Run: `git checkout -- Global/common.gamelogic 2>/dev/null; git status --short`
|
||||
Expected: `tools/gen-slaydeck.mjs`, `ui/DefaultGroup.ui`, `RootDesk/MyDesk/SlayDeckController.codeblock` (+docs). (data 변경 없음)
|
||||
|
||||
- [ ] **Step 5: 생성물 커밋**
|
||||
|
||||
```bash
|
||||
git add ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock
|
||||
git commit -m "재생성(E6a): 멀티 act·적 스케일 반영"
|
||||
```
|
||||
|
||||
- [ ] **Step 6: 메이커 Play 수동 검증 (MCP)**
|
||||
|
||||
reload→Play: 1막 보스(슬라임 킹 120) 처치 → 2막 맵(Floor 2)·적 HP 스케일(슬라임 72·보스 192) → 3막 보스 처치 → "런 클리어!". HP/골드/덱/유물 막 간 유지. MCP는 PickNode/PlayCard/CheckCombatEnd 직접 호출 + 로그.
|
||||
|
||||
---
|
||||
|
||||
## Self-Review
|
||||
- **Spec coverage:** ACT_COUNT·StartRun(Task1), StartCombat 스케일·Floor제거(Task2), 보스 다음막·막라벨(Task3), 검증(Task4). 스펙 전 항목 매핑.
|
||||
- **Placeholder scan:** 모든 단계 실제 코드/명령.
|
||||
- **Type consistency:** `Floor`(막 카운터)·`RunLength`(=ACT_COUNT)·`mult` 사용 일관. `EnemyIntents` 새 테이블 생성(공유 변형 없음). CheckCombatEnd의 `node`는 기존 정의 사용. ACT_COUNT 상수 Task1 정의·Task1·3 사용.
|
||||
213
docs/superpowers/plans/2026-06-09-map-camera.md
Normal file
213
docs/superpowers/plans/2026-06-09-map-camera.md
Normal file
@@ -0,0 +1,213 @@
|
||||
# 맵별 고정 카메라 (런타임 설정) Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 맵 로드 시 플레이어 카메라를 `data/camera.json`의 고정 framing(현재 map01 값)으로 설정하는 `MapCamera` 스크립트를 11맵에 부착.
|
||||
|
||||
**Architecture:** 새 CameraComponent를 만들지 않고(엔진 소유), `MapCamera.codeblock`이 OnBeginPlay에서 플레이어의 기존 CameraComponent 속성(ZoomRatio·ScreenOffset·ConfineCameraArea)을 설정. `gen-camera.mjs`가 codeblock 생성 + 11맵 루트에 `script.MapCamera` 부착(idempotent).
|
||||
|
||||
**Tech Stack:** Node.js ESM 생성기, MSW Lua codeblock/map JSON. 검증은 node --check+재생성+JSON유효+결정성+메이커 Play(카메라 적용 확인).
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
- Create: `data/camera.json` — 카메라 framing 값.
|
||||
- Create: `tools/gen-camera.mjs` — MapCamera.codeblock 생성 + 11맵 루트에 script.MapCamera 부착(idempotent).
|
||||
- 생성물: `RootDesk/MyDesk/MapCamera.codeblock`, `map/map01.map`~`map11.map`(패치).
|
||||
|
||||
검증: MSW Lua 단위테스트 불가 → 생성기 문법·JSON유효·결정성·idempotency·메이커 Play.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: data/camera.json + gen-camera.mjs (codeblock 생성)
|
||||
|
||||
**Files:** Create `data/camera.json`, `tools/gen-camera.mjs`
|
||||
|
||||
- [ ] **Step 1: `data/camera.json`** (현재 map01 추출값)
|
||||
|
||||
```json
|
||||
{
|
||||
"zoomRatio": 100,
|
||||
"screenOffsetX": 0.5,
|
||||
"screenOffsetY": 0.655,
|
||||
"confineCameraArea": true
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: `tools/gen-camera.mjs` 작성 — codeblock 생성 부분**
|
||||
|
||||
```js
|
||||
import { readFileSync, writeFileSync } from 'node:fs';
|
||||
|
||||
const CAM = JSON.parse(readFileSync('data/camera.json', 'utf8'));
|
||||
const MAP_NUMBERS = Array.from({ length: 11 }, (_, i) => i + 1); // map01~11
|
||||
|
||||
function method(Name, Code, Arguments = [], ExecSpace = 1) {
|
||||
return {
|
||||
Return: { Type: 'void', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: null },
|
||||
Arguments, Code, Scope: 2, ExecSpace, Attributes: [], Name,
|
||||
};
|
||||
}
|
||||
function prop(Type, Name, DefaultValue = 'nil') {
|
||||
return { Type, DefaultValue, SyncDirection: 0, Attributes: [], Name };
|
||||
}
|
||||
|
||||
function writeCodeblock() {
|
||||
const cb = {
|
||||
Id: '', GameId: '', EntryKey: 'codeblock://mapcamera', ContentType: 'x-mod/codeblock',
|
||||
Content: '', Usage: 0, UsePublish: 1, UseService: 0, CoreVersion: '26.5.0.0',
|
||||
StudioVersion: '', DynamicLoading: 0,
|
||||
ContentProto: { Use: 'Json', Json: {
|
||||
CoreVersion: { Major: 0, Minor: 2 }, ScriptVersion: { Major: 1, Minor: 0 },
|
||||
Description: '', Id: 'MapCamera', Language: 1, Name: 'MapCamera', Type: 1, Source: 0, Target: null,
|
||||
Properties: [ prop('number', 'CamTries', '0') ],
|
||||
Methods: [
|
||||
method('OnBeginPlay', `self.CamTries = 0
|
||||
local eventId = 0
|
||||
local function apply()
|
||||
self.CamTries = self.CamTries + 1
|
||||
local cam = nil
|
||||
local lp = _UserService.LocalPlayer
|
||||
if lp ~= nil then
|
||||
cam = lp.CameraComponent
|
||||
end
|
||||
if cam == nil then
|
||||
cam = _CameraService:GetCurrentCameraComponent()
|
||||
end
|
||||
if cam ~= nil then
|
||||
cam.ZoomRatio = ${CAM.zoomRatio}
|
||||
cam.ScreenOffset = Vector2(${CAM.screenOffsetX}, ${CAM.screenOffsetY})
|
||||
cam.ConfineCameraArea = ${CAM.confineCameraArea}
|
||||
_TimerService:ClearTimer(eventId)
|
||||
elseif self.CamTries > 30 then
|
||||
_TimerService:ClearTimer(eventId)
|
||||
end
|
||||
end
|
||||
eventId = _TimerService:SetTimerRepeat(apply, 0.1)`),
|
||||
],
|
||||
EntityEventHandlers: [],
|
||||
} },
|
||||
};
|
||||
writeFileSync('RootDesk/MyDesk/MapCamera.codeblock', JSON.stringify(cb, null, 2), 'utf8');
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 문법 검사 (맵 패치 전, codeblock만)** — 임시로 `writeCodeblock(); console.log('cb ok');` 호출 추가 후:
|
||||
|
||||
Run: `node --check tools/gen-camera.mjs`
|
||||
Expected: 오류 없음
|
||||
|
||||
- [ ] **Step 4: 커밋**
|
||||
|
||||
```bash
|
||||
git add data/camera.json tools/gen-camera.mjs
|
||||
git commit -m "gen-camera(map-camera): camera.json + MapCamera.codeblock 생성기"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: gen-camera.mjs — 11맵 루트에 script.MapCamera 부착
|
||||
|
||||
**Files:** Modify `tools/gen-camera.mjs`
|
||||
|
||||
- [ ] **Step 1: 맵 패치 함수 + 실행부 추가** (Step 2의 writeCodeblock 임시 호출은 제거)
|
||||
|
||||
```js
|
||||
function patchMap(nn) {
|
||||
const tag = String(nn).padStart(2, '0');
|
||||
const file = `map/map${tag}.map`;
|
||||
const map = JSON.parse(readFileSync(file, 'utf8'));
|
||||
const root = map.ContentProto.Entities.find((e) => e.path === `/maps/map${tag}`);
|
||||
if (!root) throw new Error(`[gen-camera] 맵 루트 없음: ${file}`);
|
||||
const comps = root.jsonString['@components'];
|
||||
// idempotent: 기존 script.MapCamera 제거 후 재추가
|
||||
root.jsonString['@components'] = comps.filter((c) => c['@type'] !== 'script.MapCamera');
|
||||
root.jsonString['@components'].push({ '@type': 'script.MapCamera', Enable: true });
|
||||
const names = (root.componentNames || '').split(',').filter((s) => s && s !== 'script.MapCamera');
|
||||
names.push('script.MapCamera');
|
||||
root.componentNames = names.join(',');
|
||||
writeFileSync(file, JSON.stringify(map, null, 2), 'utf8');
|
||||
return `map${tag}`;
|
||||
}
|
||||
|
||||
writeCodeblock();
|
||||
const patched = MAP_NUMBERS.map(patchMap);
|
||||
console.log('MapCamera codeblock written; patched maps:', patched.join(', '));
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 문법 검사**
|
||||
|
||||
Run: `node --check tools/gen-camera.mjs`
|
||||
Expected: 오류 없음
|
||||
|
||||
- [ ] **Step 3: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/gen-camera.mjs
|
||||
git commit -m "gen-camera(map-camera): 11맵 루트에 script.MapCamera 부착(idempotent)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: 실행 + 정적 검증
|
||||
|
||||
**Files:** 생성물
|
||||
|
||||
- [ ] **Step 1: 생성기 실행**
|
||||
|
||||
Run: `node tools/gen-camera.mjs`
|
||||
Expected: `MapCamera codeblock written; patched maps: map01, ..., map11`
|
||||
|
||||
- [ ] **Step 2: codeblock·부착 확인**
|
||||
|
||||
Run: `node -e "const c=JSON.parse(require('fs').readFileSync('RootDesk/MyDesk/MapCamera.codeblock','utf8')); console.log(c.ContentProto.Json.Id==='MapCamera'&&/ScreenOffset = Vector2\(0.5, 0.655\)/.test(c.ContentProto.Json.Methods[0].Code)?'CB OK':'CB BAD'); for(const nn of [1,6,11]){const tag=String(nn).padStart(2,'0'); const m=JSON.parse(require('fs').readFileSync('map/map'+tag+'.map','utf8')); const r=m.ContentProto.Entities.find(e=>e.path==='/maps/map'+tag); const has=r.componentNames.includes('script.MapCamera')&&r.jsonString['@components'].some(x=>x['@type']==='script.MapCamera'); console.log('map'+tag, has?'ATTACHED':'MISSING');}"`
|
||||
Expected: `CB OK`, `map01 ATTACHED`, `map06 ATTACHED`, `map11 ATTACHED`
|
||||
|
||||
- [ ] **Step 3: idempotency (2회 실행 시 중복 부착 없음)**
|
||||
|
||||
Run: `node tools/gen-camera.mjs >/dev/null && node -e "const m=JSON.parse(require('fs').readFileSync('map/map01.map','utf8')); const r=m.ContentProto.Entities.find(e=>e.path==='/maps/map01'); const n=(r.componentNames.match(/script.MapCamera/g)||[]).length; const c=r.jsonString['@components'].filter(x=>x['@type']==='script.MapCamera').length; console.log('componentNames 횟수='+n+' @components 횟수='+c+(n===1&&c===1?' IDEMPOTENT OK':' DUP!'))"`
|
||||
Expected: `IDEMPOTENT OK`
|
||||
|
||||
- [ ] **Step 4: JSON 유효 + 결정성**
|
||||
|
||||
Run: `for f in map/map*.map; do node -e "JSON.parse(require('fs').readFileSync('$f','utf8'))" || echo "BAD $f"; done; node tools/gen-camera.mjs >/dev/null && sha1sum map/map01.map RootDesk/MyDesk/MapCamera.codeblock > /tmp/a.sha && node tools/gen-camera.mjs >/dev/null && sha1sum map/map01.map RootDesk/MyDesk/MapCamera.codeblock > /tmp/b.sha && diff /tmp/a.sha /tmp/b.sha && echo DETERMINISTIC`
|
||||
Expected: BAD 없음, `DETERMINISTIC`
|
||||
|
||||
- [ ] **Step 5: git status**
|
||||
|
||||
Run: `git status --short`
|
||||
Expected: `map/map01~11.map`, `RootDesk/MyDesk/MapCamera.codeblock`, `data/camera.json`, `tools/gen-camera.mjs` (+docs). map02~11은 freeze/카메라로 변경될 수 있음.
|
||||
|
||||
- [ ] **Step 6: 생성물 커밋**
|
||||
|
||||
```bash
|
||||
git add map/*.map RootDesk/MyDesk/MapCamera.codeblock
|
||||
git commit -m "재생성(map-camera): MapCamera codeblock + 11맵 부착 반영"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: 메이커 Play 런타임 검증 (ExecSpace 확정)
|
||||
|
||||
**Files:** 없음 (런타임 검증; 필요 시 ExecSpace 조정)
|
||||
|
||||
- [ ] **Step 1: reload → Play → map01 카메라 적용 확인**
|
||||
|
||||
메이커 reload 후 Play. `maker_execute_script`(client)로 현재 카메라 값 확인:
|
||||
```lua
|
||||
local cam = _CameraService:GetCurrentCameraComponent()
|
||||
log("Zoom=" .. tostring(cam.ZoomRatio) .. " SO=(" .. tostring(cam.ScreenOffset.x) .. "," .. tostring(cam.ScreenOffset.y) .. ") Confine=" .. tostring(cam.ConfineCameraArea))
|
||||
```
|
||||
Expected: Zoom 100·SO(0.5,0.655)·Confine true (MapCamera가 적용한 값).
|
||||
|
||||
- [ ] **Step 2: ExecSpace 검증/조정** — 만약 카메라 값이 적용 안 되면(스크립트가 클라에서 안 돎), `gen-camera.mjs`의 `method()` 기본 `ExecSpace`를 1↔6 등으로 바꿔 재생성·재확인. (gen-slaydeck는 6으로 클라 동작 확인됨, Monster는 클라 메서드 1.) 적용되는 값으로 확정.
|
||||
|
||||
- [ ] **Step 3: (선택) framing 변경 반영 확인** — `data/camera.json`의 zoomRatio를 70으로 임시 변경 → 재생성 → Play에서 Zoom 70 확인 → 원복(`git checkout -- data/camera.json` + 재생성).
|
||||
|
||||
---
|
||||
|
||||
## Self-Review
|
||||
- **Spec coverage:** camera.json·codeblock(Task1), 11맵 부착(Task2), 정적검증·idempotency(Task3), 런타임·ExecSpace(Task4). 스펙 항목 매핑.
|
||||
- **Placeholder scan:** 실제 코드/명령 포함. (Task4 ExecSpace 조정은 런타임 결과 의존 — 의도된 검증 분기.)
|
||||
- **Type consistency:** `writeCodeblock`/`patchMap`/`method`/`prop` 일관. codeblock Id 'MapCamera' ↔ componentName `script.MapCamera` ↔ EntryKey `codeblock://mapcamera` 일치. camera.json 필드(zoomRatio/screenOffsetX/Y/confineCameraArea)가 codeblock 생성에서 사용됨.
|
||||
- **리스크:** 맵 루트 스크립트의 client OnBeginPlay 실행/ExecSpace는 런타임 검증으로 확정(Task4). LocalPlayer.CameraComponent 타이밍은 재시도 타이머로 흡수.
|
||||
493
docs/superpowers/plans/2026-06-09-map-nodes.md
Normal file
493
docs/superpowers/plans/2026-06-09-map-nodes.md
Normal file
@@ -0,0 +1,493 @@
|
||||
# 분기 맵 노드 진행 (TODO E3) Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 플레이어가 작성된 분기 맵(DAG)에서 다음 노드를 선택해 전투/엘리트/보스로 진행, 보스 클리어 시 "런 클리어".
|
||||
|
||||
**Architecture:** `data/map.json`(그래프)·`data/enemies.json`(다중 적)을 `gen-slaydeck.mjs`가 로드·주입. SlayDeckController에 맵 상태·네비게이션 메서드 추가, MapHud UI 생성. 자동 진행 대신 ShowMap→PickNode→StartCombat→보상→ShowMap 루프.
|
||||
|
||||
**Tech Stack:** Node.js ESM 생성기, MSW Lua codeblock/UI. 검증은 node --check+재생성+결정성+메이커 Play.
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
- Create: `data/map.json` — 분기 맵.
|
||||
- Modify: `data/enemies.json` — slime_elite·slime_boss 추가.
|
||||
- Modify: `tools/gen-slaydeck.mjs` — 맵/적 로드·검증·직렬화 헬퍼, method() returnType, 속성·메서드·MapHud UI.
|
||||
|
||||
검증: MSW Lua 단위테스트 불가 → 생성기 문법·재생성·결정성·메이커 Play.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 데이터 + 로드·검증·직렬화 헬퍼
|
||||
|
||||
**Files:** Create `data/map.json`; Modify `data/enemies.json`, `tools/gen-slaydeck.mjs`
|
||||
|
||||
- [ ] **Step 1: `data/map.json` 작성**
|
||||
|
||||
```json
|
||||
{
|
||||
"start": ["A", "B"],
|
||||
"nodes": {
|
||||
"A": { "type": "combat", "enemy": "slime", "row": 1, "col": -1, "next": ["C", "D"] },
|
||||
"B": { "type": "combat", "enemy": "slime", "row": 1, "col": 1, "next": ["D", "E"] },
|
||||
"C": { "type": "elite", "enemy": "slime_elite", "row": 2, "col": -2, "next": ["BOSS"] },
|
||||
"D": { "type": "combat", "enemy": "slime", "row": 2, "col": 0, "next": ["BOSS"] },
|
||||
"E": { "type": "combat", "enemy": "slime", "row": 2, "col": 2, "next": ["BOSS"] },
|
||||
"BOSS": { "type": "boss", "enemy": "slime_boss", "row": 3, "col": 0, "next": [] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: `data/enemies.json`에 엘리트·보스 추가** — `slime` 항목 다음에:
|
||||
|
||||
```json
|
||||
"slime_elite": {
|
||||
"name": "정예 슬라임",
|
||||
"maxHp": 70,
|
||||
"intents": [
|
||||
{ "kind": "Attack", "value": 14 },
|
||||
{ "kind": "Attack", "value": 8 },
|
||||
{ "kind": "Defend", "value": 10 }
|
||||
]
|
||||
},
|
||||
"slime_boss": {
|
||||
"name": "슬라임 킹",
|
||||
"maxHp": 120,
|
||||
"intents": [
|
||||
{ "kind": "Attack", "value": 18 },
|
||||
{ "kind": "Defend", "value": 12 },
|
||||
{ "kind": "Attack", "value": 10 },
|
||||
{ "kind": "Attack", "value": 22 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 생성기 상단에 map 로드·검증·헬퍼 추가** — `const ACTIVE_ENEMY = ...;` 다음에:
|
||||
|
||||
```js
|
||||
const MAP = JSON.parse(readFileSync('data/map.json', 'utf8'));
|
||||
for (const id of MAP.start) {
|
||||
if (!MAP.nodes[id]) throw new Error(`[gen-slaydeck] map.start에 없는 노드 id: ${id}`);
|
||||
}
|
||||
for (const [id, n] of Object.entries(MAP.nodes)) {
|
||||
if (!ENEMIES.enemies[n.enemy]) throw new Error(`[gen-slaydeck] 노드 ${id}의 enemy 없음: ${n.enemy}`);
|
||||
for (const nx of n.next) {
|
||||
if (!MAP.nodes[nx]) throw new Error(`[gen-slaydeck] 노드 ${id}.next에 없는 노드 id: ${nx}`);
|
||||
}
|
||||
}
|
||||
const MAX_ROW = Math.max(...Object.values(MAP.nodes).map((n) => n.row));
|
||||
|
||||
function luaIntentsArray(intents) {
|
||||
return '{ ' + intents.map((it) => `{ kind = ${luaStr(it.kind)}, value = ${it.value} }`).join(', ') + ' }';
|
||||
}
|
||||
function luaEnemiesTable(enemies) {
|
||||
const lines = Object.entries(enemies).map(([id, e]) =>
|
||||
`\t${id} = { name = ${luaStr(e.name)}, maxHp = ${e.maxHp}, intents = ${luaIntentsArray(e.intents)} },`);
|
||||
return `self.Enemies = {\n${lines.join('\n')}\n}`;
|
||||
}
|
||||
function luaMapNodesTable(nodes) {
|
||||
const lines = Object.entries(nodes).map(([id, n]) => {
|
||||
const nx = '{ ' + n.next.map(luaStr).join(', ') + ' }';
|
||||
return `\t${id} = { type = ${luaStr(n.type)}, enemy = ${luaStr(n.enemy)}, row = ${n.row}, col = ${n.col}, next = ${nx} },`;
|
||||
});
|
||||
return `self.MapNodes = {\n${lines.join('\n')}\n}`;
|
||||
}
|
||||
function luaStartArray(start) {
|
||||
return 'self.MapStart = { ' + start.map(luaStr).join(', ') + ' }';
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: method()에 ReturnType 파라미터 추가** — 기존 method 함수를:
|
||||
|
||||
```js
|
||||
function method(Name, Code, Arguments = [], ExecSpace = 0, ReturnType = 'void') {
|
||||
return {
|
||||
Return: { Type: ReturnType, DefaultValue: null, SyncDirection: 0, Attributes: [], Name: null },
|
||||
Arguments,
|
||||
Code,
|
||||
Scope: 2,
|
||||
ExecSpace,
|
||||
Attributes: [],
|
||||
Name,
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 5: JSON·문법 검사**
|
||||
|
||||
Run: `node -e "JSON.parse(require('fs').readFileSync('data/map.json','utf8')); JSON.parse(require('fs').readFileSync('data/enemies.json','utf8')); console.log('JSON OK')" && node --check tools/gen-slaydeck.mjs`
|
||||
Expected: `JSON OK` + 오류 없음
|
||||
|
||||
- [ ] **Step 6: 커밋**
|
||||
|
||||
```bash
|
||||
git add data/map.json data/enemies.json tools/gen-slaydeck.mjs
|
||||
git commit -m "data(E3): 분기 맵 map.json·엘리트/보스 적 + 직렬화 헬퍼"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: 맵 속성 + StartRun(맵 빌드·ShowMap)
|
||||
|
||||
**Files:** Modify `tools/gen-slaydeck.mjs`
|
||||
|
||||
- [ ] **Step 1: 맵 상태 속성 추가** — `prop('boolean', 'RunActive', 'false'),` 다음에:
|
||||
|
||||
```js
|
||||
prop('any', 'Enemies'),
|
||||
prop('any', 'MapNodes'),
|
||||
prop('any', 'MapStart'),
|
||||
prop('string', 'CurrentNodeId', '""'),
|
||||
prop('string', 'CurrentEnemyId', '""'),
|
||||
```
|
||||
|
||||
- [ ] **Step 2: StartRun 교체** — 맵 빌드 + ShowMap:
|
||||
|
||||
```js
|
||||
method('StartRun', `self.PlayerMaxHp = 80
|
||||
self.PlayerHp = self.PlayerMaxHp
|
||||
self.Gold = 0
|
||||
self.Floor = 0
|
||||
self.RunLength = ${MAX_ROW}
|
||||
self.RunDeck = { ${CARDS.starterDeck.map(luaStr).join(', ')} }
|
||||
self.RunActive = true
|
||||
${luaEnemiesTable(ENEMIES.enemies)}
|
||||
${luaMapNodesTable(MAP.nodes)}
|
||||
${luaStartArray(MAP.start)}
|
||||
self.CurrentNodeId = ""
|
||||
self.CurrentEnemyId = ""
|
||||
self:BindButtons()
|
||||
self:ShowMap()`),
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 문법 검사**
|
||||
|
||||
Run: `node --check tools/gen-slaydeck.mjs`
|
||||
Expected: 오류 없음
|
||||
|
||||
- [ ] **Step 4: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/gen-slaydeck.mjs
|
||||
git commit -m "gen-slaydeck(E3): 맵 상태 속성·StartRun 맵 빌드/ShowMap"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: StartCombat·CheckCombatEnd·PickReward (맵 연동)
|
||||
|
||||
**Files:** Modify `tools/gen-slaydeck.mjs`
|
||||
|
||||
- [ ] **Step 1: StartCombat 교체** — 적을 self.Enemies에서 로드, Floor=노드 row:
|
||||
|
||||
```js
|
||||
method('StartCombat', `self.MaxEnergy = 3
|
||||
self.Turn = 0
|
||||
local node = self.MapNodes[self.CurrentNodeId]
|
||||
if node ~= nil then
|
||||
self.Floor = node.row
|
||||
end
|
||||
local enemy = self.Enemies[self.CurrentEnemyId]
|
||||
self.PlayerBlock = 0
|
||||
self.EnemyName = enemy.name
|
||||
self.EnemyMaxHp = enemy.maxHp
|
||||
self.EnemyHp = self.EnemyMaxHp
|
||||
self.EnemyBlock = 0
|
||||
self.EnemyIntents = enemy.intents
|
||||
self.EnemyIntentIndex = 1
|
||||
self.CombatOver = false
|
||||
self.DiscardPile = {}
|
||||
self.Hand = {}
|
||||
${luaCardsTable(CARDS.cards)}
|
||||
self.DrawPile = {}
|
||||
for i = 1, #self.RunDeck do
|
||||
self.DrawPile[i] = self.RunDeck[i]
|
||||
end
|
||||
self:Shuffle(self.DrawPile)
|
||||
self:RenderCombat()
|
||||
self:StartPlayerTurn()`),
|
||||
```
|
||||
|
||||
- [ ] **Step 2: CheckCombatEnd 교체** — 보스 노드면 런 클리어:
|
||||
|
||||
```js
|
||||
method('CheckCombatEnd', `if self.EnemyHp <= 0 then
|
||||
self.CombatOver = true
|
||||
self.Gold = self.Gold + ${GOLD_PER_WIN}
|
||||
self:RenderRun()
|
||||
local node = self.MapNodes[self.CurrentNodeId]
|
||||
if node ~= nil and node.type == "boss" then
|
||||
self:ShowResult("런 클리어!")
|
||||
self.RunActive = false
|
||||
else
|
||||
self:OfferReward()
|
||||
end
|
||||
elseif self.PlayerHp <= 0 then
|
||||
self.CombatOver = true
|
||||
self:ShowResult("패배...")
|
||||
self.RunActive = false
|
||||
end`),
|
||||
```
|
||||
|
||||
- [ ] **Step 3: PickReward 마지막을 ShowMap으로** — PickReward 코드의 마지막 `self:StartCombat()`를 `self:ShowMap()`로 교체. (그 외 동일)
|
||||
|
||||
```js
|
||||
method('PickReward', `if self.CombatOver ~= true or self.RunActive ~= true then
|
||||
return
|
||||
end
|
||||
if slot ~= 0 and self.RewardChoices ~= nil then
|
||||
local id = self.RewardChoices[slot]
|
||||
if id ~= nil then
|
||||
table.insert(self.RunDeck, id)
|
||||
end
|
||||
end
|
||||
local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/RewardHud")
|
||||
if hud ~= nil then
|
||||
hud.Enable = false
|
||||
end
|
||||
self:ShowMap()`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }]),
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 문법 검사**
|
||||
|
||||
Run: `node --check tools/gen-slaydeck.mjs`
|
||||
Expected: 오류 없음
|
||||
|
||||
- [ ] **Step 5: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/gen-slaydeck.mjs
|
||||
git commit -m "gen-slaydeck(E3): StartCombat 적 데이터화·보스 런클리어·보상후 맵복귀"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: ShowMap·IsReachable·PickNode·RenderMap + BindButtons
|
||||
|
||||
**Files:** Modify `tools/gen-slaydeck.mjs`
|
||||
|
||||
- [ ] **Step 1: 맵 메서드 추가** — PickReward 메서드 다음(마지막 `]);` 직전)에 삽입:
|
||||
|
||||
```js
|
||||
method('ShowMap', `self:RenderMap()
|
||||
local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/MapHud")
|
||||
if hud ~= nil then
|
||||
hud.Enable = true
|
||||
end`),
|
||||
method('IsReachable', `local list
|
||||
if self.CurrentNodeId == "" then
|
||||
list = self.MapStart
|
||||
else
|
||||
local node = self.MapNodes[self.CurrentNodeId]
|
||||
if node == nil then
|
||||
return false
|
||||
end
|
||||
list = node.next
|
||||
end
|
||||
for i = 1, #list do
|
||||
if list[i] == id then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'id' }], 0, 'boolean'),
|
||||
method('RenderMap', `for id, node in pairs(self.MapNodes) do
|
||||
local e = _EntityService:GetEntityByPath("/ui/DefaultGroup/MapHud/Node_" .. id)
|
||||
if e ~= nil then
|
||||
local reachable = self:IsReachable(id)
|
||||
if e.SpriteGUIRendererComponent ~= nil then
|
||||
if reachable then
|
||||
e.SpriteGUIRendererComponent.Color = Color(0.3, 0.55, 0.85, 1)
|
||||
else
|
||||
e.SpriteGUIRendererComponent.Color = Color(0.2, 0.22, 0.26, 0.6)
|
||||
end
|
||||
end
|
||||
if e.ButtonComponent ~= nil then
|
||||
e.ButtonComponent.Enable = reachable
|
||||
end
|
||||
end
|
||||
end`),
|
||||
method('PickNode', `if self.RunActive ~= true then
|
||||
return
|
||||
end
|
||||
if self:IsReachable(id) ~= true then
|
||||
return
|
||||
end
|
||||
self.CurrentNodeId = id
|
||||
self.CurrentEnemyId = self.MapNodes[id].enemy
|
||||
local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/MapHud")
|
||||
if hud ~= nil then
|
||||
hud.Enable = false
|
||||
end
|
||||
self:StartCombat()`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'id' }]),
|
||||
```
|
||||
|
||||
- [ ] **Step 2: BindButtons에 맵 노드 버튼 바인딩 추가** — BindButtons 코드의 마지막 `end`(skip 바인딩) 다음에 추가. BindButtons 끝부분의 skip 블록 다음에 붙이도록, skip 블록을 아래로 교체:
|
||||
|
||||
```js
|
||||
local skip = _EntityService:GetEntityByPath("/ui/DefaultGroup/RewardHud/Skip")
|
||||
if skip ~= nil and skip.ButtonComponent ~= nil then
|
||||
skip:ConnectEvent(ButtonClickEvent, function() self:PickReward(0) end)
|
||||
end
|
||||
local mapNodeIds = { ${Object.keys(MAP.nodes).map(luaStr).join(', ')} }
|
||||
for i = 1, #mapNodeIds do
|
||||
local nid = mapNodeIds[i]
|
||||
local mn = _EntityService:GetEntityByPath("/ui/DefaultGroup/MapHud/Node_" .. nid)
|
||||
if mn ~= nil and mn.ButtonComponent ~= nil then
|
||||
mn:ConnectEvent(ButtonClickEvent, function() self:PickNode(nid) end)
|
||||
end
|
||||
end`),
|
||||
```
|
||||
(BindButtons 전체에서 기존 skip 블록 `local skip = ... end`)` 부분을 위 블록으로 교체)
|
||||
|
||||
- [ ] **Step 3: 문법 검사**
|
||||
|
||||
Run: `node --check tools/gen-slaydeck.mjs`
|
||||
Expected: 오류 없음
|
||||
|
||||
- [ ] **Step 4: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/gen-slaydeck.mjs
|
||||
git commit -m "gen-slaydeck(E3): ShowMap/IsReachable/PickNode/RenderMap·맵 노드 바인딩"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: MapHud UI 생성
|
||||
|
||||
**Files:** Modify `tools/gen-slaydeck.mjs` (`guid`, `upsertUi`)
|
||||
|
||||
- [ ] **Step 1: guid 'map' 분기** — ns 매핑에 추가:
|
||||
|
||||
```js
|
||||
const ns = prefix === 'hud' ? 0xd0 : prefix === 'dck' ? 0xca : prefix === 'cmb' ? 0xcb : prefix === 'rwd' ? 0xcc : prefix === 'map' ? 0xcd : 0xfe;
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 필터 확장** — upsertUi 필터에 MapHud 추가:
|
||||
|
||||
```js
|
||||
ui.ContentProto.Entities = E.filter((e) => !e.path.startsWith('/ui/DefaultGroup/DeckHud') && !e.path.startsWith('/ui/DefaultGroup/CombatHud') && !e.path.startsWith('/ui/DefaultGroup/RewardHud') && !e.path.startsWith('/ui/DefaultGroup/MapHud'));
|
||||
```
|
||||
|
||||
- [ ] **Step 3: MapHud 그룹 생성** — `ui.ContentProto.Entities.push(...reward);` 다음에 삽입:
|
||||
|
||||
```js
|
||||
const TYPE_KO = { combat: '전투', elite: '엘리트', boss: '보스' };
|
||||
const map = [];
|
||||
const mapHud = entity({
|
||||
id: guid('map', 0),
|
||||
path: '/ui/DefaultGroup/MapHud',
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
||||
displayOrder: 7,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1920, y: 1080 }, pos: { x: 0, y: 0 }, align: ALIGN_CENTER }),
|
||||
sprite({ color: { r: 0.05, g: 0.06, b: 0.09, a: 0.9 }, type: 1, raycast: true }),
|
||||
],
|
||||
});
|
||||
mapHud.jsonString.enable = false;
|
||||
map.push(mapHud);
|
||||
map.push(entity({
|
||||
id: guid('map', 1),
|
||||
path: '/ui/DefaultGroup/MapHud/Title',
|
||||
modelId: 'uitext',
|
||||
entryId: 'UIText',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent',
|
||||
displayOrder: 0,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 700, y: 60 }, pos: { x: 0, y: 510 } }),
|
||||
sprite({ color: TRANSPARENT }),
|
||||
text({ value: '다음 노드 선택', fontSize: 40, bold: true, color: GOLD, alignment: 4 }),
|
||||
],
|
||||
}));
|
||||
let mapN = 2;
|
||||
for (const [id, node] of Object.entries(MAP.nodes)) {
|
||||
const nodePath = `/ui/DefaultGroup/MapHud/Node_${id}`;
|
||||
const pos = { x: node.col * 180, y: node.row * 170 - 80 };
|
||||
map.push(entity({
|
||||
id: guid('map', mapN++),
|
||||
path: nodePath,
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent',
|
||||
displayOrder: node.row,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 150, y: 80 }, pos }),
|
||||
sprite({ color: { r: 0.3, g: 0.55, b: 0.85, a: 1 }, type: 1, raycast: true }),
|
||||
button(),
|
||||
],
|
||||
}));
|
||||
map.push(entity({
|
||||
id: guid('map', mapN++),
|
||||
path: `${nodePath}/Label`,
|
||||
modelId: 'uitext',
|
||||
entryId: 'UIText',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent',
|
||||
displayOrder: 0,
|
||||
components: [
|
||||
transform({ parentW: 150, parentH: 80, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 144, y: 72 }, pos: { x: 0, y: 0 } }),
|
||||
sprite({ color: TRANSPARENT }),
|
||||
text({ value: `${TYPE_KO[node.type]}\n${ENEMIES.enemies[node.enemy].name}`, fontSize: 20, bold: true }),
|
||||
],
|
||||
}));
|
||||
}
|
||||
ui.ContentProto.Entities.push(...map);
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 문법 검사**
|
||||
|
||||
Run: `node --check tools/gen-slaydeck.mjs`
|
||||
Expected: 오류 없음
|
||||
|
||||
- [ ] **Step 5: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/gen-slaydeck.mjs
|
||||
git commit -m "gen-slaydeck(E3): MapHud 노드 맵 UI 생성"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 6: 재생성 + 검증
|
||||
|
||||
**Files:** 생성물
|
||||
|
||||
- [ ] **Step 1: 생성**
|
||||
|
||||
Run: `node tools/gen-slaydeck.mjs`
|
||||
Expected: `Slay deck UI and combat codeblocks generated.`
|
||||
|
||||
- [ ] **Step 2: 메서드·UI·적 주입 확인**
|
||||
|
||||
Run: `node -e "const j=JSON.parse(require('fs').readFileSync('RootDesk/MyDesk/SlayDeckController.codeblock','utf8')); const n=j.ContentProto.Json.Methods.map(m=>m.Name); console.log(['ShowMap','PickNode','IsReachable','RenderMap'].every(x=>n.includes(x))?'METHODS OK':'MISSING'); const sc=j.ContentProto.Json.Methods.find(m=>m.Name==='StartRun').Code; console.log(/slime_boss/.test(sc)&&/슬라임 킹/.test(sc)?'ENEMIES OK':'NO ENEMIES'); const u=JSON.parse(require('fs').readFileSync('ui/DefaultGroup.ui','utf8')); const has=p=>u.ContentProto.Entities.some(e=>e.path===p); console.log(has('/ui/DefaultGroup/MapHud')&&has('/ui/DefaultGroup/MapHud/Node_BOSS')&&has('/ui/DefaultGroup/MapHud/Node_A/Label')?'UI OK':'UI MISSING')"`
|
||||
Expected: `METHODS OK` / `ENEMIES OK` / `UI OK`
|
||||
|
||||
- [ ] **Step 3: 결정성**
|
||||
|
||||
Run: `node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/a.sha && node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/b.sha && diff /tmp/a.sha /tmp/b.sha && echo DETERMINISTIC`
|
||||
Expected: `DETERMINISTIC`
|
||||
|
||||
- [ ] **Step 4: git status**
|
||||
|
||||
Run: `git checkout -- Global/common.gamelogic 2>/dev/null; git status --short`
|
||||
Expected: `data/*`, `tools/gen-slaydeck.mjs`, `ui/DefaultGroup.ui`, `RootDesk/MyDesk/SlayDeckController.codeblock` (+docs).
|
||||
|
||||
- [ ] **Step 5: 생성물 커밋**
|
||||
|
||||
```bash
|
||||
git add ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock
|
||||
git commit -m "재생성(E3): 분기 맵·다중 적 반영"
|
||||
```
|
||||
|
||||
- [ ] **Step 6: 메이커 Play 수동 검증 (MCP)**
|
||||
|
||||
reload→Play: StartRun → MapHud(A·B만 클릭 가능) → PickNode("A") → 슬라임 전투 → 승리 → 보상 → 맵(C·D 활성) → PickNode("C") → 정예 슬라임(HP70) → ... → BOSS → 슬라임킹(HP120) → 승리 → "런 클리어!". 도달 불가 노드 PickNode → 무시. MCP는 `PickNode`/`PlayCard`/`PickReward` 직접 호출 + 상태 로그로 검증.
|
||||
|
||||
---
|
||||
|
||||
## Self-Review
|
||||
- **Spec coverage:** map.json/적(Task1), 맵 상태·StartRun(Task2), StartCombat 적데이터·보스클리어·보상후맵(Task3), Show/Pick/Reachable/RenderMap·바인딩(Task4), MapHud UI(Task5), 검증(Task6). 스펙 전 항목 매핑.
|
||||
- **Placeholder scan:** 모든 단계 실제 코드/명령.
|
||||
- **Type consistency:** 메서드 `StartRun/ShowMap/IsReachable/PickNode/RenderMap/StartCombat/CheckCombatEnd/PickReward` 정의·호출 일치. 속성 `Enemies/MapNodes/MapStart/CurrentNodeId/CurrentEnemyId` 정의(Task2)·사용(Task3·4) 일치. UI 경로 `/ui/DefaultGroup/MapHud/Node_{id}`·`/Label`가 codeblock(RenderMap/PickNode/BindButtons)·생성(Task5)에서 동일(노드 id는 map.json 키). `IsReachable`는 boolean 반환(method returnType param, Task1). enemy 필드 `name/maxHp/intents`가 데이터·luaEnemiesTable·StartCombat에서 일치.
|
||||
400
docs/superpowers/plans/2026-06-09-relics.md
Normal file
400
docs/superpowers/plans/2026-06-09-relics.md
Normal file
@@ -0,0 +1,400 @@
|
||||
# 유물 (TODO E5) Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 훅 기반 유물 패시브 + 3획득 경로(시작/엘리트/상점)를 추가한다.
|
||||
|
||||
**Architecture:** `data/relics.json`을 생성기가 주입(self.Relics). `ApplyRelics(hook)`을 전투시작/턴시작/카드사용/보상 4지점에서 호출. `AddRelic`을 3경로가 공유. ShopHud 유물 슬롯·상단 유물 바 UI. 모두 `gen-slaydeck.mjs`에서 생성.
|
||||
|
||||
**Tech Stack:** Node.js ESM 생성기, MSW Lua codeblock/UI. 검증은 node --check+재생성+결정성+메이커 Play.
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
- Create: `data/relics.json`.
|
||||
- Modify: `tools/gen-slaydeck.mjs` — 로드/검증/직렬화, 상수, 속성, 훅 메서드(ApplyRelics/AddRelic/RenderRelics), 4지점 통합, 상점 유물(BuyRelic), UI(유물 바·상점 유물 슬롯).
|
||||
|
||||
검증: MSW Lua 단위테스트 불가 → 생성기 문법·재생성·결정성·메이커 Play.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 데이터 + 로드/직렬화 + 상수/속성 + 훅 메서드
|
||||
|
||||
**Files:** Create `data/relics.json`; Modify `tools/gen-slaydeck.mjs`
|
||||
|
||||
- [ ] **Step 1: `data/relics.json` 작성**
|
||||
|
||||
```json
|
||||
{
|
||||
"relics": {
|
||||
"ironHeart": { "name": "강철 심장", "desc": "전투 시작 시 방어도 +6", "hook": "combatStart", "effect": "block", "value": 6 },
|
||||
"energyCore": { "name": "에너지 코어", "desc": "턴 시작 시 에너지 +1", "hook": "turnStart", "effect": "energy", "value": 1 },
|
||||
"vampire": { "name": "흡혈 송곳니", "desc": "공격 카드 사용 시 HP +1", "hook": "cardPlayed", "effect": "healOnAttack", "value": 1 },
|
||||
"goldIdol": { "name": "황금 우상", "desc": "전투 승리 시 골드 +10", "hook": "combatReward", "effect": "gold", "value": 10 }
|
||||
},
|
||||
"startingRelic": "ironHeart",
|
||||
"relicPool": ["energyCore", "vampire", "goldIdol"]
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 로드·검증·직렬화 헬퍼** — `const MAP = ...` 로드 블록 다음(MAX_ROW 정의 뒤)에 추가:
|
||||
|
||||
```js
|
||||
const RELICS = JSON.parse(readFileSync('data/relics.json', 'utf8'));
|
||||
if (!RELICS.relics[RELICS.startingRelic]) throw new Error(`[gen-slaydeck] startingRelic 없음: ${RELICS.startingRelic}`);
|
||||
for (const id of RELICS.relicPool) {
|
||||
if (!RELICS.relics[id]) throw new Error(`[gen-slaydeck] relicPool에 없는 유물 id: ${id}`);
|
||||
}
|
||||
function luaRelicsTable(relics) {
|
||||
const lines = Object.entries(relics).map(([id, r]) =>
|
||||
`\t${id} = { name = ${luaStr(r.name)}, desc = ${luaStr(r.desc)}, hook = ${luaStr(r.hook)}, effect = ${luaStr(r.effect)}, value = ${r.value} },`);
|
||||
return `self.Relics = {\n${lines.join('\n')}\n}`;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: RELIC_PRICE 상수** — `const REST_HEAL = 30;` 다음에:
|
||||
|
||||
```js
|
||||
const RELIC_PRICE = 60;
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 속성 추가** — `prop('any', 'ShopBought'),` 다음에:
|
||||
|
||||
```js
|
||||
prop('any', 'Relics'),
|
||||
prop('any', 'RunRelics'),
|
||||
prop('any', 'RelicPool'),
|
||||
prop('string', 'ShopRelic', '""'),
|
||||
prop('boolean', 'ShopRelicBought', 'false'),
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 훅 메서드 추가** — PickReward 메서드 다음(ShowMap 앞 아무 곳, 마지막 `]);` 전 임의 위치)에 삽입:
|
||||
|
||||
```js
|
||||
method('ApplyRelics', `if self.RunRelics == nil then
|
||||
return
|
||||
end
|
||||
for i = 1, #self.RunRelics do
|
||||
local r = self.Relics[self.RunRelics[i]]
|
||||
if r ~= nil and r.hook == hook then
|
||||
if r.effect == "block" then
|
||||
self.PlayerBlock = self.PlayerBlock + r.value
|
||||
elseif r.effect == "energy" then
|
||||
self.Energy = self.Energy + r.value
|
||||
elseif r.effect == "healOnAttack" then
|
||||
self.PlayerHp = self.PlayerHp + r.value
|
||||
if self.PlayerHp > self.PlayerMaxHp then
|
||||
self.PlayerHp = self.PlayerMaxHp
|
||||
end
|
||||
elseif r.effect == "gold" then
|
||||
self.Gold = self.Gold + r.value
|
||||
end
|
||||
end
|
||||
end`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'hook' }]),
|
||||
method('AddRelic', `if self.RunRelics == nil then
|
||||
self.RunRelics = {}
|
||||
end
|
||||
table.insert(self.RunRelics, id)
|
||||
self:RenderRelics()`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'id' }]),
|
||||
method('RenderRelics', `local names = ""
|
||||
if self.RunRelics ~= nil then
|
||||
for i = 1, #self.RunRelics do
|
||||
local r = self.Relics[self.RunRelics[i]]
|
||||
if r ~= nil then
|
||||
if names == "" then
|
||||
names = r.name
|
||||
else
|
||||
names = names .. ", " .. r.name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if names == "" then
|
||||
names = "없음"
|
||||
end
|
||||
self:SetText("/ui/DefaultGroup/CombatHud/Relics", "유물: " .. names)`),
|
||||
```
|
||||
|
||||
- [ ] **Step 6: JSON·문법 검사**
|
||||
|
||||
Run: `node -e "JSON.parse(require('fs').readFileSync('data/relics.json','utf8')); console.log('JSON OK')" && node --check tools/gen-slaydeck.mjs`
|
||||
Expected: `JSON OK` + 오류 없음
|
||||
|
||||
- [ ] **Step 7: 커밋**
|
||||
|
||||
```bash
|
||||
git add data/relics.json tools/gen-slaydeck.mjs
|
||||
git commit -m "data(E5): 유물 데이터 + 훅 시스템(ApplyRelics/AddRelic/RenderRelics)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: 훅 4지점 통합 + 시작/엘리트 획득
|
||||
|
||||
**Files:** Modify `tools/gen-slaydeck.mjs`
|
||||
|
||||
- [ ] **Step 1: StartRun에 유물 주입·시작 유물** — StartRun 코드에서 `self.RunActive = true` 다음에 삽입:
|
||||
|
||||
```
|
||||
self.RunRelics = {}
|
||||
${luaRelicsTable(RELICS.relics)}
|
||||
self.RelicPool = { ${RELICS.relicPool.map(luaStr).join(', ')} }
|
||||
```
|
||||
그리고 StartRun의 `self:ShowMap()` **직전**에 삽입:
|
||||
```
|
||||
self:AddRelic("${RELICS.startingRelic}")
|
||||
```
|
||||
|
||||
- [ ] **Step 2: StartCombat에 combatStart 훅** — StartCombat 끝 `self:StartPlayerTurn()`를 아래로 교체:
|
||||
|
||||
```
|
||||
self:StartPlayerTurn()
|
||||
self:ApplyRelics("combatStart")
|
||||
self:RenderCombat()
|
||||
```
|
||||
|
||||
- [ ] **Step 3: StartPlayerTurn에 turnStart 훅** — `self.Energy = self.MaxEnergy` 다음 줄에 삽입:
|
||||
|
||||
```
|
||||
self:ApplyRelics("turnStart")
|
||||
```
|
||||
|
||||
- [ ] **Step 4: PlayCard Attack 분기에 cardPlayed 훅** — PlayCard의 Attack 분기를 교체:
|
||||
|
||||
```
|
||||
if c.kind == "Attack" then
|
||||
if c.damage ~= nil then
|
||||
self:DealDamageToEnemy(c.damage)
|
||||
end
|
||||
self:ApplyRelics("cardPlayed")
|
||||
elseif c.kind == "Skill" then
|
||||
```
|
||||
(기존: `if c.kind == "Attack" then\n\tif c.damage ~= nil then\n\t\tself:DealDamageToEnemy(c.damage)\n\tend\nelseif c.kind == "Skill" then` 에서 `end` 다음에 `\n\tself:ApplyRelics("cardPlayed")` 추가)
|
||||
|
||||
- [ ] **Step 5: CheckCombatEnd에 combatReward 훅 + 엘리트 유물** — CheckCombatEnd 승리부를 교체:
|
||||
|
||||
```
|
||||
if self.EnemyHp <= 0 then
|
||||
self.CombatOver = true
|
||||
self.Gold = self.Gold + ${GOLD_PER_WIN}
|
||||
self:ApplyRelics("combatReward")
|
||||
self:RenderRun()
|
||||
local node = self.MapNodes[self.CurrentNodeId]
|
||||
if node ~= nil and node.type == "elite" then
|
||||
self:AddRelic(self.RelicPool[math.random(1, #self.RelicPool)])
|
||||
end
|
||||
if node ~= nil and node.type == "boss" then
|
||||
self:ShowResult("런 클리어!")
|
||||
self.RunActive = false
|
||||
else
|
||||
self:OfferReward()
|
||||
end
|
||||
elseif self.PlayerHp <= 0 then
|
||||
self.CombatOver = true
|
||||
self:ShowResult("패배...")
|
||||
self.RunActive = false
|
||||
end
|
||||
```
|
||||
|
||||
- [ ] **Step 6: 문법 검사**
|
||||
|
||||
Run: `node --check tools/gen-slaydeck.mjs`
|
||||
Expected: 오류 없음
|
||||
|
||||
- [ ] **Step 7: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/gen-slaydeck.mjs
|
||||
git commit -m "gen-slaydeck(E5): 훅 4지점 통합·시작/엘리트 유물 획득"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: 상점 유물 (ShowShop/RenderShop/BuyRelic) + 바인딩
|
||||
|
||||
**Files:** Modify `tools/gen-slaydeck.mjs`
|
||||
|
||||
- [ ] **Step 1: ShowShop에 유물 선택 추가** — ShowShop의 `self.ShopBought = { false, false, false }` 다음에:
|
||||
|
||||
```
|
||||
self.ShopRelic = self.RelicPool[math.random(1, #self.RelicPool)]
|
||||
self.ShopRelicBought = false
|
||||
```
|
||||
|
||||
- [ ] **Step 2: RenderShop 끝에 유물 슬롯 렌더 + BuyRelic 메서드** — RenderShop 코드의 마지막 카드 for-loop `end` 다음(닫는 백틱 직전)에 추가:
|
||||
|
||||
```
|
||||
local rr = self.Relics[self.ShopRelic]
|
||||
if rr ~= nil then
|
||||
self:SetText("/ui/DefaultGroup/ShopHud/Relic/Label", rr.name .. " — " .. rr.desc)
|
||||
self:SetText("/ui/DefaultGroup/ShopHud/Relic/Price", string.format("%d", ${RELIC_PRICE}) .. " 골드")
|
||||
local e = _EntityService:GetEntityByPath("/ui/DefaultGroup/ShopHud/Relic")
|
||||
if e ~= nil and e.SpriteGUIRendererComponent ~= nil then
|
||||
if self.ShopRelicBought == true then
|
||||
e.SpriteGUIRendererComponent.Color = Color(0.2, 0.22, 0.26, 0.6)
|
||||
else
|
||||
e.SpriteGUIRendererComponent.Color = Color(0.7, 0.55, 0.85, 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
그리고 RenderShop 메서드 다음에 BuyRelic 메서드 추가:
|
||||
|
||||
```js
|
||||
method('BuyRelic', `if self.ShopRelicBought == true then
|
||||
return
|
||||
end
|
||||
if self.Gold < ${RELIC_PRICE} then
|
||||
return
|
||||
end
|
||||
self.Gold = self.Gold - ${RELIC_PRICE}
|
||||
self:AddRelic(self.ShopRelic)
|
||||
self.ShopRelicBought = true
|
||||
self:RenderShop()
|
||||
self:RenderRun()`),
|
||||
```
|
||||
|
||||
- [ ] **Step 3: BindButtons에 유물 슬롯 바인딩** — BindButtons의 shopLeave 바인딩 다음(restLeave 앞)에 삽입:
|
||||
|
||||
```
|
||||
local shopRelic = _EntityService:GetEntityByPath("/ui/DefaultGroup/ShopHud/Relic")
|
||||
if shopRelic ~= nil and shopRelic.ButtonComponent ~= nil then
|
||||
shopRelic:ConnectEvent(ButtonClickEvent, function() self:BuyRelic() end)
|
||||
end
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 문법 검사**
|
||||
|
||||
Run: `node --check tools/gen-slaydeck.mjs`
|
||||
Expected: 오류 없음
|
||||
|
||||
- [ ] **Step 5: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/gen-slaydeck.mjs
|
||||
git commit -m "gen-slaydeck(E5): 상점 유물 슬롯·BuyRelic·바인딩"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: UI — 유물 바 + 상점 유물 슬롯
|
||||
|
||||
**Files:** Modify `tools/gen-slaydeck.mjs` (`upsertUi`)
|
||||
|
||||
- [ ] **Step 1: CombatHud 유물 바 추가** — CombatHud의 Floor/Gold for-loop 다음(`const result = entity({` 앞)에 삽입:
|
||||
|
||||
```js
|
||||
combat.push(entity({
|
||||
id: guid('cmb', cmbN++),
|
||||
path: '/ui/DefaultGroup/CombatHud/Relics',
|
||||
modelId: 'uitext',
|
||||
entryId: 'UIText',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent',
|
||||
displayOrder: 9,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1000, y: 40 }, pos: { x: 0, y: 430 } }),
|
||||
sprite({ color: TRANSPARENT }),
|
||||
text({ value: '유물: 없음', fontSize: 22, bold: true, color: { r: 0.8, g: 0.7, b: 0.95, a: 1 }, alignment: 4 }),
|
||||
],
|
||||
}));
|
||||
```
|
||||
|
||||
- [ ] **Step 2: ShopHud 유물 슬롯 추가** — ShopHud의 Leave 버튼 push 직전에 삽입:
|
||||
|
||||
```js
|
||||
shop.push(entity({
|
||||
id: guid('shp', shpN++),
|
||||
path: '/ui/DefaultGroup/ShopHud/Relic',
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent',
|
||||
displayOrder: 9,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 560, y: 76 }, pos: { x: 0, y: -190 } }),
|
||||
sprite({ color: { r: 0.7, g: 0.55, b: 0.85, a: 1 }, type: 1, raycast: true }),
|
||||
button(),
|
||||
],
|
||||
}));
|
||||
shop.push(entity({
|
||||
id: guid('shp', shpN++),
|
||||
path: '/ui/DefaultGroup/ShopHud/Relic/Label',
|
||||
modelId: 'uitext',
|
||||
entryId: 'UIText',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent',
|
||||
displayOrder: 0,
|
||||
components: [
|
||||
transform({ parentW: 560, parentH: 76, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 540, y: 40 }, pos: { x: 0, y: 12 } }),
|
||||
sprite({ color: TRANSPARENT }),
|
||||
text({ value: '유물', fontSize: 22, bold: true, color: { r: 1, g: 1, b: 1, a: 1 }, alignment: 4 }),
|
||||
],
|
||||
}));
|
||||
shop.push(entity({
|
||||
id: guid('shp', shpN++),
|
||||
path: '/ui/DefaultGroup/ShopHud/Relic/Price',
|
||||
modelId: 'uitext',
|
||||
entryId: 'UIText',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent',
|
||||
displayOrder: 1,
|
||||
components: [
|
||||
transform({ parentW: 560, parentH: 76, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 540, y: 30 }, pos: { x: 0, y: -22 } }),
|
||||
sprite({ color: TRANSPARENT }),
|
||||
text({ value: '60 골드', fontSize: 20, bold: true, color: { r: 0.98, g: 0.85, b: 0.4, a: 1 }, alignment: 4 }),
|
||||
],
|
||||
}));
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 문법 검사**
|
||||
|
||||
Run: `node --check tools/gen-slaydeck.mjs`
|
||||
Expected: 오류 없음
|
||||
|
||||
- [ ] **Step 4: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/gen-slaydeck.mjs
|
||||
git commit -m "gen-slaydeck(E5): 유물 바·상점 유물 슬롯 UI"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: 재생성 + 검증
|
||||
|
||||
**Files:** 생성물
|
||||
|
||||
- [ ] **Step 1: 생성**
|
||||
|
||||
Run: `node tools/gen-slaydeck.mjs`
|
||||
Expected: `Slay deck UI and combat codeblocks generated.`
|
||||
|
||||
- [ ] **Step 2: 메서드·UI·데이터 확인**
|
||||
|
||||
Run: `node -e "const j=JSON.parse(require('fs').readFileSync('RootDesk/MyDesk/SlayDeckController.codeblock','utf8')); const n=j.ContentProto.Json.Methods.map(m=>m.Name); console.log(['ApplyRelics','AddRelic','RenderRelics','BuyRelic'].every(x=>n.includes(x))?'METHODS OK':'MISSING'); const sr=j.ContentProto.Json.Methods.find(m=>m.Name==='StartRun').Code; console.log(/ironHeart/.test(sr)&&/강철 심장/.test(sr)?'RELICS OK':'NO RELICS'); const u=JSON.parse(require('fs').readFileSync('ui/DefaultGroup.ui','utf8')); const has=p=>u.ContentProto.Entities.some(e=>e.path===p); console.log(has('/ui/DefaultGroup/CombatHud/Relics')&&has('/ui/DefaultGroup/ShopHud/Relic/Label')?'UI OK':'UI MISSING')"`
|
||||
Expected: `METHODS OK` / `RELICS OK` / `UI OK`
|
||||
|
||||
- [ ] **Step 3: 결정성**
|
||||
|
||||
Run: `node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/a.sha && node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/b.sha && diff /tmp/a.sha /tmp/b.sha && echo DETERMINISTIC`
|
||||
Expected: `DETERMINISTIC`
|
||||
|
||||
- [ ] **Step 4: git status**
|
||||
|
||||
Run: `git checkout -- Global/common.gamelogic 2>/dev/null; git status --short`
|
||||
Expected: `data/relics.json`, `tools/gen-slaydeck.mjs`, `ui/DefaultGroup.ui`, `RootDesk/MyDesk/SlayDeckController.codeblock` (+docs).
|
||||
|
||||
- [ ] **Step 5: 생성물 커밋**
|
||||
|
||||
```bash
|
||||
git add ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock
|
||||
git commit -m "재생성(E5): 유물 시스템·UI 반영"
|
||||
```
|
||||
|
||||
- [ ] **Step 6: 메이커 Play 수동 검증 (MCP)**
|
||||
|
||||
reload→Play: 시작 유물(강철심장)→전투 시작 PlayerBlock 6 / energyCore 보유 시 턴 에너지 4 / vampire 보유 시 공격 HP+1 / goldIdol 승리 골드+25 / 엘리트 승리→유물 획득(바 갱신) / 상점 유물 구매(골드-60). MCP는 AddRelic/BuyRelic/PlayCard/PickNode 직접 호출 + 로그.
|
||||
|
||||
---
|
||||
|
||||
## Self-Review
|
||||
- **Spec coverage:** 데이터/로드/훅메서드(Task1), 4지점통합·시작·엘리트(Task2), 상점유물(Task3), UI(Task4), 검증(Task5). 스펙 전 항목 매핑.
|
||||
- **Placeholder scan:** 모든 단계 실제 코드/명령.
|
||||
- **Type consistency:** 메서드 `ApplyRelics/AddRelic/RenderRelics/BuyRelic` 정의·호출·바인딩 일치. 속성 `Relics/RunRelics/RelicPool/ShopRelic/ShopRelicBought` 정의(Task1·1)·사용(Task2·3) 일치. UI 경로 `/CombatHud/Relics`·`/ShopHud/Relic/{Label,Price}`가 codeblock(RenderRelics/RenderShop)·생성(Task4)에서 동일. 유물 필드 `name/desc/hook/effect/value` 데이터·luaRelicsTable·ApplyRelics 일치. 상수 `RELIC_PRICE` Task1 정의·Task3 사용.
|
||||
438
docs/superpowers/plans/2026-06-09-run-loop-core.md
Normal file
438
docs/superpowers/plans/2026-06-09-run-loop-core.md
Normal file
@@ -0,0 +1,438 @@
|
||||
# 런 루프 코어 (TODO E1+E2) Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 단일 전투를 연속 N전투 런으로 확장 — 런 상태(HP/골드/덱) 영속 + 승리 후 카드 1택 보상 + 다음 전투 + N전투 후 "런 클리어".
|
||||
|
||||
**Architecture:** 기존 `SlayDeckController`(gen-slaydeck.mjs 생성)에 런 상태·보상 메서드 추가. StartRun(영속 초기화·버튼 1회 바인딩) vs StartCombat(전투별 초기화, RunDeck에서 드로) 분리. RewardHud UI 생성.
|
||||
|
||||
**Tech Stack:** Node.js ESM 생성기, MSW Lua codeblock/UI. 검증은 node --check+재생성+결정성+메이커 Play.
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
- Modify: `tools/gen-slaydeck.mjs` — 유일 변경 대상.
|
||||
- `writeCodeblocks`: 런 상수, 새 속성, OnBeginPlay/StartRun/StartCombat/BindButtons/CheckCombatEnd/OfferReward/ApplyRewardVisual/PickReward/RenderRun/RenderCombat.
|
||||
- `upsertUi`: CombatHud에 Floor/Gold, RewardHud 그룹 생성, 필터 확장, guid 'rwd' 분기.
|
||||
|
||||
MSW Lua 단위 테스트 불가 → 검증은 생성기 문법·재생성·결정성·메이커 Play.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 런 상수·속성·StartRun
|
||||
|
||||
**Files:** Modify `tools/gen-slaydeck.mjs`
|
||||
|
||||
- [ ] **Step 1: 런 상수 추가** — `writeCodeblocks()` 함수 본문 첫 줄에 삽입:
|
||||
|
||||
```js
|
||||
const RUN_LENGTH = 3;
|
||||
const GOLD_PER_WIN = 15;
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 새 속성 추가** — 속성 배열의 `prop('any', 'EnemyName'),` 다음에:
|
||||
|
||||
```js
|
||||
prop('any', 'RunDeck'),
|
||||
prop('number', 'Gold', '0'),
|
||||
prop('number', 'Floor', '0'),
|
||||
prop('number', 'RunLength', String(RUN_LENGTH)),
|
||||
prop('any', 'RewardChoices'),
|
||||
prop('boolean', 'RunActive', 'false'),
|
||||
```
|
||||
|
||||
- [ ] **Step 3: OnBeginPlay → StartRun** — `method('OnBeginPlay', \`self:StartCombat()\`),` 를:
|
||||
|
||||
```js
|
||||
method('OnBeginPlay', `self:StartRun()`),
|
||||
```
|
||||
|
||||
- [ ] **Step 4: StartRun 메서드 추가** — OnBeginPlay 다음에 삽입:
|
||||
|
||||
```js
|
||||
method('StartRun', `self.PlayerMaxHp = 80
|
||||
self.PlayerHp = self.PlayerMaxHp
|
||||
self.Gold = 0
|
||||
self.Floor = 0
|
||||
self.RunLength = ${RUN_LENGTH}
|
||||
self.RunDeck = { ${CARDS.starterDeck.map(luaStr).join(', ')} }
|
||||
self.RunActive = true
|
||||
self:BindButtons()
|
||||
self:StartCombat()`),
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 문법 검사**
|
||||
|
||||
Run: `node --check tools/gen-slaydeck.mjs`
|
||||
Expected: 오류 없음
|
||||
|
||||
- [ ] **Step 6: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/gen-slaydeck.mjs
|
||||
git commit -m "gen-slaydeck(E1): 런 상태 속성·StartRun 추가"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: StartCombat 수정 + BindButtons 수정
|
||||
|
||||
**Files:** Modify `tools/gen-slaydeck.mjs`
|
||||
|
||||
- [ ] **Step 1: StartCombat 본문 교체** — `method('StartCombat', \`...\`)`의 코드를 아래로(HP 보존·Floor++·RunDeck에서 드로·BindButtons 호출 제거):
|
||||
|
||||
```js
|
||||
method('StartCombat', `self.MaxEnergy = 3
|
||||
self.Turn = 0
|
||||
self.Floor = self.Floor + 1
|
||||
self.PlayerBlock = 0
|
||||
self.EnemyName = ${luaStr(ACTIVE_ENEMY.name)}
|
||||
self.EnemyMaxHp = ${ACTIVE_ENEMY.maxHp}
|
||||
self.EnemyHp = self.EnemyMaxHp
|
||||
self.EnemyBlock = 0
|
||||
${luaIntentsTable(ACTIVE_ENEMY.intents)}
|
||||
self.EnemyIntentIndex = 1
|
||||
self.CombatOver = false
|
||||
self.DiscardPile = {}
|
||||
self.Hand = {}
|
||||
${luaCardsTable(CARDS.cards)}
|
||||
self.DrawPile = {}
|
||||
for i = 1, #self.RunDeck do
|
||||
self.DrawPile[i] = self.RunDeck[i]
|
||||
end
|
||||
self:Shuffle(self.DrawPile)
|
||||
self:RenderCombat()
|
||||
self:StartPlayerTurn()`),
|
||||
```
|
||||
|
||||
- [ ] **Step 2: BindButtons에 보상 버튼 바인딩 추가** — BindButtons 코드 끝(마지막 `end` 다음)에 추가. 현재 마지막 부분:
|
||||
```
|
||||
for i = 1, 5 do
|
||||
local cardEntity = _EntityService:GetEntityByPath("/ui/DefaultGroup/CardHand/Card" .. tostring(i))
|
||||
if cardEntity ~= nil and cardEntity.ButtonComponent ~= nil then
|
||||
cardEntity:ConnectEvent(ButtonClickEvent, function() self:PlayCard(i) end)
|
||||
end
|
||||
end
|
||||
```
|
||||
뒤에 이어붙이도록 BindButtons 코드를 아래 전체로 교체:
|
||||
|
||||
```js
|
||||
method('BindButtons', `local endTurn = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckHud/EndTurnButton")
|
||||
if endTurn ~= nil and endTurn.ButtonComponent ~= nil then
|
||||
if self.EndTurnHandler ~= nil then
|
||||
endTurn:DisconnectEvent(ButtonClickEvent, self.EndTurnHandler)
|
||||
self.EndTurnHandler = nil
|
||||
end
|
||||
self.EndTurnHandler = endTurn:ConnectEvent(ButtonClickEvent, function() self:EndPlayerTurn() end)
|
||||
end
|
||||
for i = 1, 5 do
|
||||
local cardEntity = _EntityService:GetEntityByPath("/ui/DefaultGroup/CardHand/Card" .. tostring(i))
|
||||
if cardEntity ~= nil and cardEntity.ButtonComponent ~= nil then
|
||||
cardEntity:ConnectEvent(ButtonClickEvent, function() self:PlayCard(i) end)
|
||||
end
|
||||
end
|
||||
for i = 1, 3 do
|
||||
local rc = _EntityService:GetEntityByPath("/ui/DefaultGroup/RewardHud/Reward" .. tostring(i))
|
||||
if rc ~= nil and rc.ButtonComponent ~= nil then
|
||||
rc:ConnectEvent(ButtonClickEvent, function() self:PickReward(i) end)
|
||||
end
|
||||
end
|
||||
local skip = _EntityService:GetEntityByPath("/ui/DefaultGroup/RewardHud/Skip")
|
||||
if skip ~= nil and skip.ButtonComponent ~= nil then
|
||||
skip:ConnectEvent(ButtonClickEvent, function() self:PickReward(0) end)
|
||||
end`),
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 문법 검사**
|
||||
|
||||
Run: `node --check tools/gen-slaydeck.mjs`
|
||||
Expected: 오류 없음
|
||||
|
||||
- [ ] **Step 4: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/gen-slaydeck.mjs
|
||||
git commit -m "gen-slaydeck(E1): StartCombat 런 분리(HP보존·RunDeck드로)·BindButtons 1회+보상버튼"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: CheckCombatEnd·OfferReward·PickReward·RenderRun
|
||||
|
||||
**Files:** Modify `tools/gen-slaydeck.mjs`
|
||||
|
||||
- [ ] **Step 1: CheckCombatEnd 교체** — 보상/런클리어/패배 분기:
|
||||
|
||||
```js
|
||||
method('CheckCombatEnd', `if self.EnemyHp <= 0 then
|
||||
self.CombatOver = true
|
||||
self.Gold = self.Gold + ${GOLD_PER_WIN}
|
||||
self:RenderRun()
|
||||
if self.Floor >= self.RunLength then
|
||||
self:ShowResult("런 클리어!")
|
||||
self.RunActive = false
|
||||
else
|
||||
self:OfferReward()
|
||||
end
|
||||
elseif self.PlayerHp <= 0 then
|
||||
self.CombatOver = true
|
||||
self:ShowResult("패배...")
|
||||
self.RunActive = false
|
||||
end`),
|
||||
```
|
||||
|
||||
- [ ] **Step 2: OfferReward·ApplyRewardVisual·PickReward·RenderRun 추가** — RenderCombat 메서드 다음에 삽입:
|
||||
|
||||
```js
|
||||
method('OfferReward', `local pool = {}
|
||||
for id, _ in pairs(self.Cards) do
|
||||
table.insert(pool, id)
|
||||
end
|
||||
self.RewardChoices = {}
|
||||
for i = 1, 3 do
|
||||
self.RewardChoices[i] = pool[math.random(1, #pool)]
|
||||
self:ApplyRewardVisual(i, self.RewardChoices[i])
|
||||
end
|
||||
local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/RewardHud")
|
||||
if hud ~= nil then
|
||||
hud.Enable = true
|
||||
end`),
|
||||
method('ApplyRewardVisual', `local c = self.Cards[cardId]
|
||||
if c == nil then
|
||||
return
|
||||
end
|
||||
local base = "/ui/DefaultGroup/RewardHud/Reward" .. tostring(slot)
|
||||
self:SetText(base .. "/Name", c.name)
|
||||
self:SetText(base .. "/Cost", tostring(c.cost))
|
||||
self:SetText(base .. "/Desc", c.desc)
|
||||
local e = _EntityService:GetEntityByPath(base)
|
||||
if e ~= nil and e.SpriteGUIRendererComponent ~= nil then
|
||||
if c.kind == "Attack" then
|
||||
e.SpriteGUIRendererComponent.Color = Color(0.86, 0.42, 0.38, 1)
|
||||
elseif c.kind == "Skill" then
|
||||
e.SpriteGUIRendererComponent.Color = Color(0.42, 0.55, 0.85, 1)
|
||||
else
|
||||
e.SpriteGUIRendererComponent.Color = Color(0.46, 0.68, 0.52, 1)
|
||||
end
|
||||
end`, [
|
||||
{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' },
|
||||
{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'cardId' },
|
||||
]),
|
||||
method('PickReward', `if self.CombatOver ~= true or self.RunActive ~= true then
|
||||
return
|
||||
end
|
||||
if slot ~= 0 and self.RewardChoices ~= nil then
|
||||
local id = self.RewardChoices[slot]
|
||||
if id ~= nil then
|
||||
table.insert(self.RunDeck, id)
|
||||
end
|
||||
end
|
||||
local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/RewardHud")
|
||||
if hud ~= nil then
|
||||
hud.Enable = false
|
||||
end
|
||||
self:StartCombat()`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }]),
|
||||
method('RenderRun', `self:SetText("/ui/DefaultGroup/CombatHud/Floor", "층 " .. string.format("%d", self.Floor) .. "/" .. string.format("%d", self.RunLength))
|
||||
self:SetText("/ui/DefaultGroup/CombatHud/Gold", "골드 " .. string.format("%d", self.Gold))`),
|
||||
```
|
||||
|
||||
- [ ] **Step 3: RenderCombat 끝에 RenderRun 호출 추가** — RenderCombat 코드의 마지막 줄(`...PlayerBlock...`) 다음에 `\nself:RenderRun()` 추가. 즉 RenderCombat 마지막을:
|
||||
```
|
||||
self:SetText("/ui/DefaultGroup/CombatHud/PlayerBlock", "방어 " .. string.format("%d", self.PlayerBlock))
|
||||
self:RenderRun()
|
||||
```
|
||||
로. (Edit: 기존 마지막 줄 끝에 `\nself:RenderRun()` 삽입)
|
||||
|
||||
- [ ] **Step 4: 문법 검사**
|
||||
|
||||
Run: `node --check tools/gen-slaydeck.mjs`
|
||||
Expected: 오류 없음
|
||||
|
||||
- [ ] **Step 5: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/gen-slaydeck.mjs
|
||||
git commit -m "gen-slaydeck(E2): 보상(OfferReward/PickReward)·런 클리어·층/골드 렌더"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: UI — CombatHud 층/골드 + RewardHud
|
||||
|
||||
**Files:** Modify `tools/gen-slaydeck.mjs` (`upsertUi`, `guid`)
|
||||
|
||||
- [ ] **Step 1: guid 'rwd' 분기 추가** — guid()의 ns 매핑을:
|
||||
|
||||
```js
|
||||
const ns = prefix === 'hud' ? 0xd0 : prefix === 'dck' ? 0xca : prefix === 'cmb' ? 0xcb : prefix === 'rwd' ? 0xcc : 0xfe;
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 정리 필터 확장** — upsertUi 시작부 필터를:
|
||||
|
||||
```js
|
||||
ui.ContentProto.Entities = E.filter((e) => !e.path.startsWith('/ui/DefaultGroup/DeckHud') && !e.path.startsWith('/ui/DefaultGroup/CombatHud') && !e.path.startsWith('/ui/DefaultGroup/RewardHud'));
|
||||
```
|
||||
|
||||
- [ ] **Step 3: CombatHud에 Floor·Gold 텍스트 추가** — `const result = entity({` 선언 직전(즉 result 추가 전)에 삽입:
|
||||
|
||||
```js
|
||||
for (const [suffix, pos, value, color] of [
|
||||
['Floor', { x: -820, y: 480 }, '층 1/3', GOLD],
|
||||
['Gold', { x: 820, y: 480 }, '골드 0', { r: 0.98, g: 0.85, b: 0.4, a: 1 }],
|
||||
]) {
|
||||
combat.push(entity({
|
||||
id: guid('cmb', cmbN++),
|
||||
path: `/ui/DefaultGroup/CombatHud/${suffix}`,
|
||||
modelId: 'uitext',
|
||||
entryId: 'UIText',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent',
|
||||
displayOrder: 9,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 240, y: 44 }, pos }),
|
||||
sprite({ color: TRANSPARENT }),
|
||||
text({ value, fontSize: 26, bold: true, color, alignment: 4 }),
|
||||
],
|
||||
}));
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: RewardHud 그룹 생성** — `ui.ContentProto.Entities.push(...combat);` 직후, `JSON.parse(JSON.stringify(ui));` 직전에 삽입:
|
||||
|
||||
```js
|
||||
const reward = [];
|
||||
const rewardHud = entity({
|
||||
id: guid('rwd', 0),
|
||||
path: '/ui/DefaultGroup/RewardHud',
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
||||
displayOrder: 6,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1920, y: 1080 }, pos: { x: 0, y: 0 }, align: ALIGN_CENTER }),
|
||||
sprite({ color: { r: 0.04, g: 0.05, b: 0.07, a: 0.86 }, type: 1, raycast: true }),
|
||||
],
|
||||
});
|
||||
rewardHud.jsonString.enable = false;
|
||||
reward.push(rewardHud);
|
||||
reward.push(entity({
|
||||
id: guid('rwd', 1),
|
||||
path: '/ui/DefaultGroup/RewardHud/Title',
|
||||
modelId: 'uitext',
|
||||
entryId: 'UIText',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent',
|
||||
displayOrder: 0,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 700, y: 64 }, pos: { x: 0, y: 300 } }),
|
||||
sprite({ color: TRANSPARENT }),
|
||||
text({ value: '보상 카드 선택', fontSize: 44, bold: true, color: GOLD, alignment: 4 }),
|
||||
],
|
||||
}));
|
||||
let rwdN = 2;
|
||||
const rewardXs = [-300, 0, 300];
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
const cardPath = `/ui/DefaultGroup/RewardHud/Reward${i}`;
|
||||
reward.push(entity({
|
||||
id: guid('rwd', rwdN++),
|
||||
path: cardPath,
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent',
|
||||
displayOrder: i,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: CARD_W, y: CARD_H }, pos: { x: rewardXs[i - 1], y: 0 } }),
|
||||
sprite({ color: ATTACK, type: 1, raycast: true }),
|
||||
button(),
|
||||
],
|
||||
}));
|
||||
for (const [suffix, cfg] of [
|
||||
['Cost', { size: { x: 50, y: 50 }, pos: { x: -60, y: 95 }, value: '1', fontSize: 34, bold: true }],
|
||||
['Name', { size: { x: 160, y: 50 }, pos: { x: 0, y: 50 }, value: '카드', fontSize: 26, bold: true }],
|
||||
['Desc', { size: { x: 160, y: 82 }, pos: { x: 0, y: -80 }, value: '', fontSize: 20, bold: false }],
|
||||
]) {
|
||||
reward.push(entity({
|
||||
id: guid('rwd', rwdN++),
|
||||
path: `${cardPath}/${suffix}`,
|
||||
modelId: 'uitext',
|
||||
entryId: 'UIText',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent',
|
||||
displayOrder: suffix === 'Cost' ? 0 : suffix === 'Name' ? 1 : 2,
|
||||
components: [
|
||||
transform({ parentW: CARD_W, parentH: CARD_H, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: cfg.size, pos: cfg.pos }),
|
||||
sprite({ color: TRANSPARENT }),
|
||||
text({ value: cfg.value, fontSize: cfg.fontSize, bold: cfg.bold }),
|
||||
],
|
||||
}));
|
||||
}
|
||||
}
|
||||
reward.push(entity({
|
||||
id: guid('rwd', rwdN++),
|
||||
path: '/ui/DefaultGroup/RewardHud/Skip',
|
||||
modelId: 'uibutton',
|
||||
entryId: 'UIButton',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent',
|
||||
displayOrder: 10,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 200, y: 60 }, pos: { x: 0, y: -260 } }),
|
||||
sprite({ color: DARK, type: 1, raycast: true }),
|
||||
button(),
|
||||
text({ value: '건너뛰기', fontSize: 26, bold: true, color: GOLD, alignment: 4 }),
|
||||
],
|
||||
}));
|
||||
ui.ContentProto.Entities.push(...reward);
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 문법 검사**
|
||||
|
||||
Run: `node --check tools/gen-slaydeck.mjs`
|
||||
Expected: 오류 없음
|
||||
|
||||
- [ ] **Step 6: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/gen-slaydeck.mjs
|
||||
git commit -m "gen-slaydeck(E2): CombatHud 층/골드 + RewardHud(보상 카드 3+건너뛰기) UI"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: 재생성 + 검증
|
||||
|
||||
**Files:** 생성물 2종
|
||||
|
||||
- [ ] **Step 1: 생성**
|
||||
|
||||
Run: `node tools/gen-slaydeck.mjs`
|
||||
Expected: `Slay deck UI and combat codeblocks generated.`
|
||||
|
||||
- [ ] **Step 2: 메서드·UI 생성 확인**
|
||||
|
||||
Run: `node -e "const j=JSON.parse(require('fs').readFileSync('RootDesk/MyDesk/SlayDeckController.codeblock','utf8')); const n=j.ContentProto.Json.Methods.map(m=>m.Name); console.log(['StartRun','OfferReward','PickReward','RenderRun','ApplyRewardVisual'].every(x=>n.includes(x))?'METHODS OK':'MISSING'); const u=JSON.parse(require('fs').readFileSync('ui/DefaultGroup.ui','utf8')); console.log(u.ContentProto.Entities.some(e=>e.path==='/ui/DefaultGroup/RewardHud')&&u.ContentProto.Entities.some(e=>e.path==='/ui/DefaultGroup/CombatHud/Gold')?'UI OK':'UI MISSING')"`
|
||||
Expected: `METHODS OK` / `UI OK`
|
||||
|
||||
- [ ] **Step 3: 결정성**
|
||||
|
||||
Run: `node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/a.sha && node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/b.sha && diff /tmp/a.sha /tmp/b.sha && echo DETERMINISTIC`
|
||||
Expected: `DETERMINISTIC`
|
||||
|
||||
- [ ] **Step 4: git status (의도 파일만)**
|
||||
|
||||
Run: `git checkout -- Global/common.gamelogic 2>/dev/null; git status --short`
|
||||
Expected: `tools/gen-slaydeck.mjs`, `ui/DefaultGroup.ui`, `RootDesk/MyDesk/SlayDeckController.codeblock` (+docs).
|
||||
|
||||
- [ ] **Step 5: 생성물 커밋**
|
||||
|
||||
```bash
|
||||
git add ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock
|
||||
git commit -m "재생성(E1+E2): 런 루프·보상 UI 반영"
|
||||
```
|
||||
|
||||
- [ ] **Step 6: 메이커 Play 수동 검증 (사용자/MCP)**
|
||||
|
||||
reload→Play: 승리 → RewardHud 카드 3장·골드+15·층 표시 → 1택 시 RunDeck+1·다음 전투(HP 유지) → 3전투째 승리 시 "런 클리어!". 패배 시 "패배...". MCP는 `PlayCard`/`EndPlayerTurn`/`PickReward` 직접 호출 + 상태 로그로 검증.
|
||||
|
||||
---
|
||||
|
||||
## Self-Review
|
||||
- **Spec coverage:** 상수·속성·StartRun(Task1), StartCombat분리·BindButtons1회(Task2), 보상·런클리어·렌더(Task3), 층/골드·RewardHud UI(Task4), 검증(Task5). 스펙 전 항목 매핑.
|
||||
- **Placeholder scan:** 모든 단계 실제 코드/명령.
|
||||
- **Type consistency:** 메서드명 `StartRun/StartCombat/BindButtons/CheckCombatEnd/OfferReward/ApplyRewardVisual/PickReward/RenderRun/RenderCombat` 정의·호출 일치. UI 경로 `/ui/DefaultGroup/RewardHud/Reward{1..3}/{Name,Cost,Desc}`·`/Skip`·`/CombatHud/{Floor,Gold}`가 codeblock(ApplyRewardVisual/RenderRun/BindButtons)과 생성(Task4) 일치. 속성 `RunDeck/Gold/Floor/RunLength/RewardChoices/RunActive` 정의(Task1)·사용(Task2·3) 일치.
|
||||
488
docs/superpowers/plans/2026-06-09-shop-rest.md
Normal file
488
docs/superpowers/plans/2026-06-09-shop-rest.md
Normal file
@@ -0,0 +1,488 @@
|
||||
# 상점/휴식 노드 (TODO E4) Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 맵에 상점(골드→카드)·휴식(HP 회복) 노드를 추가하고, 진입 시 전투 대신 상점/휴식 UI로 분기.
|
||||
|
||||
**Architecture:** `data/map.json`에 shop/rest 노드 추가(enemy 없음). SlayDeckController에 상점/휴식 메서드, PickNode 타입 분기, ShopHud/RestHud UI. 모두 `gen-slaydeck.mjs`에서 생성.
|
||||
|
||||
**Tech Stack:** Node.js ESM 생성기, MSW Lua codeblock/UI. 검증은 node --check+재생성+결정성+메이커 Play.
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
- Modify: `data/map.json` — 4행, shop/rest 노드.
|
||||
- Modify: `tools/gen-slaydeck.mjs` — 검증 완화, enemy 조건부 직렬화, 상수, 속성, PickNode 분기, 상점/휴식 메서드, ShopHud/RestHud UI, MapHud y 중앙정렬.
|
||||
|
||||
검증: MSW Lua 단위테스트 불가 → 생성기 문법·재생성·결정성·메이커 Play.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 데이터 + 검증완화 + enemy 조건부 직렬화 + 상수·속성
|
||||
|
||||
**Files:** Modify `data/map.json`, `tools/gen-slaydeck.mjs`
|
||||
|
||||
- [ ] **Step 1: `data/map.json` 교체**
|
||||
|
||||
```json
|
||||
{
|
||||
"start": ["A", "B"],
|
||||
"nodes": {
|
||||
"A": { "type": "combat", "enemy": "slime", "row": 1, "col": -1, "next": ["C", "D"] },
|
||||
"B": { "type": "combat", "enemy": "slime", "row": 1, "col": 1, "next": ["C", "D"] },
|
||||
"C": { "type": "rest", "row": 2, "col": -1, "next": ["E", "F"] },
|
||||
"D": { "type": "shop", "row": 2, "col": 1, "next": ["E", "F"] },
|
||||
"E": { "type": "elite", "enemy": "slime_elite", "row": 3, "col": -1, "next": ["BOSS"] },
|
||||
"F": { "type": "combat", "enemy": "slime", "row": 3, "col": 1, "next": ["BOSS"] },
|
||||
"BOSS": { "type": "boss", "enemy": "slime_boss", "row": 4, "col": 0, "next": [] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 검증 완화 (enemy 선택적)** — 생성기의 맵 검증 루프를 교체:
|
||||
|
||||
```js
|
||||
for (const [id, n] of Object.entries(MAP.nodes)) {
|
||||
if (n.enemy && !ENEMIES.enemies[n.enemy]) throw new Error(`[gen-slaydeck] 노드 ${id}의 enemy 없음: ${n.enemy}`);
|
||||
for (const nx of n.next) {
|
||||
if (!MAP.nodes[nx]) throw new Error(`[gen-slaydeck] 노드 ${id}.next에 없는 노드 id: ${nx}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: luaMapNodesTable enemy 조건부** — 함수를 교체:
|
||||
|
||||
```js
|
||||
function luaMapNodesTable(nodes) {
|
||||
const lines = Object.entries(nodes).map(([id, n]) => {
|
||||
const nx = '{ ' + n.next.map(luaStr).join(', ') + ' }';
|
||||
const enemyField = n.enemy ? `enemy = ${luaStr(n.enemy)}, ` : '';
|
||||
return `\t${id} = { type = ${luaStr(n.type)}, ${enemyField}row = ${n.row}, col = ${n.col}, next = ${nx} },`;
|
||||
});
|
||||
return `self.MapNodes = {\n${lines.join('\n')}\n}`;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 상수 추가** — `writeCodeblocks()` 안 `const GOLD_PER_WIN = 15;` 다음에:
|
||||
|
||||
```js
|
||||
const CARD_PRICE = 30;
|
||||
const REST_HEAL = 30;
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 상점 상태 속성 추가** — `prop('string', 'CurrentEnemyId', '""'),` 다음에:
|
||||
|
||||
```js
|
||||
prop('any', 'ShopChoices'),
|
||||
prop('any', 'ShopBought'),
|
||||
```
|
||||
|
||||
- [ ] **Step 6: JSON·문법 검사**
|
||||
|
||||
Run: `node -e "JSON.parse(require('fs').readFileSync('data/map.json','utf8')); console.log('JSON OK')" && node --check tools/gen-slaydeck.mjs`
|
||||
Expected: `JSON OK` + 오류 없음
|
||||
|
||||
- [ ] **Step 7: 커밋**
|
||||
|
||||
```bash
|
||||
git add data/map.json tools/gen-slaydeck.mjs
|
||||
git commit -m "data(E4): 상점/휴식 노드 맵 + enemy 선택적 검증/직렬화 + 상수/속성"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: PickNode 분기 + 상점/휴식 메서드
|
||||
|
||||
**Files:** Modify `tools/gen-slaydeck.mjs`
|
||||
|
||||
- [ ] **Step 1: PickNode 교체 (타입 분기)**
|
||||
|
||||
```js
|
||||
method('PickNode', `if self.RunActive ~= true then
|
||||
return
|
||||
end
|
||||
if self:IsReachable(id) ~= true then
|
||||
return
|
||||
end
|
||||
self.CurrentNodeId = id
|
||||
local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/MapHud")
|
||||
if hud ~= nil then
|
||||
hud.Enable = false
|
||||
end
|
||||
local node = self.MapNodes[id]
|
||||
if node.type == "shop" then
|
||||
self:ShowShop()
|
||||
elseif node.type == "rest" then
|
||||
self:ShowRest()
|
||||
else
|
||||
self.CurrentEnemyId = node.enemy
|
||||
self:StartCombat()
|
||||
end`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'id' }]),
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 상점/휴식 메서드 추가** — PickNode 메서드 다음에 삽입:
|
||||
|
||||
```js
|
||||
method('ShowShop', `local pool = {}
|
||||
for cid, _ in pairs(self.Cards) do
|
||||
table.insert(pool, cid)
|
||||
end
|
||||
self.ShopChoices = {}
|
||||
self.ShopBought = { false, false, false }
|
||||
for i = 1, 3 do
|
||||
self.ShopChoices[i] = pool[math.random(1, #pool)]
|
||||
end
|
||||
self:RenderShop()
|
||||
local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/ShopHud")
|
||||
if hud ~= nil then
|
||||
hud.Enable = true
|
||||
end`),
|
||||
method('RenderShop', `self:SetText("/ui/DefaultGroup/ShopHud/Gold", "골드 " .. string.format("%d", self.Gold))
|
||||
for i = 1, 3 do
|
||||
local cid = self.ShopChoices[i]
|
||||
local c = self.Cards[cid]
|
||||
local base = "/ui/DefaultGroup/ShopHud/Card" .. tostring(i)
|
||||
if c ~= nil then
|
||||
self:SetText(base .. "/Name", c.name)
|
||||
self:SetText(base .. "/Cost", tostring(c.cost))
|
||||
self:SetText(base .. "/Desc", c.desc)
|
||||
self:SetText(base .. "/Price", string.format("%d", ${CARD_PRICE}) .. " 골드")
|
||||
local e = _EntityService:GetEntityByPath(base)
|
||||
if e ~= nil and e.SpriteGUIRendererComponent ~= nil then
|
||||
if self.ShopBought[i] == true then
|
||||
e.SpriteGUIRendererComponent.Color = Color(0.2, 0.22, 0.26, 0.6)
|
||||
elseif c.kind == "Attack" then
|
||||
e.SpriteGUIRendererComponent.Color = Color(0.86, 0.42, 0.38, 1)
|
||||
elseif c.kind == "Skill" then
|
||||
e.SpriteGUIRendererComponent.Color = Color(0.42, 0.55, 0.85, 1)
|
||||
else
|
||||
e.SpriteGUIRendererComponent.Color = Color(0.46, 0.68, 0.52, 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end`),
|
||||
method('BuyCard', `if self.ShopBought == nil or self.ShopBought[slot] == true then
|
||||
return
|
||||
end
|
||||
if self.Gold < ${CARD_PRICE} then
|
||||
return
|
||||
end
|
||||
self.Gold = self.Gold - ${CARD_PRICE}
|
||||
table.insert(self.RunDeck, self.ShopChoices[slot])
|
||||
self.ShopBought[slot] = true
|
||||
self:RenderShop()
|
||||
self:RenderRun()`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }]),
|
||||
method('ShowRest', `local old = self.PlayerHp
|
||||
self.PlayerHp = self.PlayerHp + ${REST_HEAL}
|
||||
if self.PlayerHp > self.PlayerMaxHp then
|
||||
self.PlayerHp = self.PlayerMaxHp
|
||||
end
|
||||
local healed = self.PlayerHp - old
|
||||
self:SetText("/ui/DefaultGroup/RestHud/Info", "HP " .. string.format("%d", old) .. " → " .. string.format("%d", self.PlayerHp) .. " (+" .. string.format("%d", healed) .. ")")
|
||||
self:RenderCombat()
|
||||
local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/RestHud")
|
||||
if hud ~= nil then
|
||||
hud.Enable = true
|
||||
end`),
|
||||
method('LeaveNode', `local s = _EntityService:GetEntityByPath("/ui/DefaultGroup/ShopHud")
|
||||
if s ~= nil then
|
||||
s.Enable = false
|
||||
end
|
||||
local r = _EntityService:GetEntityByPath("/ui/DefaultGroup/RestHud")
|
||||
if r ~= nil then
|
||||
r.Enable = false
|
||||
end
|
||||
self:ShowMap()`),
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 문법 검사**
|
||||
|
||||
Run: `node --check tools/gen-slaydeck.mjs`
|
||||
Expected: 오류 없음
|
||||
|
||||
- [ ] **Step 4: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/gen-slaydeck.mjs
|
||||
git commit -m "gen-slaydeck(E4): PickNode 타입 분기·상점(구매)/휴식(회복) 메서드"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: BindButtons 바인딩
|
||||
|
||||
**Files:** Modify `tools/gen-slaydeck.mjs`
|
||||
|
||||
- [ ] **Step 1: BindButtons 맵 노드 루프 다음에 상점/휴식 바인딩 추가** — BindButtons 코드의 맵 노드 for-loop(`...PickNode(nid)...end\nend`) 다음, 닫는 백틱 직전에 삽입. 맵 노드 루프 끝 부분을 아래로 교체:
|
||||
|
||||
```js
|
||||
for i = 1, #mapNodeIds do
|
||||
local nid = mapNodeIds[i]
|
||||
local mn = _EntityService:GetEntityByPath("/ui/DefaultGroup/MapHud/Node_" .. nid)
|
||||
if mn ~= nil and mn.ButtonComponent ~= nil then
|
||||
mn:ConnectEvent(ButtonClickEvent, function() self:PickNode(nid) end)
|
||||
end
|
||||
end
|
||||
for i = 1, 3 do
|
||||
local sc = _EntityService:GetEntityByPath("/ui/DefaultGroup/ShopHud/Card" .. tostring(i))
|
||||
if sc ~= nil and sc.ButtonComponent ~= nil then
|
||||
sc:ConnectEvent(ButtonClickEvent, function() self:BuyCard(i) end)
|
||||
end
|
||||
end
|
||||
local shopLeave = _EntityService:GetEntityByPath("/ui/DefaultGroup/ShopHud/Leave")
|
||||
if shopLeave ~= nil and shopLeave.ButtonComponent ~= nil then
|
||||
shopLeave:ConnectEvent(ButtonClickEvent, function() self:LeaveNode() end)
|
||||
end
|
||||
local restLeave = _EntityService:GetEntityByPath("/ui/DefaultGroup/RestHud/Leave")
|
||||
if restLeave ~= nil and restLeave.ButtonComponent ~= nil then
|
||||
restLeave:ConnectEvent(ButtonClickEvent, function() self:LeaveNode() end)
|
||||
end`),
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 문법 검사**
|
||||
|
||||
Run: `node --check tools/gen-slaydeck.mjs`
|
||||
Expected: 오류 없음
|
||||
|
||||
- [ ] **Step 3: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/gen-slaydeck.mjs
|
||||
git commit -m "gen-slaydeck(E4): 상점 구매/나가기·휴식 나가기 버튼 바인딩"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: ShopHud·RestHud UI + MapHud 4행 정렬
|
||||
|
||||
**Files:** Modify `tools/gen-slaydeck.mjs` (`guid`, `upsertUi`)
|
||||
|
||||
- [ ] **Step 1: guid 'shp'·'rst' 분기** — ns 매핑에 추가(map 분기 다음):
|
||||
|
||||
```js
|
||||
const ns = prefix === 'hud' ? 0xd0 : prefix === 'dck' ? 0xca : prefix === 'cmb' ? 0xcb : prefix === 'rwd' ? 0xcc : prefix === 'map' ? 0xcd : prefix === 'shp' ? 0xce : prefix === 'rst' ? 0xcf : 0xfe;
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 필터 확장** — upsertUi 필터에 ShopHud·RestHud 추가:
|
||||
|
||||
```js
|
||||
ui.ContentProto.Entities = E.filter((e) => !e.path.startsWith('/ui/DefaultGroup/DeckHud') && !e.path.startsWith('/ui/DefaultGroup/CombatHud') && !e.path.startsWith('/ui/DefaultGroup/RewardHud') && !e.path.startsWith('/ui/DefaultGroup/MapHud') && !e.path.startsWith('/ui/DefaultGroup/ShopHud') && !e.path.startsWith('/ui/DefaultGroup/RestHud'));
|
||||
```
|
||||
|
||||
- [ ] **Step 3: MapHud 노드 y 중앙정렬** — upsertUi의 노드 pos 계산을 교체:
|
||||
|
||||
```js
|
||||
const pos = { x: node.col * 180, y: (node.row - (MAX_ROW + 1) / 2) * 140 };
|
||||
```
|
||||
|
||||
- [ ] **Step 4: ShopHud·RestHud 생성** — `ui.ContentProto.Entities.push(...map);` 다음에 삽입:
|
||||
|
||||
```js
|
||||
const shop = [];
|
||||
const shopHud = entity({
|
||||
id: guid('shp', 0),
|
||||
path: '/ui/DefaultGroup/ShopHud',
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
||||
displayOrder: 8,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1920, y: 1080 }, pos: { x: 0, y: 0 }, align: ALIGN_CENTER }),
|
||||
sprite({ color: { r: 0.05, g: 0.06, b: 0.09, a: 0.92 }, type: 1, raycast: true }),
|
||||
],
|
||||
});
|
||||
shopHud.jsonString.enable = false;
|
||||
shop.push(shopHud);
|
||||
shop.push(entity({
|
||||
id: guid('shp', 1),
|
||||
path: '/ui/DefaultGroup/ShopHud/Title',
|
||||
modelId: 'uitext',
|
||||
entryId: 'UIText',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent',
|
||||
displayOrder: 0,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 700, y: 60 }, pos: { x: 0, y: 400 } }),
|
||||
sprite({ color: TRANSPARENT }),
|
||||
text({ value: '상점', fontSize: 44, bold: true, color: GOLD, alignment: 4 }),
|
||||
],
|
||||
}));
|
||||
shop.push(entity({
|
||||
id: guid('shp', 2),
|
||||
path: '/ui/DefaultGroup/ShopHud/Gold',
|
||||
modelId: 'uitext',
|
||||
entryId: 'UIText',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent',
|
||||
displayOrder: 1,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 300, y: 44 }, pos: { x: 0, y: 330 } }),
|
||||
sprite({ color: TRANSPARENT }),
|
||||
text({ value: '골드 0', fontSize: 28, bold: true, color: { r: 0.98, g: 0.85, b: 0.4, a: 1 }, alignment: 4 }),
|
||||
],
|
||||
}));
|
||||
let shpN = 3;
|
||||
const shopXs = [-300, 0, 300];
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
const cardPath = `/ui/DefaultGroup/ShopHud/Card${i}`;
|
||||
shop.push(entity({
|
||||
id: guid('shp', shpN++),
|
||||
path: cardPath,
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent',
|
||||
displayOrder: i,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: CARD_W, y: CARD_H }, pos: { x: shopXs[i - 1], y: 20 } }),
|
||||
sprite({ color: ATTACK, type: 1, raycast: true }),
|
||||
button(),
|
||||
],
|
||||
}));
|
||||
for (const [suffix, cfg] of [
|
||||
['Cost', { size: { x: 50, y: 50 }, pos: { x: -60, y: 95 }, value: '1', fontSize: 34, bold: true, color: { r: 1, g: 1, b: 1, a: 1 } }],
|
||||
['Name', { size: { x: 160, y: 50 }, pos: { x: 0, y: 50 }, value: '카드', fontSize: 26, bold: true, color: { r: 1, g: 1, b: 1, a: 1 } }],
|
||||
['Desc', { size: { x: 160, y: 60 }, pos: { x: 0, y: -50 }, value: '', fontSize: 20, bold: false, color: { r: 1, g: 1, b: 1, a: 1 } }],
|
||||
['Price', { size: { x: 160, y: 40 }, pos: { x: 0, y: -105 }, value: '30 골드', fontSize: 22, bold: true, color: { r: 0.98, g: 0.85, b: 0.4, a: 1 } }],
|
||||
]) {
|
||||
shop.push(entity({
|
||||
id: guid('shp', shpN++),
|
||||
path: `${cardPath}/${suffix}`,
|
||||
modelId: 'uitext',
|
||||
entryId: 'UIText',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent',
|
||||
displayOrder: suffix === 'Cost' ? 0 : suffix === 'Name' ? 1 : suffix === 'Desc' ? 2 : 3,
|
||||
components: [
|
||||
transform({ parentW: CARD_W, parentH: CARD_H, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: cfg.size, pos: cfg.pos }),
|
||||
sprite({ color: TRANSPARENT }),
|
||||
text({ value: cfg.value, fontSize: cfg.fontSize, bold: cfg.bold, color: cfg.color }),
|
||||
],
|
||||
}));
|
||||
}
|
||||
}
|
||||
shop.push(entity({
|
||||
id: guid('shp', shpN++),
|
||||
path: '/ui/DefaultGroup/ShopHud/Leave',
|
||||
modelId: 'uibutton',
|
||||
entryId: 'UIButton',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent',
|
||||
displayOrder: 10,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 200, y: 60 }, pos: { x: 0, y: -300 } }),
|
||||
sprite({ color: DARK, type: 1, raycast: true }),
|
||||
button(),
|
||||
text({ value: '나가기', fontSize: 26, bold: true, color: GOLD, alignment: 4 }),
|
||||
],
|
||||
}));
|
||||
ui.ContentProto.Entities.push(...shop);
|
||||
|
||||
const rest = [];
|
||||
const restHud = entity({
|
||||
id: guid('rst', 0),
|
||||
path: '/ui/DefaultGroup/RestHud',
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
||||
displayOrder: 9,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1920, y: 1080 }, pos: { x: 0, y: 0 }, align: ALIGN_CENTER }),
|
||||
sprite({ color: { r: 0.05, g: 0.08, b: 0.06, a: 0.92 }, type: 1, raycast: true }),
|
||||
],
|
||||
});
|
||||
restHud.jsonString.enable = false;
|
||||
rest.push(restHud);
|
||||
rest.push(entity({
|
||||
id: guid('rst', 1),
|
||||
path: '/ui/DefaultGroup/RestHud/Title',
|
||||
modelId: 'uitext',
|
||||
entryId: 'UIText',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent',
|
||||
displayOrder: 0,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 700, y: 60 }, pos: { x: 0, y: 140 } }),
|
||||
sprite({ color: TRANSPARENT }),
|
||||
text({ value: '휴식', fontSize: 44, bold: true, color: GOLD, alignment: 4 }),
|
||||
],
|
||||
}));
|
||||
rest.push(entity({
|
||||
id: guid('rst', 2),
|
||||
path: '/ui/DefaultGroup/RestHud/Info',
|
||||
modelId: 'uitext',
|
||||
entryId: 'UIText',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent',
|
||||
displayOrder: 1,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 600, y: 50 }, pos: { x: 0, y: 30 } }),
|
||||
sprite({ color: TRANSPARENT }),
|
||||
text({ value: 'HP 회복', fontSize: 30, bold: true, color: { r: 1, g: 1, b: 1, a: 1 }, alignment: 4 }),
|
||||
],
|
||||
}));
|
||||
rest.push(entity({
|
||||
id: guid('rst', 3),
|
||||
path: '/ui/DefaultGroup/RestHud/Leave',
|
||||
modelId: 'uibutton',
|
||||
entryId: 'UIButton',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent',
|
||||
displayOrder: 2,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 200, y: 60 }, pos: { x: 0, y: -120 } }),
|
||||
sprite({ color: DARK, type: 1, raycast: true }),
|
||||
button(),
|
||||
text({ value: '나가기', fontSize: 26, bold: true, color: GOLD, alignment: 4 }),
|
||||
],
|
||||
}));
|
||||
ui.ContentProto.Entities.push(...rest);
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 문법 검사**
|
||||
|
||||
Run: `node --check tools/gen-slaydeck.mjs`
|
||||
Expected: 오류 없음
|
||||
|
||||
- [ ] **Step 6: 커밋**
|
||||
|
||||
```bash
|
||||
git add tools/gen-slaydeck.mjs
|
||||
git commit -m "gen-slaydeck(E4): ShopHud/RestHud UI·MapHud 4행 중앙정렬"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: 재생성 + 검증
|
||||
|
||||
**Files:** 생성물
|
||||
|
||||
- [ ] **Step 1: 생성**
|
||||
|
||||
Run: `node tools/gen-slaydeck.mjs`
|
||||
Expected: `Slay deck UI and combat codeblocks generated.`
|
||||
|
||||
- [ ] **Step 2: 메서드·UI 확인**
|
||||
|
||||
Run: `node -e "const j=JSON.parse(require('fs').readFileSync('RootDesk/MyDesk/SlayDeckController.codeblock','utf8')); const n=j.ContentProto.Json.Methods.map(m=>m.Name); console.log(['ShowShop','BuyCard','ShowRest','LeaveNode','RenderShop'].every(x=>n.includes(x))?'METHODS OK':'MISSING'); const u=JSON.parse(require('fs').readFileSync('ui/DefaultGroup.ui','utf8')); const has=p=>u.ContentProto.Entities.some(e=>e.path===p); console.log(has('/ui/DefaultGroup/ShopHud/Card1/Price')&&has('/ui/DefaultGroup/RestHud/Info')&&has('/ui/DefaultGroup/MapHud/Node_D')?'UI OK':'UI MISSING')"`
|
||||
Expected: `METHODS OK` / `UI OK`
|
||||
|
||||
- [ ] **Step 3: 결정성**
|
||||
|
||||
Run: `node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/a.sha && node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/b.sha && diff /tmp/a.sha /tmp/b.sha && echo DETERMINISTIC`
|
||||
Expected: `DETERMINISTIC`
|
||||
|
||||
- [ ] **Step 4: git status**
|
||||
|
||||
Run: `git checkout -- Global/common.gamelogic 2>/dev/null; git status --short`
|
||||
Expected: `data/map.json`, `tools/gen-slaydeck.mjs`, `ui/DefaultGroup.ui`, `RootDesk/MyDesk/SlayDeckController.codeblock` (+docs).
|
||||
|
||||
- [ ] **Step 5: 생성물 커밋**
|
||||
|
||||
```bash
|
||||
git add ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock
|
||||
git commit -m "재생성(E4): 상점/휴식 노드·UI 반영"
|
||||
```
|
||||
|
||||
- [ ] **Step 6: 메이커 Play 수동 검증 (MCP)**
|
||||
|
||||
reload→Play: 맵(4행, 2행에 휴식C·상점D) → PickNode("D")→상점(카드3·골드) → BuyCard(골드≥30이면 -30·RunDeck+1·비활성; 부족하면 무시) → LeaveNode→맵 / PickNode("C")→휴식(HP+30 클램프)→LeaveNode→맵 / 전투·보스·런 클리어 회귀 확인. MCP는 PickNode/BuyCard/LeaveNode 직접 호출 + 로그.
|
||||
|
||||
---
|
||||
|
||||
## Self-Review
|
||||
- **Spec coverage:** 맵/검증/직렬화/상수/속성(Task1), PickNode분기·상점·휴식(Task2), 바인딩(Task3), UI·MapHud정렬(Task4), 검증(Task5). 스펙 전 항목 매핑.
|
||||
- **Placeholder scan:** 모든 단계 실제 코드/명령.
|
||||
- **Type consistency:** 메서드 `PickNode/ShowShop/RenderShop/BuyCard/ShowRest/LeaveNode` 정의·호출·바인딩 일치. 속성 `ShopChoices/ShopBought` 정의(Task1)·사용(Task2) 일치. UI 경로 `/ui/DefaultGroup/ShopHud/Card{1..3}/{Name,Cost,Desc,Price}`·`/Gold`·`/Leave`, `/RestHud/{Info,Leave}`가 codeblock(RenderShop/ShowRest/BindButtons)·생성(Task4)에서 동일. 상수 `CARD_PRICE/REST_HEAL` Task1 정의·Task2 사용 일치.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user