Particle Sandbox/구상
세 대 모두 완료했습니다. 결과를 통합해 정리합니다.
---
# 신규 물질 후보 조사 결과
## 0. 한눈에
| 축 | 결론 |
|---|---|
| **약한 폭발** | 빈자리는 「연출 아이디어」가 아니라 **아직 아무 폭약도 안 건드린 네 개의 축**이다. 1순위는 **마찰 뇌관** — 필요한 엔진 인프라(`agitation`)가 완성된 채 사용자를 기다리고 있다 |
| **이그조틱** | 구상 6개 중 **3개는 이미 `docs/EXOTIC.md`에 설계돼 있다**(필터=§2, 중력자≈§5, 항온벽=§3). 진짜 새것은 **반발젤+미끄럼젤+고무 한 묶음**이고, 셋이 `objects.ts` **17줄에서 전부 나온다**. **후속 조사에서 이 줄의 두 판정이 뒤집혔다** — 「셀은 못 튕긴다」와 「중력자는 못생긴 정지장이다」가 둘 다 잘못 읽은 전제 위에 서 있었다([[#s-1.3.4|§2.4]]·[[#s-1.3.5|§2.5]]) |
| **연금술** | **최소안만 간다.** 이 게임은 이미 연금술 게임이다 — 12조작 중 11개가 「제련·석유화학·요리」라는 다른 이름으로 굴러간다. 넣을 것은 어휘가 아니라 **그게 눈에 보이는 자리 하나**(진사). 본격안과 「납→금」은 버린다 |
---
## 1. 약한 폭발 — 파괴력 6 티어
### 1.1 지금 비어 있는 자리 (이게 조사의 핵심)
파괴력 6의 세 물질(화약·섬광화약·불꽃놀이)은 **딱 두 축으로만 갈라져 있다** — `onCell`(원반을 무엇으로 채우나)과 `rimHandler`(가장자리에서 무엇을 던지나). 나머지는 전부 같다: 셋 다 가루, 셋 다 「불·용암·Blast 접촉 또는 온도 문턱」으로 터지고, 셋 다 젖으면 불발, 셋 다 뭉친 양에 비례해 커지고, 셋 다 등방이고, 셋 다 유리를 깬다.
그래서 진짜 빈칸은 넷이다.
| 빈 축 | 지금 상태 |
|---|---|
| **① 기폭 조건** | 마찰·충격·시한·물 접촉·산 접촉이 전부 비어 있다. **그중 마찰만은 인프라가 이미 완성돼 있다** |
| **② 크기 법칙** | `reach` + `soloSource`로 「쌓아도 안 커지는」 약폭이 하나도 없다. 유저가 약폭을 다루는 유일한 다이얼이 「얼마나 부었나」뿐 |
| **③ 폭발이 남기는 물질** | 약폭은 밀고·빛나고·던질 뿐 **아무것도 만들지 않는다**. CO₂·연기·산 증기·수소 중 무엇이든 첫 사례 |
| **④ 시간에 맡기는 파괴** | 폭발 순간이 아니라 그 뒤 수십 틱에 결과가 나오는 약폭이 없다 |
반대로 **이미 꽉 찬 자리**: 「불로 터지는 가루 + 젖으면 불발 + 원반 연출만 다름」은 세 번 반복됐다. 네 번째를 여기 두면 유저 눈에 **화약의 색깔 변주**로만 읽힌다.
### 1.2 옵션 인프라 실사 — 뜻밖의 발견 둘
- **`rimEmberChance`는 죽은 옵션이다.** `nitro.ts`가 유일한 사용자였는데 `blastEmbers: false` 물질 선언으로 옮겨 가면서 호출부가 사라졌다. 그런데 **약폭에서는 되살릴 수도 없다** — `EMBER_MIN_POWER = DURABILITY_SOLID = 200`이 기본 림 핸들러를 막는다. 약폭이 림에서 뭘 뿌리려면 **예외 없이 `rimHandler`를 직접 써야 한다**.
- **`Material.durability`를 선언한 물질이 로스터에 하나도 없다.** 고체는 전부 200이다. 즉 내구력 축은 아직 절반만 쓰이고 있다 — 파괴력만 다양하고 내구력은 상 기본값 4개뿐.
### 1.3 후보 (권장 3종 + 차순위)
**1위 — 마찰 뇌관 / Friction Primer** (가루)
> 「휘젓거나 충격을 받으면 터집니다. 불에는 반응하지 않습니다.」
게임 유일의 **비열(非熱) 기폭 폭약**. `Material.agitation` 인프라가 완비돼 있는데(섞기 브러시 `brushTools.ts:212`, Debris 착지 `debris.ts:236·253`가 이미 카운터를 올린다) **팔레트 물질이 하나도 선언하지 않았고**, `codex/fields.ts:90`이 직접 이렇게 적어 뒀다: *"Revisit this the moment a palette material declares `agitation`"*. **엔진 수정 0줄.**
채점 재미 5 / 비용 2 / 중복도 1.
함정: **Debris 착지 = 카운터 full**이라, 다른 약폭 진탕에 휩쓸린 뇌관은 착지 즉시 100% 유폭한다. 기능이자 함정 — 임계를 `AGITATE_MAX`(7)로 두고 브러시 7회를 요구하는 편이 안전하다.
**2위 — 산 유탄 / Acid Charge** (고체)
> 「터지면 산 증기가 퍼지며 닿은 것을 서서히 녹입니다.」
파괴력 6을 유지한 채 **결국 구조물을 없애는** 첫 약폭. 파괴를 폭발 순간이 아니라 그 뒤에 맡긴다 — 위 ④번 축이 통째로 비어 있다. 첫 **고체** 약폭이라 상 다양화도 같이 해결. 부식은 `corrosion.ts`가 이미 담당하므로 새 반응 코드 0줄.
채점 재미 5 / 비용 2 / 중복도 1.
확인 필요: 방폭 물질(다이아·우라늄 계열)이 산에도 면역인지 — 아니면 「약폭이 우라늄 격납을 녹인다」가 된다.
**3위 — 소화탄 / Extinguisher Charge** (가루)
> 「터지면 반경 안의 불이 꺼지고 이산화탄소가 들어찹니다.」
**불을 끄는 폭발.** 지금 모든 폭발은 불을 만들거나 아무 열도 안 낸다. `suppress.ts`(`spendDousingAgent`·`chillThroughFuel`)를 **폭발 계통이 처음 쓰는 자리**이고, 네이팜·백린이 만든 화재를 정리하는 실용 도구다.
채점 재미 4 / 비용 2 / 중복도 1.
**차순위**: 지연 신관(자체 카운트다운) → 냉동탄(열을 안 내는 폭발) → 발포탄(파괴가 아니라 **건설**을 하는 폭발) → 카바이드(물에 닿으면 터짐, 소듐과 축 절반 겹침) → 압축 가스통(지향성 진탕, `costMul` 두 번째 사용) → 연막통(백린과 겹쳐 약함).
### 1.4 모든 후보가 공통으로 밟을 함정 셋
1. **파괴력 6은 음식을 튀긴다.** `friesUnderBlast`는 `power > 0 && power < durability`만 본다. 냉동탄처럼 그림이 어긋나는 물질은 `onCell`에서 `Material.fry` 칸을 직접 클레임해야 한다.
2. **연출은 「누가 기폭됐나」가 결정하고 덩어리 전체에 적용된다.** `surveyMass`는 물질을 안 가리고 인접한 모든 `explosive`를 하나로 묶으며 **파괴력은 최강 셀이 이긴다**. 새 약폭을 TNT 옆에 두면 `onCell`이 아예 안 돈다. 정본 해법은 `soloSource` + 자체 클러스터 수집(`napalm.ts`).
3. **`aux`에 상태를 넣는 폭약은 진탕에서 그 상태를 잃는다.** `launchDebris`가 aux를 운반 물질 id로 덮어쓰고 착지는 `spawn`이라 0이 된다. 타이머·방향이 전부 해당.
---
## 2. 이그조틱
### 2.1 구상 6개 대조표
| # | 구상 | 상태 |
|---|---|---|
| 1 | 상만 통과시키는 필터 | **이미 설계돼 있음** — `EXOTIC.md` §2. 검증 통과, 함정 하나(§2.2) |
| 2 | 중력자 | **부분 중복** — §5(반중력)가 「장이 직접 옮긴다」로 같은 답을 이미 냄. 새것은 「대상이 전부」 하나뿐이고 **비용의 100%가 거기서 나온다** |
| 3 | 항온벽 | **이미 설계돼 있음** — §3. 검증 통과, aux 논점 해소 |
| 4 | 반발 젤 | **새것.** 오브젝트 반발 = 17줄. 셀 반발은 **하지 마라**(§2.4) |
| 5 | 가속 젤 | **절반 중복.** 「가속」은 컨베이어와 겹침. **「미끄럽다」만 남기면 새것** |
| 6 | 고무 | **새것.** `insulated`와 안 겹침. 4번이 선행 |
### 2.2 항온벽 — 1순위, 가장 싸고 가장 좋다
**aux 영속성 논쟁이 해소됐습니다: aux는 세이브에 실립니다.** `Grid.ts:88` 주석, `persistence.ts:938-946/996`의 실제 RLE 인코딩, `docs/SNAPSHOTS.md` 세 곳이 일치합니다. 제가 「런타임 전용」이라고 의심했던 주석은 **`SimContext.sparkFrontierX/Y`(스파크 파면 큐)에 붙은 것**이었습니다.
그런데 **더 위험한 주석이 따로 있었습니다.** `Grid.ts:359-362`:
> *"it starts zeroed — safe because every aux use is transient state that self-heals (a Clone re-adopts, a conductor's refractory clears)."*
**막·항온벽의 aux는 self-heal하지 않습니다.** 이건 상태가 아니라 **설정**이고, 이 주석의 전제가 **처음으로 깨지는 자리**입니다. 0으로 떨어지는 경로가 셋 실재합니다 — 옛 세이브, `resizeFrom`, 그리고 **Clone 복제**(`clone.ts:32`의 `canAdopt`는 `isWall`만 거르고, 방출 `spawn`은 `setAux(x,y,0)`을 강제).
→ **규칙: aux 0 = 「해롭지 않은 기본값」.** 막은 0=벽(문서가 이미 그렇게 정함, 좋음). **항온벽은 단계표 인덱스 0을 −200°가 아니라 「끔/상온」으로 잡을 것.** 안 그러면 셋 다 극저온 블록을 뱉습니다.
그리고 **틱 순서가 이 설계를 공짜로 도와줍니다** — `step`이 열 확산 → 복사 → 셀 스캔 순이라, 항온벽은 틱 시작에 정확히 목표 온도에 있고(확산이 주변을 끌고 감), 밀린 값은 자기 스캔 차례에 되돌아옵니다. **한 번도 목표에서 벗어나 보이지 않습니다.** `thermalExcluded`는 **달면 안 됩니다**(`conductivity: 0`과 짝이어야 하는데 전도도 0.9와 충돌).
채점: 재미 5 / 비용 2 / 침습도 **물질 update만**.
### 2.3 선택 투과막 — 설계는 성립, 함정 하나
`canHostOverlap` 실물이 문서 인용 그대로이고, `auxPalette`가 `aux % length`로 인덱싱하므로 **8항 팔레트에 마스크 0..7이 정확히 대응**합니다(나머지 연산이 항등). 「호출처 넷이 다 x·y를 들고 있다」도 참.
**다만 「시그니처만 넓히면 된다」는 절반만 맞습니다.** 호출처가 두 종류입니다 — `canOverlapAt`은 호스트가 이미 그 칸에 있어 안전하지만, **`set`/`spawn`은 「아직 셀에 없는 id」를 호스트로 놓고 묻습니다.** 그 좌표의 aux는 직전 점유자 것이고, `set()`은 비어 있지 않은 쓰기에서 aux를 지우지 않습니다. → 「막을 칠했더니 컨베이어 방향 비트가 마스크가 돼 있더라」가 납니다. **질의된 host id가 실제 현재 점유자일 때만 aux를 읽도록 잠글 것.**
UI 자리도 문서보다 구체적으로 나왔습니다: `ControlPanel`에 새 버튼을 붙일 게 아니라 **재료 버튼 더블클릭 → `BrushSettings.svelte`**(이미 「한 번 정하고 놔두는 브러시 설정」의 집합소로 선언돼 있음)에 필드 하나. 체크박스 3개는 `HeatCoolSettings.svelte`의 `.seg` 패턴 복사.
**wire 규약 권고**: 막·항온벽이 필드를 각각 갖지 말고 **`auxSetting?: number` 하나를 공유**할 것. `fanDir`이 이미 팬·레이저·성형작약 셋을 한 필드로 나르는 선례입니다. 저쪽 저장소가 정본이라 필드 추가가 비싸므로.
### 2.4 반발 젤 / 미끄럼 젤 / 고무 — 셋이 한 묶음, 17줄
오브젝트 반발의 정확한 편집 목록이 나왔습니다(제가 짚은 자리에서 한 군데 정정: 마찰 상한이 원은 `BALL_FRICTION`, **캡슐은 `DRUM_FRICTION`**으로 상수가 둘입니다):
헬퍼 신설 6줄 + 구조체 2개에 각 2필드 4줄 + 생성 지점 2곳 3줄 + 소비 4곳 4줄 = **`objects.ts` 안 실질 17줄. 엔진 CA 루프는 안 건드립니다.** 젤이 `Phase.Solid`이기만 하면 `isSolidCell`이 이미 접촉 대상으로 세어 줍니다.
**기존 `elasticity`/`friction` 재활용은 하지 마십시오.** 런타임 충돌은 없지만(선언자가 전부 액체·가루라 접촉 대상이 아님) **`codex/stats.ts`가 두 필드를 「마찰·안식각」·「탄성」 라벨로 이미 카드에 찍습니다.** 고체 젤 카드에 「안식각」이 뜨면 거짓말입니다. 새 필드 둘(`surfaceBounce?`/`surfaceSlip?`)이 정본 문서와 안 어긋납니다.
**「가속 젤」은 이름이 원하는 것과 코드가 줄 수 있는 것이 다릅니다.** 「미끄럽다」(μ↓, 토크 붙음, 이미 굴러오던 게 안 멈춤)와 「가속한다」(표면이 움직임, 정지한 것도 움직임, 토크 없음)는 코드에서 **정확히 다른 자리**입니다. `sv`를 재활용하면 그건 **방향 없는 컨베이어**라 2번 조항에 걸립니다. → **`surfaceSlip` 하나로 「미끄러운 젤」만 만들고, 가속은 유저가 경사면으로 만들게 할 것.** 그게 이 게임의 문법이고, 물질 하나가 bounce/slip 두 값을 다르게 선언하는 것만으로 **반발젤·미끄럼젤이 같은 17줄에서 둘 다 나옵니다.**
**고무**는 `insulated`와 **안 겹칩니다** — 그건 「절연체」가 아니라 **`conductive`인 물질만 읽는** 「다른 도체로 안 새는 피복」 태그입니다(`spark.ts:523`). 비전도 고체는 태그 없이 이미 스파크를 막습니다. 그리고 **지금 고무공(오브젝트)은 있는데 그걸 만드는 재료가 없습니다** — 로스터의 구멍이고, 4번 메커니즘의 첫 고객입니다.
#### 「셀은 튕기지 마십시오」를 철회합니다 — 문을 잘못 짚고 있었습니다
이 절은 원래 **「셀(파티클)까지 튕기는 것은 하지 마십시오」**로 끝났습니다. 근거는 이랬습니다: `launchDebris`로 쏘면 복원이 `spawn`이라 온도가 `thermal.init`으로 리셋되고, 그러면 900° 쇳가루를 반발 젤에 튕겼을 때 상온 쇳가루로 착지해 **반발 젤이 온도 세탁기**가 된다. 한 칸 밀어내기는 반발이 아니라 척력장이라 반중력의 부호만 뒤집은 물건이 된다.
**두 사실은 지금도 맞습니다. 틀린 것은 「그러므로 셀은 못 튕긴다」는 결론입니다** — 그 두 문이 **전부**라고 가정했기 때문입니다. 셋째 문이 있습니다.
**`SimContext.swap`은 `cells`/`temp`/`aux`/`tint`/`overlay`/`overlayAux`를 전부 함께 교환합니다.** 그래서 swap으로만 짜면 온도 문제가 **발생하지 않습니다.** 자석의 끌기가 「빨갛게 달군 쇳가루가 뜨거운 채로 도착한다」고 자랑하는 근거가 이것이고, 가루의 낙하 프리미티브(`moveDown` → `tryMove`)도 전부 swap입니다. 파편(ballistic)이 필요한 이유는 **여러 칸을 포물선으로 나는 것**인데, 유저가 실제로 원한 사양은 그것을 요구하지 않습니다 — **「가루만, 낙하 중에, 부딪히면 위로. 액체와 기체는 안 튕긴다.」**
**판정 주체는 젤입니다(가루가 아닙니다).** 가루가 아래를 보게 하면 그 조회가 `behaviors.fallAndPile` — 게임에서 제일 뜨거운 루프 — 에 들어가고 **가루 셀 수만큼** 비용을 냅니다. 젤이 위를 보면 **젤 셀 수만큼**이고, 코드는 새 물질 파일 하나에 갇힙니다. 컨베이어와 선풍기가 둘 다 기계 쪽에서 판정하는 선례입니다. 젤은 `Phase.Solid`라 기본 `update`가 없으므로 하나 붙이는 것이 순증 비용의 전부이고, CA 스캔이 중력 방향 끝에서부터 도므로 **젤이 그 위의 가루보다 먼저 스캔됩니다** — 아직 자기 차례를 안 쓴 가루를 보고 처리할 수 있습니다.
**규모** — 신규 파일 `materials/bouncegel.ts` 60~80줄 + `Material.surfaceBounce` 필드 하나. 세이브 새 필드 0개, 렌더러 변경 0줄, 엔진 루프 변경 0줄.
**밟게 될 함정 하나만 미리** — swap은 양끝을 `moved`로 찍고, `moved`인 셀은 `tryReact`도 수명 붕괴도 자기 `update`도 통째로 건너뜁니다. 매 틱 튕기면 **그 가루는 영영 반응하지 않습니다**(녹슬지도 타지도 않는 모래). 컨베이어가 정확히 이것으로 「1칸짜리 가루 탑」을 만들었던 기록이 있습니다. **젤 자신의 `aux`에 쿨다운(20틱)을 두는 것이 그 값**이고, 새 물질이라 aux에 임자가 없으며 세이브에도 그대로 실립니다.
**그리고 「파편이 온도를 세탁한다」는 것 자체는 여전히 미해결 버그입니다.** 최근에 「충격파가 언 액체를 던지던 것」을 고쳤지만 그것은 **언 액체를 밀어내기 대상에서 뺀 것**이지 온도를 나르게 만든 것이 아닙니다. 구멍은 `debris.ts`의 `spawn` 두 줄이고, `launchDebris`를 부르는 **입구가 열 개가 넘습니다**(튀김 기름·팝콘·톱밥·화염병 유리·오브젝트 물보라…). 끓는 물에 쇠공을 떨어뜨리면 튀어 오른 물이 지금도 20°로 착지합니다. 자세한 실측과 후보별 값은 저장소 `docs/PHYSICS.md`의 「파편이 온도를 세탁하는 것」 절에 있습니다.
### 2.5 중력자 — 「일회성」으로 다시 읽으면 판정이 뒤집힙니다
**이 항목은 한 번 부정 판정을 받았고, 그 판정을 철회합니다.** 먼저 무엇을 잘못 읽었는지 남깁니다.
원래 판정문은 「상시 발동 + 지속적 붙잡기」를 전제로 이렇게 적었습니다: 매 틱 0.29~0.84 ms를 무는데 자석과 달리 전원을 끌 도피구가 없다, `markMoved` + `holdMagnetically`가 반경 10 안의 느슨한 것 전부의 차례를 통째로 건너뛰어 물이 안 끓고 불이 안 번지고 씨앗이 안 자란다, 그래서 **「알록달록한 덩어리가 정지한 채 붙어 있는 못생긴 정지장」**이 된다.
**전제가 틀렸습니다.** 의도한 것은 처음부터 **소모성·일회성**이었습니다 — 반물질처럼 **물질이 닿는 것 자체가 트리거**이고, 1초가 안 되는 동안 장을 세운 뒤 사라집니다. 지속적 붙잡기는 성립하지 않습니다. 바로잡고 다시 세면 세 근거 중 둘이 그냥 없어지고, 셋째는 성질이 바뀝니다.
**한 문장 사양**(도감 문체):
> 다른 물질에 닿으면 발동해 주변의 가루·액체·기체를 자기 쪽으로 끌어당기고, 잠시 뒤 사라집니다.
* **트리거** — 8이웃에 자기 자신·빈 칸·벽·Void가 아닌 것이 있으면 즉시. 반물질의 `DIR8` 루프와 같은 모양이고, 저쪽이 「없앤다」인 자리에 「발동한다」를 놓으면 됩니다. 발동 전에는 그냥 떨어지는 가루입니다.
* **지속** — 15틱. ×1 속도가 30틱/초이므로 **0.5초**. 카운트다운은 자기 `aux`(자석·선풍기·컨베이어가 전부 그렇게 합니다).
* **대상** — 느슨한 것 전부 + 기어다니는 것. 구조 고체와 **언 액체**는 제외 — 자석의 `isFieldPassable`이 이미 그 선을 긋고 있습니다.
* **끝** — 카운트다운 0에서 자기 칸을 지웁니다. 뒤에는 한 점으로 뭉친 덩어리가 남습니다.
**메커니즘은 「반경 임펄스」가 아니라 자기장 재활용이어야 합니다. 이건 취향이 아니라 강제입니다.** 셀에는 속도장이 없습니다 — 가루도 액체도 매 틱 한 칸씩 swap으로 옮겨 다닐 뿐입니다. 셀에 「중심으로 향하는 속도」를 주는 방법은 이 저장소에 정확히 하나, `launchDebris`로 파편으로 만드는 것뿐이고, **그것은 착지할 때 온도를 상온으로 되돌립니다**(§2.4의 그 버그). 반경 임펄스로 짜면 **중력자가 반발 젤과 똑같은 온도 세탁기**가 됩니다. 반대로 자석의 끌기는 `swap`이라 온도를 그대로 나릅니다. → **셀은 자석의 측지 스윕, 오브젝트(강체)는 반경 임펄스 1회**의 하이브리드. 강체는 진짜 속도장을 갖고 있고 그쪽엔 `applyWooferKnockback`이라는 부호만 뒤집으면 되는 선례가 이미 있습니다.
**「`acceptsPull ≡ EMPTY`가 되어 빈 칸으로만 끌어당긴다」 — 사실이지만 중력자에겐 결함이 아닙니다.** 자석의 간판 기술(모래 더미를 뚫고 철가루를 뽑아 올리기)이 성립하는 이유는 자석이 **선택적**이기 때문입니다. 모래는 대상이 아니라서 제자리에 있고, 그래서 밀어내고 지나갈 무언가가 존재합니다. **중력자는 비선택적이라 「뚫고 지나갈 대상」이 아예 없습니다** — 모래도 같이 옵니다. 그리고 `pullField`가 **가까운 것부터** 처리하므로 안쪽 링이 먼저 비고 다음 링이 그 빈 칸으로 들어갑니다. 기둥은 정상적으로 빨려 듭니다. 남는 진짜 제약은 **「모래 속에 파묻힌 중력자는 빨아들일 빈 칸이 없어 아무 일도 안 한다」** 하나이고, 이건 오히려 읽히는 규칙입니다.
**붙잡기는 안 넣습니다. 그러면 「정지장」 문제가 사라집니다.** 재앙의 범인은 끌기가 아니라 `holdMagnetically`였습니다 — 그것이 셀의 차례를 **다음 틱까지** 건너뛰게 만듭니다. 자석이 그걸 넣은 이유는 「중력을 이기고 면에 매달려 있어야 한다」이고, 중력자는 매달릴 필요가 없습니다. 남는 비용은 실제로 옮겨진 틱에 swap이 `moved`를 찍는 것 하나뿐이고, 그건 선풍기·컨베이어가 매 틱 내고 있는 값입니다. **정직하게 남기면** — 15틱 내내 끌려오는 셀은 0.5초 동안 반응을 못 합니다. 눈에 보이는지는 브라우저에서 확인해야 하고, 보인다면 격틱으로 끌거나 지속을 10틱으로 줄이면 됩니다.
**`MAGNET_PULL_SPEED = 1.3` 부등식은 일회성에는 안 걸립니다.** 그 부등식(1.3 × 0.26 = 0.34 < 0.35)이 존재하는 이유는 **자석이 속도 하한을 매 틱 다시 부과하기 때문**입니다 — 튕겨 나간 속도가 rest epsilon을 넘으면 다음 틱에 다시 밀어붙여 **영원히** 떱니다. 중력자는 장이 15틱 뒤 사라지므로 떨 무대가 없고, 게다가 오브젝트에는 **발동 틱 한 번만** 임펄스를 주므로 반발계수 0.82짜리 고무공도 떨 자리가 없습니다.
**비용도 다시 셌습니다.** 실측 0.29~0.84 ms는 **몸체 크기별**이고 스윕 비용은 몸체 바운딩 박스를 REACH만큼 부풀린 상자에 비례합니다. 중력자는 **한 칸짜리 몸체**라 REACH 8이면 17×17 = 289칸, 8×8 자석 상자(576칸)의 절반입니다 — **틱당 0.15 ms 안쪽을, 15틱만.** 「10개면 3~8 ms/틱」이라던 셈은 상시 발동 + 8×8 몸체 가정이었습니다. 같은 10개를 동시에 터뜨려도 1.5 ms/틱을 0.5초 물고 그 뒤엔 0입니다.
**규모** — `materials/graviton.ts` 90줄 + `pullField`를 「대상 판정 둘을 인자로 받게」 빼내는 리팩터 30줄 + `SimContext` 이벤트 큐 10줄 + `objects.ts` 40줄. **엔진 CA 루프는 안 건드립니다.** 「태그만 빼면 붙잡기가 안 걸려 배열이 하나 더 필요하다」던 항목은 붙잡기를 버리면서 같이 없어집니다.
**§5 반중력과 같은 기계라는 지적은 그대로 유효합니다.** 다만 결론이 「둘 중 하나만」에서 **「중력자를 먼저」**로 바뀝니다 — 둘은 지속·방향·대상범위·발동만 다른 한 벌의 코드이고, 일회성인 쪽이 「상시 비용이 얼마인가」를 안 물어봐도 되므로 그 실측을 쥔 다음에 반중력의 상시 판을 결정하는 편이 쌉니다. 둘 다 넣는다면 도감 문장에서 일회성/상시 축을 분명히 가를 것.
**판정: 넣습니다.** 상세는 저장소 `docs/EXOTIC.md` §6.
### 2.6 새 후보 넷 (§7 기각 목록을 통과한 것만)
| | 후보 | 한 문장 | 왜 새로운가 |
|---|---|---|---|
| **A** | **열 문턱막** / Thermal Gate | 「지정한 온도보다 뜨거운 것만 통과시킵니다.」 | §2 막이 **상**으로 거른다면 이건 같은 기계를 **온도** 축으로 돌린 것. **증류탑이 「응결과 재비등의 균형으로 겨우 되는 일」에서 설계 가능한 일이 된다.** 재미 5 / 비용 2 |
| **B** | **흡착벽** / Sorbent | 「닿은 기체를 빨아들이고, 300℃를 넘으면 도로 내놓습니다.」 | **이 게임에 기체를 저장하는 수단이 없다.** 통에 가두면 새고 Void로 지우면 못 되찾는다. `overlapUpdate` 훅으로 물질 파일 하나. 재미 4 / 비용 2 |
| **C** | **셔터** / Shutter (electric) | 「전기가 흐르는 동안 열려 모든 것을 통과시킵니다.」 | §2 막이 「무엇을」이면 이건 「**언제**」. **이 게임엔 여닫히는 것이 하나도 없어서, 배치식 공정(넣고→반응시키고→빼기)이 지금 불가능하다.** 재미 5 / 비용 2 |
| **D** | **촉진장** / Accelerator | 「주변의 물질이 두 배로 빠르게 변화합니다.」 | §4 정지장의 부호 반대. 양생·발효·발아를 **지우지 않고 시간만 준다**(1번 조항 통과). **§4와 같은 사전 패스를 공유**하므로 §4를 하면 한계비용 ≈ 0 |
D의 함정 하나는 크게 적어 둘 만합니다: 두 번 업데이트는 **RNG 소비량을 두 배로** 만듭니다. `docs/TESTING.md`에 「물질 update에서 난수를 새로 만지면 공용 스트림 위상이 밀린다」로 무관한 테스트가 55/55 → 10/55로 뒤집힌 기록이 있고, **멀티플레이 록스텝에서는 결정적 발산**입니다.
**뺀 것**: 선택적 배기구(§2 + Void로 조립됨) · 도관/파이프(`pump.ts` 머리말이 **이미 기각한 설계**) · 열 다이오드(열 커널이 `cond[i]` 하나만 읽는 공용 패스라 커널 변경) · 포털(좌표가 aux 16비트에 안 들어감 — 360×203 = 73,080 > 65,535).
### 2.7 이그조틱 우선순위
| | 후보 | 재미 | 비용 | 침습도 | 선행 |
|---|---|---|---|---|---|
| 1 | **항온벽** | 5 | 2 | 물질 update만 | — |
| 2 | **선택 투과막** | 5 | 3 | 공용 헬퍼 | — (항온벽과 wire 필드 공유) |
| 3 | **열 문턱막** | 5 | 2 | 공용 헬퍼 | §2 |
| 4 | **셔터** | 5 | 2 | 물질 update만 | §2 |
| 5 | **반발젤+미끄럼젤+고무** | 3 | 2 | 공용 헬퍼(오브젝트) | — (셋이 한 PR) |
| 6 | **흡착벽** | 4 | 2 | 물질 update만 | — |
| 7 | 정지장 | 5 | 4 | **엔진 루프** | 사전 패스 별건 |
| 8 | 촉진장 | 4 | 4 | 엔진 루프(§4 공유) | §4 |
| 9 | 중력자 | 4 | 4 | 엔진 루프 근처 | 붙잡기 정리 |
5번의 재미 점수가 낮은 건 비용이 아니라 **놀 상대가 적어서**입니다 — 오브젝트가 6종뿐입니다.
**후속 조사로 5번과 9번이 둘 다 올라갔습니다.** 5번은 「셀은 못 튕긴다」가 철회되면서 오브젝트만이 아니라 **가루도 대상**이 되었고(놀 상대가 늘었습니다), 9번은 전제를 「일회성」으로 바로잡으면서 **비용 4 → 2, 선행 없음, 엔진 루프 안 건드림**이 되었습니다. 근거는 각각 [[#s-1.3.4|§2.4]]와 [[#s-1.3.5|§2.5]].
| | 후보 | 재미 | 비용 | 침습도 | 선행 |
|---|---|---|---|---|---|
| 5′ | **중력자** (일회성) | 5 | 2 | `pullField` 리팩터 + 물질 하나 | — |
| 6′ | **반발젤+미끄럼젤+고무** | 4 | 2 | 공용 헬퍼(오브젝트) + 젤 `update` | — |
---
## 3. 연금술 — 최소안만 간다
### 3.1 이 게임은 이미 연금술 게임입니다
| 연금술 어휘 | 게임의 대응 |
|---|---|
| **삼원질 황·수은·소금** | **Sulfur(125) · Mercury(40) · Salt(7) — 셋 다 이름 그대로 팔레트에 있다** |
| 4원소 | Dirt/Soil · Water · Oxygen · Fire |
| 하소·용해·증류·발효·응결·승화 | 재/석회석 · 산 부식 · 원유 분별증류 · 효모 · 시멘트 양생 · 드라이아이스 |
| 니그레도/알베도/루베도 | 탄 고기·아스팔트 / Bleach·표백 산호 / `glow` 램프의 벌건 쇳물 |
| 아쿠아 비타이 | Alcohol(42) |
| 현자의 돌 = 소모 안 되는 촉진자 | **`ReactionRule.catalyst` + `catalystFactor`(×4)** — 구현까지 돼 있고 실사용은 한 줄뿐 |
| 무한 공급 / 만능 파괴 | Clone(49) / Void(50)·Antimatter |
**12조작 중 열한 개가 이미 라인으로 굴러갑니다.** 다만 그걸 「석유화학」·「제련」·「요리」라고 부르고 있을 뿐입니다.
### 3.2 문체 규칙이 이미 필터로 작동합니다
이게 이번 조사에서 가장 유용한 발견입니다. 판타지 물질에 문체 규칙을 적용해 실제로 써 보면:
| 물질 | 규칙을 지킨 문장 | 남은 것 |
|---|---|---|
| 진사 | 「수은에 황을 뿌리면 생기는 붉은 가루입니다. 580℃ 이상으로 구우면 수은 증기가 되어 날아갑니다.」 | **문장이 그대로 스펙이다.** 통과 |
| 현자의 돌 | 「닿은 납을 금으로 바꿉니다.」 | §7이 기각한 **만능 변환기의 문장 그 자체** |
| 알카헤스트 | 「모든 고체를 녹입니다.」 | **Void의 문장에서 「모든」만 남음** |
| 엘릭시르 | 「담근 것을 원래 상태로 되돌립니다.」 | 제련 라인 전체를 한 문장으로 삭제 |
| 제1질료 | 「모든 물질의 근원입니다.」 | **결과가 없다** — 규칙 1에 아예 걸림 |
문체 규칙은 **판타지 물질에서 신비를 걷어내면 무엇이 남는지를 강제로 보여 줍니다.** 남는 게 「만능 ○○」 한 줄이면 그건 물질이 아니라 치트고, §1의 3번 조항이 원래 그걸 잡으라고 있던 조항입니다.
### 3.3 최소안 — 진사 고리 (신규 1종, 새 엔진 코드 0줄)
```
액체 금속(수은)에 노란 가루(황)를 뿌린다
↓ 상온 접촉
붉은 가루(진사)가 피어오른다
↓ 580℃로 굽는다
수은 증기가 되어 날아간다
↓ 식으면
다시 액체 수은
```
**고리가 닫히고**, 그 고리를 도는 것이 이름 그대로의 삼원질 둘입니다. 「신비로운」이라는 단어를 한 번도 쓰지 않고 연금술을 넣는 유일한 방법입니다.
**진사 / Cinnabar** · Powder · 주홍 · **밀도 8.1**(실제 HgS 값)
> 「수은에 황을 뿌리면 생기는 붉은 가루입니다. 580℃ 이상으로 구우면 수은 증기가 되어 날아갑니다.」
구현은 **전부 선언**입니다. 새 파일은 진사 하나, 기존 파일 수정은 `mercury.ts` 한 줄:
- 레시피 = 반응 테이블 한 줄 — `{ with: SULFUR.id, produce: CINNABAR.id, otherBecomes: CINNABAR.id, probability: 0.25, tempMax: 150 }`. **테르밋과 완전히 같은 모양·같은 감각값.**
- 분해 = `phaseChange` 한 줄 — `{ at: () => 580, when: 'atOrAbove', into: () => MERCURY_VAPOR.id }`.
**제가 직접 확인한 것**: 수은(id 40, 밀도 9, `conductive: true`) → 357°C `phaseChange` → 수은 증기(id 118)가 식으면 `sim.set(x, y, MERCURY.id)`로 **실제로 다시 수은이 됩니다.** 황은 id 125, 밀도 3.9. **고리가 닫힐 조건이 코드에 이미 있습니다.**
주의할 자리 셋:
1. **규칙은 반드시 수은 쪽에 선언할 것.** `tryReact`가 물질 `update` **앞에** 돕니다. 황 쪽에 선언하면 황 알갱이가 자기 `update` 첫 줄인 `tryMixGunpowder()`에 닿기 전에 진사 반응에 먹혀, **수은 근처에서는 흑색화약이 안 만들어집니다.**
2. **밀도 층 갈림이 여기서만은 스스로 풀립니다.** 흑색화약·테르밋이 겪는 「층이 갈려 경계면만 전환」이 진사에서는 안 생깁니다 — 황(3.9)은 수은(9) 위에 뜨고, 생성된 진사(8.1)도 수은보다 가벼워 **위로 떠올라 경계면을 비켜 줍니다.** 고증치 8.1이 마침 게임적으로 옳은 자리에 떨어진 것이라 바꿀 이유가 없습니다.
3. **아이콘 비용 0** — `colorVary`를 가진 평범한 붉은 가루는 자동 유도 버킷입니다.
**실용 용도까지 붙습니다**: 액체 수은은 밀도 9라 뭐든 뚫고 바닥까지 가라앉아 다루기 최악인데, **진사는 가루라 쌓고 나르고 컨베이어에 싣고 펌프로 보낼 수 있습니다.** 「수은을 고체로 포장해 회로 자리까지 배달한 뒤 구워서 꺼낸다」가 성립하고, 수은은 도체입니다. 「재료가 재료로만 쓰이면 팔레트 한 칸이 아깝다」 기준을 통과합니다.
채점: 재미 4 / 구현비용 5(가장 쌈) / 세계관 정합성 5(**판타지가 아니라 실화학**).
### 3.4 본격안(`alchemy` 탭)과 「납 → 금」 — 둘 다 버립니다
여덟 후보(제1질료·현자의 돌·알카헤스트·엘릭시르·호문쿨루스·왕수·금·아조트)를 다 세워 보면 **하나도 남지 않습니다.** 그리고 그건 우연이 아닙니다:
> **판타지 연금술의 물질 목록은 「단계를 건너뛰는 물건」의 목록입니다.** 현자의 돌은 제련을 건너뛰고, 알카헤스트는 용해를 건너뛰고, 엘릭시르는 시간을 건너뜁니다. 그게 그 장르의 정의고, 이 저장소는 「단계를 건너뛰게 하지 않는다」를 §1의 **1번 조항**으로 못박아 뒀습니다. **장르의 핵심과 저장소의 1번 조항이 정확히 반대 방향을 가리킵니다.**
거기에 탭 자체의 문제가 겹칩니다. `CATEGORY_META` 16개 중 **15개는 「물질의 부류」**(상태 넷, 공정 라인 넷, 형질 다섯, 생명 하나)이고 `exotic` 하나만 「도구 선반」입니다. **`alchemy`는 소재도 공정 라인도 아닌 「장르로 묶인 선반」**이 되어, 16개 중 처음으로 나머지 15개가 지키는 문법을 깹니다. 판타지가 이물질인 게 아닙니다 — Clone·Void·Antimatter는 이미 판타지입니다. **이물질이 되는 건 「물질의 부류가 아닌 것으로 정의된 탭」**입니다.
**「납 → 금」**은 별도로도 탈락입니다:
- **금이 가져오는 새 성질이 「용융 상태에서 가장 무겁다」 한 줄뿐입니다.** 고체 금은 다른 정적 고체와 똑같이 `density: 1000`(이 게임은 정적 고체 전부에 1000을 줍니다). 내산성은 **Lead가 이미 `acidResistant`**, 전도는 구리·철·수은·납, 방사선 차폐는 납의 정체성, 융점 1064℃는 구리 980↔철 1200 사이라 새 온도대가 안 생깁니다.
- **변환 수단이 무엇이든 집니다.** 현자의 돌이면 만능 변환기, 온도 문턱이면 — **이 게임의 모든 온도 문턱은 실측치입니다**(660·1064·1200·1749…). 거기 하나만 지어낸 숫자를 끼우면 도감 전체의 신뢰가 깎입니다.
- **금은 사슬 종결자입니다.** 변하지 않는 것이 금의 정체성이라, A→B→C가 재미인 게임에서 정의상 C에서 끝납니다. 로켓 캔디는 태울 수 있고 폴리에틸렌은 성형할 수 있고 철은 뭐든 지을 수 있는데, 금은 없습니다.
언젠가 넣는다면 **연금술이 아니라 제련의 문법으로** — 「금광석 → 용해 → 환원」이고, 명분도 「가장 무거운 액체」나 「부식되지 않는 접점」 같은 기계적 니치여야 합니다. 지금은 그 니치가 안 비어 있습니다.
---
## 4. 조사 중 나온 부수 발견 (별건으로 고칠 거리)
1. **`docs/MATERIAL-SYSTEMS.md:126`의 참조가 끊어진 링크입니다.** 「설계 근거·티어 채점은 [[Web sandbox 프로젝트/폭발 다변화 구상]] 참조」가 가리키는 Cloudwiki 페이지가 **지금 위키에 없습니다**(제목·전문 검색 모두 무결과 — 최근 위키 정리 때 사라진 것으로 보임). 대조할 기존 채점표가 없다는 뜻이기도 합니다.
2. **`rimEmberChance`가 죽은 옵션입니다** — 사용자가 하나도 없고, 파괴력 6 티어에서는 구조적으로 되살릴 수도 없습니다.
3. **`Material.durability` 선언자가 로스터에 0종입니다** — `blast.ts:412`가 「지금은 참이 될 수 없는 가지」라고 스스로 적어 뒀습니다.
4. **`Grid.ts:359-362`의 「every aux use is transient state that self-heals」 전제**가 막·항온벽으로 처음 깨집니다. 그 물질을 만들 때 주석도 같이 고쳐야 합니다.
5. **`codex/fields.ts:90`이 `agitation` 카드 승격을 명시적으로 기다리고 있습니다** — 마찰 뇌관을 만들면 그 자리를 같이 닫게 됩니다.
6. **셀 물질로서의 고무가 없습니다** — 고무공(오브젝트)은 있는데 만들 재료가 없는 로스터의 구멍.
---
## 5. 제가 보는 다음 한 걸음
가장 값싸고 가장 확실한 묶음 셋입니다.
- **PR ①: 항온벽** — 엔진 무손, 틱 순서가 공짜로 도와주고, 이 게임에서 제일 자주 실패하는 일을 정면으로 풉니다. 이그조틱 전체에서 비용 대비 이득이 가장 큽니다. (단계표 인덱스 0 = 상온을 착수 시점에 못박을 것)
- **PR ②: 반발젤 + 미끄럼젤 + 고무** — `objects.ts` 17줄에서 셋이 다 나옵니다. 새 필드 둘(`surfaceBounce`/`surfaceSlip`), 셀 반발은 안 하고 Debris 리코셰 한 줄만 곁들입니다.
- **PR ③: 마찰 뇌관** — 인프라가 완성된 채 주석이 사용을 기다리고 있고, 엔진 수정 0줄에 「불 없이 터지는 폭약」이라는 완전히 새 트리거가 열립니다.
**진사**는 위 셋 중 아무거나에 곁들여도 되는 크기(파일 1개 + 기존 1줄)이고, **선택 투과막**은 wire 규약을 건드리므로 항온벽과 묶어 `auxSetting` 필드를 한 번에 정하는 편이 낫습니다.
착수할 것을 고르시면 그때부터 `add-material-or-object` 체크리스트대로 진행하겠습니다.