Compare commits
29
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6bf22f2d6e | ||
|
|
5da782d21b | ||
|
|
4f97fc704c | ||
|
|
5b0827072b | ||
|
|
ccabd3e7ac | ||
|
|
cba87dcb31 | ||
|
|
932cd0582f | ||
|
|
1ef1720bae | ||
|
|
bea880a999 | ||
|
|
6f2225f911 | ||
|
|
fea0a29d10 | ||
|
|
f89781d78b | ||
|
|
be63c21e15 | ||
|
|
86614f3104 | ||
|
|
38ba2b0eff | ||
|
|
a42f25d5fa | ||
|
|
14f542c8a1 | ||
|
|
af6bc82c67 | ||
|
|
e5f41bde07 | ||
|
|
57bc10ba79 | ||
|
|
22b8c7a438 | ||
|
|
9e5c3277bf | ||
|
|
063e94f6eb | ||
|
|
f8def361fa | ||
|
|
98fe1794c6 | ||
|
|
f3057c65aa | ||
|
|
463ee7ecfa | ||
|
|
c483bda3ea | ||
|
|
c140236bf7 |
@@ -108,6 +108,13 @@ yarn-error.log*
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# .NET tooling under tools/ (Unity's own Library/ ignores live in the Unity
|
||||
# project's .gitignore and don't reach this far up)
|
||||
# ---------------------------------------------------------------------------
|
||||
tools/**/bin/
|
||||
tools/**/obj/
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# AI / agent tooling
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Vendored
+64
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"cSpell.enableFiletypes": [
|
||||
"yarnspinner"
|
||||
],
|
||||
"cSpell.languageSettings": [
|
||||
{
|
||||
"languageId": "yarnspinner",
|
||||
"patterns": [
|
||||
{
|
||||
"name": "yarn-commands",
|
||||
"pattern": "/<<[^>]*>>/g"
|
||||
},
|
||||
{
|
||||
"name": "yarn-expressions",
|
||||
"pattern": "/\\{[^}]*\\}/g"
|
||||
},
|
||||
{
|
||||
"name": "yarn-variables",
|
||||
"pattern": "/\\$[a-zA-Z_][a-zA-Z0-9_]*/g"
|
||||
},
|
||||
{
|
||||
"name": "yarn-markup",
|
||||
"pattern": "/\\[[^\\]]*\\]/g"
|
||||
},
|
||||
{
|
||||
"name": "yarn-hashtags",
|
||||
"pattern": "/#[a-zA-Z_][a-zA-Z0-9_:-]*/g"
|
||||
},
|
||||
{
|
||||
"name": "yarn-node-markers",
|
||||
"pattern": "/^(---|===)$/gm"
|
||||
},
|
||||
{
|
||||
"name": "yarn-headers",
|
||||
"pattern": "/^(title|tags|position|color|colorID|tracking|custom|style):.*/gm"
|
||||
},
|
||||
{
|
||||
"name": "yarn-character-names",
|
||||
"pattern": "/^\\s*[A-Za-z_][A-Za-z0-9_]*(?=:)/gm"
|
||||
},
|
||||
{
|
||||
"name": "yarn-comments",
|
||||
"pattern": "/\\/\\/.*$/gm"
|
||||
}
|
||||
],
|
||||
"ignoreRegExpList": [
|
||||
"yarn-commands",
|
||||
"yarn-expressions",
|
||||
"yarn-variables",
|
||||
"yarn-markup",
|
||||
"yarn-hashtags",
|
||||
"yarn-node-markers",
|
||||
"yarn-headers",
|
||||
"yarn-character-names",
|
||||
"yarn-comments"
|
||||
]
|
||||
}
|
||||
],
|
||||
"cSpell.words": [
|
||||
"Cassian",
|
||||
"Thal",
|
||||
"Vaselian"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
# AGENTS.md
|
||||
|
||||
This repository's agent instructions live in [`CLAUDE.md`](./CLAUDE.md). It is
|
||||
tool-agnostic — repository layout, the Unity command line, the dialogue content
|
||||
pipeline, validation, conventions, and known traps.
|
||||
|
||||
Read `CLAUDE.md` before making changes, regardless of which agent you are.
|
||||
|
||||
Design and migration plans are in [`docs/`](./docs/), most relevantly
|
||||
[`docs/restructure-plan.md`](./docs/restructure-plan.md).
|
||||
@@ -0,0 +1,361 @@
|
||||
# Nightclub Arcadia — agent reference
|
||||
|
||||
Reference for coding agents (Claude Code and others) working in this repository.
|
||||
Human-facing design docs live in `docs/`. This file is the operational one: where
|
||||
things are, what is generated, and how to build/test/validate from the terminal.
|
||||
|
||||
Companion: `AGENTS.md` (pointer file, same content applies).
|
||||
|
||||
---
|
||||
|
||||
## 1. Repository layout
|
||||
|
||||
Repo root is `nightlcub-arcadia` (the typo is in the directory name, not a mistake
|
||||
to fix — it is the checkout path). The Unity project is a **subdirectory**:
|
||||
|
||||
```
|
||||
/Users/lennart/Dev/nightlcub-arcadia/ # git root
|
||||
├── CLAUDE.md, AGENTS.md # this reference
|
||||
├── docs/ # design + plan docs (markdown)
|
||||
├── tools/YarnCheck/ # .NET 9 Yarn compiler/player, no Unity needed
|
||||
├── .gitignore # macOS / IDE / toolchain rules
|
||||
└── NightclubArcadia/ # ← the Unity project (-projectPath)
|
||||
├── .gitignore # Unity rules (anchored, must stay here)
|
||||
├── Assets/
|
||||
├── Packages/ # incl. embedded dev.yarnspinner.unity
|
||||
└── ProjectSettings/
|
||||
```
|
||||
|
||||
**Two `.gitignore` files, deliberately.** The Unity one anchors every rule with a
|
||||
leading `/`, so it only works at the root of the Unity project folder. Do not merge them.
|
||||
|
||||
Unity **6000.5.8f1**, URP 17.5.0, Cinemachine 3.1.7, Input System 1.20.0,
|
||||
AI Navigation 2.0.14, ProBuilder 6.1.2, Test Framework 1.7.0. Yarn Spinner is an
|
||||
**embedded package** at `NightclubArcadia/Packages/dev.yarnspinner.unity` (plus the
|
||||
`textanimator` addon) — it is committed, not resolved from a registry.
|
||||
|
||||
---
|
||||
|
||||
## 2. Ground rules
|
||||
|
||||
1. **Never hand-edit generated `.asset` files.** `Assets/Skills/Definitions/*.asset`,
|
||||
`Assets/Skills/SkillDatabase.asset`, `Assets/Candidates/Definitions/*.asset` and
|
||||
`Assets/Candidates/CandidateDatabase.asset` are *build output*. Their sources are
|
||||
`Assets/Skills/skill_bible.json` and `Assets/Candidates/candidates.json`. Edits are
|
||||
overwritten by the Editor setup scripts. Edit the JSON.
|
||||
2. **Never hand-edit scene or prefab YAML unless you have read the surrounding block.**
|
||||
GUID/fileID surgery in `.unity` / `.prefab` files silently detaches components.
|
||||
3. **One Unity instance per project path.** Unity refuses to open a project in batch
|
||||
mode while the Editor has it open — a single instance at a time. Always check before
|
||||
any `-batchmode` invocation (§3.1).
|
||||
4. **Prefer YarnCheck over Unity for dialogue validation.** It is seconds, not minutes,
|
||||
and needs no Editor lock (§5.3).
|
||||
5. **Do not commit on the user's behalf** unless asked. Unity writes to tracked files
|
||||
(`ProjectSettings/*`, `.meta`, scenes) as a side effect of simply being open, so
|
||||
`git status` noise is expected and is not always yours.
|
||||
|
||||
---
|
||||
|
||||
## 3. Unity from the command line
|
||||
|
||||
### 3.0 Two different tools, both present
|
||||
|
||||
| | What it is | Status |
|
||||
|---|---|---|
|
||||
| `/Users/lennart/.unity/bin/unity` | The **Unity CLI** (v1.0.0-beta.6). Wraps the Editor for CI-shaped work: `build`, `test`, `run`, `open`, plus editor/license/cloud management. | Installed. **Prefer this.** |
|
||||
| `/Applications/Unity/Hub/Editor/6000.5.8f1/Unity.app/Contents/MacOS/Unity` | The **Editor executable**. Raw `-batchmode` flags. | Installed. Fallback, for flags the CLI does not expose. |
|
||||
|
||||
The CLI is **not on `PATH` for non-interactive shells** — `which unity` fails even though the
|
||||
binary exists, and `unity doctor` reports `check.binary-on-path warn`. Always invoke it by
|
||||
absolute path, or export it:
|
||||
|
||||
```bash
|
||||
export UNITY_CLI="/Users/lennart/.unity/bin/unity"
|
||||
export UNITY="/Applications/Unity/Hub/Editor/6000.5.8f1/Unity.app/Contents/MacOS/Unity"
|
||||
export UNITY_PROJECT="/Users/lennart/Dev/nightlcub-arcadia/NightclubArcadia"
|
||||
```
|
||||
|
||||
`unity doctor --no-banner` confirms the environment: signed in via OAuth keyring, editor
|
||||
`6000.5.8f1` arm64 resolved from `ProjectVersion.txt`. Add `--json` to any command for
|
||||
machine-readable output and `--no-banner` to keep logs clean.
|
||||
|
||||
### 3.1 Always check the lock first
|
||||
|
||||
Only one Unity instance per project path. Check before any headless run:
|
||||
|
||||
```bash
|
||||
pgrep -fl "Unity.app/Contents/MacOS/Unity" | grep -v AssetImportWorker
|
||||
```
|
||||
|
||||
`AssetImportWorker*` processes are children of the main Editor — filter them out or you will
|
||||
misread the check. `Temp/UnityLockfile` and `unity status` are the other two signals.
|
||||
|
||||
### 3.2 Run tests
|
||||
|
||||
```bash
|
||||
"$UNITY_CLI" test "$UNITY_PROJECT" --no-banner --mode EditMode \
|
||||
--output /tmp/editmode-results.xml \
|
||||
--report-format both --junit-output /tmp/editmode-junit.xml
|
||||
```
|
||||
|
||||
Useful flags: `--filter <pattern>`, `--retries <0-10>` (reports tests that pass on retry as
|
||||
flaky), `--rerun-failed`, `--shard N/M` for parallel CI, `--coverage`, `--timeout <seconds>`.
|
||||
|
||||
`--mode PlayMode` runs the PlayMode suite. **There are none yet** — `Assets/Tests/` is EditMode
|
||||
only, so that command reports zero tests. A PlayMode suite needs a new
|
||||
`Assets/Tests/PlayMode/*.asmdef` referencing `UnityEngine.TestRunner` with `includePlatforms`
|
||||
left empty.
|
||||
|
||||
Raw-Editor equivalent, if you need a flag the CLI does not expose:
|
||||
|
||||
```bash
|
||||
"$UNITY" -batchmode -nographics -projectPath "$UNITY_PROJECT" \
|
||||
-runTests -testPlatform EditMode \
|
||||
-testResults /tmp/editmode-results.xml -logFile /tmp/unity-editmode.log
|
||||
```
|
||||
|
||||
**Do not add `-quit` to a `-runTests` run** — the runner terminates the Editor itself, and
|
||||
`-quit` on top of async work can hang it.
|
||||
|
||||
**Parse the results XML; do not trust the exit code.** Unity's own docs state there is no common
|
||||
exit-code definition across the components under test. Read `total`/`passed`/`failed` off the
|
||||
root element and list any `test-case` whose `result` is not `Passed`.
|
||||
|
||||
### 3.3 Build a player
|
||||
|
||||
```bash
|
||||
"$UNITY_CLI" build "$UNITY_PROJECT" --no-banner \
|
||||
--target StandaloneOSX -o /tmp/NightclubArcadia.app
|
||||
```
|
||||
|
||||
Without `--execute-method` the CLI uses Unity's built-in build — a Build Profile on Unity 6+, or
|
||||
the legacy desktop player flags — and builds whatever scenes `EditorBuildSettings` enables.
|
||||
`--profile <name>` selects a profile from `Assets/Settings/Build Profiles` (the profile defines
|
||||
the target). `-l/--log-file` overrides the log path; the log streams to console unless `--no-tail`.
|
||||
|
||||
**No build script exists yet.** When one is added, put it at `Assets/Editor/BuildPipeline.cs` and
|
||||
call `--execute-method NightclubArcadia.Editor.BuildPipeline.BuildMacOS`. The CLI forwards the
|
||||
output path as `-buildOutput` and **your method is responsible for honouring it**. A build method
|
||||
must call `EditorApplication.Exit(1)` on failure, or Unity exits 0 and CI reports a green build
|
||||
that produced nothing.
|
||||
|
||||
### 3.4 Run an arbitrary Editor method
|
||||
|
||||
```bash
|
||||
"$UNITY_CLI" run "$UNITY_PROJECT" --no-banner --timeout 900 -- \
|
||||
-executeMethod SomeClass.SomeStaticMethod
|
||||
```
|
||||
|
||||
**`unity run` owns `-batchmode`, `-nographics`, `-quit` and `-logFile`.** Passing any of them
|
||||
after `--` fails with a reserved-flag error. Pass only your own arguments.
|
||||
|
||||
Output does **not** stream to stdout. The log lands at `$UNITY_PROJECT/Logs/Editor.log` (the
|
||||
previous run rotates to `Editor-prev.log`), so tag your `Debug.Log` calls and grep for them:
|
||||
|
||||
```bash
|
||||
grep -n "\[YourTag\]" "$UNITY_PROJECT/Logs/Editor.log" | tail -20
|
||||
```
|
||||
|
||||
This is the mechanism for the JSON → ScriptableObject regeneration in
|
||||
`Assets/Editor/SkillSystemSetup.cs` and `CandidateSystemSetup.cs`. Read the actual class and
|
||||
method names out of those files before invoking — do not guess them.
|
||||
|
||||
## 4. The dialogue content pipeline
|
||||
|
||||
### 4.1 Where content lives
|
||||
|
||||
```
|
||||
NightclubArcadia/Assets/Dialogue/
|
||||
├── NightclubArcadia.yarnproject # globs **/*.yarn — new files need no registration
|
||||
├── Common.yarn # every <<declare>>, project-wide. Never played.
|
||||
├── Commentary.yarn # passive skill barks, one node per (context, skill)
|
||||
├── Scenes/ SC101, SC102, Debug
|
||||
├── Characters/ Bartender, Bradford Kane, Chevalier Cassian Thal
|
||||
└── Objects/ Chair
|
||||
```
|
||||
|
||||
**Dialogue is already plain text in the repo.** It is authored in `.yarn` files, not inside
|
||||
the Unity Editor, and any new `.yarn` file dropped anywhere under `Assets/Dialogue/` is picked
|
||||
up automatically by the `**/*.yarn` glob in the `.yarnproject`. Adding a scene or a character
|
||||
requires **no C#, no Unity, and no `.yarnproject` edit**.
|
||||
|
||||
The one caveat: the `.yarnproject` also stores `editorOptions.yarnScriptEditor.projectGraphPositions`
|
||||
— per-file x/y coordinates for the Yarn graph editor. These churn whenever anyone opens the
|
||||
graph view. Treat that block as editor state; do not fight over it in review.
|
||||
|
||||
### 4.2 The variable contract
|
||||
|
||||
Every variable used anywhere must be `<<declare>>`d in `Common.yarn`. This is the whole point
|
||||
of that file: the compiler then catches `$reputaton` instead of silently creating a second
|
||||
variable at runtime. Adding a scene flag means adding a `<<declare>>` line — that is a writer
|
||||
action, not a code action.
|
||||
|
||||
Naming families that are load-bearing (enforced or lint-checked):
|
||||
|
||||
| Family | Meaning | Checked by |
|
||||
|---|---|---|
|
||||
| `$self_lean_<candidate_id>` | identity-lean counter | `CandidateYarnLintTests` — id must exist in `candidates.json` |
|
||||
| `$found_<clue_id>` | clue coverage | `CandidateYarnLintTests` — id must exist in `candidates.json` |
|
||||
| `$visited_<location_id>` | first arrival | convention; `unlockFlag` in `candidates.json` may only name these |
|
||||
| `$errand_<errand_id>` | errand state | same |
|
||||
| `$seen_<scene_id>` | scene witnessed | convention |
|
||||
| `$check_*` | written by `<<check>>` | **never `<<set>>` by hand** |
|
||||
|
||||
### 4.3 Writer-facing Yarn vocabulary
|
||||
|
||||
Commands and functions registered by game code, i.e. the full set a writer may use:
|
||||
|
||||
| Syntax | Defined in | Notes |
|
||||
|---|---|---|
|
||||
| `<<check <skill> <Band>>>` | `Skills/Yarn/YarnSkillCommands.cs` | writes `$check_result`, `$check_roll`, `$check_total`, `$check_dc`, `$check_modifier`, `$check_degree`, `$check_skill` |
|
||||
| `<<skill_mod <source> <skill> <n>>>` | same | `source` = bucket key, e.g. `scene`, `drunk` |
|
||||
| `<<clear_skill_mods <source>>>` | same | |
|
||||
| `skill_rank(<skill>)` | same | function |
|
||||
| `top_skill(a, b, …)` | `Dialogue/DialougueSkillComparison.cs` | **returns its FIRST argument on a tie** — argument order is a design decision, not incidental |
|
||||
| `alias_name(<name>)` | `Dialogue/AliasNameGenerator.cs` | deterministic; `$player_alias` is a smart variable derived from it |
|
||||
| `<<enter_environment>>` | `Dialogue/DialogueTransitionController.cs` | hands control back to the world |
|
||||
|
||||
(The filename `DialougueSkillComparison.cs` is misspelled in the repo. Left alone deliberately —
|
||||
renaming it changes a `.meta` GUID. Fold it into a rename batch, not a drive-by fix.)
|
||||
|
||||
### 4.4 Flow control discipline
|
||||
|
||||
`<<detour>>` returns to the caller; `<<jump>>` does not and clears the return stack.
|
||||
The rule the existing files follow, and which new content must follow:
|
||||
|
||||
- A node started directly by `DialogueInteractable` has nothing waiting on it → its branches
|
||||
`<<jump>>`, and each destination calls `<<enter_environment>>` itself.
|
||||
- A sub-conversation that must resume its caller → `<<detour>>`, and **never** `<<jump>>` inside it.
|
||||
|
||||
Nodes sharing a `title:` form a **node group**; Yarn runs the most specific variation whose
|
||||
`when:` conditions pass. Prefer adding a variation over growing an `<<if>>` chain.
|
||||
|
||||
### 4.5 How content reaches Unity
|
||||
|
||||
There is **no manual sync step** for dialogue. The chain is:
|
||||
|
||||
```
|
||||
.yarn file saved → Unity's YarnProjectImporter recompiles the .yarnproject on focus
|
||||
→ DialogueRunner plays the new nodes on next Play
|
||||
```
|
||||
|
||||
The only things that need a human/Editor step are the *data* files, not the dialogue:
|
||||
|
||||
```
|
||||
skill_bible.json → Tools ▸ Nightclub Arcadia ▸ (SkillSystemSetup) → Skills/Definitions/*.asset + SkillDatabase.asset
|
||||
candidates.json → Tools ▸ Nightclub Arcadia ▸ (CandidateSystemSetup) → Candidates/Definitions/*.asset + CandidateDatabase.asset
|
||||
```
|
||||
|
||||
Those setup scripts also register a `[DidReloadScripts]` callback, so in practice the assets
|
||||
regenerate on script reload too. The JSON is the source of truth either way.
|
||||
|
||||
### 4.6 Code-owned vs writer-owned
|
||||
|
||||
| Writer-owned (edit freely, no Unity) | Code-owned (needs an engineer) |
|
||||
|---|---|
|
||||
| `Assets/Dialogue/**/*.yarn` | `Assets/Scripts/**/*.cs` |
|
||||
| `<<declare>>` lines in `Common.yarn` | `Assets/Editor/**/*.cs` |
|
||||
| `Assets/Skills/skill_bible.json` (prose fields) | Yarn command/function *implementations* |
|
||||
| `Assets/Candidates/candidates.json` (`status`, `sceneId`, `notes`, `unlockFlag`) | `.asset`, `.prefab`, `.unity`, `.meta` |
|
||||
| `docs/` writing docs | `ProjectSettings/**` |
|
||||
|
||||
**A writer must never need to open Unity or touch C# to write or revise a scene, a character,
|
||||
or a branch.** If a writing task requires either, that is a pipeline bug — file it.
|
||||
|
||||
---
|
||||
|
||||
## 5. Validation
|
||||
|
||||
### 5.1 EditMode test suite
|
||||
|
||||
`NightclubArcadia/Assets/Tests/EditMode/` — 7 files, ~870 LOC, two assemblies:
|
||||
|
||||
- `NightclubArcadia.Skills.Tests` — `SkillRosterTests`, `SkillCheckSystemTests`, `DcBandTests`,
|
||||
`CommentaryPickerTests`, `CandidateRegistryTests`, `CandidateYarnLintTests`
|
||||
- `NightclubArcadia.Locomotion.Math.Tests` — `MotionInputMathTests`
|
||||
|
||||
`CandidateYarnLintTests` is a **regex lint over Yarn source**: it asserts every
|
||||
`$self_lean_*` / `$found_*` in any `.yarn` file resolves to a real id in `CandidateDatabase`.
|
||||
It is a tripwire, not a proof.
|
||||
|
||||
### 5.2 One-click in-Editor run
|
||||
|
||||
`Tools ▸ Nightclub Arcadia ▸ Run Skill EditMode Tests` (`Assets/Editor/SkillSystemTestRunner.cs`).
|
||||
|
||||
### 5.3 YarnCheck — validate dialogue without Unity ⭐
|
||||
|
||||
`tools/YarnCheck` is a .NET 9 console app that compiles **and plays** the Yarn scripts using
|
||||
the exact Yarn Spinner DLLs shipped in `Packages/dev.yarnspinner.unity/Runtime/DLLs`, so it
|
||||
can never drift from the compiler the game uses. This is the fastest correctness signal in the
|
||||
repo and it needs no Editor lock. Use it on every dialogue change.
|
||||
|
||||
```bash
|
||||
cd /Users/lennart/Dev/nightlcub-arcadia/tools/YarnCheck
|
||||
|
||||
# compile everything — exit 1 + diagnostics on failure
|
||||
dotnet run -- ../../NightclubArcadia/Assets/Dialogue
|
||||
|
||||
# compile, then play a node, choosing option 2, 2, 0, 0
|
||||
dotnet run -- ../../NightclubArcadia/Assets/Dialogue Bartender_Talk 2 2 0 0
|
||||
```
|
||||
|
||||
Leftover picks restart the node — that deliberately models the player walking away and coming
|
||||
back, which is how these scenes are actually played.
|
||||
|
||||
**What it does not model** (read traces with this in mind): Unity commands are printed, not
|
||||
executed, so `<<check>>` / `<<enter_environment>>` / `<<skill_mod>>` are stepped over and
|
||||
`$check_result` stays `false` — every check-gated branch shows its *failure* side. Yarn
|
||||
functions are stubbed: `top_skill` mirrors the real first-argument-wins tie behaviour,
|
||||
`alias_name` returns a placeholder, `skill_rank` returns 0. Variables reset to their
|
||||
`<<declare>>` defaults each run; there is no save state.
|
||||
|
||||
Requires the .NET 9 SDK. `tools/YarnCheck/bin/` and `obj/` are currently **committed** and
|
||||
should not be — see `docs/restructure-plan.md`.
|
||||
|
||||
---
|
||||
|
||||
## 6. Code conventions
|
||||
|
||||
- Namespaces: `NightclubArcadia.<Area>` — `Skills`, `Dialogue`, `UI`, `UI.Dialogue`,
|
||||
`UI.HUD`, `UI.Menu`, `Cinematics`, `Interaction`, `Player`, `Player.Math`.
|
||||
**Exception:** `NPCStandIn.cs` sits in the global namespace. That is a scaffold tell, not a
|
||||
convention to copy — new runtime types always get a namespace.
|
||||
- Assembly definitions exist for `NightclubArcadia.Skills` and `NightclubArcadia.Locomotion.Math`
|
||||
only. Camera, Player, UI, Dialogue, NPCs and Interaction all currently compile into the
|
||||
default `Assembly-CSharp`. Widening asmdef coverage is planned, not done.
|
||||
- Comments in this codebase carry *decision history* — why a flag is written at the bottom of a
|
||||
node rather than the top, why four voices in SC-101 breaks the three-voice cap on purpose.
|
||||
Match that density. Do not strip such comments as "noise"; they are the design record.
|
||||
- Editor-only code lives under `Assets/Editor/`. Anything there is stripped from builds and may
|
||||
reference `UnityEditor`.
|
||||
|
||||
---
|
||||
|
||||
## 7. Traps
|
||||
|
||||
1. **`Assets/Scenes/DialogueTest.unity` is serialized as binary**, and Unity will not convert it
|
||||
from a headless run. The project is set to Force Text (`m_SerializationMode: 2`) and the API
|
||||
agrees — `EditorSettings.serializationMode` reads `ForceText` — but Force Text governs
|
||||
conversion **at the moment the setting changes**, not on every save. A file that entered the
|
||||
project as binary stays binary. Four approaches were tried and all failed silently:
|
||||
`AssetDatabase.ForceReserializeAssets` does not rewrite scene files; `OpenScene` +
|
||||
`MarkSceneDirty` + `SaveScene` to the same path returns `true` and writes nothing, because
|
||||
`MarkSceneDirty` does not take in batch mode; `SaveScene` to a *new* path writes a fresh file
|
||||
that is also binary; and re-assigning `serializationMode` (Mixed → ForceText) from script
|
||||
does not trigger a reserialize pass. **The fix is Editor-UI only:** Project Settings ▸ Editor
|
||||
▸ Asset Serialization, switch the mode and switch it back, which reserializes on apply.
|
||||
Until then the scene is not diffable or mergeable. It is committed and intact (14 roots).
|
||||
2. **`unity run` reserves `-batchmode`, `-nographics`, `-quit`, `-logFile`.** Passing any of them
|
||||
after `--` is a hard error, and its output does not reach stdout — read `Logs/Editor.log` (§3.4).
|
||||
3. **`-quit` with `-runTests`** hangs or truncates. Omit it (§3.2).
|
||||
4. **Exit code alone is not a test verdict.** Parse the results XML (§3.2).
|
||||
5. **`which unity` fails** in non-interactive shells even though the CLI is installed. Use the
|
||||
absolute path `/Users/lennart/.unity/bin/unity` (§3.0).
|
||||
6. **Editor lock** — check before every headless run, filtering out `AssetImportWorker` children (§3.1).
|
||||
7. **Regenerated assets** — hand edits to `Skills/Definitions/*`, `Candidates/Definitions/*`
|
||||
and the two `*Database.asset` files are silently discarded (§2.1).
|
||||
8. **`top_skill` ties** resolve to the first argument. Reordering arguments changes the game.
|
||||
9. **No `.gitattributes`** — no Unity YAML merge driver, no LFS rule for `Assets/Art/`.
|
||||
Scene and prefab merges will conflict destructively under branching.
|
||||
10. **Template leftovers still present**: `Assets/Scenes/SampleScene.unity`, `Assets/Readme.asset`,
|
||||
`Assets/TutorialInfo/`. `SampleScene` is in the build scene list. Not load-bearing.
|
||||
@@ -149,3 +149,71 @@ InitTestScene*.unity*
|
||||
|
||||
# Auto-generated assembly info from IL post-processing
|
||||
/[Aa]ssets/csc.rsp.meta.tmp
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TextMesh Pro (shipped inside com.unity.ugui; the editor copies resources into
|
||||
# /Assets/TextMesh Pro when you run "Import TMP Essential Resources")
|
||||
# ---------------------------------------------------------------------------
|
||||
# The essentials — TMP Settings.asset, the LiberationSans SDF font asset, the
|
||||
# shaders and the style sheet — are COMMITTED on purpose. ProjectSettings and
|
||||
# every TMP component reference them by GUID, so a teammate who clones without
|
||||
# them gets pink text and missing-reference errors instead of a working scene.
|
||||
#
|
||||
# "Examples & Extras" is a separate, optional import (~15 MB of sample scenes,
|
||||
# sprites and fonts). Nothing in the game references it and anyone who wants it
|
||||
# can re-import it from Window > TextMeshPro.
|
||||
/[Aa]ssets/TextMesh Pro/Examples & Extras/
|
||||
/[Aa]ssets/TextMesh Pro/Examples & Extras.meta
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Yarn Spinner (dev.yarnspinner.unity + addons, installed as embedded packages
|
||||
# under /Packages — those package folders are committed on purpose)
|
||||
# ---------------------------------------------------------------------------
|
||||
# Language-server definition files, regenerated on every recompile from the
|
||||
# [YarnCommand]/[YarnFunction] attributes in each assembly. Only useful to the
|
||||
# local VS Code extension, and they churn constantly.
|
||||
/ProjectSettings/Packages/dev.yarnspinner/generated.ysls.json
|
||||
/ProjectSettings/Packages/dev.yarnspinner/*-generated.ysls.json
|
||||
# Keep the actual settings file:
|
||||
!/ProjectSettings/Packages/dev.yarnspinner/YarnSpinnerProjectSettings.json
|
||||
|
||||
# Source-generator output for attributed commands/functions (regenerated).
|
||||
YarnActionRegistration.cs
|
||||
YarnActionRegistration.cs.meta
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ProBuilder (com.unity.probuilder, embedded as source under /Packages instead
|
||||
# of resolving through the registry into Library/PackageCache)
|
||||
# ---------------------------------------------------------------------------
|
||||
# Folders ending in "~" are already invisible to the Unity Editor/AssetDatabase
|
||||
# (no .meta files are ever generated for them), so committing them only bloats
|
||||
# the repo for content nobody can reference from the project:
|
||||
# Documentation~/ ~16 MB of package docs
|
||||
# Samples~/ ~1 MB of optional sample scenes/scripts
|
||||
/[Pp]ackages/com.unity.probuilder/Documentation~/
|
||||
/[Pp]ackages/com.unity.probuilder/Samples~/
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# StarterAssets (Unity's ThirdPerson Starter Assets package, imported under
|
||||
# /Assets/StarterAssets)
|
||||
# ---------------------------------------------------------------------------
|
||||
# Vendor onboarding readme + its screenshot, shown once in the Editor's
|
||||
# "Readme" inspector window. Not referenced by any scene or script.
|
||||
/[Aa]ssets/StarterAssets/Readme.asset
|
||||
/[Aa]ssets/StarterAssets/Readme.asset.meta
|
||||
/[Aa]ssets/StarterAssets/TutorialInfo/
|
||||
|
||||
# Vendor PDF manual, not used at runtime.
|
||||
/[Aa]ssets/StarterAssets/StarterAssets_Documentation_v1.1.pdf
|
||||
/[Aa]ssets/StarterAssets/StarterAssets_Documentation_v1.1.pdf.meta
|
||||
|
||||
# "Playground" demo scene the package ships to showcase the controller.
|
||||
# Not in EditorBuildSettings and not referenced by any of the project's own
|
||||
# scenes — includes baked LightingData + reflection probe .exr captures that
|
||||
# only apply to that demo scene's lighting setup.
|
||||
/[Aa]ssets/StarterAssets/ThirdPersonController/Scenes/Playground.unity
|
||||
/[Aa]ssets/StarterAssets/ThirdPersonController/Scenes/Playground.unity.meta
|
||||
/[Aa]ssets/StarterAssets/ThirdPersonController/Scenes/Playground/
|
||||
/[Aa]ssets/StarterAssets/ThirdPersonController/Scenes/Playground.meta
|
||||
/[Aa]ssets/StarterAssets/ThirdPersonController/Scenes/PlaygroundSettings.lighting
|
||||
/[Aa]ssets/StarterAssets/ThirdPersonController/Scenes/PlaygroundSettings.lighting.meta
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 98c69c750f094ae79e2f9362c4422647
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6270e22570864d7c862434241018328f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bffd4617fb74418f9eb0d0d6378cf79a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff84645ddce3641b0a7e9c68db56c8db
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
@@ -0,0 +1,84 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a1b2c3d4e5f60718293a4b5c6d7e8f90
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 8
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings: []
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db0c69a0766e477bb76a94393518cf60
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,18 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: c4dc6e246e9a462dbae29cd42b16317e, type: 3}
|
||||
m_Name: CandidateDatabase
|
||||
m_EditorClassIdentifier:
|
||||
candidates:
|
||||
- {fileID: 11400000, guid: af55e04701494a71a0b292d910db3d13, type: 2}
|
||||
- {fileID: 11400000, guid: 7282093aca2e4d408d4972354ebcd634, type: 2}
|
||||
- {fileID: 11400000, guid: b6184c723bab4bef8f8fbaaf462a324d, type: 2}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2fa27d75ae14f498235dd461791765d
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 75c54c58c29b4b738e7c0f55e960540b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 1f0a63ccd8da4e5ab64dea83c64eeb6a, type: 3}
|
||||
m_Name: inheritor
|
||||
m_EditorClassIdentifier:
|
||||
id: inheritor
|
||||
displayName: The Inheritor
|
||||
reading: This was never the PC's own story; a parent or elder relative had standing
|
||||
or a debt with a faction, which passed without full consent.
|
||||
intuitionAxis: 3
|
||||
clues:
|
||||
- id: inheritor_object
|
||||
type: 0
|
||||
sceneId:
|
||||
unlockFlag: $visited_private_box
|
||||
status: 0
|
||||
skillId:
|
||||
notes: "Object clue \u2014 Planned. Not yet authored."
|
||||
- id: inheritor_testimony
|
||||
type: 1
|
||||
sceneId:
|
||||
unlockFlag: $errand_ask_about_name
|
||||
status: 0
|
||||
skillId:
|
||||
notes: "Testimony clue \u2014 Planned. Not yet authored."
|
||||
- id: inheritor_document
|
||||
type: 2
|
||||
sceneId:
|
||||
unlockFlag: $visited_membership_ledger
|
||||
status: 0
|
||||
skillId:
|
||||
notes: "Document clue \u2014 Planned. Not yet authored."
|
||||
- id: inheritor_intuition
|
||||
type: 3
|
||||
sceneId: SC101
|
||||
unlockFlag: $visited_desk
|
||||
status: 1
|
||||
skillId: standing_order
|
||||
notes: 'SC-101 Self intuition: correcting an etiquette rule never consciously
|
||||
learned.'
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b6184c723bab4bef8f8fbaaf462a324d
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 1f0a63ccd8da4e5ab64dea83c64eeb6a, type: 3}
|
||||
m_Name: journalist
|
||||
m_EditorClassIdentifier:
|
||||
id: journalist
|
||||
displayName: The Journalist
|
||||
reading: The PC came for a specific person, not a story; 'journalist' is a cover
|
||||
identity.
|
||||
intuitionAxis: 2
|
||||
clues:
|
||||
- id: journalist_object
|
||||
type: 0
|
||||
sceneId:
|
||||
unlockFlag: $visited_coat_check
|
||||
status: 0
|
||||
skillId:
|
||||
notes: "Object clue \u2014 Planned. Not yet authored."
|
||||
- id: journalist_testimony
|
||||
type: 1
|
||||
sceneId:
|
||||
unlockFlag: $errand_find_contact
|
||||
status: 0
|
||||
skillId:
|
||||
notes: "Testimony clue \u2014 Planned. Not yet authored."
|
||||
- id: journalist_document
|
||||
type: 2
|
||||
sceneId:
|
||||
unlockFlag: $visited_press_room
|
||||
status: 0
|
||||
skillId:
|
||||
notes: "Document clue \u2014 Planned. Not yet authored."
|
||||
- id: journalist_intuition
|
||||
type: 3
|
||||
sceneId: SC101
|
||||
unlockFlag: $visited_desk
|
||||
status: 1
|
||||
skillId: facework
|
||||
notes: 'SC-101 Social intuition: a reaction that lands wrong and can''t yet be
|
||||
explained.'
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7282093aca2e4d408d4972354ebcd634
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 1f0a63ccd8da4e5ab64dea83c64eeb6a, type: 3}
|
||||
m_Name: made_asset
|
||||
m_EditorClassIdentifier:
|
||||
id: made_asset
|
||||
displayName: The Made Asset
|
||||
reading: The PC was trained, conditioned, or activated by one of the factions for
|
||||
a role near the central conference.
|
||||
intuitionAxis: 0
|
||||
clues:
|
||||
- id: made_asset_object
|
||||
type: 0
|
||||
sceneId:
|
||||
unlockFlag: $visited_back_office
|
||||
status: 0
|
||||
skillId:
|
||||
notes: "Object clue \u2014 Planned. Not yet authored."
|
||||
- id: made_asset_testimony
|
||||
type: 1
|
||||
sceneId:
|
||||
unlockFlag: $errand_door_list
|
||||
status: 0
|
||||
skillId:
|
||||
notes: "Testimony clue \u2014 Planned. Not yet authored."
|
||||
- id: made_asset_document
|
||||
type: 2
|
||||
sceneId:
|
||||
unlockFlag: $visited_archives
|
||||
status: 0
|
||||
skillId:
|
||||
notes: "Document clue \u2014 Planned. Not yet authored."
|
||||
- id: made_asset_intuition
|
||||
type: 3
|
||||
sceneId: SC101
|
||||
unlockFlag: $visited_desk
|
||||
status: 1
|
||||
skillId: confluence
|
||||
notes: 'SC-101 Reason intuition: parsing something too fluently, before consciously
|
||||
trying to.'
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af55e04701494a71a0b292d910db3d13
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"candidates": [
|
||||
{
|
||||
"id": "made_asset",
|
||||
"displayName": "The Made Asset",
|
||||
"reading": "The PC was trained, conditioned, or activated by one of the factions for a role near the central conference.",
|
||||
"intuitionAxis": "Reason",
|
||||
"clues": [
|
||||
{
|
||||
"id": "made_asset_object",
|
||||
"type": "Object",
|
||||
"sceneId": "",
|
||||
"unlockFlag": "$visited_back_office",
|
||||
"status": "Planned",
|
||||
"skillId": "",
|
||||
"notes": "Object clue — Planned. Not yet authored."
|
||||
},
|
||||
{
|
||||
"id": "made_asset_testimony",
|
||||
"type": "Testimony",
|
||||
"sceneId": "",
|
||||
"unlockFlag": "$errand_door_list",
|
||||
"status": "Planned",
|
||||
"skillId": "",
|
||||
"notes": "Testimony clue — Planned. Not yet authored."
|
||||
},
|
||||
{
|
||||
"id": "made_asset_document",
|
||||
"type": "Document",
|
||||
"sceneId": "",
|
||||
"unlockFlag": "$visited_archives",
|
||||
"status": "Planned",
|
||||
"skillId": "",
|
||||
"notes": "Document clue — Planned. Not yet authored."
|
||||
},
|
||||
{
|
||||
"id": "made_asset_intuition",
|
||||
"type": "Intuition",
|
||||
"sceneId": "SC101",
|
||||
"unlockFlag": "$visited_desk",
|
||||
"status": "Authored",
|
||||
"skillId": "confluence",
|
||||
"notes": "SC-101 Reason intuition: parsing something too fluently, before consciously trying to."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "journalist",
|
||||
"displayName": "The Journalist",
|
||||
"reading": "The PC came for a specific person, not a story; 'journalist' is a cover identity.",
|
||||
"intuitionAxis": "Social",
|
||||
"clues": [
|
||||
{
|
||||
"id": "journalist_object",
|
||||
"type": "Object",
|
||||
"sceneId": "",
|
||||
"unlockFlag": "$visited_coat_check",
|
||||
"status": "Planned",
|
||||
"skillId": "",
|
||||
"notes": "Object clue — Planned. Not yet authored."
|
||||
},
|
||||
{
|
||||
"id": "journalist_testimony",
|
||||
"type": "Testimony",
|
||||
"sceneId": "",
|
||||
"unlockFlag": "$errand_find_contact",
|
||||
"status": "Planned",
|
||||
"skillId": "",
|
||||
"notes": "Testimony clue — Planned. Not yet authored."
|
||||
},
|
||||
{
|
||||
"id": "journalist_document",
|
||||
"type": "Document",
|
||||
"sceneId": "",
|
||||
"unlockFlag": "$visited_press_room",
|
||||
"status": "Planned",
|
||||
"skillId": "",
|
||||
"notes": "Document clue — Planned. Not yet authored."
|
||||
},
|
||||
{
|
||||
"id": "journalist_intuition",
|
||||
"type": "Intuition",
|
||||
"sceneId": "SC101",
|
||||
"unlockFlag": "$visited_desk",
|
||||
"status": "Authored",
|
||||
"skillId": "facework",
|
||||
"notes": "SC-101 Social intuition: a reaction that lands wrong and can't yet be explained."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "inheritor",
|
||||
"displayName": "The Inheritor",
|
||||
"reading": "This was never the PC's own story; a parent or elder relative had standing or a debt with a faction, which passed without full consent.",
|
||||
"intuitionAxis": "Self",
|
||||
"clues": [
|
||||
{
|
||||
"id": "inheritor_object",
|
||||
"type": "Object",
|
||||
"sceneId": "",
|
||||
"unlockFlag": "$visited_private_box",
|
||||
"status": "Planned",
|
||||
"skillId": "",
|
||||
"notes": "Object clue — Planned. Not yet authored."
|
||||
},
|
||||
{
|
||||
"id": "inheritor_testimony",
|
||||
"type": "Testimony",
|
||||
"sceneId": "",
|
||||
"unlockFlag": "$errand_ask_about_name",
|
||||
"status": "Planned",
|
||||
"skillId": "",
|
||||
"notes": "Testimony clue — Planned. Not yet authored."
|
||||
},
|
||||
{
|
||||
"id": "inheritor_document",
|
||||
"type": "Document",
|
||||
"sceneId": "",
|
||||
"unlockFlag": "$visited_membership_ledger",
|
||||
"status": "Planned",
|
||||
"skillId": "",
|
||||
"notes": "Document clue — Planned. Not yet authored."
|
||||
},
|
||||
{
|
||||
"id": "inheritor_intuition",
|
||||
"type": "Intuition",
|
||||
"sceneId": "SC101",
|
||||
"unlockFlag": "$visited_desk",
|
||||
"status": "Authored",
|
||||
"skillId": "standing_order",
|
||||
"notes": "SC-101 Self intuition: correcting an etiquette rule never consciously learned."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8255e20527a84dbabb918a5f5ad590d7
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f5e861c1af8414031bfd9600169fac0b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3148b8572d5194e449b23abaac9e717f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,107 @@
|
||||
// Everything the bartender ever says.
|
||||
//
|
||||
// Stack discipline (the player comes back to the bar, so it matters):
|
||||
// `Bartender_Talk` is the node DialogueInteractable starts, so nothing is
|
||||
// waiting for it to return. Its branches therefore <<jump>> — they are the last
|
||||
// thing the node does — and each destination hands control back with
|
||||
// <<enter_environment>> itself. <<detour>> is only for sub-conversations that
|
||||
// have to resume the caller; a <<jump>> inside one would clear the return stack.
|
||||
|
||||
title: Bartender_Talk
|
||||
position: -121,-60
|
||||
---
|
||||
<<if $spoke_to_bartender>>
|
||||
<<if $revealed_fugue_to_bartender>>
|
||||
Bartender: Ah, still hazy? A drink?
|
||||
<<else>>
|
||||
Bartender: Ah, {$player_alias}. A drink?
|
||||
<<endif>>
|
||||
-> Yes, please.
|
||||
<<jump SC101_Hook_Drink_Bartender_Talk>>
|
||||
-> No, I have questions.
|
||||
<<jump SC101_Hook_Questions_Bartender_Talk>>
|
||||
<<else>>
|
||||
<<set $spoke_to_bartender = true>>
|
||||
Bartender: Ah, {$player_alias}, back again?
|
||||
Narrator: What does he mean? "Back again?"
|
||||
-> Back? I have never been here.
|
||||
<<set $revealed_fugue_to_bartender = true>>
|
||||
Narrator: The bartender squints with a critical look.
|
||||
FACEWORK: He will tell on you. This is the face of a man who is not only here to tend the bar, but to watch, to observe. And to report back to his masters.
|
||||
Bartender: I could have sworn you are {$player_alias}, weren't you with Mr. Doss a few days ago?
|
||||
<<if $looking_for_doss eq false>>
|
||||
<<set $looking_for_doss = true>>
|
||||
Narrator: Mr. Doss?
|
||||
PROVENANCE: Mr. Doss. We need to find him, appraise him, document our findings, file them, cross-reference them ...
|
||||
<<else>>
|
||||
CONFLUENCE: There he is again: Mr. Doss.
|
||||
<<endif>>
|
||||
<<set $self_lean_made_asset += 1>>
|
||||
Bartender: What can I get ya?
|
||||
-> What can you recommend?
|
||||
Bartender: Last time you tooke the Estate.
|
||||
Narrator: You ignore the jab.
|
||||
<<jump SC101_Hook_Drink_Bartender_Talk>>
|
||||
-> Whatever.
|
||||
<<jump SC101_Hook_Drink_Bartender_Talk>>
|
||||
-> Yes. I'll take the usual.
|
||||
Narrator: The bartender doesn't even flinch, while taking ingredients from the bar.
|
||||
PROVENANCE: We cannot remember these substances.
|
||||
AMNESTY: Is this really our favorite drink?
|
||||
<<set $self_lean_inheritor += 1>>
|
||||
<<jump SC101_Hook_Drink_Bartender_Talk>>
|
||||
-> I am, and I have a few questions.
|
||||
<<jump SC101_Hook_Questions_Bartender_Talk>>
|
||||
<<endif>>
|
||||
===
|
||||
|
||||
title: SC101_Hook_Drink_Bartender_Talk
|
||||
position: 116,222
|
||||
---
|
||||
// $had_the_estate_drink is written at the BOTTOM of this node, not here. The whole
|
||||
// node reads first-glass state — the pour, the taste, and the lean write under the
|
||||
// answer — and the glass is only recorded once all of it has run. Setting the flag
|
||||
// up here (as this node used to) leaves the tests below reading a flag this same
|
||||
// run already flipped, and the lean write can never fire.
|
||||
Bartender: One Estate coming right up.
|
||||
<<if $had_the_estate_drink>>
|
||||
Narrator: He puts another amber drink before you.
|
||||
<<else>>
|
||||
Narrator: He puts a deep amber drink before you.
|
||||
HOUSE POUR: It smells like old wood, oak, which went sour with age, like a library shelf that's been rained on once and dried wrong.
|
||||
Narrator: You take a sip.
|
||||
HOUSE POUR: Deceptively soft. Brown sugar, a little burnt. Vanilla that's actually oak-tannin doing an impression of vanilla.
|
||||
<<endif>>
|
||||
AMNESTY: Is that the taste of our favorite drink? I cannot remember ...
|
||||
-> Yes, it is!
|
||||
<<if $had_the_estate_drink eq false>>
|
||||
<<set $self_lean_inheritor += 1>>
|
||||
HOUSE POUR: Long, dry, faintly bitter - gentian root or scorched citrus pith - and it doesn't fade so much as recede, like it's waiting to be poured again.
|
||||
<<else>>
|
||||
HOUSE POUR: Long and dry, it recedes again.
|
||||
<<endif>>
|
||||
-> I don't think so.
|
||||
<<if $had_the_estate_drink eq false>>
|
||||
<<set $self_lean_inheritor -= 1>>
|
||||
HOUSE POUR: Bitter, at the end. Like it's waiting to be poured again.
|
||||
<<else>>
|
||||
HOUSE POUR: Still bitter.
|
||||
<<endif>>
|
||||
<<set $had_the_estate_drink = true>>
|
||||
<<enter_environment>>
|
||||
===
|
||||
|
||||
title: SC101_Hook_Questions_Bartender_Talk
|
||||
position: -139,224
|
||||
---
|
||||
Bartender: Sure, what do you want to know?
|
||||
-> Question 1
|
||||
Bartender: Answer 1
|
||||
-> Question 2
|
||||
Bartender: Answer 2
|
||||
-> Nothing.
|
||||
Bartender: Alright.
|
||||
<<enter_environment>>
|
||||
<<stop>>
|
||||
<<jump SC101_Hook_Questions_Bartender_Talk>>
|
||||
===
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b8ac538a619b45b8a197ad048a94505
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 11500000, guid: 94073015eacc34c1d8fc6786e43d60ca, type: 3}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Everything Bradford Kane ever says.
|
||||
//
|
||||
// These nodes share a title, which makes them a node group. Callers just
|
||||
// `<<detour Bradford_Kane_Talk>>` and Yarn runs the most specific variation whose
|
||||
// `when:` conditions pass. Adding a new variation later means adding a node,
|
||||
// not editing a growing <<if>> chain.
|
||||
//
|
||||
// Each variation sets $went_inside so the calling scene knows the verdict.
|
||||
|
||||
title: Bradford_Kane_Talk
|
||||
---
|
||||
<<set $spoke_to_bradford_kane = true>>
|
||||
===
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a1ee2125b1dbf4068868b09cbb685aa8
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 11500000, guid: 94073015eacc34c1d8fc6786e43d60ca, type: 3}
|
||||
@@ -0,0 +1,150 @@
|
||||
// Everything Ser Cassian Thal ever says.
|
||||
//
|
||||
// These nodes share a title, which makes them a node group. Callers just
|
||||
// `<<detour Chevalier_Cassian_Thal_Talk>>` and Yarn runs the most specific variation whose
|
||||
// `when:` conditions pass. Adding a new variation later means adding a node,
|
||||
// not editing a growing <<if>> chain.
|
||||
//
|
||||
// Each variation sets $went_inside so the calling scene knows the verdict.
|
||||
|
||||
title: Chevalier_Cassian_Thal_Talk
|
||||
position: 44,-117
|
||||
---
|
||||
<<if $spoke_to_chevalier_cassian_thal>>
|
||||
Chevalier Cassian Thal: Well met good sir.
|
||||
<<else>>
|
||||
<<set $spoke_to_chevalier_cassian_thal = true>>
|
||||
Narrator: You introduce yourself to the man. He takes measure before bowing his head and tipping his forehead with a half-open fist.
|
||||
Chevalier Cassian Thal: {$player_name_full}, nice to meet you. I'm Chevalier Cassian Thal of Basel and Cyprus.
|
||||
THE FLOAT: His name reeks of money. Old money.
|
||||
<<check provenance Hard>>
|
||||
<<if $check_result>>
|
||||
PROVENANCE: Basel? Makes total sense. But which connections does this old codger have to Cyprus?
|
||||
-> Inquire further about Cyprus.
|
||||
<<detour SC101_Inquire_Cypurs_Chevalier_Cassian_Thal_Talk>>
|
||||
-> Leave it be.
|
||||
<<endif>>
|
||||
<<endif>>
|
||||
<<if $saw_knights_atlantean_cross_on_place_card eq true and $had_the_estate_drink eq true>>
|
||||
<<detour SC101_Hook_Asks_Candidate_Chevalier_Cassian_Thal_Talk>>
|
||||
<<endif>>
|
||||
===
|
||||
|
||||
title: SC101_Inquire_Cypurs_Chevalier_Cassian_Thal_Talk
|
||||
position: -175,95
|
||||
---
|
||||
<<set $inquired_about_cyprus = true>>
|
||||
Chevalier Cassian Thal: Oh, a sharp question, {$player_name_full}.
|
||||
Narrator: The knight laughs.
|
||||
Chevalier Cassian Thal: I assume you should ask your friend Mr. Doss, I am of no right to reveal this connection.
|
||||
<<if $looking_for_doss eq false>>
|
||||
<<set $looking_for_doss = true>>
|
||||
Narrator: Mr. Doss?
|
||||
PROVENANCE: Mr. Doss. We need to find him, appraise him, document our findings, file them, cross-reference them ...
|
||||
<<else>>
|
||||
CONFLUENCE: There he is again: Mr. Doss.
|
||||
<<endif>>
|
||||
===
|
||||
|
||||
title: SC101_Hook_Asks_Candidate_Chevalier_Cassian_Thal_Talk
|
||||
position: 146,85
|
||||
---
|
||||
<<if $inquired_about_cyprus>>
|
||||
Chevalier Cassian Thal: This were a lot of questions. Now tell me something about yourself, old chap.
|
||||
<<endif>>
|
||||
<<detour SC101_Asks_Candidate_Chevalier_Cassian_Thal_Talk>>
|
||||
<<if $declared_candidate_to_chevalier_cassian_thal>>
|
||||
Narrator: Chevalier Cassian Thal turns towards the head of the table again, just, when another man stands up.
|
||||
<<jump SC102_Table>>
|
||||
<<endif>>
|
||||
===
|
||||
|
||||
title: SC101_Asks_Candidate_Chevalier_Cassian_Thal_Talk
|
||||
position: 157,261
|
||||
---
|
||||
<<set $winner = top_skill($self_lean_made_asset, "asset", $self_lean_journalist, "journalist", $self_lean_inheritor, "inheritor")>>
|
||||
<<set $previous_winner = $winner>>
|
||||
<<if $winner eq "asset">>
|
||||
Narrator: He leans in and whispers:
|
||||
Chevalier Cassian Thal: I assume you are here to observe the gathering in the interest of ... our friends of the Vaselian Concordat?
|
||||
-> Yes, I am.
|
||||
<<set $self_lean_made_asset += 1>>
|
||||
<<detour SC101_Declares_Asset_Chevalier_Cassian_Thal_Talk>>
|
||||
-> You must be mistaken.
|
||||
<<set $self_lean_journalist += 1>>
|
||||
<<set $self_lean_inheritor += 1>>
|
||||
<<detour SC101_Deny_Candidate_Chevalier_Cassian_Thal_Talk>>
|
||||
<<elseif $winner eq "journalist">>
|
||||
Chevalier Cassian Thal: I got the feeling I may read my answers in tomorrow's newspaper.
|
||||
Narrator: He says it with a boyish grin, as if he is sure he hasn't disclosed anything contentious.
|
||||
-> You actually may.
|
||||
<<set $self_lean_journalist += 1>>
|
||||
<<detour SC101_Declares_Journalist_Chevalier_Cassian_Thal_Talk>>
|
||||
-> No, I got nothing to do with newspapers and such!
|
||||
<<set $self_lean_made_asset += 1>>
|
||||
<<set $self_lean_inheritor += 1>>
|
||||
<<detour SC101_Deny_Candidate_Chevalier_Cassian_Thal_Talk>>
|
||||
<<else>>
|
||||
Chevalier Cassian Thal: You probably look to settle your parent's debts?
|
||||
FACEWORK: He doesn't seem emotional about it, he is just interested.
|
||||
-> I actually am.
|
||||
<<set $self_lean_inheritor += 1>>
|
||||
<<detour SC101_Declares_Inheritor_Chevalier_Cassian_Thal_Talk>>
|
||||
-> Oh, I don't know about any debt, I hope they didn't borrow money from you?
|
||||
<<set $self_lean_journalist += 1>>
|
||||
<<set $self_lean_made_asset +=1>>
|
||||
Chevalier Cassian Thal: Oh god, no, they didn't borrow anything from us.
|
||||
CONFLUENCE: Us?
|
||||
<<detour SC101_Deny_Candidate_Chevalier_Cassian_Thal_Talk>>
|
||||
<<endif>>
|
||||
===
|
||||
|
||||
title: SC101_Declares_Asset_Chevalier_Cassian_Thal_Talk
|
||||
position: -9,670
|
||||
---
|
||||
<<set $declared_candidate_to_chevalier_cassian_thal = true>>
|
||||
Narrator: Chevalier Cassian Thal looks knowingly and cannot hide a little smirk.
|
||||
Chevalier Cassian Thal: Well, then let us hope the ... others follow the Knight's compromise.
|
||||
Narrator: He winks at you.
|
||||
FACEWORK: He seems to know more about us, than we know about ourselves. Though if we back out now he will become suspicious.
|
||||
===
|
||||
|
||||
title: SC101_Declares_Journalist_Chevalier_Cassian_Thal_Talk
|
||||
position: -222,667
|
||||
---
|
||||
<<set $declared_candidate_to_chevalier_cassian_thal = true>>
|
||||
Narrator: Chevalier Cassian Thal laughs.
|
||||
Chevalier Cassian Thal: Well, then let me give you something to write about: You and your readers will be surprised to hear, what you will witness in a second.
|
||||
CONFLUENCE: He can hardly hide his smirk.
|
||||
===
|
||||
|
||||
title: SC101_Declares_Inheritor_Chevalier_Cassian_Thal_Talk
|
||||
position: 207,664
|
||||
---
|
||||
<<set $declared_candidate_to_chevalier_cassian_thal = true>>
|
||||
Chevalier Cassian Thal: You may very well get your chance this week, my boy.
|
||||
FACEWORK: His look is benevolent, nearly fatherly.
|
||||
CONFLUENCE: What should our parents have done, that requires repayment?
|
||||
===
|
||||
|
||||
title: SC101_Deny_Candidate_Chevalier_Cassian_Thal_Talk
|
||||
position: 436,260
|
||||
---
|
||||
Chevalier Cassian Thal: Oh, I am rarely wrong in matters like these. Though enlighten me: What else could have been your wish coming here?
|
||||
FACEWORK: He looks genuinely interested.
|
||||
<<set $winner = top_skill($self_lean_made_asset, "asset", $self_lean_journalist, "journalist", $self_lean_inheritor, "inheritor")>>
|
||||
<<if $previous_winner == $winner>>
|
||||
-> No, you are totally right, indeed.
|
||||
<<if $winner == "asset">>
|
||||
<<detour SC101_Declares_Asset_Chevalier_Cassian_Thal_Talk>>
|
||||
<<elseif $winner == "journalist">>
|
||||
<<detour SC101_Declares_Journalist_Chevalier_Cassian_Thal_Talk>>
|
||||
<<else>>
|
||||
<<detour SC101_Declares_Inheritor_Chevalier_Cassian_Thal_Talk>>
|
||||
<<endif>>
|
||||
-> Actually ...
|
||||
<<detour SC101_Asks_Candidate_Chevalier_Cassian_Thal_Talk>>
|
||||
<<else>>
|
||||
<<detour SC101_Asks_Candidate_Chevalier_Cassian_Thal_Talk>>
|
||||
<<endif>>
|
||||
===
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9f603a6134614e0fa576727d10d487a
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 11500000, guid: 94073015eacc34c1d8fc6786e43d60ca, type: 3}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Passive skill commentary. Fired by CommentarySystem, never <<jump>>ed to.
|
||||
// One node per (context, skill) pair; if a skill has nothing to say about a
|
||||
// context, simply omit the node and it won't be considered.
|
||||
//
|
||||
// Writing rules (code does not enforce these):
|
||||
// - Wrong Truth failures: no failure signal in the prose; the UI banner is the only tell.
|
||||
// - Documentary routes are gated on Act and errand state, never on a voice.
|
||||
//
|
||||
// TODO: ambient fallback. Every voice in the Skill Bible has one context-free
|
||||
// PASSIVE VOICE line, and right now those lines have nowhere to live — a voice
|
||||
// with no Commentary_{context}_{id} node simply stays silent in that context.
|
||||
// Proposal: when no context node matches an eligible skill, CommentarySystem
|
||||
// falls back to Commentary_Ambient_{id}. Deferred from the refactor as optional;
|
||||
// only worth doing if it doesn't complicate the candidate loop.
|
||||
// See docs/skill-system-refactor-plan.md §5.6.
|
||||
|
||||
title: Commentary_Entrance_Queue_facework
|
||||
---
|
||||
FACEWORK: His hands are too still for someone this far back in a queue. What's he practising not to reach for?
|
||||
===
|
||||
|
||||
title: Commentary_Entrance_Queue_long_shift
|
||||
---
|
||||
THE LONG SHIFT: Forty minutes. Nobody's shifted their weight the way cold people do. They're paying for this wait in something else.
|
||||
===
|
||||
|
||||
title: Commentary_Entrance_Queue_the_float
|
||||
---
|
||||
THE FLOAT: Door's been "slow tonight" for forty minutes. Convert that: it's a price, not a queue. Who's eating that?
|
||||
===
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 26e9b5dea1744e39a5fe59bb1bc20fa7
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 11500000, guid: 94073015eacc34c1d8fc6786e43d60ca, type: 3}
|
||||
@@ -0,0 +1,117 @@
|
||||
// Shared declarations for the whole project.
|
||||
//
|
||||
// This node is never played. Declaring every variable in one place means the
|
||||
// compiler catches typos ("$reputaton") instead of silently creating a second
|
||||
// variable at runtime.
|
||||
//
|
||||
// Writing rules the code deliberately does not enforce (Skill Bible §7 / refactor plan §2.1):
|
||||
// - Wrong Truth failures carry no failure signal in the prose — the UI banner is the only tell.
|
||||
// - Documentary routes are gated on Act and errand state, never on a voice.
|
||||
//
|
||||
// Modifier Yarn commands (see YarnSkillCommands):
|
||||
// <<skill_mod scene facework 2>> — source key "scene" = situational; clear on scene exit
|
||||
// <<clear_skill_mods scene>>
|
||||
// Other source keys (drunk, injured, …) are state buckets.
|
||||
|
||||
title: Declarations
|
||||
---
|
||||
// How much the door staff likes you.
|
||||
<<declare $reputation = 0 as number>>
|
||||
|
||||
// Written by <<check>> (YarnSkillCommands). Never <<set>> these by hand.
|
||||
<<declare $check_result = false as bool>>
|
||||
<<declare $check_roll = 0 as number>>
|
||||
<<declare $check_total = 0 as number>>
|
||||
<<declare $check_dc = 0 as number>>
|
||||
<<declare $check_modifier = 0 as number>>
|
||||
<<declare $check_degree = "" as string>>
|
||||
<<declare $check_skill = "" as string>>
|
||||
|
||||
// Used by top_skill
|
||||
<<declare $winner = "" as string>>
|
||||
<<declare $previous_winner = "" as string>>
|
||||
|
||||
// Candidate lean. Three permanently-live readings of who the PC is; they are
|
||||
// not mutually exclusive and never resolve into a canonical identity.
|
||||
//
|
||||
// DESIGN DECISION 2026-08-16 — amends the "no code ever reads lean" guardrail in
|
||||
// docs/candidate-system-implementation-plan.md §6.1: scenes are meant to tilt
|
||||
// toward one reading and NPCs are meant to act on the current tilt, so that a
|
||||
// player who thinks they have settled on a path gets challenged on it in the
|
||||
// next scene. Reading lean through top_skill(...) is therefore sanctioned, as in
|
||||
// SC101_Asks_Candidate_Chevalier_Cassian_Thal_Talk. top_skill returns its FIRST
|
||||
// argument on a tie, which is why made_asset is passed first — the Made Asset
|
||||
// bias on an even score is deliberate, not an accident of argument order.
|
||||
//
|
||||
// What the amendment does NOT license: a stored winner. There is still no
|
||||
// $dominant_candidate, no ground-truth identity field, and nothing in save data
|
||||
// beyond these three integers. $winner above is scratch, recomputed per use.
|
||||
<<declare $self_lean_made_asset = 0 as number>>
|
||||
<<declare $self_lean_journalist = 0 as number>>
|
||||
<<declare $self_lean_inheritor = 0 as number>>
|
||||
|
||||
// Scene witnessed. Convention: $seen_<scene_id>.
|
||||
<<declare $seen_sc_101 = false as bool>>
|
||||
|
||||
// Clue coverage flags. One per registry clue id ($found_<clue_id>). Writers
|
||||
// <<set>> these at the discovery beat alongside the lean write. Lean tracks
|
||||
// belief (and can move on a Wrong Truth failure); $found_* tracks coverage.
|
||||
// No code reads these.
|
||||
<<declare $found_made_asset_object = false as bool>>
|
||||
<<declare $found_made_asset_testimony = false as bool>>
|
||||
<<declare $found_made_asset_document = false as bool>>
|
||||
<<declare $found_made_asset_intuition = false as bool>>
|
||||
|
||||
<<declare $found_journalist_object = false as bool>>
|
||||
<<declare $found_journalist_testimony = false as bool>>
|
||||
<<declare $found_journalist_document = false as bool>>
|
||||
<<declare $found_journalist_intuition = false as bool>>
|
||||
|
||||
<<declare $found_inheritor_object = false as bool>>
|
||||
<<declare $found_inheritor_testimony = false as bool>>
|
||||
<<declare $looking_for_doss = false as bool>>
|
||||
<<declare $had_the_estate_drink = false as bool>>
|
||||
<<declare $found_inheritor_document = false as bool>>
|
||||
<<declare $found_inheritor_intuition = false as bool>>
|
||||
|
||||
// Exploration gating. Clue availability is exploration-gated, never skill-gated.
|
||||
// Convention: $visited_<location_id> on first arrival; $errand_<errand_id> when
|
||||
// an errand reaches the state that matters. Writers <<set>> these from scene
|
||||
// content. unlockFlag fields in candidates.json may only name these families.
|
||||
<<declare $visited_desk = false as bool>>
|
||||
<<declare $visited_back_office = false as bool>>
|
||||
<<declare $visited_archives = false as bool>>
|
||||
<<declare $visited_coat_check = false as bool>>
|
||||
<<declare $visited_press_room = false as bool>>
|
||||
<<declare $visited_private_box = false as bool>>
|
||||
<<declare $visited_membership_ledger = false as bool>>
|
||||
<<declare $errand_door_list = false as bool>>
|
||||
<<declare $errand_find_contact = false as bool>>
|
||||
<<declare $errand_ask_about_name = false as bool>>
|
||||
<<declare $spoke_to_bartender = false as bool>>
|
||||
<<declare $revealed_fugue_to_bartender = false as bool>>
|
||||
|
||||
// Player name substitution. Uppercase [PC] in the Skill Bible means the full,
|
||||
// formal form (STANDING ORDER, PROVENANCE); lowercase [pc] means the familiar
|
||||
// short form (CONFLUENCE, FACEWORK). Two variables, not one — collapsing them
|
||||
// loses a characterisation rule the bible states for four separate voices.
|
||||
// Defaults are placeholders; naming the PC is design's call.
|
||||
<<declare $player_name = "Vesper" as string>>
|
||||
<<declare $player_name_full = "Vesper Adaire" as string>>
|
||||
|
||||
// Club alias. Smart variable: deterministically derived from $player_name via
|
||||
// AliasNameGenerator.alias_name (hash-seeded pick from two word pools), so it
|
||||
// is never in sync-drift and never <<set>> by hand. Same real name always
|
||||
// yields the same alias; the alias itself bears no resemblance to the input.
|
||||
<<declare $player_alias = alias_name($player_name) as string>>
|
||||
|
||||
// Information about history and symbols
|
||||
<<declare $sat_down_at_table = false as bool>>
|
||||
<<declare $saw_knights_atlantean_cross_on_place_card = false as bool>>
|
||||
|
||||
// Character dialogue gates
|
||||
<<declare $spoke_to_chevalier_cassian_thal = false as bool>>
|
||||
<<declare $spoke_to_bradford_kane = false as bool>>
|
||||
<<declare $inquired_about_cyprus = false as bool>>
|
||||
<<declare $declared_candidate_to_chevalier_cassian_thal = false as bool>>
|
||||
===
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 29774b2adf9214b77a6d280d2e604353
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 11500000, guid: 94073015eacc34c1d8fc6786e43d60ca, type: 3}
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"projectFileVersion": 4,
|
||||
"sourceFiles": [
|
||||
"**/*.yarn"
|
||||
],
|
||||
"excludeFiles": [
|
||||
"**/*~/*",
|
||||
"./Samples/Yarn Spinner*/*"
|
||||
],
|
||||
"localisation": {},
|
||||
"baseLanguage": "en",
|
||||
"compilerOptions": {
|
||||
"diagnosticsSeverity": {}
|
||||
},
|
||||
"projectName": "NightclubArcade",
|
||||
"authorName": [
|
||||
"LennartGrabowski"
|
||||
],
|
||||
"editorOptions": {
|
||||
"yarnScriptEditor": {
|
||||
"projectGraphPositions": {
|
||||
"Bartender.yarn": {
|
||||
"x": 282,
|
||||
"y": 12
|
||||
},
|
||||
"Bradford Kane.yarn": {
|
||||
"x": 1320,
|
||||
"y": 2287
|
||||
},
|
||||
"Chevalier Cassian Thal.yarn": {
|
||||
"x": 1621,
|
||||
"y": 2260
|
||||
},
|
||||
"Commentary.yarn": {
|
||||
"x": 12,
|
||||
"y": 231
|
||||
},
|
||||
"Common.yarn": {
|
||||
"x": 12,
|
||||
"y": 12
|
||||
},
|
||||
"Chair.yarn": {
|
||||
"x": 866,
|
||||
"y": 1104
|
||||
},
|
||||
"Debug.yarn": {
|
||||
"x": 14,
|
||||
"y": 448
|
||||
},
|
||||
"SC101.yarn": {
|
||||
"x": 820,
|
||||
"y": -3
|
||||
},
|
||||
"SC102.yarn": {
|
||||
"x": 2595,
|
||||
"y": 2682
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4eb94e12c91f9432e8742a3cee6774e9
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 11500000, guid: 053561912b1654d80a424bb0696a74ae, type: 3}
|
||||
generateVariablesSourceFile: 0
|
||||
variablesClassName: YarnVariables
|
||||
variablesClassNamespace:
|
||||
variablesClassParent: Yarn.Unity.InMemoryVariableStorage
|
||||
useAddressableAssets: 0
|
||||
unityLocalisationStringTableCollectionGUID:
|
||||
UseUnityLocalisationSystem: 0
|
||||
useCustomLineTagger: 0
|
||||
lineTaggerClass:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: beedfd387b0d049ffb7534a81d2df45f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,121 @@
|
||||
// Everything that can be interacted with the chair.
|
||||
//
|
||||
// Stack discipline (the player re-enters all of this, so it matters):
|
||||
// `Chair_Interaction` is the node DialogueInteractable starts, so nothing above
|
||||
// it is waiting for a return. The menu therefore LOOPS with <<jump>>, which
|
||||
// replaces the current node and pushes nothing, and only sub-conversations that
|
||||
// have to come back use <<detour>>. Never <<jump>> out of a detoured node —
|
||||
// that clears the return stack and the caller never resumes.
|
||||
//
|
||||
// The sit-down line lives in its own node so the menu loop can't replay it,
|
||||
// while a player who stands up and sits down again still gets it.
|
||||
|
||||
title: Chair_Interaction
|
||||
position: -334,-601
|
||||
---
|
||||
Narrator: An empty chair with a place card: {$player_name}.
|
||||
-> Sit down.
|
||||
<<set $sat_down_at_table = true>>
|
||||
<<jump SC101_Hook_Sitting_Chair_Interaction>>
|
||||
-> Not yet.
|
||||
<<enter_environment>>
|
||||
===
|
||||
|
||||
title: SC101_Hook_Sitting_Chair_Interaction
|
||||
position: -328,-360
|
||||
---
|
||||
Narrator: You sit down between two very distinguished gentlemen.
|
||||
<<jump SC101_Hook_Menu_Chair_Interaction>>
|
||||
===
|
||||
|
||||
// The loop target. Every menu variation jumps back here when its sub-conversation
|
||||
// returns, so which variation shows is re-evaluated after every interaction.
|
||||
title: SC101_Hook_Menu_Chair_Interaction
|
||||
position: -328,-205
|
||||
---
|
||||
<<if $spoke_to_chevalier_cassian_thal eq true and $spoke_to_bradford_kane eq true>>
|
||||
<<jump SC101_Hook_Both_Chair_Interaction>>
|
||||
<<elseif $spoke_to_chevalier_cassian_thal eq true and $spoke_to_bradford_kane eq false>>
|
||||
<<jump SC101_Hook_Left_Not_Right_Chair_Interaction>>
|
||||
<<elseif $spoke_to_chevalier_cassian_thal eq false and $spoke_to_bradford_kane eq true>>
|
||||
<<jump SC101_Hook_Right_Not_Left_Chair_Interaction>>
|
||||
<<else>>
|
||||
<<jump SC101_Hook_First_Sitting_Chair_Interaction>>
|
||||
<<endif>>
|
||||
===
|
||||
|
||||
title: SC101_Hook_First_Sitting_Chair_Interaction
|
||||
position: -320,-51
|
||||
---
|
||||
-> Look at the details of the place card.
|
||||
<<detour SC101_Hook_Place_Card_Chair_Interaction>>
|
||||
-> Introduce yourself to the gentleman on your left, sitting closer to the head of the table.
|
||||
<<detour Chevalier_Cassian_Thal_Talk>>
|
||||
-> Introduce yourself to the gentleman on your right.
|
||||
<<detour Bradford_Kane_Talk>>
|
||||
-> Stand up.
|
||||
<<enter_environment>>
|
||||
<<stop>>
|
||||
<<jump SC101_Hook_Menu_Chair_Interaction>>
|
||||
===
|
||||
|
||||
title: SC101_Hook_Left_Not_Right_Chair_Interaction
|
||||
position: -95,-48
|
||||
---
|
||||
-> Look at the details of the place card.
|
||||
<<detour SC101_Hook_Place_Card_Chair_Interaction>>
|
||||
-> Talk to Chevalier Cassian Thal.
|
||||
<<detour Chevalier_Cassian_Thal_Talk>>
|
||||
-> Introduce yourself to the gentleman on your right.
|
||||
<<detour Bradford_Kane_Talk>>
|
||||
-> Stand up.
|
||||
<<enter_environment>>
|
||||
<<stop>>
|
||||
<<jump SC101_Hook_Menu_Chair_Interaction>>
|
||||
===
|
||||
|
||||
title: SC101_Hook_Right_Not_Left_Chair_Interaction
|
||||
position: 134,-32
|
||||
---
|
||||
-> Look at the details of the place card.
|
||||
<<detour SC101_Hook_Place_Card_Chair_Interaction>>
|
||||
-> Introduce yourself to the gentleman on your left, sitting closer to the head of the table.
|
||||
<<detour Chevalier_Cassian_Thal_Talk>>
|
||||
-> Talk to Bradford Kane.
|
||||
<<detour Bradford_Kane_Talk>>
|
||||
-> Stand up.
|
||||
<<enter_environment>>
|
||||
<<stop>>
|
||||
<<jump SC101_Hook_Menu_Chair_Interaction>>
|
||||
===
|
||||
|
||||
title: SC101_Hook_Both_Chair_Interaction
|
||||
position: 363,-36
|
||||
---
|
||||
-> Look at the details of the place card.
|
||||
<<detour SC101_Hook_Place_Card_Chair_Interaction>>
|
||||
-> Talk to Chevalier Cassian Thal.
|
||||
<<detour Chevalier_Cassian_Thal_Talk>>
|
||||
-> Talk to Bradford Kane.
|
||||
<<detour Bradford_Kane_Talk>>
|
||||
-> Stand up.
|
||||
<<enter_environment>>
|
||||
<<stop>>
|
||||
<<jump SC101_Hook_Menu_Chair_Interaction>>
|
||||
===
|
||||
|
||||
title: SC101_Hook_Place_Card_Chair_Interaction
|
||||
position: -296,331
|
||||
---
|
||||
Narrator: You take the place card and take a look.
|
||||
<<check provenance Hard>>
|
||||
<<if $check_result>>
|
||||
<<set $saw_knights_atlantean_cross_on_place_card = true>>
|
||||
PROVENANCE: What is this?
|
||||
Narrator: You see a plain equal-armed cross inside a circle embossed in the upper right corner.
|
||||
PROVENANCE: We are in Switzerland, after all. Though this symbol is not known to be associated with Switzerland. Most peculiar.
|
||||
<<else>>
|
||||
Narrator: You see a cross inside a circle embossed in the upper right corner.
|
||||
PROVENANCE: This is probably related to Switzerland.
|
||||
<<endif>>
|
||||
===
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f6437b77328240e590f205a6605cfcb
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 11500000, guid: 94073015eacc34c1d8fc6786e43d60ca, type: 3}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c89177cea38e403eb2f13330e1245be
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,59 @@
|
||||
// Scratch nodes for verifying mechanics in isolation. Not part of the story.
|
||||
// Point DialogueRunner.startNode at the node you want to run, then set it
|
||||
// back to "Start" when you're done.
|
||||
//
|
||||
// Candidate active-check pattern (docs/candidate-system-implementation-plan.md §5 Phase 3):
|
||||
// <<check skillId band>>
|
||||
// <<if $check_result>> success branch writes $found_* + $self_lean_*
|
||||
// <<else>> failure is a Wrong Truth — equally confident, unmarked in prose,
|
||||
// and still writes lean (belief, not truth). Banner is the only tell.
|
||||
|
||||
title: Debug_SkillCheck
|
||||
---
|
||||
Narrator: Skill check harness.
|
||||
<<check placement routine>>
|
||||
<<if $check_result>>
|
||||
Narrator: Passed. Rolled {$check_roll}, total {$check_total} against DC {$check_dc}.
|
||||
<<else>>
|
||||
Narrator: Failed. Rolled {$check_roll}, total {$check_total} against DC {$check_dc}.
|
||||
<<endif>>
|
||||
|
||||
// Explicit numeric DC plus a situational modifier.
|
||||
<<check confluence 16 2>>
|
||||
Narrator: Confluence came out {$check_degree}.
|
||||
|
||||
// Deliberate typo: should log an error and continue, not deadlock.
|
||||
<<check nosuchskill routine>>
|
||||
Narrator: Survived the bad skill id.
|
||||
===
|
||||
|
||||
// Candidate lean accumulator round-trip. Expect made_asset 2 / journalist 1 /
|
||||
// inheritor 0, and the threshold branch to fire.
|
||||
title: Debug_CandidateLean
|
||||
---
|
||||
<<set $self_lean_made_asset += 1>>
|
||||
<<set $self_lean_made_asset += 1>>
|
||||
<<set $self_lean_journalist += 1>>
|
||||
Narrator: made_asset {$self_lean_made_asset} / journalist {$self_lean_journalist} / inheritor {$self_lean_inheritor}
|
||||
<<if $self_lean_made_asset >= 2>>
|
||||
Narrator: threshold branch reached.
|
||||
<<endif>>
|
||||
===
|
||||
|
||||
// Candidate active-check pattern. Set SkillRuntime.rollSeed non-zero for a
|
||||
// SeededRollSource so both branches are reachable deterministically across runs.
|
||||
// Success feeds made_asset; failure feeds inheritor (Wrong Truth lean — §4.4).
|
||||
title: Debug_CandidateCheck
|
||||
---
|
||||
Narrator: Candidate check harness. Banner is the only pass/fail tell.
|
||||
<<check confluence hard>>
|
||||
<<if $check_result>>
|
||||
CONFLUENCE: One: the card's stock matches the Concordat watermark. Two: the date is a week before the invitation went out. Three — and three is the one — somebody left this for you to find. Which means you were expected.
|
||||
<<set $found_made_asset_document = true>>
|
||||
<<set $self_lean_made_asset += 1>>
|
||||
<<else>>
|
||||
CONFLUENCE: One: the card's stock matches a wedding invitation from the thirties. Two: the date is your mother's. Three — and three is the one — you already knew the seating. Which means this was never yours to choose.
|
||||
<<set $self_lean_inheritor += 1>>
|
||||
<<endif>>
|
||||
Narrator: lean made_asset {$self_lean_made_asset} / inheritor {$self_lean_inheritor}. Degree {$check_degree}.
|
||||
===
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e497c84fe7bd48ea8c828ac1702f79bf
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 11500000, guid: 94073015eacc34c1d8fc6786e43d60ca, type: 3}
|
||||
@@ -0,0 +1,76 @@
|
||||
// SC-101 — The Card at the Desk. Opening candidate hooks.
|
||||
//
|
||||
// Scene files stay thin: set the stage, <<detour>> into hook nodes, return.
|
||||
//
|
||||
// Evidence rule: each candidate gets exactly one of each evidence type —
|
||||
// object, testimony, document, intuition. Preserve that balance; do not
|
||||
// stack types so trust becomes an artifact of imbalance.
|
||||
// See Assets/Candidates/candidates.json.
|
||||
//
|
||||
// Beat order: hooks fire in sequence from SC101_Desk (STANDING ORDER →
|
||||
// HOUSE POUR → FACEWORK → CONFLUENCE). Each remains an independently authored
|
||||
// node. Never <<if>>-guard a hook on another hook having fired, on any
|
||||
// $self_lean_*, or on skill_rank(...).
|
||||
//
|
||||
// Passive pattern (clue-bearing):
|
||||
// one voice per hook, from the candidate's attached pair;
|
||||
// the line is that character's argument, never a report;
|
||||
// exactly one $self_lean_* write; set $found_<clue_id> alongside it.
|
||||
//
|
||||
// Four-voice exception: this opening uses four voice introductions (one per
|
||||
// pair) against the writing handbook's "cap introductions at three voices"
|
||||
// rule. Deliberate and for SC-101 only — do not "correct" it down to three,
|
||||
// and do not copy it as a new precedent elsewhere.
|
||||
//
|
||||
// Body stays neutral on purpose: no lean, no candidate attachment.
|
||||
|
||||
title: Start
|
||||
position: -17,-276
|
||||
---
|
||||
<<jump SC101_Desk>>
|
||||
===
|
||||
|
||||
title: SC101_Desk
|
||||
position: -19,-36
|
||||
---
|
||||
<<set $seen_sc_101 = true>>
|
||||
<<set $visited_desk = true>>
|
||||
Narrator: The mahogany doors are too heavy for the hinges someone recently, badly, tightened — you feel the extra half-second of resistance before either side gives. Beyond them: a hall built for four hundred people politely disagreeing about tariffs, hosting perhaps sixty, none of whom look like they've read the programme.
|
||||
Narrator: Someone left a card with your name on it at the registration desk three hours before you decided to come. You don't remember deciding.
|
||||
<<detour SC101_Hook_Inheritor>>
|
||||
<<detour SC101_Hook_Neutral>>
|
||||
<<detour SC101_Hook_Journalist>>
|
||||
<<detour SC101_Hook_MadeAsset>>
|
||||
Narrator: You're drawn toward a seat — toward a waiting conversation you didn't agree to have yet.
|
||||
<<enter_environment>>
|
||||
===
|
||||
|
||||
title: SC101_Hook_MadeAsset
|
||||
position: -40,567
|
||||
---
|
||||
CONFLUENCE: One: the seating chart's alphabetical. Two: one gap in it, held open past the start time. Three, and three is the one, {$player_name}: whoever printed this knew you'd be late to your own arrival. Which means somebody in this room has already met you, {$player_name}, and you're the only one who doesn't know it.
|
||||
<<set $found_made_asset_intuition = true>>
|
||||
<<set $self_lean_made_asset += 1>>
|
||||
===
|
||||
|
||||
title: SC101_Hook_Journalist
|
||||
position: -299,452
|
||||
---
|
||||
FACEWORK: His hands gave it away before his face did — a flinch toward you, checked, turned into reaching for a drink that wasn't there. That's not a stranger's hands. What did you do to earn that flinch?
|
||||
<<set $found_journalist_intuition = true>>
|
||||
<<set $self_lean_journalist += 1>>
|
||||
===
|
||||
|
||||
title: SC101_Hook_Inheritor
|
||||
position: 335,366
|
||||
---
|
||||
STANDING ORDER: You are standing in a doorway, {$player_name_full}. That's the whole of what's certain. Build outward from there — a person who starts anywhere else is a person coming apart in public.
|
||||
<<set $found_inheritor_intuition = true>>
|
||||
<<set $self_lean_inheritor += 1>>
|
||||
===
|
||||
|
||||
title: SC101_Hook_Neutral
|
||||
position: 87,310
|
||||
---
|
||||
HOUSE POUR: Cigar smoke, spilled aperitif, and under it — cold, sharp — the ammonia bite of a floor mopped in a hurry, love. Someone's trying to make this room smell like money. It smells like panic with good cologne on top.
|
||||
===
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ef4adc1687e744cd0afd7041ab38c377
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 11500000, guid: 94073015eacc34c1d8fc6786e43d60ca, type: 3}
|
||||
@@ -0,0 +1,6 @@
|
||||
// SC-102 — The figure at the head of the table just stood up.
|
||||
|
||||
title: SC102_Table
|
||||
---
|
||||
Figure at the head of the table: "Welcome."
|
||||
===
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 717dc562b961f41c49f5615fb8415f67
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 11500000, guid: 94073015eacc34c1d8fc6786e43d60ca, type: 3}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 014d397ae9b37414fac69531573396d8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,218 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using NightclubArcadia.Skills;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Callbacks;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Regenerates CandidateDefinition assets + CandidateDatabase from candidates.json.
|
||||
///
|
||||
/// The JSON is the source of truth; .asset files are build output and hand-edits are lost
|
||||
/// on script reload by EnsureCandidateAssetsOnly.
|
||||
/// </summary>
|
||||
public static class CandidateSystemSetup
|
||||
{
|
||||
const string CandidatesFolder = "Assets/Candidates";
|
||||
const string DefinitionsFolder = "Assets/Candidates/Definitions";
|
||||
const string JsonPath = "Assets/Candidates/candidates.json";
|
||||
const string DatabasePath = "Assets/Candidates/CandidateDatabase.asset";
|
||||
const string AutoSetupSessionKey = "NightclubArcadia.CandidateSystem.AutoSetupDone";
|
||||
|
||||
[DidReloadScripts]
|
||||
static void AutoSetupAfterReload()
|
||||
{
|
||||
if (EditorApplication.isPlayingOrWillChangePlaymode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (SessionState.GetBool(AutoSetupSessionKey, false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
if (SessionState.GetBool(AutoSetupSessionKey, false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SessionState.SetBool(AutoSetupSessionKey, true);
|
||||
EnsureCandidateAssetsOnly();
|
||||
};
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Nightclub Arcadia/Set Up Candidate System")]
|
||||
public static void SetUpCandidateSystem()
|
||||
{
|
||||
EnsureCandidateAssetsOnly();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates/updates ScriptableObject assets without opening a scene.
|
||||
/// Safe to run on domain reload.
|
||||
/// </summary>
|
||||
public static void EnsureCandidateAssetsOnly()
|
||||
{
|
||||
if (!TryLoadRoster(out var roster))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureFolder("Assets", "Candidates");
|
||||
EnsureFolder("Assets/Candidates", "Definitions");
|
||||
|
||||
var definitions = new List<CandidateDefinition>();
|
||||
var rosterIds = new HashSet<string>(StringComparer.Ordinal);
|
||||
foreach (var entry in roster)
|
||||
{
|
||||
rosterIds.Add(entry.id);
|
||||
definitions.Add(CreateOrUpdateDefinition(entry));
|
||||
}
|
||||
|
||||
DeleteOrphanDefinitions(rosterIds);
|
||||
|
||||
var database = AssetDatabase.LoadAssetAtPath<CandidateDatabase>(DatabasePath);
|
||||
if (database == null)
|
||||
{
|
||||
database = ScriptableObject.CreateInstance<CandidateDatabase>();
|
||||
AssetDatabase.CreateAsset(database, DatabasePath);
|
||||
}
|
||||
|
||||
database.EditorSetCandidates(definitions);
|
||||
EditorUtility.SetDirty(database);
|
||||
AssetDatabase.SaveAssets();
|
||||
Debug.Log($"[CandidateSystemSetup] Candidate assets ready at {DatabasePath}.");
|
||||
}
|
||||
|
||||
static bool TryLoadRoster(out CandidateEntry[] roster)
|
||||
{
|
||||
roster = null;
|
||||
var text = AssetDatabase.LoadAssetAtPath<TextAsset>(JsonPath);
|
||||
if (text == null)
|
||||
{
|
||||
// JSON may exist on disk before Unity has imported it as a TextAsset.
|
||||
if (!File.Exists(JsonPath))
|
||||
{
|
||||
Debug.LogError($"[CandidateSystemSetup] Missing registry at {JsonPath}.");
|
||||
return false;
|
||||
}
|
||||
|
||||
text = new TextAsset(File.ReadAllText(JsonPath));
|
||||
}
|
||||
|
||||
CandidateRoot root;
|
||||
try
|
||||
{
|
||||
root = JsonUtility.FromJson<CandidateRoot>(text.text);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"[CandidateSystemSetup] Failed to parse {JsonPath}: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (root?.candidates == null || root.candidates.Length == 0)
|
||||
{
|
||||
Debug.LogError($"[CandidateSystemSetup] {JsonPath} has no candidates.");
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var entry in root.candidates)
|
||||
{
|
||||
if (entry == null || string.IsNullOrEmpty(entry.id))
|
||||
{
|
||||
Debug.LogError($"[CandidateSystemSetup] {JsonPath} contains a candidate with empty id.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Enum.TryParse<SkillAxis>(entry.intuitionAxis, ignoreCase: true, out _))
|
||||
{
|
||||
Debug.LogError(
|
||||
$"[CandidateSystemSetup] Candidate '{entry.id}': invalid intuitionAxis '{entry.intuitionAxis}'.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry.clues == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var clue in entry.clues)
|
||||
{
|
||||
if (clue == null || string.IsNullOrEmpty(clue.id))
|
||||
{
|
||||
Debug.LogError($"[CandidateSystemSetup] Candidate '{entry.id}' has a clue with empty id.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Enum.TryParse<CandidateEvidenceType>(clue.type, ignoreCase: true, out _))
|
||||
{
|
||||
Debug.LogError(
|
||||
$"[CandidateSystemSetup] Clue '{clue.id}': invalid type '{clue.type}'.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var status = string.IsNullOrEmpty(clue.status) ? "Planned" : clue.status;
|
||||
if (!Enum.TryParse<CandidateClueStatus>(status, ignoreCase: true, out _))
|
||||
{
|
||||
Debug.LogError(
|
||||
$"[CandidateSystemSetup] Clue '{clue.id}': invalid status '{clue.status}'.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
roster = root.candidates;
|
||||
return true;
|
||||
}
|
||||
|
||||
static CandidateDefinition CreateOrUpdateDefinition(CandidateEntry entry)
|
||||
{
|
||||
var path = $"{DefinitionsFolder}/{entry.id}.asset";
|
||||
var asset = AssetDatabase.LoadAssetAtPath<CandidateDefinition>(path);
|
||||
if (asset == null)
|
||||
{
|
||||
asset = ScriptableObject.CreateInstance<CandidateDefinition>();
|
||||
AssetDatabase.CreateAsset(asset, path);
|
||||
}
|
||||
|
||||
asset.EditorSet(entry);
|
||||
EditorUtility.SetDirty(asset);
|
||||
return asset;
|
||||
}
|
||||
|
||||
static void DeleteOrphanDefinitions(HashSet<string> rosterIds)
|
||||
{
|
||||
if (!AssetDatabase.IsValidFolder(DefinitionsFolder))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var guids = AssetDatabase.FindAssets("t:CandidateDefinition", new[] { DefinitionsFolder });
|
||||
foreach (var guid in guids)
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
var fileName = Path.GetFileNameWithoutExtension(path);
|
||||
if (rosterIds.Contains(fileName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Debug.Log($"[CandidateSystemSetup] Deleting orphan definition {path}");
|
||||
AssetDatabase.DeleteAsset(path);
|
||||
}
|
||||
}
|
||||
|
||||
static void EnsureFolder(string parent, string child)
|
||||
{
|
||||
var path = $"{parent}/{child}";
|
||||
if (!AssetDatabase.IsValidFolder(path))
|
||||
{
|
||||
AssetDatabase.CreateFolder(parent, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 871be622b3a34b8fb46ce81bb87272d0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,316 @@
|
||||
using NightclubArcadia.Cinematics;
|
||||
using NightclubArcadia.Interaction;
|
||||
using NightclubArcadia.Player;
|
||||
using StarterAssets;
|
||||
using Unity.AI.Navigation;
|
||||
using Unity.Cinemachine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
|
||||
/// <summary>
|
||||
/// Wires player control, navigation, and camera systems into DialogueTest.unity.
|
||||
/// Run via Tools → Nightclub Arcadia → Set Up Player Control & Camera.
|
||||
/// </summary>
|
||||
public static class PlayerControlSetup
|
||||
{
|
||||
const string ScenePath = "Assets/Scenes/DialogueTest.unity";
|
||||
const string PlayerPrefabPath = "Assets/StarterAssets/ThirdPersonController/Prefabs/PlayerArmature.prefab";
|
||||
const string SettingsFolder = "Assets/Settings";
|
||||
|
||||
[MenuItem("Tools/Nightclub Arcadia/Set Up Player Control & Camera")]
|
||||
public static void SetUp()
|
||||
{
|
||||
EnsurePlayerPrefab();
|
||||
WireScene();
|
||||
AssetDatabase.SaveAssets();
|
||||
Debug.Log("[PlayerControlSetup] Player control and camera setup complete.");
|
||||
}
|
||||
|
||||
static void EnsurePlayerPrefab()
|
||||
{
|
||||
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(PlayerPrefabPath);
|
||||
if (prefab == null)
|
||||
{
|
||||
Debug.LogError($"[PlayerControlSetup] Missing prefab at {PlayerPrefabPath}");
|
||||
return;
|
||||
}
|
||||
|
||||
var root = PrefabUtility.LoadPrefabContents(PlayerPrefabPath);
|
||||
|
||||
EnsureComponent<PlayerPointerInput>(root);
|
||||
EnsureComponent<PlayerMotionArbiter>(root);
|
||||
EnsureComponent<ClickToMoveController>(root);
|
||||
EnsureComponent<PointerNavigationInput>(root);
|
||||
EnsureComponent<NavMeshAgent>(root);
|
||||
|
||||
var inputs = root.GetComponent<StarterAssetsInputs>();
|
||||
if (inputs != null)
|
||||
{
|
||||
inputs.analogMovement = true;
|
||||
inputs.cursorLocked = false;
|
||||
}
|
||||
|
||||
var controller = root.GetComponent<ThirdPersonController>();
|
||||
if (controller != null)
|
||||
{
|
||||
controller.LockCameraPosition = true;
|
||||
}
|
||||
|
||||
var clickToMove = root.GetComponent<ClickToMoveController>();
|
||||
var pointerNav = root.GetComponent<PointerNavigationInput>();
|
||||
var pointerInput = root.GetComponent<PlayerPointerInput>();
|
||||
var arbiter = root.GetComponent<PlayerMotionArbiter>();
|
||||
|
||||
WireSerializedReference(clickToMove, "brainCamera", null);
|
||||
WireSerializedReference(pointerNav, "pointerInput", pointerInput);
|
||||
WireSerializedReference(pointerNav, "clickToMove", clickToMove);
|
||||
WireSerializedReference(pointerNav, "motionArbiter", arbiter);
|
||||
|
||||
PrefabUtility.SaveAsPrefabAsset(root, PlayerPrefabPath);
|
||||
PrefabUtility.UnloadPrefabContents(root);
|
||||
}
|
||||
|
||||
static void WireScene()
|
||||
{
|
||||
var scene = EditorSceneManager.OpenScene(ScenePath, OpenSceneMode.Single);
|
||||
var player = GameObject.FindGameObjectWithTag("Player");
|
||||
if (player == null)
|
||||
{
|
||||
Debug.LogError("[PlayerControlSetup] No Player-tagged object found in scene.");
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureComponent<PlayerPointerInput>(player);
|
||||
EnsureComponent<PlayerMotionArbiter>(player);
|
||||
EnsureComponent<ClickToMoveController>(player);
|
||||
EnsureComponent<PointerNavigationInput>(player);
|
||||
EnsureComponent<NavMeshAgent>(player);
|
||||
|
||||
var inputs = player.GetComponent<StarterAssetsInputs>();
|
||||
if (inputs != null)
|
||||
{
|
||||
inputs.analogMovement = true;
|
||||
inputs.cursorLocked = false;
|
||||
}
|
||||
|
||||
var controller = player.GetComponent<ThirdPersonController>();
|
||||
if (controller != null)
|
||||
{
|
||||
controller.LockCameraPosition = true;
|
||||
}
|
||||
|
||||
var brainCamera = Camera.main;
|
||||
var pointerInput = player.GetComponent<PlayerPointerInput>();
|
||||
var clickToMove = player.GetComponent<ClickToMoveController>();
|
||||
var pointerNav = player.GetComponent<PointerNavigationInput>();
|
||||
var arbiter = player.GetComponent<PlayerMotionArbiter>();
|
||||
|
||||
WireSerializedReference(clickToMove, "brainCamera", brainCamera);
|
||||
WireSerializedReference(pointerNav, "brainCamera", brainCamera);
|
||||
WireSerializedReference(pointerNav, "pointerInput", pointerInput);
|
||||
WireSerializedReference(pointerNav, "clickToMove", clickToMove);
|
||||
WireSerializedReference(pointerNav, "motionArbiter", arbiter);
|
||||
|
||||
var groundLayer = LayerMask.NameToLayer("Ground");
|
||||
var interactableLayer = LayerMask.NameToLayer("Interactable");
|
||||
SetLayerIfExists("Floor", groundLayer);
|
||||
SetLayerIfExists("Room_101", groundLayer);
|
||||
SetLayerIfExists("Wall_Back", groundLayer);
|
||||
SetLayerIfExists("Wall_Front", groundLayer);
|
||||
SetLayerIfExists("Table", groundLayer);
|
||||
|
||||
WireBartender("Bartender", interactableLayer);
|
||||
WireBartender("Bartender (1)", interactableLayer);
|
||||
|
||||
EnsureNavigation(player.transform.position);
|
||||
EnsureCameraAnchor(player.transform, brainCamera);
|
||||
|
||||
EditorSceneManager.MarkSceneDirty(scene);
|
||||
EditorSceneManager.SaveScene(scene);
|
||||
}
|
||||
|
||||
static void WireBartender(string objectName, int interactableLayer)
|
||||
{
|
||||
var bartender = GameObject.Find(objectName);
|
||||
if (bartender == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (interactableLayer >= 0)
|
||||
{
|
||||
bartender.layer = interactableLayer;
|
||||
}
|
||||
|
||||
EnsureComponent<ClickInteractableBridge>(bartender);
|
||||
EnsureComponent<CameraPointOfInterest>(bartender);
|
||||
}
|
||||
|
||||
static void EnsureNavigation(Vector3 sampleOrigin)
|
||||
{
|
||||
var navigation = GameObject.Find("Navigation");
|
||||
if (navigation == null)
|
||||
{
|
||||
navigation = new GameObject("Navigation");
|
||||
}
|
||||
|
||||
var surface = EnsureComponent<NavMeshSurface>(navigation);
|
||||
surface.agentTypeID = 0;
|
||||
surface.collectObjects = CollectObjects.All;
|
||||
surface.useGeometry = NavMeshCollectGeometry.RenderMeshes;
|
||||
|
||||
var included = 0;
|
||||
var defaultLayer = LayerMask.NameToLayer("Default");
|
||||
var groundLayer = LayerMask.NameToLayer("Ground");
|
||||
if (defaultLayer >= 0)
|
||||
{
|
||||
included |= 1 << defaultLayer;
|
||||
}
|
||||
|
||||
if (groundLayer >= 0)
|
||||
{
|
||||
included |= 1 << groundLayer;
|
||||
}
|
||||
|
||||
surface.layerMask = included;
|
||||
|
||||
var table = GameObject.Find("Table");
|
||||
if (table != null)
|
||||
{
|
||||
var modifier = EnsureComponent<NavMeshModifier>(table);
|
||||
modifier.overrideArea = true;
|
||||
modifier.area = NavMesh.GetAreaFromName("Not Walkable");
|
||||
}
|
||||
|
||||
surface.BuildNavMesh();
|
||||
|
||||
if (!NavMesh.SamplePosition(sampleOrigin, out _, 2f, NavMesh.AllAreas))
|
||||
{
|
||||
Debug.LogWarning("[PlayerControlSetup] NavMesh bake did not cover the player start position.");
|
||||
}
|
||||
}
|
||||
|
||||
static void EnsureCameraAnchor(Transform followTarget, Camera brainCamera)
|
||||
{
|
||||
var vcamObject = GameObject.Find("CinemachineCamera");
|
||||
if (vcamObject == null)
|
||||
{
|
||||
Debug.LogError("[PlayerControlSetup] CinemachineCamera not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
var vcam = vcamObject.GetComponent<CinemachineCamera>();
|
||||
var composer = vcamObject.GetComponent<CinemachinePositionComposer>();
|
||||
|
||||
var anchorObject = GameObject.Find("CameraAnchor");
|
||||
if (anchorObject == null)
|
||||
{
|
||||
anchorObject = new GameObject("CameraAnchor");
|
||||
}
|
||||
|
||||
anchorObject.transform.position = followTarget.position;
|
||||
|
||||
var anchor = EnsureComponent<CameraRigAnchor>(anchorObject);
|
||||
EnsureComponent<CameraPanInput>(anchorObject);
|
||||
EnsureComponent<CameraPointOfInterestDirector>(anchorObject);
|
||||
EnsureComponent<CameraStateDirector>(anchorObject);
|
||||
|
||||
WireSerializedReference(anchor, "followTarget", followTarget);
|
||||
WireSerializedReference(anchor, "composer", composer);
|
||||
|
||||
if (vcam != null)
|
||||
{
|
||||
vcam.Target.TrackingTarget = anchorObject.transform;
|
||||
vcam.Priority.Value = 10;
|
||||
}
|
||||
|
||||
var pan = anchorObject.GetComponent<CameraPanInput>();
|
||||
WireSerializedReference(pan, "brainCamera", brainCamera);
|
||||
WireSerializedReference(pan, "anchor", anchor);
|
||||
WireSerializedReference(pan, "stateDirector", anchorObject.GetComponent<CameraStateDirector>());
|
||||
|
||||
var poiDirector = anchorObject.GetComponent<CameraPointOfInterestDirector>();
|
||||
WireSerializedReference(poiDirector, "anchor", anchor);
|
||||
WireSerializedReference(poiDirector, "stateDirector", anchorObject.GetComponent<CameraStateDirector>());
|
||||
|
||||
var stateDirector = anchorObject.GetComponent<CameraStateDirector>();
|
||||
WireSerializedReference(stateDirector, "anchor", anchor);
|
||||
|
||||
EnsureRevealCamera(vcamObject, followTarget);
|
||||
}
|
||||
|
||||
static void EnsureRevealCamera(GameObject followVcam, Transform followTarget)
|
||||
{
|
||||
var revealName = "CM_Reveal_Room101";
|
||||
var revealObject = GameObject.Find(revealName);
|
||||
if (revealObject == null)
|
||||
{
|
||||
revealObject = new GameObject(revealName);
|
||||
revealObject.transform.SetPositionAndRotation(
|
||||
followVcam.transform.position + new Vector3(-4f, 2f, -2f),
|
||||
followVcam.transform.rotation);
|
||||
}
|
||||
|
||||
var revealCam = EnsureComponent<CinemachineCamera>(revealObject);
|
||||
revealCam.Target.TrackingTarget = followTarget;
|
||||
revealCam.Priority.Value = 0;
|
||||
revealCam.Lens.FieldOfView = followVcam.GetComponent<CinemachineCamera>().Lens.FieldOfView;
|
||||
|
||||
var volumeObject = GameObject.Find("RevealVolume_Room101");
|
||||
if (volumeObject == null)
|
||||
{
|
||||
volumeObject = new GameObject("RevealVolume_Room101");
|
||||
volumeObject.transform.position = followTarget.position + new Vector3(0f, 1f, 2f);
|
||||
}
|
||||
|
||||
var box = EnsureComponent<BoxCollider>(volumeObject);
|
||||
box.isTrigger = true;
|
||||
box.size = new Vector3(6f, 3f, 4f);
|
||||
|
||||
var volume = EnsureComponent<CameraFramingVolume>(volumeObject);
|
||||
WireSerializedReference(volume, "revealCamera", revealCam);
|
||||
}
|
||||
|
||||
static T EnsureComponent<T>(GameObject target) where T : Component
|
||||
{
|
||||
var component = target.GetComponent<T>();
|
||||
if (component == null)
|
||||
{
|
||||
component = target.AddComponent<T>();
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
static void SetLayerIfExists(string objectName, int layer)
|
||||
{
|
||||
if (layer < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var obj = GameObject.Find(objectName);
|
||||
if (obj != null)
|
||||
{
|
||||
obj.layer = layer;
|
||||
}
|
||||
}
|
||||
|
||||
static void WireSerializedReference(Object target, string propertyName, Object value)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var so = new SerializedObject(target);
|
||||
var property = so.FindProperty(propertyName);
|
||||
if (property != null)
|
||||
{
|
||||
property.objectReferenceValue = value;
|
||||
so.ApplyModifiedPropertiesWithoutUndo();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eedfabcda58c94099b2ad3e19f141b09
|
||||
@@ -0,0 +1,352 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using NightclubArcadia.Skills;
|
||||
using TMPro;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Callbacks;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using Yarn.Unity;
|
||||
|
||||
/// <summary>
|
||||
/// Regenerates SkillDefinition assets + SkillDatabase from the Skill Bible JSON mirror.
|
||||
///
|
||||
/// Source of truth for prose/roster: the Obsidian Skill System Draft.md.
|
||||
/// Checked-in machine mirror: Assets/Skills/skill_bible.json (edit that, not the .assets).
|
||||
/// Hand-edits to Definitions/*.asset are overwritten on script reload by EnsureSkillAssetsOnly.
|
||||
/// </summary>
|
||||
public static class SkillSystemSetup
|
||||
{
|
||||
const string DefinitionsFolder = "Assets/Skills/Definitions";
|
||||
const string SkillsFolder = "Assets/Skills";
|
||||
const string BiblePath = "Assets/Skills/skill_bible.json";
|
||||
const string DatabasePath = "Assets/Skills/SkillDatabase.asset";
|
||||
const string ScenePath = "Assets/Scenes/DialogueTest.unity";
|
||||
const string AutoSetupSessionKey = "NightclubArcadia.SkillSystem.AutoSetupDone";
|
||||
|
||||
[DidReloadScripts]
|
||||
static void AutoSetupAfterReload()
|
||||
{
|
||||
if (EditorApplication.isPlayingOrWillChangePlaymode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (SessionState.GetBool(AutoSetupSessionKey, false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Defer one frame so AssetDatabase is fully ready after domain reload.
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
if (SessionState.GetBool(AutoSetupSessionKey, false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SessionState.SetBool(AutoSetupSessionKey, true);
|
||||
EnsureSkillAssetsOnly();
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates/updates ScriptableObject assets without opening a scene.
|
||||
/// Safe to run on domain reload. Scene wiring stays on the menu item /
|
||||
/// the checked-in DialogueTest.unity Skill System objects.
|
||||
/// </summary>
|
||||
static void EnsureSkillAssetsOnly()
|
||||
{
|
||||
if (!TryLoadRoster(out var roster))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureFolder("Assets", "Skills");
|
||||
EnsureFolder("Assets/Skills", "Definitions");
|
||||
|
||||
var definitions = new List<SkillDefinition>();
|
||||
var rosterIds = new HashSet<string>(StringComparer.Ordinal);
|
||||
foreach (var entry in roster)
|
||||
{
|
||||
rosterIds.Add(entry.id);
|
||||
definitions.Add(CreateOrUpdateDefinition(entry));
|
||||
}
|
||||
|
||||
DeleteOrphanDefinitions(rosterIds);
|
||||
|
||||
var database = AssetDatabase.LoadAssetAtPath<SkillDatabase>(DatabasePath);
|
||||
if (database == null)
|
||||
{
|
||||
database = ScriptableObject.CreateInstance<SkillDatabase>();
|
||||
AssetDatabase.CreateAsset(database, DatabasePath);
|
||||
}
|
||||
|
||||
database.EditorSetSkills(definitions);
|
||||
EditorUtility.SetDirty(database);
|
||||
AssetDatabase.SaveAssets();
|
||||
Debug.Log($"[SkillSystemSetup] Skill assets ready at {DatabasePath}. Run Tools/Nightclub Arcadia/Set Up Skill System if scene refs need rewiring.");
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Nightclub Arcadia/Set Up Skill System")]
|
||||
public static void SetUpSkillSystem()
|
||||
{
|
||||
if (!TryLoadRoster(out var roster))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureFolder("Assets", "Skills");
|
||||
EnsureFolder("Assets/Skills", "Definitions");
|
||||
|
||||
var definitions = new List<SkillDefinition>();
|
||||
var rosterIds = new HashSet<string>(StringComparer.Ordinal);
|
||||
foreach (var entry in roster)
|
||||
{
|
||||
rosterIds.Add(entry.id);
|
||||
definitions.Add(CreateOrUpdateDefinition(entry));
|
||||
}
|
||||
|
||||
DeleteOrphanDefinitions(rosterIds);
|
||||
|
||||
var database = AssetDatabase.LoadAssetAtPath<SkillDatabase>(DatabasePath);
|
||||
if (database == null)
|
||||
{
|
||||
database = ScriptableObject.CreateInstance<SkillDatabase>();
|
||||
AssetDatabase.CreateAsset(database, DatabasePath);
|
||||
}
|
||||
|
||||
database.EditorSetSkills(definitions);
|
||||
EditorUtility.SetDirty(database);
|
||||
AssetDatabase.SaveAssets();
|
||||
|
||||
WireDialogueTestScene(database);
|
||||
AssetDatabase.SaveAssets();
|
||||
Debug.Log($"Skill system setup complete. Database at {DatabasePath}; scene wired at {ScenePath}.");
|
||||
}
|
||||
|
||||
static bool TryLoadRoster(out SkillBibleEntry[] roster)
|
||||
{
|
||||
roster = null;
|
||||
var text = AssetDatabase.LoadAssetAtPath<TextAsset>(BiblePath);
|
||||
if (text == null)
|
||||
{
|
||||
Debug.LogError($"[SkillSystemSetup] Missing bible at {BiblePath}.");
|
||||
return false;
|
||||
}
|
||||
|
||||
SkillBibleRoot root;
|
||||
try
|
||||
{
|
||||
root = JsonUtility.FromJson<SkillBibleRoot>(text.text);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"[SkillSystemSetup] Failed to parse {BiblePath}: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (root?.skills == null || root.skills.Length == 0)
|
||||
{
|
||||
Debug.LogError($"[SkillSystemSetup] {BiblePath} has no skills.");
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var entry in root.skills)
|
||||
{
|
||||
if (entry == null || string.IsNullOrEmpty(entry.id))
|
||||
{
|
||||
Debug.LogError($"[SkillSystemSetup] {BiblePath} contains a skill with empty id.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fail loudly on enum typos — silent defaults are content bugs.
|
||||
if (!Enum.TryParse<SkillAxis>(entry.axis, ignoreCase: true, out _))
|
||||
{
|
||||
Debug.LogError($"[SkillSystemSetup] Skill '{entry.id}': invalid axis '{entry.axis}'.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Enum.TryParse<TraceChannel>(entry.primaryChannel, ignoreCase: true, out _))
|
||||
{
|
||||
Debug.LogError($"[SkillSystemSetup] Skill '{entry.id}': invalid primaryChannel '{entry.primaryChannel}'.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var secondary = string.IsNullOrEmpty(entry.secondaryChannel) ? "None" : entry.secondaryChannel;
|
||||
if (!Enum.TryParse<TraceChannel>(secondary, ignoreCase: true, out _))
|
||||
{
|
||||
Debug.LogError($"[SkillSystemSetup] Skill '{entry.id}': invalid secondaryChannel '{entry.secondaryChannel}'.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Enum.TryParse<ClueYield>(entry.clueYield, ignoreCase: true, out _))
|
||||
{
|
||||
Debug.LogError($"[SkillSystemSetup] Skill '{entry.id}': invalid clueYield '{entry.clueYield}'.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
roster = root.skills;
|
||||
return true;
|
||||
}
|
||||
|
||||
static SkillDefinition CreateOrUpdateDefinition(SkillBibleEntry entry)
|
||||
{
|
||||
var path = $"{DefinitionsFolder}/{entry.id}.asset";
|
||||
var asset = AssetDatabase.LoadAssetAtPath<SkillDefinition>(path);
|
||||
if (asset == null)
|
||||
{
|
||||
asset = ScriptableObject.CreateInstance<SkillDefinition>();
|
||||
AssetDatabase.CreateAsset(asset, path);
|
||||
}
|
||||
|
||||
asset.EditorSet(entry);
|
||||
EditorUtility.SetDirty(asset);
|
||||
return asset;
|
||||
}
|
||||
|
||||
static void DeleteOrphanDefinitions(HashSet<string> rosterIds)
|
||||
{
|
||||
if (!AssetDatabase.IsValidFolder(DefinitionsFolder))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var guids = AssetDatabase.FindAssets("t:SkillDefinition", new[] { DefinitionsFolder });
|
||||
foreach (var guid in guids)
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
var fileName = Path.GetFileNameWithoutExtension(path);
|
||||
if (rosterIds.Contains(fileName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Debug.Log($"[SkillSystemSetup] Deleting orphan definition {path}");
|
||||
AssetDatabase.DeleteAsset(path);
|
||||
}
|
||||
}
|
||||
|
||||
static void WireDialogueTestScene(SkillDatabase database)
|
||||
{
|
||||
if (!File.Exists(ScenePath))
|
||||
{
|
||||
Debug.LogError($"Scene not found at {ScenePath}. Run Tools/Nightclub Arcadia/Set Up Yarn Dialogue Test first.");
|
||||
return;
|
||||
}
|
||||
|
||||
var scene = EditorSceneManager.OpenScene(ScenePath, OpenSceneMode.Single);
|
||||
|
||||
var runner = UnityEngine.Object.FindAnyObjectByType<DialogueRunner>();
|
||||
if (runner == null)
|
||||
{
|
||||
Debug.LogError("No DialogueRunner in DialogueTest.unity.");
|
||||
return;
|
||||
}
|
||||
|
||||
var skillGo = GameObject.Find("Skill System");
|
||||
if (skillGo == null)
|
||||
{
|
||||
skillGo = new GameObject("Skill System");
|
||||
}
|
||||
|
||||
var runtime = skillGo.GetComponent<SkillRuntime>() ?? skillGo.AddComponent<SkillRuntime>();
|
||||
var commentary = skillGo.GetComponent<CommentarySystem>() ?? skillGo.AddComponent<CommentarySystem>();
|
||||
var idle = skillGo.GetComponent<CommentaryIdleTimer>() ?? skillGo.AddComponent<CommentaryIdleTimer>();
|
||||
|
||||
var banner = EnsureBanner(runner.transform);
|
||||
var view = banner.GetComponent<SkillCheckResultView>() ?? banner.AddComponent<SkillCheckResultView>();
|
||||
|
||||
var runtimeSo = new SerializedObject(runtime);
|
||||
runtimeSo.FindProperty("database").objectReferenceValue = database;
|
||||
runtimeSo.FindProperty("dialogueRunner").objectReferenceValue = runner;
|
||||
runtimeSo.FindProperty("resultView").objectReferenceValue = view;
|
||||
runtimeSo.FindProperty("commentary").objectReferenceValue = commentary;
|
||||
runtimeSo.FindProperty("debugContextId").stringValue = "Entrance_Queue";
|
||||
runtimeSo.ApplyModifiedPropertiesWithoutUndo();
|
||||
|
||||
var commentarySo = new SerializedObject(commentary);
|
||||
commentarySo.FindProperty("dialogueRunner").objectReferenceValue = runner;
|
||||
commentarySo.FindProperty("database").objectReferenceValue = database;
|
||||
commentarySo.ApplyModifiedPropertiesWithoutUndo();
|
||||
|
||||
var idleSo = new SerializedObject(idle);
|
||||
idleSo.FindProperty("dialogueRunner").objectReferenceValue = runner;
|
||||
idleSo.FindProperty("contextId").stringValue = "Entrance_Queue";
|
||||
idleSo.ApplyModifiedPropertiesWithoutUndo();
|
||||
|
||||
var group = banner.GetComponent<CanvasGroup>();
|
||||
var label = banner.GetComponentInChildren<TMP_Text>(true);
|
||||
var viewSo = new SerializedObject(view);
|
||||
viewSo.FindProperty("group").objectReferenceValue = group;
|
||||
viewSo.FindProperty("label").objectReferenceValue = label;
|
||||
viewSo.ApplyModifiedPropertiesWithoutUndo();
|
||||
|
||||
EditorSceneManager.MarkSceneDirty(scene);
|
||||
EditorSceneManager.SaveScene(scene);
|
||||
}
|
||||
|
||||
static GameObject EnsureBanner(Transform dialogueRoot)
|
||||
{
|
||||
var canvas = dialogueRoot.GetComponentInChildren<Canvas>(true);
|
||||
if (canvas == null)
|
||||
{
|
||||
var canvasGo = new GameObject("Skill Check Canvas", typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster));
|
||||
canvas = canvasGo.GetComponent<Canvas>();
|
||||
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
||||
canvasGo.transform.SetParent(dialogueRoot, false);
|
||||
}
|
||||
|
||||
var existing = canvas.transform.Find("Skill Check Banner");
|
||||
if (existing != null)
|
||||
{
|
||||
return existing.gameObject;
|
||||
}
|
||||
|
||||
var banner = new GameObject("Skill Check Banner", typeof(RectTransform), typeof(CanvasGroup), typeof(Image));
|
||||
banner.transform.SetParent(canvas.transform, false);
|
||||
|
||||
var rt = banner.GetComponent<RectTransform>();
|
||||
rt.anchorMin = new Vector2(0.1f, 0.82f);
|
||||
rt.anchorMax = new Vector2(0.9f, 0.95f);
|
||||
rt.offsetMin = Vector2.zero;
|
||||
rt.offsetMax = Vector2.zero;
|
||||
|
||||
var image = banner.GetComponent<Image>();
|
||||
image.color = new Color(0f, 0f, 0f, 0.75f);
|
||||
|
||||
var group = banner.GetComponent<CanvasGroup>();
|
||||
group.alpha = 0f;
|
||||
group.blocksRaycasts = false;
|
||||
group.interactable = false;
|
||||
|
||||
var labelGo = new GameObject("Label", typeof(RectTransform));
|
||||
labelGo.transform.SetParent(banner.transform, false);
|
||||
var labelRt = labelGo.GetComponent<RectTransform>();
|
||||
labelRt.anchorMin = Vector2.zero;
|
||||
labelRt.anchorMax = Vector2.one;
|
||||
labelRt.offsetMin = new Vector2(16f, 8f);
|
||||
labelRt.offsetMax = new Vector2(-16f, -8f);
|
||||
|
||||
var tmp = labelGo.AddComponent<TextMeshProUGUI>();
|
||||
tmp.text = string.Empty;
|
||||
tmp.alignment = TextAlignmentOptions.Center;
|
||||
tmp.fontSize = 28f;
|
||||
tmp.textWrappingMode = TextWrappingModes.Normal;
|
||||
tmp.color = Color.white;
|
||||
|
||||
return banner;
|
||||
}
|
||||
|
||||
static void EnsureFolder(string parent, string child)
|
||||
{
|
||||
var path = $"{parent}/{child}";
|
||||
if (!AssetDatabase.IsValidFolder(path))
|
||||
{
|
||||
AssetDatabase.CreateFolder(parent, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 231654cf350a49ce9920bc4f24a1f002
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,48 @@
|
||||
using UnityEditor;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// One-click EditMode test run for the skill system.
|
||||
/// </summary>
|
||||
public static class SkillSystemTestRunner
|
||||
{
|
||||
[MenuItem("Tools/Nightclub Arcadia/Run Skill EditMode Tests")]
|
||||
public static void RunEditModeTests()
|
||||
{
|
||||
var api = ScriptableObject.CreateInstance<TestRunnerApi>();
|
||||
var filter = new Filter
|
||||
{
|
||||
testMode = TestMode.EditMode,
|
||||
assemblyNames = new[] { "NightclubArcadia.Skills.Tests" },
|
||||
};
|
||||
|
||||
api.RegisterCallbacks(new Callbacks());
|
||||
api.Execute(new ExecutionSettings(filter));
|
||||
Debug.Log("[SkillSystemTestRunner] EditMode tests started — watch the Console / Test Runner.");
|
||||
}
|
||||
|
||||
sealed class Callbacks : ICallbacks
|
||||
{
|
||||
public void RunStarted(ITestAdaptor testsToRun) { }
|
||||
|
||||
public void RunFinished(ITestResultAdaptor result)
|
||||
{
|
||||
Debug.Log($"[SkillSystemTestRunner] Finished. Pass:{result.PassCount} Fail:{result.FailCount} Skip:{result.SkipCount}");
|
||||
if (result.FailCount > 0)
|
||||
{
|
||||
Debug.LogError("[SkillSystemTestRunner] One or more skill EditMode tests failed.");
|
||||
}
|
||||
}
|
||||
|
||||
public void TestStarted(ITestAdaptor test) { }
|
||||
|
||||
public void TestFinished(ITestResultAdaptor result)
|
||||
{
|
||||
if (result.TestStatus == TestStatus.Failed)
|
||||
{
|
||||
Debug.LogError($"[SkillSystemTestRunner] FAIL {result.FullName}\n{result.Message}\n{result.StackTrace}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c8e1a2b3d4f4e6a9b0c1d2e3f4a5b6c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,818 @@
|
||||
using System.IO;
|
||||
using NightclubArcadia.Cinematics;
|
||||
using NightclubArcadia.Dialogue;
|
||||
using NightclubArcadia.Skills;
|
||||
using NightclubArcadia.UI;
|
||||
using NightclubArcadia.UI.Dialogue;
|
||||
using NightclubArcadia.UI.HUD;
|
||||
using NightclubArcadia.UI.Menu;
|
||||
using StarterAssets;
|
||||
using TMPro;
|
||||
using Unity.Cinemachine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using Yarn.Unity;
|
||||
|
||||
/// <summary>
|
||||
/// Builds UI prefabs and wires them into DialogueTest.unity.
|
||||
/// Run via Tools → Nightclub Arcadia → Set Up UI Layer.
|
||||
/// </summary>
|
||||
public static class UILayerSetup
|
||||
{
|
||||
const string ScenePath = "Assets/Scenes/DialogueTest.unity";
|
||||
const string PrefabFolder = "Assets/Prefabs/UI";
|
||||
const string ArtFolder = "Assets/Art/UI/Placeholder";
|
||||
|
||||
// Nested Resources folder: UILayerBootstrap builds the HUD at runtime and
|
||||
// can only reach the sprite through Resources.Load. Keeping it under
|
||||
// Art/UI/Placeholder preserves the organisation while making it
|
||||
// build-safe — an AssetDatabase-only path is null outside the editor.
|
||||
const string PortraitFolder = ArtFolder + "/Resources";
|
||||
const string PortraitPath = PortraitFolder + "/portrait_placeholder.png";
|
||||
const string MessageRowPrefabPath = PrefabFolder + "/DialogueMessageRow.prefab";
|
||||
const string StreamCanvasPrefabPath = PrefabFolder + "/DialogueStreamCanvas.prefab";
|
||||
const string HudCanvasPrefabPath = PrefabFolder + "/HudCanvas.prefab";
|
||||
const string SkillRowPrefabPath = PrefabFolder + "/SkillRow.prefab";
|
||||
const string MenuCanvasPrefabPath = PrefabFolder + "/PlayerMenuCanvas.prefab";
|
||||
|
||||
[MenuItem("Tools/Nightclub Arcadia/Set Up UI Layer")]
|
||||
public static void SetUp()
|
||||
{
|
||||
EnsureFolders();
|
||||
CreatePortraitPlaceholder();
|
||||
|
||||
var messageRow = CreateMessageRowPrefab();
|
||||
var skillRow = CreateSkillRowPrefab();
|
||||
var streamCanvas = CreateDialogueStreamCanvasPrefab(messageRow);
|
||||
var hudCanvas = CreateHudCanvasPrefab();
|
||||
var menuCanvas = CreatePlayerMenuCanvasPrefab(skillRow);
|
||||
|
||||
WireScene(streamCanvas, hudCanvas, menuCanvas);
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
Debug.Log("[UILayerSetup] UI layer setup complete.");
|
||||
}
|
||||
|
||||
static void EnsureFolders()
|
||||
{
|
||||
EnsureFolder("Assets/Prefabs");
|
||||
EnsureFolder(PrefabFolder);
|
||||
EnsureFolder("Assets/Art");
|
||||
EnsureFolder("Assets/Art/UI");
|
||||
EnsureFolder(ArtFolder);
|
||||
EnsureFolder(PortraitFolder);
|
||||
}
|
||||
|
||||
static void EnsureFolder(string path)
|
||||
{
|
||||
if (AssetDatabase.IsValidFolder(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var parent = Path.GetDirectoryName(path)?.Replace('\\', '/');
|
||||
var name = Path.GetFileName(path);
|
||||
if (!string.IsNullOrEmpty(parent) && !string.IsNullOrEmpty(name))
|
||||
{
|
||||
AssetDatabase.CreateFolder(parent, name);
|
||||
}
|
||||
}
|
||||
|
||||
static void CreatePortraitPlaceholder()
|
||||
{
|
||||
if (File.Exists(PortraitPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var tex = new Texture2D(512, 512, TextureFormat.RGBA32, false);
|
||||
var dark = new Color(0.12f, 0.12f, 0.14f, 1f);
|
||||
var mid = new Color(0.35f, 0.35f, 0.4f, 1f);
|
||||
for (var y = 0; y < 512; y++)
|
||||
{
|
||||
for (var x = 0; x < 512; x++)
|
||||
{
|
||||
var dx = (x - 256) / 256f;
|
||||
var dy = (y - 280) / 256f;
|
||||
var inHead = dx * dx + dy * dy < 0.12f;
|
||||
var inBody = Mathf.Abs(dx) < 0.22f && y < 220 && y > 40;
|
||||
tex.SetPixel(x, y, inHead || inBody ? mid : dark);
|
||||
}
|
||||
}
|
||||
|
||||
tex.Apply();
|
||||
File.WriteAllBytes(PortraitPath, tex.EncodeToPNG());
|
||||
Object.DestroyImmediate(tex);
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
var importer = AssetImporter.GetAtPath(PortraitPath) as TextureImporter;
|
||||
if (importer != null)
|
||||
{
|
||||
importer.textureType = TextureImporterType.Sprite;
|
||||
importer.spritePixelsPerUnit = 100;
|
||||
importer.SaveAndReimport();
|
||||
}
|
||||
}
|
||||
|
||||
static GameObject CreateMessageRowPrefab()
|
||||
{
|
||||
var root = new GameObject("DialogueMessageRow", typeof(RectTransform), typeof(DialogueMessageRow));
|
||||
var rootRt = root.GetComponent<RectTransform>();
|
||||
rootRt.sizeDelta = new Vector2(0f, 80f);
|
||||
|
||||
var layout = root.AddComponent<VerticalLayoutGroup>();
|
||||
layout.padding = new RectOffset(12, 12, 8, 8);
|
||||
layout.spacing = 4f;
|
||||
layout.childControlHeight = true;
|
||||
layout.childControlWidth = true;
|
||||
layout.childForceExpandHeight = false;
|
||||
layout.childForceExpandWidth = true;
|
||||
|
||||
var fitter = root.AddComponent<ContentSizeFitter>();
|
||||
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
fitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
|
||||
|
||||
var speakerGo = CreateTmp("Speaker", root.transform, 22f, FontStyles.Bold, TextAlignmentOptions.Left);
|
||||
var bodyGo = CreateTmp("Body", root.transform, 28f, FontStyles.Normal, TextAlignmentOptions.TopLeft);
|
||||
|
||||
var row = root.GetComponent<DialogueMessageRow>();
|
||||
var so = new SerializedObject(row);
|
||||
so.FindProperty("speakerText").objectReferenceValue = speakerGo.GetComponent<TMP_Text>();
|
||||
so.FindProperty("bodyText").objectReferenceValue = bodyGo.GetComponent<TMP_Text>();
|
||||
so.FindProperty("speakerLabel").objectReferenceValue = speakerGo;
|
||||
so.ApplyModifiedPropertiesWithoutUndo();
|
||||
|
||||
var prefab = PrefabUtility.SaveAsPrefabAsset(root, MessageRowPrefabPath);
|
||||
Object.DestroyImmediate(root);
|
||||
return prefab;
|
||||
}
|
||||
|
||||
static GameObject CreateSkillRowPrefab()
|
||||
{
|
||||
var root = new GameObject("SkillRow", typeof(RectTransform), typeof(SkillRowView));
|
||||
var rootRt = root.GetComponent<RectTransform>();
|
||||
rootRt.sizeDelta = new Vector2(0f, 48f);
|
||||
|
||||
var hlg = root.AddComponent<HorizontalLayoutGroup>();
|
||||
hlg.padding = new RectOffset(8, 8, 4, 4);
|
||||
hlg.spacing = 12f;
|
||||
hlg.childAlignment = TextAnchor.MiddleLeft;
|
||||
hlg.childControlHeight = true;
|
||||
hlg.childControlWidth = true;
|
||||
hlg.childForceExpandHeight = true;
|
||||
hlg.childForceExpandWidth = false;
|
||||
|
||||
var stripe = CreateUiImage("Accent", root.transform, new Color(1f, 1f, 1f, 1f));
|
||||
stripe.GetComponent<LayoutElement>().preferredWidth = 6f;
|
||||
stripe.GetComponent<LayoutElement>().flexibleWidth = 0f;
|
||||
|
||||
var nameGo = CreateTmp("Name", root.transform, 24f, FontStyles.Bold, TextAlignmentOptions.Left);
|
||||
nameGo.GetComponent<LayoutElement>().flexibleWidth = 1f;
|
||||
|
||||
var barGo = new GameObject("Bar", typeof(RectTransform), typeof(Image), typeof(LayoutElement));
|
||||
barGo.transform.SetParent(root.transform, false);
|
||||
barGo.GetComponent<Image>().color = new Color(0.2f, 0.2f, 0.22f, 1f);
|
||||
barGo.GetComponent<LayoutElement>().preferredWidth = 160f;
|
||||
barGo.GetComponent<LayoutElement>().flexibleWidth = 0f;
|
||||
barGo.GetComponent<LayoutElement>().preferredHeight = 16f;
|
||||
|
||||
var fillGo = CreateUiImage("Fill", barGo.transform, Color.white);
|
||||
var fillRt = fillGo.GetComponent<RectTransform>();
|
||||
fillRt.anchorMin = Vector2.zero;
|
||||
fillRt.anchorMax = Vector2.one;
|
||||
fillRt.offsetMin = Vector2.zero;
|
||||
fillRt.offsetMax = Vector2.zero;
|
||||
var fillImage = fillGo.GetComponent<Image>();
|
||||
fillImage.type = Image.Type.Filled;
|
||||
fillImage.fillMethod = Image.FillMethod.Horizontal;
|
||||
fillImage.fillAmount = 0.2f;
|
||||
Object.DestroyImmediate(fillGo.GetComponent<LayoutElement>());
|
||||
|
||||
var rankGo = CreateTmp("Rank", root.transform, 24f, FontStyles.Normal, TextAlignmentOptions.Right);
|
||||
rankGo.GetComponent<LayoutElement>().preferredWidth = 40f;
|
||||
rankGo.GetComponent<LayoutElement>().flexibleWidth = 0f;
|
||||
|
||||
var view = root.GetComponent<SkillRowView>();
|
||||
var so = new SerializedObject(view);
|
||||
so.FindProperty("nameLabel").objectReferenceValue = nameGo.GetComponent<TMP_Text>();
|
||||
so.FindProperty("rankLabel").objectReferenceValue = rankGo.GetComponent<TMP_Text>();
|
||||
so.FindProperty("barFill").objectReferenceValue = fillImage;
|
||||
so.FindProperty("accentStripe").objectReferenceValue = stripe.GetComponent<Image>();
|
||||
so.ApplyModifiedPropertiesWithoutUndo();
|
||||
|
||||
var prefab = PrefabUtility.SaveAsPrefabAsset(root, SkillRowPrefabPath);
|
||||
Object.DestroyImmediate(root);
|
||||
return prefab;
|
||||
}
|
||||
|
||||
static GameObject CreateDialogueStreamCanvasPrefab(GameObject messageRowPrefab)
|
||||
{
|
||||
var canvasGo = CreateCanvas("DialogueStreamCanvas", 5);
|
||||
var canvasGroup = canvasGo.AddComponent<CanvasGroup>();
|
||||
|
||||
var panel = new GameObject("Panel", typeof(RectTransform), typeof(Image));
|
||||
panel.transform.SetParent(canvasGo.transform, false);
|
||||
var panelRt = panel.GetComponent<RectTransform>();
|
||||
panelRt.anchorMin = new Vector2(0.66f, 0f);
|
||||
panelRt.anchorMax = new Vector2(1f, 1f);
|
||||
panelRt.offsetMin = Vector2.zero;
|
||||
panelRt.offsetMax = Vector2.zero;
|
||||
panel.GetComponent<Image>().color = new Color(0.05f, 0.05f, 0.08f, 0.92f);
|
||||
|
||||
var scrollGo = new GameObject("Scroll", typeof(RectTransform), typeof(ScrollRect), typeof(Image));
|
||||
scrollGo.transform.SetParent(panel.transform, false);
|
||||
Stretch(scrollGo.GetComponent<RectTransform>(), 8f);
|
||||
scrollGo.GetComponent<Image>().color = new Color(0f, 0f, 0f, 0.01f);
|
||||
|
||||
var viewport = new GameObject("Viewport", typeof(RectTransform), typeof(RectMask2D), typeof(Image));
|
||||
viewport.transform.SetParent(scrollGo.transform, false);
|
||||
Stretch(viewport.GetComponent<RectTransform>());
|
||||
viewport.GetComponent<Image>().color = new Color(1f, 1f, 1f, 0.01f);
|
||||
|
||||
var content = new GameObject("Content", typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter));
|
||||
content.transform.SetParent(viewport.transform, false);
|
||||
var contentRt = content.GetComponent<RectTransform>();
|
||||
contentRt.anchorMin = new Vector2(0f, 0f);
|
||||
contentRt.anchorMax = new Vector2(1f, 0f);
|
||||
contentRt.pivot = new Vector2(0.5f, 0f);
|
||||
contentRt.sizeDelta = new Vector2(0f, 0f);
|
||||
|
||||
var vlg = content.GetComponent<VerticalLayoutGroup>();
|
||||
vlg.childAlignment = TextAnchor.LowerCenter;
|
||||
vlg.childControlHeight = true;
|
||||
vlg.childControlWidth = true;
|
||||
vlg.childForceExpandHeight = false;
|
||||
vlg.childForceExpandWidth = true;
|
||||
vlg.spacing = 6f;
|
||||
vlg.padding = new RectOffset(4, 4, 8, 8);
|
||||
|
||||
var csf = content.GetComponent<ContentSizeFitter>();
|
||||
csf.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
|
||||
var messages = new GameObject("Messages", typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter));
|
||||
messages.transform.SetParent(content.transform, false);
|
||||
ConfigureVerticalContent(messages);
|
||||
|
||||
var options = new GameObject("Options", typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter), typeof(CanvasGroup));
|
||||
options.transform.SetParent(content.transform, false);
|
||||
ConfigureVerticalContent(options);
|
||||
var optionsGroup = options.GetComponent<CanvasGroup>();
|
||||
optionsGroup.alpha = 0f;
|
||||
optionsGroup.interactable = false;
|
||||
optionsGroup.blocksRaycasts = false;
|
||||
|
||||
var scroll = scrollGo.GetComponent<ScrollRect>();
|
||||
scroll.content = contentRt;
|
||||
scroll.viewport = viewport.GetComponent<RectTransform>();
|
||||
scroll.horizontal = false;
|
||||
scroll.vertical = true;
|
||||
scroll.movementType = ScrollRect.MovementType.Clamped;
|
||||
|
||||
var streamPresenter = canvasGo.AddComponent<DialogueStreamPresenter>();
|
||||
var streamSo = new SerializedObject(streamPresenter);
|
||||
streamSo.FindProperty("messageRowPrefab").objectReferenceValue = messageRowPrefab.GetComponent<DialogueMessageRow>();
|
||||
streamSo.FindProperty("messageContainer").objectReferenceValue = messages.GetComponent<RectTransform>();
|
||||
streamSo.FindProperty("scrollRect").objectReferenceValue = scroll;
|
||||
streamSo.ApplyModifiedPropertiesWithoutUndo();
|
||||
|
||||
var optionPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(
|
||||
"Packages/dev.yarnspinner.unity/Prefabs/Option Item.prefab");
|
||||
var optionsPresenter = canvasGo.AddComponent<DialogueStreamOptionsPresenter>();
|
||||
var optSo = new SerializedObject(optionsPresenter);
|
||||
if (optionPrefab != null)
|
||||
{
|
||||
optSo.FindProperty("optionViewPrefab").objectReferenceValue = optionPrefab.GetComponent<OptionItem>();
|
||||
}
|
||||
|
||||
optSo.FindProperty("optionsContainer").objectReferenceValue = options.transform;
|
||||
optSo.FindProperty("canvasGroup").objectReferenceValue = optionsGroup;
|
||||
optSo.ApplyModifiedPropertiesWithoutUndo();
|
||||
|
||||
// Start hidden — DialogueTransitionController shows it.
|
||||
canvasGroup.alpha = 0f;
|
||||
canvasGroup.interactable = false;
|
||||
canvasGroup.blocksRaycasts = false;
|
||||
|
||||
var prefab = PrefabUtility.SaveAsPrefabAsset(canvasGo, StreamCanvasPrefabPath);
|
||||
Object.DestroyImmediate(canvasGo);
|
||||
return prefab;
|
||||
}
|
||||
|
||||
static GameObject CreateHudCanvasPrefab()
|
||||
{
|
||||
var canvasGo = CreateCanvas("HudCanvas", 10);
|
||||
var portraitSprite = AssetDatabase.LoadAssetAtPath<Sprite>(PortraitPath);
|
||||
|
||||
var panel = new GameObject("PortraitPanel", typeof(RectTransform), typeof(CanvasGroup), typeof(Image), typeof(Button));
|
||||
panel.transform.SetParent(canvasGo.transform, false);
|
||||
var panelRt = panel.GetComponent<RectTransform>();
|
||||
panelRt.anchorMin = new Vector2(1f, 0f);
|
||||
panelRt.anchorMax = new Vector2(1f, 0f);
|
||||
panelRt.pivot = new Vector2(1f, 0f);
|
||||
panelRt.sizeDelta = new Vector2(220f, 280f);
|
||||
panelRt.anchoredPosition = new Vector2(-24f, 24f);
|
||||
panel.GetComponent<Image>().color = new Color(0.15f, 0.15f, 0.18f, 0.95f);
|
||||
if (portraitSprite != null)
|
||||
{
|
||||
panel.GetComponent<Image>().sprite = portraitSprite;
|
||||
panel.GetComponent<Image>().preserveAspect = true;
|
||||
}
|
||||
|
||||
var hint = CreateTmp("TabHint", panel.transform, 18f, FontStyles.Normal, TextAlignmentOptions.Center);
|
||||
var hintRt = hint.GetComponent<RectTransform>();
|
||||
hintRt.anchorMin = new Vector2(0f, 0f);
|
||||
hintRt.anchorMax = new Vector2(1f, 0f);
|
||||
hintRt.pivot = new Vector2(0.5f, 0f);
|
||||
hintRt.sizeDelta = new Vector2(0f, 28f);
|
||||
hintRt.anchoredPosition = new Vector2(0f, 4f);
|
||||
hint.GetComponent<TMP_Text>().text = "Tab";
|
||||
Object.DestroyImmediate(hint.GetComponent<LayoutElement>());
|
||||
|
||||
var view = canvasGo.AddComponent<PortraitHudView>();
|
||||
var so = new SerializedObject(view);
|
||||
so.FindProperty("panelRoot").objectReferenceValue = panel;
|
||||
so.ApplyModifiedPropertiesWithoutUndo();
|
||||
|
||||
var prefab = PrefabUtility.SaveAsPrefabAsset(canvasGo, HudCanvasPrefabPath);
|
||||
Object.DestroyImmediate(canvasGo);
|
||||
return prefab;
|
||||
}
|
||||
|
||||
static GameObject CreatePlayerMenuCanvasPrefab(GameObject skillRowPrefab)
|
||||
{
|
||||
var canvasGo = CreateCanvas("PlayerMenuCanvas", 20);
|
||||
var rootGroup = canvasGo.AddComponent<CanvasGroup>();
|
||||
|
||||
var dimmer = CreateUiImage("Dimmer", canvasGo.transform, new Color(0f, 0f, 0f, 0.6f));
|
||||
Stretch(dimmer.GetComponent<RectTransform>());
|
||||
Object.DestroyImmediate(dimmer.GetComponent<LayoutElement>());
|
||||
|
||||
var body = new GameObject("MenuBody", typeof(RectTransform), typeof(Image));
|
||||
body.transform.SetParent(canvasGo.transform, false);
|
||||
var bodyRt = body.GetComponent<RectTransform>();
|
||||
bodyRt.anchorMin = new Vector2(0.15f, 0.1f);
|
||||
bodyRt.anchorMax = new Vector2(0.85f, 0.9f);
|
||||
bodyRt.offsetMin = Vector2.zero;
|
||||
bodyRt.offsetMax = Vector2.zero;
|
||||
body.GetComponent<Image>().color = new Color(0.08f, 0.08f, 0.1f, 0.98f);
|
||||
|
||||
var tabBarGo = new GameObject("TabBar", typeof(RectTransform), typeof(HorizontalLayoutGroup), typeof(MenuTabBar));
|
||||
tabBarGo.transform.SetParent(body.transform, false);
|
||||
var tabBarRt = tabBarGo.GetComponent<RectTransform>();
|
||||
tabBarRt.anchorMin = new Vector2(0f, 1f);
|
||||
tabBarRt.anchorMax = new Vector2(1f, 1f);
|
||||
tabBarRt.pivot = new Vector2(0.5f, 1f);
|
||||
tabBarRt.sizeDelta = new Vector2(0f, 56f);
|
||||
tabBarRt.anchoredPosition = Vector2.zero;
|
||||
var tabHlg = tabBarGo.GetComponent<HorizontalLayoutGroup>();
|
||||
tabHlg.spacing = 8f;
|
||||
tabHlg.padding = new RectOffset(12, 12, 8, 8);
|
||||
// childControl* default to FALSE on a script-added layout group. Left
|
||||
// false, each tab button keeps its own 100px sizeDelta ("Character"
|
||||
// clips to "Charact") and childForceExpandWidth dumps all the leftover
|
||||
// room between them as a gap instead of widening them.
|
||||
tabHlg.childControlWidth = true;
|
||||
tabHlg.childControlHeight = true;
|
||||
tabHlg.childForceExpandWidth = true;
|
||||
tabHlg.childForceExpandHeight = true;
|
||||
|
||||
var contentArea = new GameObject("ContentArea", typeof(RectTransform));
|
||||
contentArea.transform.SetParent(body.transform, false);
|
||||
var contentRt = contentArea.GetComponent<RectTransform>();
|
||||
contentRt.anchorMin = new Vector2(0f, 0f);
|
||||
contentRt.anchorMax = new Vector2(1f, 1f);
|
||||
contentRt.offsetMin = new Vector2(16f, 16f);
|
||||
contentRt.offsetMax = new Vector2(-16f, -64f);
|
||||
|
||||
var characterPanel = CreateTabPanel("CharacterPanel", contentArea.transform);
|
||||
var skillsPanel = CreateTabPanel("SkillsPanel", contentArea.transform);
|
||||
var journalPanel = CreateTabPanel("JournalPanel", contentArea.transform);
|
||||
|
||||
// Character content
|
||||
var charView = characterPanel.AddComponent<CharacterPanelView>();
|
||||
var nameLabel = CreateTmp("Name", characterPanel.transform, 36f, FontStyles.Bold, TextAlignmentOptions.Left);
|
||||
var aliasLabel = CreateTmp("Alias", characterPanel.transform, 28f, FontStyles.Italic, TextAlignmentOptions.Left);
|
||||
var invLabel = CreateTmp("Inventory", characterPanel.transform, 24f, FontStyles.Normal, TextAlignmentOptions.Left);
|
||||
var charVlg = characterPanel.AddComponent<VerticalLayoutGroup>();
|
||||
charVlg.spacing = 12f;
|
||||
charVlg.padding = new RectOffset(8, 8, 8, 8);
|
||||
charVlg.childControlWidth = true;
|
||||
charVlg.childControlHeight = true;
|
||||
charVlg.childForceExpandWidth = true;
|
||||
charVlg.childForceExpandHeight = false;
|
||||
var charSo = new SerializedObject(charView);
|
||||
charSo.FindProperty("nameLabel").objectReferenceValue = nameLabel.GetComponent<TMP_Text>();
|
||||
charSo.FindProperty("aliasLabel").objectReferenceValue = aliasLabel.GetComponent<TMP_Text>();
|
||||
charSo.FindProperty("inventoryPlaceholder").objectReferenceValue = invLabel.GetComponent<TMP_Text>();
|
||||
charSo.ApplyModifiedPropertiesWithoutUndo();
|
||||
|
||||
// Skills content
|
||||
var skillsScroll = new GameObject("SkillsScroll", typeof(RectTransform), typeof(ScrollRect));
|
||||
skillsScroll.transform.SetParent(skillsPanel.transform, false);
|
||||
Stretch(skillsScroll.GetComponent<RectTransform>());
|
||||
var skillsViewport = new GameObject("Viewport", typeof(RectTransform), typeof(RectMask2D), typeof(Image));
|
||||
skillsViewport.transform.SetParent(skillsScroll.transform, false);
|
||||
Stretch(skillsViewport.GetComponent<RectTransform>());
|
||||
skillsViewport.GetComponent<Image>().color = new Color(1f, 1f, 1f, 0.01f);
|
||||
var skillsContent = new GameObject("Content", typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter));
|
||||
skillsContent.transform.SetParent(skillsViewport.transform, false);
|
||||
ConfigureVerticalContent(skillsContent);
|
||||
var skillsContentRt = skillsContent.GetComponent<RectTransform>();
|
||||
skillsContentRt.anchorMin = new Vector2(0f, 1f);
|
||||
skillsContentRt.anchorMax = new Vector2(1f, 1f);
|
||||
skillsContentRt.pivot = new Vector2(0.5f, 1f);
|
||||
var skillsScrollRect = skillsScroll.GetComponent<ScrollRect>();
|
||||
skillsScrollRect.content = skillsContentRt;
|
||||
skillsScrollRect.viewport = skillsViewport.GetComponent<RectTransform>();
|
||||
skillsScrollRect.horizontal = false;
|
||||
|
||||
var axisHeaderTemplate = CreateTmp("AxisHeaderTemplate", skillsContent.transform, 20f, FontStyles.Bold, TextAlignmentOptions.Left);
|
||||
axisHeaderTemplate.SetActive(false);
|
||||
|
||||
var skillsView = skillsPanel.AddComponent<SkillsPanelView>();
|
||||
var skillsSo = new SerializedObject(skillsView);
|
||||
skillsSo.FindProperty("contentRoot").objectReferenceValue = skillsContentRt;
|
||||
skillsSo.FindProperty("rowPrefab").objectReferenceValue = skillRowPrefab.GetComponent<SkillRowView>();
|
||||
skillsSo.FindProperty("axisHeaderPrefab").objectReferenceValue = axisHeaderTemplate.GetComponent<TMP_Text>();
|
||||
skillsSo.FindProperty("displayMaxRank").intValue = 6;
|
||||
skillsSo.ApplyModifiedPropertiesWithoutUndo();
|
||||
|
||||
// Journal content
|
||||
var journalView = journalPanel.AddComponent<JournalPanelView>();
|
||||
var journalLabel = CreateTmp("Placeholder", journalPanel.transform, 28f, FontStyles.Italic, TextAlignmentOptions.Center);
|
||||
Stretch(journalLabel.GetComponent<RectTransform>());
|
||||
Object.DestroyImmediate(journalLabel.GetComponent<LayoutElement>());
|
||||
var journalSo = new SerializedObject(journalView);
|
||||
journalSo.FindProperty("placeholderLabel").objectReferenceValue = journalLabel.GetComponent<TMP_Text>();
|
||||
journalSo.ApplyModifiedPropertiesWithoutUndo();
|
||||
|
||||
var charBtn = CreateTabButton("Character", tabBarGo.transform);
|
||||
var skillsBtn = CreateTabButton("Skills", tabBarGo.transform);
|
||||
var journalBtn = CreateTabButton("Journal", tabBarGo.transform);
|
||||
|
||||
var tabBar = tabBarGo.GetComponent<MenuTabBar>();
|
||||
var tabSo = new SerializedObject(tabBar);
|
||||
var tabsProp = tabSo.FindProperty("tabs");
|
||||
tabsProp.arraySize = 3;
|
||||
// Character tab must refresh on show for the same reason as Skills:
|
||||
// panels toggle via CanvasGroup, so OnEnable never fires again.
|
||||
SetTabEntry(tabsProp.GetArrayElementAtIndex(0), charBtn.GetComponent<Button>(), characterPanel.GetComponent<CanvasGroup>(), charView, "Refresh");
|
||||
SetTabEntry(tabsProp.GetArrayElementAtIndex(1), skillsBtn.GetComponent<Button>(), skillsPanel.GetComponent<CanvasGroup>(), skillsView, "Refresh");
|
||||
SetTabEntry(tabsProp.GetArrayElementAtIndex(2), journalBtn.GetComponent<Button>(), journalPanel.GetComponent<CanvasGroup>(), null);
|
||||
tabSo.ApplyModifiedPropertiesWithoutUndo();
|
||||
|
||||
var menuController = canvasGo.AddComponent<PlayerMenuController>();
|
||||
var menuSo = new SerializedObject(menuController);
|
||||
menuSo.FindProperty("menuRoot").objectReferenceValue = canvasGo;
|
||||
menuSo.FindProperty("tabBar").objectReferenceValue = tabBar;
|
||||
menuSo.ApplyModifiedPropertiesWithoutUndo();
|
||||
|
||||
rootGroup.alpha = 0f;
|
||||
rootGroup.interactable = false;
|
||||
rootGroup.blocksRaycasts = false;
|
||||
|
||||
var prefab = PrefabUtility.SaveAsPrefabAsset(canvasGo, MenuCanvasPrefabPath);
|
||||
Object.DestroyImmediate(canvasGo);
|
||||
return prefab;
|
||||
}
|
||||
|
||||
static void SetTabEntry(SerializedProperty entry, Button button, CanvasGroup panel, Object target, string method = null)
|
||||
{
|
||||
entry.FindPropertyRelative("button").objectReferenceValue = button;
|
||||
entry.FindPropertyRelative("panel").objectReferenceValue = panel;
|
||||
if (target != null && method != null)
|
||||
{
|
||||
var onShown = entry.FindPropertyRelative("onShown");
|
||||
onShown.FindPropertyRelative("m_PersistentCalls.m_Calls").arraySize = 1;
|
||||
var call = onShown.FindPropertyRelative("m_PersistentCalls.m_Calls").GetArrayElementAtIndex(0);
|
||||
call.FindPropertyRelative("m_Target").objectReferenceValue = target;
|
||||
call.FindPropertyRelative("m_MethodName").stringValue = method;
|
||||
call.FindPropertyRelative("m_Mode").enumValueIndex = 1; // Void
|
||||
call.FindPropertyRelative("m_CallState").enumValueIndex = 2; // RuntimeOnly
|
||||
}
|
||||
}
|
||||
|
||||
static void WireScene(GameObject streamCanvasPrefab, GameObject hudCanvasPrefab, GameObject menuCanvasPrefab)
|
||||
{
|
||||
var scene = EditorSceneManager.OpenScene(ScenePath, OpenSceneMode.Single);
|
||||
|
||||
// UI System root
|
||||
var uiSystem = GameObject.Find("UI System");
|
||||
if (uiSystem == null)
|
||||
{
|
||||
uiSystem = new GameObject("UI System");
|
||||
}
|
||||
|
||||
if (uiSystem.GetComponent<UIStateController>() == null)
|
||||
{
|
||||
uiSystem.AddComponent<UIStateController>();
|
||||
}
|
||||
|
||||
// Player components
|
||||
var player = GameObject.Find("PlayerArmature");
|
||||
if (player == null)
|
||||
{
|
||||
Debug.LogError("[UILayerSetup] PlayerArmature not found in scene.");
|
||||
return;
|
||||
}
|
||||
|
||||
var controlLock = player.GetComponent<PlayerControlLock>();
|
||||
if (controlLock == null)
|
||||
{
|
||||
controlLock = player.AddComponent<PlayerControlLock>();
|
||||
}
|
||||
|
||||
var menuInput = player.GetComponent<PlayerMenuInput>();
|
||||
if (menuInput == null)
|
||||
{
|
||||
menuInput = player.AddComponent<PlayerMenuInput>();
|
||||
}
|
||||
|
||||
var inputs = player.GetComponent<StarterAssetsInputs>();
|
||||
if (inputs != null)
|
||||
{
|
||||
inputs.cursorLocked = false;
|
||||
EditorUtility.SetDirty(inputs);
|
||||
}
|
||||
|
||||
// DialogueTransitionController
|
||||
var transition = Object.FindFirstObjectByType<DialogueTransitionController>();
|
||||
if (transition != null)
|
||||
{
|
||||
var tSo = new SerializedObject(transition);
|
||||
tSo.FindProperty("controlLock").objectReferenceValue = controlLock;
|
||||
tSo.ApplyModifiedPropertiesWithoutUndo();
|
||||
}
|
||||
|
||||
// Instantiate / refresh UI prefab instances
|
||||
var streamInstance = EnsurePrefabInstance(streamCanvasPrefab, "DialogueStreamCanvas");
|
||||
var hudInstance = EnsurePrefabInstance(hudCanvasPrefab, "HudCanvas");
|
||||
var menuInstance = EnsurePrefabInstance(menuCanvasPrefab, "PlayerMenuCanvas");
|
||||
|
||||
// Wire menu controller refs
|
||||
var menuController = menuInstance.GetComponent<PlayerMenuController>();
|
||||
if (menuController != null)
|
||||
{
|
||||
var mSo = new SerializedObject(menuController);
|
||||
mSo.FindProperty("menuInput").objectReferenceValue = menuInput;
|
||||
mSo.FindProperty("controlLock").objectReferenceValue = controlLock;
|
||||
mSo.ApplyModifiedPropertiesWithoutUndo();
|
||||
}
|
||||
|
||||
// Portrait → Open menu
|
||||
var portraitButton = hudInstance.GetComponentInChildren<Button>(true);
|
||||
if (portraitButton != null && menuController != null)
|
||||
{
|
||||
while (portraitButton.onClick.GetPersistentEventCount() > 0)
|
||||
{
|
||||
UnityEditor.Events.UnityEventTools.RemovePersistentListener(portraitButton.onClick, 0);
|
||||
}
|
||||
|
||||
UnityEditor.Events.UnityEventTools.AddPersistentListener(portraitButton.onClick, menuController.Open);
|
||||
EditorUtility.SetDirty(portraitButton);
|
||||
}
|
||||
|
||||
// Camera framing
|
||||
var vcam = GameObject.Find("CinemachineCamera");
|
||||
if (vcam != null)
|
||||
{
|
||||
var framing = vcam.GetComponent<DialogueCameraFraming>();
|
||||
if (framing == null)
|
||||
{
|
||||
framing = vcam.AddComponent<DialogueCameraFraming>();
|
||||
}
|
||||
|
||||
var composer = vcam.GetComponent<CinemachinePositionComposer>();
|
||||
var fSo = new SerializedObject(framing);
|
||||
fSo.FindProperty("composer").objectReferenceValue = composer;
|
||||
fSo.FindProperty("dialogueScreenX").floatValue = -0.17f;
|
||||
fSo.ApplyModifiedPropertiesWithoutUndo();
|
||||
}
|
||||
|
||||
// Re-point DialogueRunner presenters
|
||||
var runner = Object.FindFirstObjectByType<DialogueRunner>();
|
||||
var streamPresenter = streamInstance.GetComponent<DialogueStreamPresenter>();
|
||||
var optionsPresenter = streamInstance.GetComponent<DialogueStreamOptionsPresenter>();
|
||||
var lineAdvancer = Object.FindFirstObjectByType<LineAdvancer>();
|
||||
|
||||
if (runner != null && streamPresenter != null && optionsPresenter != null)
|
||||
{
|
||||
var rSo = new SerializedObject(runner);
|
||||
var presenters = rSo.FindProperty("dialoguePresenters");
|
||||
if (presenters != null)
|
||||
{
|
||||
presenters.arraySize = lineAdvancer != null ? 3 : 2;
|
||||
presenters.GetArrayElementAtIndex(0).objectReferenceValue = streamPresenter;
|
||||
presenters.GetArrayElementAtIndex(1).objectReferenceValue = optionsPresenter;
|
||||
if (lineAdvancer != null)
|
||||
{
|
||||
presenters.GetArrayElementAtIndex(2).objectReferenceValue = lineAdvancer;
|
||||
}
|
||||
|
||||
rSo.ApplyModifiedPropertiesWithoutUndo();
|
||||
}
|
||||
}
|
||||
|
||||
if (lineAdvancer != null && streamPresenter != null)
|
||||
{
|
||||
var aSo = new SerializedObject(lineAdvancer);
|
||||
aSo.FindProperty("presenter").objectReferenceValue = streamPresenter;
|
||||
aSo.ApplyModifiedPropertiesWithoutUndo();
|
||||
}
|
||||
|
||||
// Point DialogueTransitionController.dialogueUI at the new canvas
|
||||
if (transition != null && streamInstance != null)
|
||||
{
|
||||
var tSo = new SerializedObject(transition);
|
||||
tSo.FindProperty("dialogueUI").objectReferenceValue = streamInstance;
|
||||
tSo.FindProperty("controlLock").objectReferenceValue = controlLock;
|
||||
tSo.ApplyModifiedPropertiesWithoutUndo();
|
||||
}
|
||||
|
||||
// Deactivate the package Dialogue System Canvas (keep EventSystem alive)
|
||||
var dialogueSystem = GameObject.Find("Dialogue System");
|
||||
if (dialogueSystem != null)
|
||||
{
|
||||
var packageCanvas = dialogueSystem.transform.Find("Canvas");
|
||||
if (packageCanvas != null)
|
||||
{
|
||||
// Rescue the Skill Check Banner onto the scene-owned Skill Check
|
||||
// Canvas — NOT into streamInstance. EnsurePrefabInstance
|
||||
// DestroyImmediates the stream canvas on every re-run, so
|
||||
// anything parented under it dies with it; that is what left
|
||||
// SkillRuntime.resultView null after the second setup run.
|
||||
// Skill Check Canvas is scene-owned, survives re-runs, and its
|
||||
// sortingOrder 100 already draws above the dialogue canvas.
|
||||
var banner = packageCanvas.Find("Skill Check Banner");
|
||||
if (banner != null)
|
||||
{
|
||||
var bannerHost = GameObject.Find("Skill Check Canvas");
|
||||
if (bannerHost != null)
|
||||
{
|
||||
banner.SetParent(bannerHost.transform, false);
|
||||
}
|
||||
}
|
||||
|
||||
packageCanvas.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Repair SkillRuntime.resultView. It can be left dangling by an earlier
|
||||
// run of this menu item, and a null here silently disables every roll
|
||||
// banner ("SkillCheckResultView is not assigned; skipping roll banner").
|
||||
// Prefer an active view if more than one survives.
|
||||
SkillCheckResultView resultView = null;
|
||||
foreach (var candidate in Object.FindObjectsByType<SkillCheckResultView>(
|
||||
FindObjectsInactive.Include, FindObjectsSortMode.None))
|
||||
{
|
||||
if (resultView == null
|
||||
|| (!resultView.gameObject.activeInHierarchy && candidate.gameObject.activeInHierarchy))
|
||||
{
|
||||
resultView = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
var skillRuntime = Object.FindFirstObjectByType<SkillRuntime>();
|
||||
if (skillRuntime != null && resultView != null)
|
||||
{
|
||||
var skillSo = new SerializedObject(skillRuntime);
|
||||
skillSo.FindProperty("resultView").objectReferenceValue = resultView;
|
||||
skillSo.ApplyModifiedPropertiesWithoutUndo();
|
||||
}
|
||||
else if (skillRuntime != null)
|
||||
{
|
||||
Debug.LogWarning("[UILayerSetup] No SkillCheckResultView found in the scene; roll banners will not display.", skillRuntime);
|
||||
}
|
||||
|
||||
// Update NPCStandIn dialogueUI refs to the new canvas
|
||||
foreach (var npc in Object.FindObjectsByType<NPCStandIn>(FindObjectsInactive.Include, FindObjectsSortMode.None))
|
||||
{
|
||||
var so = new SerializedObject(npc);
|
||||
var uiProp = so.FindProperty("dialogueUI");
|
||||
if (uiProp != null)
|
||||
{
|
||||
uiProp.objectReferenceValue = streamInstance;
|
||||
so.ApplyModifiedPropertiesWithoutUndo();
|
||||
}
|
||||
}
|
||||
|
||||
EditorSceneManager.MarkSceneDirty(scene);
|
||||
EditorSceneManager.SaveScene(scene);
|
||||
}
|
||||
|
||||
static GameObject EnsurePrefabInstance(GameObject prefab, string name)
|
||||
{
|
||||
var existing = GameObject.Find(name);
|
||||
if (existing != null)
|
||||
{
|
||||
Object.DestroyImmediate(existing);
|
||||
}
|
||||
|
||||
var instance = (GameObject)PrefabUtility.InstantiatePrefab(prefab);
|
||||
instance.name = name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
static GameObject CreateCanvas(string name, int sortingOrder)
|
||||
{
|
||||
var go = new GameObject(name, typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster));
|
||||
var canvas = go.GetComponent<Canvas>();
|
||||
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
||||
canvas.sortingOrder = sortingOrder;
|
||||
var scaler = go.GetComponent<CanvasScaler>();
|
||||
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
||||
scaler.referenceResolution = new Vector2(1920f, 1080f);
|
||||
scaler.matchWidthOrHeight = 0.5f;
|
||||
return go;
|
||||
}
|
||||
|
||||
static GameObject CreateTabPanel(string name, Transform parent)
|
||||
{
|
||||
var go = new GameObject(name, typeof(RectTransform), typeof(CanvasGroup));
|
||||
go.transform.SetParent(parent, false);
|
||||
Stretch(go.GetComponent<RectTransform>());
|
||||
var group = go.GetComponent<CanvasGroup>();
|
||||
group.alpha = 0f;
|
||||
group.interactable = false;
|
||||
group.blocksRaycasts = false;
|
||||
return go;
|
||||
}
|
||||
|
||||
static GameObject CreateTabButton(string label, Transform parent)
|
||||
{
|
||||
var go = new GameObject(label, typeof(RectTransform), typeof(Image), typeof(Button), typeof(LayoutElement));
|
||||
go.transform.SetParent(parent, false);
|
||||
go.GetComponent<Image>().color = new Color(0.2f, 0.2f, 0.25f, 1f);
|
||||
var text = CreateTmp("Label", go.transform, 22f, FontStyles.Bold, TextAlignmentOptions.Center);
|
||||
Stretch(text.GetComponent<RectTransform>());
|
||||
Object.DestroyImmediate(text.GetComponent<LayoutElement>());
|
||||
text.GetComponent<TMP_Text>().text = label;
|
||||
return go;
|
||||
}
|
||||
|
||||
static GameObject CreateTmp(string name, Transform parent, float size, FontStyles style, TextAlignmentOptions align)
|
||||
{
|
||||
var go = new GameObject(name, typeof(RectTransform), typeof(TextMeshProUGUI), typeof(LayoutElement));
|
||||
go.transform.SetParent(parent, false);
|
||||
var tmp = go.GetComponent<TextMeshProUGUI>();
|
||||
tmp.fontSize = size;
|
||||
tmp.fontStyle = style;
|
||||
tmp.alignment = align;
|
||||
tmp.color = Color.white;
|
||||
tmp.text = name;
|
||||
tmp.textWrappingMode = TextWrappingModes.Normal;
|
||||
|
||||
// Deliberately no LayoutElement.preferredHeight. A fixed height here is
|
||||
// one line's worth, and LayoutElement outranks TMP's own ILayoutElement
|
||||
// report — so a wrapped 4-line body would be allocated ~40px while
|
||||
// rendering ~150px, and consecutive rows draw on top of each other.
|
||||
// Leaving it at the -1 default lets TMP measure the wrapped text.
|
||||
// The component stays so callers can still set preferredWidth /
|
||||
// flexibleWidth (see the skill row).
|
||||
return go;
|
||||
}
|
||||
|
||||
static GameObject CreateUiImage(string name, Transform parent, Color color)
|
||||
{
|
||||
var go = new GameObject(name, typeof(RectTransform), typeof(Image), typeof(LayoutElement));
|
||||
go.transform.SetParent(parent, false);
|
||||
go.GetComponent<Image>().color = color;
|
||||
return go;
|
||||
}
|
||||
|
||||
static void Stretch(RectTransform rt, float pad = 0f)
|
||||
{
|
||||
rt.anchorMin = Vector2.zero;
|
||||
rt.anchorMax = Vector2.one;
|
||||
rt.offsetMin = new Vector2(pad, pad);
|
||||
rt.offsetMax = new Vector2(-pad, -pad);
|
||||
}
|
||||
|
||||
static void ConfigureVerticalContent(GameObject go)
|
||||
{
|
||||
var rt = go.GetComponent<RectTransform>();
|
||||
rt.anchorMin = new Vector2(0f, 0f);
|
||||
rt.anchorMax = new Vector2(1f, 0f);
|
||||
rt.pivot = new Vector2(0.5f, 0f);
|
||||
|
||||
// A RectTransform added via `new GameObject(..., typeof(RectTransform))`
|
||||
// starts at sizeDelta (100,100). With horizontal-stretch anchors that
|
||||
// makes the rect parentWidth + 100 wide, centred — overhanging 50px off
|
||||
// each side, and the RectMask2D shears the left edge off every label.
|
||||
// ContentSizeFitter only governs the vertical axis, so it never
|
||||
// corrects this. Zero the horizontal size explicitly.
|
||||
rt.sizeDelta = new Vector2(0f, rt.sizeDelta.y);
|
||||
rt.anchoredPosition = Vector2.zero;
|
||||
|
||||
var vlg = go.GetComponent<VerticalLayoutGroup>();
|
||||
vlg.childControlHeight = true;
|
||||
vlg.childControlWidth = true;
|
||||
vlg.childForceExpandHeight = false;
|
||||
vlg.childForceExpandWidth = true;
|
||||
vlg.spacing = 4f;
|
||||
var csf = go.GetComponent<ContentSizeFitter>();
|
||||
csf.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5bdf044f1ab04312b76895dac1740b37
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,48 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Reminds the user to run the one-shot prefab/scene wiring menu after scripts
|
||||
/// compile. Runtime play works without it via UILayerBootstrap.
|
||||
/// </summary>
|
||||
[InitializeOnLoad]
|
||||
static class UILayerSetupAutoRun
|
||||
{
|
||||
const string PrefKey = "NightclubArcadia.UILayerSetup.Reminded";
|
||||
const string MarkerPrefab = "Assets/Prefabs/UI/PlayerMenuCanvas.prefab";
|
||||
|
||||
static UILayerSetupAutoRun()
|
||||
{
|
||||
EditorApplication.delayCall += RemindIfNeeded;
|
||||
}
|
||||
|
||||
static void RemindIfNeeded()
|
||||
{
|
||||
if (EditorApplication.isPlayingOrWillChangePlaymode || EditorApplication.isCompiling)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (AssetDatabase.LoadAssetAtPath<GameObject>(MarkerPrefab) != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (EditorPrefs.GetBool(PrefKey, false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EditorPrefs.SetBool(PrefKey, true);
|
||||
Debug.Log(
|
||||
"[UILayer] Prefabs not built yet. Play Mode works via UILayerBootstrap. " +
|
||||
"To author prefabs into the scene, run Tools → Nightclub Arcadia → Set Up UI Layer.");
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Nightclub Arcadia/Reset UI Layer Auto-Setup Flag")]
|
||||
static void ResetFlag()
|
||||
{
|
||||
EditorPrefs.DeleteKey(PrefKey);
|
||||
Debug.Log("[UILayer] Reminder flag cleared.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c0e3c886ff143d3b26b7f55d7f22410
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,80 @@
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using Yarn.Compiler;
|
||||
using Yarn.Unity;
|
||||
using Yarn.Unity.Editor;
|
||||
|
||||
public static class YarnDemoSetup
|
||||
{
|
||||
const string YarnProjectPath = "Assets/Dialogue/NightclubArcadia.yarnproject";
|
||||
const string ScenePath = "Assets/Scenes/DialogueTest.unity";
|
||||
|
||||
[MenuItem("Tools/Nightclub Arcadia/Set Up Yarn Dialogue Test")]
|
||||
public static void SetUpYarnDialogueTest()
|
||||
{
|
||||
if (!AssetDatabase.IsValidFolder("Assets/TextMesh Pro"))
|
||||
{
|
||||
Debug.LogWarning("TextMesh Pro essential resources are missing. Run Window → TextMeshPro → Import TMP Essential Resources.");
|
||||
}
|
||||
|
||||
if (!AssetDatabase.IsValidFolder("Assets/Dialogue"))
|
||||
{
|
||||
AssetDatabase.CreateFolder("Assets", "Dialogue");
|
||||
}
|
||||
|
||||
if (!AssetDatabase.IsValidFolder("Assets/Scenes"))
|
||||
{
|
||||
AssetDatabase.CreateFolder("Assets", "Scenes");
|
||||
}
|
||||
|
||||
var yarnProject = AssetDatabase.LoadAssetAtPath<YarnProject>(YarnProjectPath);
|
||||
if (yarnProject == null)
|
||||
{
|
||||
// YarnProjectUtility.CreateDefaultYarnProject is internal, so
|
||||
// construct the same default project here.
|
||||
var project = new Project
|
||||
{
|
||||
ExcludeFilePatterns = new[] { "**/*~/*", "./Samples/Yarn Spinner*/*" },
|
||||
};
|
||||
YarnEditorUtility.CreateYarnProject(YarnProjectPath, project);
|
||||
AssetDatabase.Refresh();
|
||||
yarnProject = AssetDatabase.LoadAssetAtPath<YarnProject>(YarnProjectPath);
|
||||
}
|
||||
|
||||
var scene = EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects, NewSceneMode.Single);
|
||||
|
||||
var runner = CreateDialogueRunnerMenuItem.CreateDialogueRunner();
|
||||
|
||||
var so = new SerializedObject(runner);
|
||||
so.FindProperty("yarnProject").objectReferenceValue = yarnProject;
|
||||
so.ApplyModifiedPropertiesWithoutUndo();
|
||||
|
||||
runner.autoStart = true;
|
||||
runner.startNode = "Start";
|
||||
EditorUtility.SetDirty(runner);
|
||||
|
||||
#if UNITY_2023_1_OR_NEWER
|
||||
var eventSystems = Object.FindObjectsByType<EventSystem>(FindObjectsInactive.Include);
|
||||
#else
|
||||
var eventSystems = Object.FindObjectsOfType<EventSystem>(true);
|
||||
#endif
|
||||
foreach (var eventSystem in eventSystems)
|
||||
{
|
||||
if (eventSystem.GetComponent<BaseInputModule>() != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
eventSystem.gameObject.AddComponent<UnityEngine.InputSystem.UI.InputSystemUIInputModule>();
|
||||
#endif
|
||||
}
|
||||
|
||||
EditorSceneManager.SaveScene(scene, ScenePath);
|
||||
AssetDatabase.SaveAssets();
|
||||
|
||||
Debug.Log($"Yarn dialogue test setup complete. Scene saved to {ScenePath}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6d9af60546b8c47c892a7b924fd4c91e
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fe41419ba50842189534933e2b64e958
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 11e75200bdad4ff48299a7d24bb8df99
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,413 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &3506879458838080580
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1649868454372858534}
|
||||
- component: {fileID: 5353601244371665078}
|
||||
- component: {fileID: 3249155410833833935}
|
||||
- component: {fileID: 3956878563407369997}
|
||||
m_Layer: 0
|
||||
m_Name: DialogueMessageRow
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &1649868454372858534
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3506879458838080580}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 5063401541721017946}
|
||||
- {fileID: 7857316982467238224}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 80}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &5353601244371665078
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3506879458838080580}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: a7773ea9a6f44798aa98356903037d16, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::NightclubArcadia.UI.Dialogue.DialogueMessageRow
|
||||
speakerText: {fileID: 5900251945930220314}
|
||||
bodyText: {fileID: 1670956017877202442}
|
||||
speakerLabel: {fileID: 5822252844234204748}
|
||||
--- !u!114 &3249155410833833935
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3506879458838080580}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.VerticalLayoutGroup
|
||||
m_Padding:
|
||||
m_Left: 12
|
||||
m_Right: 12
|
||||
m_Top: 8
|
||||
m_Bottom: 8
|
||||
m_ChildAlignment: 0
|
||||
m_Spacing: 4
|
||||
m_ChildForceExpandWidth: 1
|
||||
m_ChildForceExpandHeight: 0
|
||||
m_ChildControlWidth: 1
|
||||
m_ChildControlHeight: 1
|
||||
m_ChildScaleWidth: 0
|
||||
m_ChildScaleHeight: 0
|
||||
m_ReverseArrangement: 0
|
||||
--- !u!114 &3956878563407369997
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3506879458838080580}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.ContentSizeFitter
|
||||
m_HorizontalFit: 0
|
||||
m_VerticalFit: 2
|
||||
--- !u!1 &5450003319614969337
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 7857316982467238224}
|
||||
- component: {fileID: 3605186933310922639}
|
||||
- component: {fileID: 1670956017877202442}
|
||||
- component: {fileID: 6572944391823112727}
|
||||
m_Layer: 0
|
||||
m_Name: Body
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &7857316982467238224
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5450003319614969337}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 1649868454372858534}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 200, y: 50}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &3605186933310922639
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5450003319614969337}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &1670956017877202442
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5450003319614969337}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text: Body
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_fontSharedMaterials: []
|
||||
m_fontMaterial: {fileID: 0}
|
||||
m_fontMaterials: []
|
||||
m_fontColor32:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_enableVertexGradient: 0
|
||||
m_colorMode: 3
|
||||
m_fontColorGradient:
|
||||
topLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
topRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_fontColorGradientPreset: {fileID: 0}
|
||||
m_spriteAsset: {fileID: 0}
|
||||
m_tintAllSprites: 0
|
||||
m_StyleSheet: {fileID: 0}
|
||||
m_TextStyleHashCode: 0
|
||||
m_overrideHtmlColors: 0
|
||||
m_faceColor:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontSize: 28
|
||||
m_fontSizeBase: 28
|
||||
m_fontWeight: 400
|
||||
m_enableAutoSizing: 0
|
||||
m_fontSizeMin: 18
|
||||
m_fontSizeMax: 72
|
||||
m_fontStyle: 0
|
||||
m_HorizontalAlignment: 1
|
||||
m_VerticalAlignment: 256
|
||||
m_textAlignment: 65535
|
||||
m_characterSpacing: 0
|
||||
m_characterHorizontalScale: 1
|
||||
m_wordSpacing: 0
|
||||
m_lineSpacing: 0
|
||||
m_lineSpacingMax: 0
|
||||
m_paragraphSpacing: 0
|
||||
m_charWidthMaxAdj: 0
|
||||
m_TextWrappingMode: 1
|
||||
m_wordWrappingRatios: 0.4
|
||||
m_overflowMode: 0
|
||||
m_linkedTextComponent: {fileID: 0}
|
||||
parentLinkedComponent: {fileID: 0}
|
||||
m_enableKerning: 0
|
||||
m_ActiveFontFeatures: 6e72656b
|
||||
m_enableExtraPadding: 0
|
||||
checkPaddingRequired: 0
|
||||
m_isRichText: 1
|
||||
m_EmojiFallbackSupport: 1
|
||||
m_parseCtrlCharacters: 1
|
||||
m_isOrthographic: 1
|
||||
m_isCullingEnabled: 0
|
||||
m_horizontalMapping: 0
|
||||
m_verticalMapping: 0
|
||||
m_uvLineOffset: 0
|
||||
m_geometrySortingOrder: 0
|
||||
m_IsTextObjectScaleStatic: 0
|
||||
m_VertexBufferAutoSizeReduction: 0
|
||||
m_useMaxVisibleDescender: 1
|
||||
m_pageToDisplay: 1
|
||||
m_margin: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_isUsingLegacyAnimationComponent: 0
|
||||
m_isVolumetricText: 0
|
||||
m_hasFontAssetChanged: 0
|
||||
m_baseMaterial: {fileID: 0}
|
||||
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
|
||||
--- !u!114 &6572944391823112727
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5450003319614969337}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.LayoutElement
|
||||
m_IgnoreLayout: 0
|
||||
m_MinWidth: -1
|
||||
m_MinHeight: -1
|
||||
m_PreferredWidth: -1
|
||||
m_PreferredHeight: -1
|
||||
m_FlexibleWidth: -1
|
||||
m_FlexibleHeight: -1
|
||||
m_LayoutPriority: 1
|
||||
--- !u!1 &5822252844234204748
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 5063401541721017946}
|
||||
- component: {fileID: 4569120956328637162}
|
||||
- component: {fileID: 5900251945930220314}
|
||||
- component: {fileID: 3733932380812113541}
|
||||
m_Layer: 0
|
||||
m_Name: Speaker
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &5063401541721017946
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5822252844234204748}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 1649868454372858534}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 200, y: 50}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &4569120956328637162
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5822252844234204748}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &5900251945930220314
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5822252844234204748}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text: Speaker
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_fontSharedMaterials: []
|
||||
m_fontMaterial: {fileID: 0}
|
||||
m_fontMaterials: []
|
||||
m_fontColor32:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_enableVertexGradient: 0
|
||||
m_colorMode: 3
|
||||
m_fontColorGradient:
|
||||
topLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
topRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_fontColorGradientPreset: {fileID: 0}
|
||||
m_spriteAsset: {fileID: 0}
|
||||
m_tintAllSprites: 0
|
||||
m_StyleSheet: {fileID: 0}
|
||||
m_TextStyleHashCode: 0
|
||||
m_overrideHtmlColors: 0
|
||||
m_faceColor:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontSize: 22
|
||||
m_fontSizeBase: 22
|
||||
m_fontWeight: 400
|
||||
m_enableAutoSizing: 0
|
||||
m_fontSizeMin: 18
|
||||
m_fontSizeMax: 72
|
||||
m_fontStyle: 1
|
||||
m_HorizontalAlignment: 1
|
||||
m_VerticalAlignment: 512
|
||||
m_textAlignment: 65535
|
||||
m_characterSpacing: 0
|
||||
m_characterHorizontalScale: 1
|
||||
m_wordSpacing: 0
|
||||
m_lineSpacing: 0
|
||||
m_lineSpacingMax: 0
|
||||
m_paragraphSpacing: 0
|
||||
m_charWidthMaxAdj: 0
|
||||
m_TextWrappingMode: 1
|
||||
m_wordWrappingRatios: 0.4
|
||||
m_overflowMode: 0
|
||||
m_linkedTextComponent: {fileID: 0}
|
||||
parentLinkedComponent: {fileID: 0}
|
||||
m_enableKerning: 0
|
||||
m_ActiveFontFeatures: 6e72656b
|
||||
m_enableExtraPadding: 0
|
||||
checkPaddingRequired: 0
|
||||
m_isRichText: 1
|
||||
m_EmojiFallbackSupport: 1
|
||||
m_parseCtrlCharacters: 1
|
||||
m_isOrthographic: 1
|
||||
m_isCullingEnabled: 0
|
||||
m_horizontalMapping: 0
|
||||
m_verticalMapping: 0
|
||||
m_uvLineOffset: 0
|
||||
m_geometrySortingOrder: 0
|
||||
m_IsTextObjectScaleStatic: 0
|
||||
m_VertexBufferAutoSizeReduction: 0
|
||||
m_useMaxVisibleDescender: 1
|
||||
m_pageToDisplay: 1
|
||||
m_margin: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_isUsingLegacyAnimationComponent: 0
|
||||
m_isVolumetricText: 0
|
||||
m_hasFontAssetChanged: 0
|
||||
m_baseMaterial: {fileID: 0}
|
||||
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
|
||||
--- !u!114 &3733932380812113541
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5822252844234204748}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.LayoutElement
|
||||
m_IgnoreLayout: 0
|
||||
m_MinWidth: -1
|
||||
m_MinHeight: -1
|
||||
m_PreferredWidth: -1
|
||||
m_PreferredHeight: -1
|
||||
m_FlexibleWidth: -1
|
||||
m_FlexibleHeight: -1
|
||||
m_LayoutPriority: 1
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90818ef7dc74247ceb348994755cea86
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,673 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &1431520567503728820
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4838388606347767229}
|
||||
- component: {fileID: 848167695161913818}
|
||||
- component: {fileID: 872887529246740654}
|
||||
m_Layer: 0
|
||||
m_Name: Messages
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &4838388606347767229
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1431520567503728820}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 6371610150170887506}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 100}
|
||||
m_Pivot: {x: 0.5, y: 0}
|
||||
--- !u!114 &848167695161913818
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1431520567503728820}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.VerticalLayoutGroup
|
||||
m_Padding:
|
||||
m_Left: 0
|
||||
m_Right: 0
|
||||
m_Top: 0
|
||||
m_Bottom: 0
|
||||
m_ChildAlignment: 0
|
||||
m_Spacing: 4
|
||||
m_ChildForceExpandWidth: 1
|
||||
m_ChildForceExpandHeight: 0
|
||||
m_ChildControlWidth: 1
|
||||
m_ChildControlHeight: 1
|
||||
m_ChildScaleWidth: 0
|
||||
m_ChildScaleHeight: 0
|
||||
m_ReverseArrangement: 0
|
||||
--- !u!114 &872887529246740654
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1431520567503728820}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.ContentSizeFitter
|
||||
m_HorizontalFit: 0
|
||||
m_VerticalFit: 2
|
||||
--- !u!1 &2888832924926796905
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 8553742246756266605}
|
||||
- component: {fileID: 7475958128581596541}
|
||||
- component: {fileID: 5788080976967704390}
|
||||
m_Layer: 0
|
||||
m_Name: Panel
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &8553742246756266605
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2888832924926796905}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 6287368187237088396}
|
||||
m_Father: {fileID: 1243836425688988300}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.66, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &7475958128581596541
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2888832924926796905}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &5788080976967704390
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2888832924926796905}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0.05, g: 0.05, b: 0.08, a: 0.92}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 0}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!1 &4923635399097880277
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 3746324764683222304}
|
||||
- component: {fileID: 3035847077530824546}
|
||||
- component: {fileID: 5149667566061008557}
|
||||
- component: {fileID: 2566128074157561357}
|
||||
m_Layer: 0
|
||||
m_Name: Viewport
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &3746324764683222304
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4923635399097880277}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 6371610150170887506}
|
||||
m_Father: {fileID: 6287368187237088396}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &3035847077530824546
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4923635399097880277}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 3312d7739989d2b4e91e6319e9a96d76, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.RectMask2D
|
||||
m_Padding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Softness: {x: 0, y: 0}
|
||||
--- !u!222 &5149667566061008557
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4923635399097880277}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &2566128074157561357
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4923635399097880277}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 0.01}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 0}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!1 &5260492858234310193
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6371610150170887506}
|
||||
- component: {fileID: 2397794206583674815}
|
||||
- component: {fileID: 5419335647702174254}
|
||||
m_Layer: 0
|
||||
m_Name: Content
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &6371610150170887506
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5260492858234310193}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 4838388606347767229}
|
||||
- {fileID: 6329947636030823459}
|
||||
m_Father: {fileID: 3746324764683222304}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0}
|
||||
--- !u!114 &2397794206583674815
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5260492858234310193}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.VerticalLayoutGroup
|
||||
m_Padding:
|
||||
m_Left: 4
|
||||
m_Right: 4
|
||||
m_Top: 8
|
||||
m_Bottom: 8
|
||||
m_ChildAlignment: 7
|
||||
m_Spacing: 6
|
||||
m_ChildForceExpandWidth: 1
|
||||
m_ChildForceExpandHeight: 0
|
||||
m_ChildControlWidth: 1
|
||||
m_ChildControlHeight: 1
|
||||
m_ChildScaleWidth: 0
|
||||
m_ChildScaleHeight: 0
|
||||
m_ReverseArrangement: 0
|
||||
--- !u!114 &5419335647702174254
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5260492858234310193}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.ContentSizeFitter
|
||||
m_HorizontalFit: 0
|
||||
m_VerticalFit: 2
|
||||
--- !u!1 &7249108092969100431
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6329947636030823459}
|
||||
- component: {fileID: 5824694681547497274}
|
||||
- component: {fileID: 6817391513969849154}
|
||||
- component: {fileID: 5117926241503925785}
|
||||
m_Layer: 0
|
||||
m_Name: Options
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &6329947636030823459
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7249108092969100431}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 6371610150170887506}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 100}
|
||||
m_Pivot: {x: 0.5, y: 0}
|
||||
--- !u!114 &5824694681547497274
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7249108092969100431}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.VerticalLayoutGroup
|
||||
m_Padding:
|
||||
m_Left: 0
|
||||
m_Right: 0
|
||||
m_Top: 0
|
||||
m_Bottom: 0
|
||||
m_ChildAlignment: 0
|
||||
m_Spacing: 4
|
||||
m_ChildForceExpandWidth: 1
|
||||
m_ChildForceExpandHeight: 0
|
||||
m_ChildControlWidth: 1
|
||||
m_ChildControlHeight: 1
|
||||
m_ChildScaleWidth: 0
|
||||
m_ChildScaleHeight: 0
|
||||
m_ReverseArrangement: 0
|
||||
--- !u!114 &6817391513969849154
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7249108092969100431}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.ContentSizeFitter
|
||||
m_HorizontalFit: 0
|
||||
m_VerticalFit: 2
|
||||
--- !u!225 &5117926241503925785
|
||||
CanvasGroup:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7249108092969100431}
|
||||
m_Enabled: 1
|
||||
m_Alpha: 0
|
||||
m_Interactable: 0
|
||||
m_BlocksRaycasts: 0
|
||||
m_IgnoreParentGroups: 0
|
||||
--- !u!1 &8691714907102969075
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1243836425688988300}
|
||||
- component: {fileID: 1592513625297910240}
|
||||
- component: {fileID: 7055069541643637941}
|
||||
- component: {fileID: 4390775391658295920}
|
||||
- component: {fileID: 3476584232491665726}
|
||||
- component: {fileID: 6097567599041115085}
|
||||
- component: {fileID: 307489608854241188}
|
||||
m_Layer: 0
|
||||
m_Name: DialogueStreamCanvas
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &1243836425688988300
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8691714907102969075}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 0, y: 0, z: 0}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 8553742246756266605}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0, y: 0}
|
||||
--- !u!223 &1592513625297910240
|
||||
Canvas:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8691714907102969075}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_RenderMode: 0
|
||||
m_Camera: {fileID: 0}
|
||||
m_PlaneDistance: 100
|
||||
m_PixelPerfect: 0
|
||||
m_ReceivesEvents: 1
|
||||
m_OverrideSorting: 0
|
||||
m_OverridePixelPerfect: 0
|
||||
m_SortingBucketNormalizedSize: 0
|
||||
m_VertexColorAlwaysGammaSpace: 0
|
||||
m_UseReflectionProbes: 0
|
||||
m_AdditionalShaderChannelsFlag: 0
|
||||
m_UpdateRectTransformForStandalone: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingOrder: 5
|
||||
m_TargetDisplay: 0
|
||||
--- !u!114 &7055069541643637941
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8691714907102969075}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.CanvasScaler
|
||||
m_UiScaleMode: 1
|
||||
m_ReferencePixelsPerUnit: 100
|
||||
m_ScaleFactor: 1
|
||||
m_ReferenceResolution: {x: 1920, y: 1080}
|
||||
m_ScreenMatchMode: 0
|
||||
m_MatchWidthOrHeight: 0.5
|
||||
m_PhysicalUnit: 3
|
||||
m_FallbackScreenDPI: 96
|
||||
m_DefaultSpriteDPI: 96
|
||||
m_DynamicPixelsPerUnit: 1
|
||||
m_PresetInfoIsWorld: 0
|
||||
--- !u!114 &4390775391658295920
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8691714907102969075}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.GraphicRaycaster
|
||||
m_IgnoreReversedGraphics: 1
|
||||
m_BlockingObjects: 0
|
||||
m_BlockingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
--- !u!225 &3476584232491665726
|
||||
CanvasGroup:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8691714907102969075}
|
||||
m_Enabled: 1
|
||||
m_Alpha: 0
|
||||
m_Interactable: 0
|
||||
m_BlocksRaycasts: 0
|
||||
m_IgnoreParentGroups: 0
|
||||
--- !u!114 &6097567599041115085
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8691714907102969075}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 88cb6495795a4e13ac7fb72343c132af, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::NightclubArcadia.UI.Dialogue.DialogueStreamPresenter
|
||||
messageRowPrefab: {fileID: 5353601244371665078, guid: 90818ef7dc74247ceb348994755cea86, type: 3}
|
||||
messageContainer: {fileID: 4838388606347767229}
|
||||
scrollRect: {fileID: 7105587415705135446}
|
||||
maxRows: 100
|
||||
lettersPerSecond: 60
|
||||
--- !u!114 &307489608854241188
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8691714907102969075}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8d86cc99d5b545c4a3fad9e1482031a2, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::NightclubArcadia.UI.Dialogue.DialogueStreamOptionsPresenter
|
||||
optionViewPrefab: {fileID: 2576261519635394347, guid: e605972137b0b4c1399ffb07beae9a9b, type: 3}
|
||||
optionsContainer: {fileID: 6329947636030823459}
|
||||
canvasGroup: {fileID: 5117926241503925785}
|
||||
showUnavailableOptions: 0
|
||||
--- !u!1 &8898733438161318853
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6287368187237088396}
|
||||
- component: {fileID: 7105587415705135446}
|
||||
- component: {fileID: 6289533314517078144}
|
||||
- component: {fileID: 7217060756955950799}
|
||||
m_Layer: 0
|
||||
m_Name: Scroll
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &6287368187237088396
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8898733438161318853}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 3746324764683222304}
|
||||
m_Father: {fileID: 8553742246756266605}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: -16, y: -16}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &7105587415705135446
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8898733438161318853}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 1aa08ab6e0800fa44ae55d278d1423e3, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.ScrollRect
|
||||
m_Content: {fileID: 6371610150170887506}
|
||||
m_Horizontal: 0
|
||||
m_Vertical: 1
|
||||
m_MovementType: 2
|
||||
m_Elasticity: 0.1
|
||||
m_Inertia: 1
|
||||
m_DecelerationRate: 0.135
|
||||
m_ScrollSensitivity: 1
|
||||
m_Viewport: {fileID: 3746324764683222304}
|
||||
m_HorizontalScrollbar: {fileID: 0}
|
||||
m_VerticalScrollbar: {fileID: 0}
|
||||
m_HorizontalScrollbarVisibility: 0
|
||||
m_VerticalScrollbarVisibility: 0
|
||||
m_HorizontalScrollbarSpacing: 0
|
||||
m_VerticalScrollbarSpacing: 0
|
||||
m_OnValueChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
--- !u!222 &6289533314517078144
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8898733438161318853}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &7217060756955950799
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8898733438161318853}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0, g: 0, b: 0, a: 0.01}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 0}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 55fc979a64b174ce796ffa39b13b7259
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,390 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &3845905829654890729
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4691680407920056117}
|
||||
- component: {fileID: 9194898352646057999}
|
||||
- component: {fileID: 7450534218736675587}
|
||||
m_Layer: 0
|
||||
m_Name: TabHint
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &4691680407920056117
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3845905829654890729}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 252585930844827291}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 4}
|
||||
m_SizeDelta: {x: 0, y: 28}
|
||||
m_Pivot: {x: 0.5, y: 0}
|
||||
--- !u!222 &9194898352646057999
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3845905829654890729}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &7450534218736675587
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3845905829654890729}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text: Tab
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_fontSharedMaterials: []
|
||||
m_fontMaterial: {fileID: 0}
|
||||
m_fontMaterials: []
|
||||
m_fontColor32:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_enableVertexGradient: 0
|
||||
m_colorMode: 3
|
||||
m_fontColorGradient:
|
||||
topLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
topRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_fontColorGradientPreset: {fileID: 0}
|
||||
m_spriteAsset: {fileID: 0}
|
||||
m_tintAllSprites: 0
|
||||
m_StyleSheet: {fileID: 0}
|
||||
m_TextStyleHashCode: 0
|
||||
m_overrideHtmlColors: 0
|
||||
m_faceColor:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontSize: 18
|
||||
m_fontSizeBase: 18
|
||||
m_fontWeight: 400
|
||||
m_enableAutoSizing: 0
|
||||
m_fontSizeMin: 18
|
||||
m_fontSizeMax: 72
|
||||
m_fontStyle: 0
|
||||
m_HorizontalAlignment: 2
|
||||
m_VerticalAlignment: 512
|
||||
m_textAlignment: 65535
|
||||
m_characterSpacing: 0
|
||||
m_characterHorizontalScale: 1
|
||||
m_wordSpacing: 0
|
||||
m_lineSpacing: 0
|
||||
m_lineSpacingMax: 0
|
||||
m_paragraphSpacing: 0
|
||||
m_charWidthMaxAdj: 0
|
||||
m_TextWrappingMode: 1
|
||||
m_wordWrappingRatios: 0.4
|
||||
m_overflowMode: 0
|
||||
m_linkedTextComponent: {fileID: 0}
|
||||
parentLinkedComponent: {fileID: 0}
|
||||
m_enableKerning: 0
|
||||
m_ActiveFontFeatures: 6e72656b
|
||||
m_enableExtraPadding: 0
|
||||
checkPaddingRequired: 0
|
||||
m_isRichText: 1
|
||||
m_EmojiFallbackSupport: 1
|
||||
m_parseCtrlCharacters: 1
|
||||
m_isOrthographic: 1
|
||||
m_isCullingEnabled: 0
|
||||
m_horizontalMapping: 0
|
||||
m_verticalMapping: 0
|
||||
m_uvLineOffset: 0
|
||||
m_geometrySortingOrder: 0
|
||||
m_IsTextObjectScaleStatic: 0
|
||||
m_VertexBufferAutoSizeReduction: 0
|
||||
m_useMaxVisibleDescender: 1
|
||||
m_pageToDisplay: 1
|
||||
m_margin: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_isUsingLegacyAnimationComponent: 0
|
||||
m_isVolumetricText: 0
|
||||
m_hasFontAssetChanged: 0
|
||||
m_baseMaterial: {fileID: 0}
|
||||
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
|
||||
--- !u!1 &4914891251226459434
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 8125896019114993279}
|
||||
- component: {fileID: 637496887729740374}
|
||||
- component: {fileID: 7333171482869056901}
|
||||
- component: {fileID: 3439018567020279905}
|
||||
- component: {fileID: 4573334312575796821}
|
||||
m_Layer: 0
|
||||
m_Name: HudCanvas
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &8125896019114993279
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4914891251226459434}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 0, y: 0, z: 0}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 252585930844827291}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0, y: 0}
|
||||
--- !u!223 &637496887729740374
|
||||
Canvas:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4914891251226459434}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_RenderMode: 0
|
||||
m_Camera: {fileID: 0}
|
||||
m_PlaneDistance: 100
|
||||
m_PixelPerfect: 0
|
||||
m_ReceivesEvents: 1
|
||||
m_OverrideSorting: 0
|
||||
m_OverridePixelPerfect: 0
|
||||
m_SortingBucketNormalizedSize: 0
|
||||
m_VertexColorAlwaysGammaSpace: 0
|
||||
m_UseReflectionProbes: 0
|
||||
m_AdditionalShaderChannelsFlag: 0
|
||||
m_UpdateRectTransformForStandalone: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingOrder: 10
|
||||
m_TargetDisplay: 0
|
||||
--- !u!114 &7333171482869056901
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4914891251226459434}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.CanvasScaler
|
||||
m_UiScaleMode: 1
|
||||
m_ReferencePixelsPerUnit: 100
|
||||
m_ScaleFactor: 1
|
||||
m_ReferenceResolution: {x: 1920, y: 1080}
|
||||
m_ScreenMatchMode: 0
|
||||
m_MatchWidthOrHeight: 0.5
|
||||
m_PhysicalUnit: 3
|
||||
m_FallbackScreenDPI: 96
|
||||
m_DefaultSpriteDPI: 96
|
||||
m_DynamicPixelsPerUnit: 1
|
||||
m_PresetInfoIsWorld: 0
|
||||
--- !u!114 &3439018567020279905
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4914891251226459434}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.GraphicRaycaster
|
||||
m_IgnoreReversedGraphics: 1
|
||||
m_BlockingObjects: 0
|
||||
m_BlockingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
--- !u!114 &4573334312575796821
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4914891251226459434}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fd1354ade14b4b1d80da9acac4aa9e12, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::NightclubArcadia.UI.HUD.PortraitHudView
|
||||
panelRoot: {fileID: 4989543578856257477}
|
||||
--- !u!1 &4989543578856257477
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 252585930844827291}
|
||||
- component: {fileID: 7861001760273437131}
|
||||
- component: {fileID: 1348039723167098503}
|
||||
- component: {fileID: 4045319608296230342}
|
||||
- component: {fileID: 1473442362196520338}
|
||||
m_Layer: 0
|
||||
m_Name: PortraitPanel
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &252585930844827291
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4989543578856257477}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 4691680407920056117}
|
||||
m_Father: {fileID: 8125896019114993279}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 1, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 0}
|
||||
m_AnchoredPosition: {x: -24, y: 24}
|
||||
m_SizeDelta: {x: 220, y: 280}
|
||||
m_Pivot: {x: 1, y: 0}
|
||||
--- !u!225 &7861001760273437131
|
||||
CanvasGroup:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4989543578856257477}
|
||||
m_Enabled: 1
|
||||
m_Alpha: 1
|
||||
m_Interactable: 1
|
||||
m_BlocksRaycasts: 1
|
||||
m_IgnoreParentGroups: 0
|
||||
--- !u!222 &1348039723167098503
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4989543578856257477}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &4045319608296230342
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4989543578856257477}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0.15, g: 0.15, b: 0.18, a: 0.95}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 21300000, guid: a1b2c3d4e5f60718293a4b5c6d7e8f90, type: 3}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 1
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!114 &1473442362196520338
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4989543578856257477}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Button
|
||||
m_Navigation:
|
||||
m_Mode: 3
|
||||
m_WrapAround: 0
|
||||
m_SelectOnUp: {fileID: 0}
|
||||
m_SelectOnDown: {fileID: 0}
|
||||
m_SelectOnLeft: {fileID: 0}
|
||||
m_SelectOnRight: {fileID: 0}
|
||||
m_Transition: 1
|
||||
m_Colors:
|
||||
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
|
||||
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
|
||||
m_ColorMultiplier: 1
|
||||
m_FadeDuration: 0.1
|
||||
m_SpriteState:
|
||||
m_HighlightedSprite: {fileID: 0}
|
||||
m_PressedSprite: {fileID: 0}
|
||||
m_SelectedSprite: {fileID: 0}
|
||||
m_DisabledSprite: {fileID: 0}
|
||||
m_AnimationTriggers:
|
||||
m_NormalTrigger: Normal
|
||||
m_HighlightedTrigger: Highlighted
|
||||
m_PressedTrigger: Pressed
|
||||
m_SelectedTrigger: Selected
|
||||
m_DisabledTrigger: Disabled
|
||||
m_Interactable: 1
|
||||
m_TargetGraphic: {fileID: 4045319608296230342}
|
||||
m_OnClick:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 792e1d4c4cde942e8a71947301a3868c
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb4c52aaa2c9e41aabc4bb11fa6372ba
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,669 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &738428826500747930
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 7033684844244958404}
|
||||
- component: {fileID: 8854647563054178895}
|
||||
- component: {fileID: 7773152306665733045}
|
||||
- component: {fileID: 3269936702826226914}
|
||||
m_Layer: 0
|
||||
m_Name: Accent
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &7033684844244958404
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 738428826500747930}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 6014094232834212140}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 100, y: 100}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &8854647563054178895
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 738428826500747930}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &7773152306665733045
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 738428826500747930}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 0}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!114 &3269936702826226914
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 738428826500747930}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.LayoutElement
|
||||
m_IgnoreLayout: 0
|
||||
m_MinWidth: -1
|
||||
m_MinHeight: -1
|
||||
m_PreferredWidth: 6
|
||||
m_PreferredHeight: -1
|
||||
m_FlexibleWidth: 0
|
||||
m_FlexibleHeight: -1
|
||||
m_LayoutPriority: 1
|
||||
--- !u!1 &1043270585848180102
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6266953780690568889}
|
||||
- component: {fileID: 6662307575930959716}
|
||||
- component: {fileID: 874797289038649292}
|
||||
- component: {fileID: 3005905926608676038}
|
||||
m_Layer: 0
|
||||
m_Name: Bar
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &6266953780690568889
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1043270585848180102}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 437950331768363985}
|
||||
m_Father: {fileID: 6014094232834212140}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 100, y: 100}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &6662307575930959716
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1043270585848180102}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &874797289038649292
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1043270585848180102}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0.2, g: 0.2, b: 0.22, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 0}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!114 &3005905926608676038
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1043270585848180102}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.LayoutElement
|
||||
m_IgnoreLayout: 0
|
||||
m_MinWidth: -1
|
||||
m_MinHeight: -1
|
||||
m_PreferredWidth: 160
|
||||
m_PreferredHeight: 16
|
||||
m_FlexibleWidth: 0
|
||||
m_FlexibleHeight: -1
|
||||
m_LayoutPriority: 1
|
||||
--- !u!1 &1311471292218943158
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4484942689062487776}
|
||||
- component: {fileID: 285052103366591294}
|
||||
- component: {fileID: 6986784551111800944}
|
||||
- component: {fileID: 4453305505185908855}
|
||||
m_Layer: 0
|
||||
m_Name: Rank
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &4484942689062487776
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1311471292218943158}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 6014094232834212140}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 200, y: 50}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &285052103366591294
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1311471292218943158}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &6986784551111800944
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1311471292218943158}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text: Rank
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_fontSharedMaterials: []
|
||||
m_fontMaterial: {fileID: 0}
|
||||
m_fontMaterials: []
|
||||
m_fontColor32:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_enableVertexGradient: 0
|
||||
m_colorMode: 3
|
||||
m_fontColorGradient:
|
||||
topLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
topRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_fontColorGradientPreset: {fileID: 0}
|
||||
m_spriteAsset: {fileID: 0}
|
||||
m_tintAllSprites: 0
|
||||
m_StyleSheet: {fileID: 0}
|
||||
m_TextStyleHashCode: 0
|
||||
m_overrideHtmlColors: 0
|
||||
m_faceColor:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontSize: 24
|
||||
m_fontSizeBase: 24
|
||||
m_fontWeight: 400
|
||||
m_enableAutoSizing: 0
|
||||
m_fontSizeMin: 18
|
||||
m_fontSizeMax: 72
|
||||
m_fontStyle: 0
|
||||
m_HorizontalAlignment: 4
|
||||
m_VerticalAlignment: 512
|
||||
m_textAlignment: 65535
|
||||
m_characterSpacing: 0
|
||||
m_characterHorizontalScale: 1
|
||||
m_wordSpacing: 0
|
||||
m_lineSpacing: 0
|
||||
m_lineSpacingMax: 0
|
||||
m_paragraphSpacing: 0
|
||||
m_charWidthMaxAdj: 0
|
||||
m_TextWrappingMode: 1
|
||||
m_wordWrappingRatios: 0.4
|
||||
m_overflowMode: 0
|
||||
m_linkedTextComponent: {fileID: 0}
|
||||
parentLinkedComponent: {fileID: 0}
|
||||
m_enableKerning: 0
|
||||
m_ActiveFontFeatures: 6e72656b
|
||||
m_enableExtraPadding: 0
|
||||
checkPaddingRequired: 0
|
||||
m_isRichText: 1
|
||||
m_EmojiFallbackSupport: 1
|
||||
m_parseCtrlCharacters: 1
|
||||
m_isOrthographic: 1
|
||||
m_isCullingEnabled: 0
|
||||
m_horizontalMapping: 0
|
||||
m_verticalMapping: 0
|
||||
m_uvLineOffset: 0
|
||||
m_geometrySortingOrder: 0
|
||||
m_IsTextObjectScaleStatic: 0
|
||||
m_VertexBufferAutoSizeReduction: 0
|
||||
m_useMaxVisibleDescender: 1
|
||||
m_pageToDisplay: 1
|
||||
m_margin: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_isUsingLegacyAnimationComponent: 0
|
||||
m_isVolumetricText: 0
|
||||
m_hasFontAssetChanged: 0
|
||||
m_baseMaterial: {fileID: 0}
|
||||
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
|
||||
--- !u!114 &4453305505185908855
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1311471292218943158}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.LayoutElement
|
||||
m_IgnoreLayout: 0
|
||||
m_MinWidth: -1
|
||||
m_MinHeight: -1
|
||||
m_PreferredWidth: 40
|
||||
m_PreferredHeight: -1
|
||||
m_FlexibleWidth: 0
|
||||
m_FlexibleHeight: -1
|
||||
m_LayoutPriority: 1
|
||||
--- !u!1 &3239427835033733195
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4775921688302885094}
|
||||
- component: {fileID: 3498508026980942921}
|
||||
- component: {fileID: 4172720891822695416}
|
||||
- component: {fileID: 6787294827931527212}
|
||||
m_Layer: 0
|
||||
m_Name: Name
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &4775921688302885094
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3239427835033733195}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 6014094232834212140}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 200, y: 50}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &3498508026980942921
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3239427835033733195}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &4172720891822695416
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3239427835033733195}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text: Name
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_fontSharedMaterials: []
|
||||
m_fontMaterial: {fileID: 0}
|
||||
m_fontMaterials: []
|
||||
m_fontColor32:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_enableVertexGradient: 0
|
||||
m_colorMode: 3
|
||||
m_fontColorGradient:
|
||||
topLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
topRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_fontColorGradientPreset: {fileID: 0}
|
||||
m_spriteAsset: {fileID: 0}
|
||||
m_tintAllSprites: 0
|
||||
m_StyleSheet: {fileID: 0}
|
||||
m_TextStyleHashCode: 0
|
||||
m_overrideHtmlColors: 0
|
||||
m_faceColor:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontSize: 24
|
||||
m_fontSizeBase: 24
|
||||
m_fontWeight: 400
|
||||
m_enableAutoSizing: 0
|
||||
m_fontSizeMin: 18
|
||||
m_fontSizeMax: 72
|
||||
m_fontStyle: 1
|
||||
m_HorizontalAlignment: 1
|
||||
m_VerticalAlignment: 512
|
||||
m_textAlignment: 65535
|
||||
m_characterSpacing: 0
|
||||
m_characterHorizontalScale: 1
|
||||
m_wordSpacing: 0
|
||||
m_lineSpacing: 0
|
||||
m_lineSpacingMax: 0
|
||||
m_paragraphSpacing: 0
|
||||
m_charWidthMaxAdj: 0
|
||||
m_TextWrappingMode: 1
|
||||
m_wordWrappingRatios: 0.4
|
||||
m_overflowMode: 0
|
||||
m_linkedTextComponent: {fileID: 0}
|
||||
parentLinkedComponent: {fileID: 0}
|
||||
m_enableKerning: 0
|
||||
m_ActiveFontFeatures: 6e72656b
|
||||
m_enableExtraPadding: 0
|
||||
checkPaddingRequired: 0
|
||||
m_isRichText: 1
|
||||
m_EmojiFallbackSupport: 1
|
||||
m_parseCtrlCharacters: 1
|
||||
m_isOrthographic: 1
|
||||
m_isCullingEnabled: 0
|
||||
m_horizontalMapping: 0
|
||||
m_verticalMapping: 0
|
||||
m_uvLineOffset: 0
|
||||
m_geometrySortingOrder: 0
|
||||
m_IsTextObjectScaleStatic: 0
|
||||
m_VertexBufferAutoSizeReduction: 0
|
||||
m_useMaxVisibleDescender: 1
|
||||
m_pageToDisplay: 1
|
||||
m_margin: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_isUsingLegacyAnimationComponent: 0
|
||||
m_isVolumetricText: 0
|
||||
m_hasFontAssetChanged: 0
|
||||
m_baseMaterial: {fileID: 0}
|
||||
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
|
||||
--- !u!114 &6787294827931527212
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3239427835033733195}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.LayoutElement
|
||||
m_IgnoreLayout: 0
|
||||
m_MinWidth: -1
|
||||
m_MinHeight: -1
|
||||
m_PreferredWidth: -1
|
||||
m_PreferredHeight: -1
|
||||
m_FlexibleWidth: 1
|
||||
m_FlexibleHeight: -1
|
||||
m_LayoutPriority: 1
|
||||
--- !u!1 &3768236759226886124
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 437950331768363985}
|
||||
- component: {fileID: 5606763019278455248}
|
||||
- component: {fileID: 1019288287942172810}
|
||||
m_Layer: 0
|
||||
m_Name: Fill
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &437950331768363985
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3768236759226886124}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 6266953780690568889}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &5606763019278455248
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3768236759226886124}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &1019288287942172810
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3768236759226886124}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 0}
|
||||
m_Type: 3
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 0
|
||||
m_FillAmount: 0.2
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!1 &7442810364304304402
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6014094232834212140}
|
||||
- component: {fileID: 1141587833647839303}
|
||||
- component: {fileID: 8736413170534072599}
|
||||
m_Layer: 0
|
||||
m_Name: SkillRow
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &6014094232834212140
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7442810364304304402}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 7033684844244958404}
|
||||
- {fileID: 4775921688302885094}
|
||||
- {fileID: 6266953780690568889}
|
||||
- {fileID: 4484942689062487776}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 48}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &1141587833647839303
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7442810364304304402}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 6ccda37aee7e42b09a0628f8a5729060, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: NightclubArcadia.Skills::NightclubArcadia.Skills.SkillRowView
|
||||
nameLabel: {fileID: 4172720891822695416}
|
||||
rankLabel: {fileID: 6986784551111800944}
|
||||
barFill: {fileID: 1019288287942172810}
|
||||
accentStripe: {fileID: 7773152306665733045}
|
||||
--- !u!114 &8736413170534072599
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7442810364304304402}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 30649d3a9faa99c48a7b1166b86bf2a0, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.HorizontalLayoutGroup
|
||||
m_Padding:
|
||||
m_Left: 8
|
||||
m_Right: 8
|
||||
m_Top: 4
|
||||
m_Bottom: 4
|
||||
m_ChildAlignment: 3
|
||||
m_Spacing: 12
|
||||
m_ChildForceExpandWidth: 0
|
||||
m_ChildForceExpandHeight: 1
|
||||
m_ChildControlWidth: 1
|
||||
m_ChildControlHeight: 1
|
||||
m_ChildScaleWidth: 0
|
||||
m_ChildScaleHeight: 0
|
||||
m_ReverseArrangement: 0
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e14f68d6a57ea4945bd93c44202db910
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67a0ad6ebebd640a59b91b85221b30df
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 13f14dfeaec742b38567bb2e4c1ba440
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1647a101742545fe8af6187e3a743858
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,81 @@
|
||||
using Unity.Cinemachine;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NightclubArcadia.Cinematics
|
||||
{
|
||||
/// <summary>
|
||||
/// Raises a reveal camera priority while the player is inside the volume.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(BoxCollider))]
|
||||
public sealed class CameraFramingVolume : MonoBehaviour
|
||||
{
|
||||
[SerializeField] CinemachineCamera revealCamera;
|
||||
[SerializeField] int activePriority = 20;
|
||||
[SerializeField] float minDwell = 0.5f;
|
||||
|
||||
CameraStateDirector stateDirector;
|
||||
int defaultPriority;
|
||||
int occupants;
|
||||
float exitTimer;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
var box = GetComponent<BoxCollider>();
|
||||
box.isTrigger = true;
|
||||
|
||||
if (revealCamera != null)
|
||||
{
|
||||
defaultPriority = revealCamera.Priority.Value;
|
||||
}
|
||||
|
||||
stateDirector = FindFirstObjectByType<CameraStateDirector>();
|
||||
}
|
||||
|
||||
void OnTriggerEnter(Collider other)
|
||||
{
|
||||
if (!other.CompareTag("Player") || revealCamera == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
occupants++;
|
||||
exitTimer = 0f;
|
||||
revealCamera.Priority.Value = activePriority;
|
||||
stateDirector?.RegisterReveal();
|
||||
}
|
||||
|
||||
void OnTriggerExit(Collider other)
|
||||
{
|
||||
if (!other.CompareTag("Player") || revealCamera == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
occupants = Mathf.Max(0, occupants - 1);
|
||||
if (occupants == 0)
|
||||
{
|
||||
exitTimer = minDwell;
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (occupants > 0 || revealCamera == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (exitTimer > 0f)
|
||||
{
|
||||
exitTimer -= Time.deltaTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if (revealCamera.Priority.Value != defaultPriority)
|
||||
{
|
||||
revealCamera.Priority.Value = defaultPriority;
|
||||
stateDirector?.UnregisterReveal();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7cccc93b78eb84019b4524d65305612a
|
||||
@@ -0,0 +1,162 @@
|
||||
using NightclubArcadia.Player;
|
||||
using NightclubArcadia.Player.Math;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NightclubArcadia.Cinematics
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts mouse drag-pan into CameraRigAnchor manual offset with recenter rules.
|
||||
/// </summary>
|
||||
public sealed class CameraPanInput : MonoBehaviour
|
||||
{
|
||||
[SerializeField] Camera brainCamera;
|
||||
[SerializeField] CameraRigAnchor anchor;
|
||||
[SerializeField] CameraStateDirector stateDirector;
|
||||
[SerializeField] PlayerPointerInput pointerInput;
|
||||
[SerializeField] float panMetresPerPixel = 0.02f;
|
||||
[SerializeField] float maxPanDistance = 12f;
|
||||
[SerializeField] float recenterDelay = 2.5f;
|
||||
[SerializeField] float recenterSmoothTime = 0.8f;
|
||||
[SerializeField] float movementRecenterDelay = 0.4f;
|
||||
|
||||
float releaseTimer = float.MaxValue;
|
||||
Vector3 recenterVelocity;
|
||||
Vector3 camRight;
|
||||
Vector3 camForward;
|
||||
bool basisInitialized;
|
||||
|
||||
public bool IsManualPanActive => pointerInput != null && pointerInput.IsPanning || releaseTimer < recenterDelay;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (anchor == null)
|
||||
{
|
||||
anchor = GetComponent<CameraRigAnchor>();
|
||||
}
|
||||
|
||||
if (stateDirector == null)
|
||||
{
|
||||
stateDirector = GetComponent<CameraStateDirector>();
|
||||
}
|
||||
|
||||
if (brainCamera == null)
|
||||
{
|
||||
brainCamera = Camera.main;
|
||||
}
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (pointerInput == null)
|
||||
{
|
||||
var player = GameObject.FindGameObjectWithTag("Player");
|
||||
if (player != null)
|
||||
{
|
||||
pointerInput = player.GetComponent<PlayerPointerInput>();
|
||||
}
|
||||
}
|
||||
|
||||
if (pointerInput != null)
|
||||
{
|
||||
pointerInput.PanDelta += OnPanDelta;
|
||||
pointerInput.PanEnded += OnPanEnded;
|
||||
}
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (pointerInput != null)
|
||||
{
|
||||
pointerInput.PanDelta -= OnPanDelta;
|
||||
pointerInput.PanEnded -= OnPanEnded;
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (anchor == null || stateDirector == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (stateDirector.AllowsManualPan && pointerInput != null && pointerInput.IsPanning)
|
||||
{
|
||||
releaseTimer = 0f;
|
||||
}
|
||||
else if (releaseTimer < recenterDelay)
|
||||
{
|
||||
releaseTimer += Time.deltaTime;
|
||||
}
|
||||
|
||||
if (ShouldShortenRecenter())
|
||||
{
|
||||
releaseTimer = Mathf.Max(releaseTimer, recenterDelay - movementRecenterDelay);
|
||||
}
|
||||
|
||||
if (!stateDirector.AllowsManualPan)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (releaseTimer >= recenterDelay && (pointerInput == null || !pointerInput.IsPanning))
|
||||
{
|
||||
anchor.ManualPanOffset = Vector3.SmoothDamp(
|
||||
anchor.ManualPanOffset,
|
||||
Vector3.zero,
|
||||
ref recenterVelocity,
|
||||
recenterSmoothTime);
|
||||
}
|
||||
|
||||
anchor.ClampManualPan(maxPanDistance);
|
||||
}
|
||||
|
||||
void OnPanDelta(Vector2 delta)
|
||||
{
|
||||
if (stateDirector == null || !stateDirector.AllowsManualPan || anchor == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureCameraBasis();
|
||||
anchor.Nudge(MotionInputMath.ScreenDeltaToWorldPan(delta, camRight, camForward, panMetresPerPixel));
|
||||
anchor.ClampManualPan(maxPanDistance);
|
||||
}
|
||||
|
||||
void OnPanEnded()
|
||||
{
|
||||
releaseTimer = 0f;
|
||||
}
|
||||
|
||||
bool ShouldShortenRecenter()
|
||||
{
|
||||
var player = GameObject.FindGameObjectWithTag("Player");
|
||||
if (player == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var arbiter = player.GetComponent<PlayerMotionArbiter>();
|
||||
return arbiter != null && arbiter.ActiveSource != MotionSource.None;
|
||||
}
|
||||
|
||||
void EnsureCameraBasis()
|
||||
{
|
||||
if (brainCamera == null)
|
||||
{
|
||||
brainCamera = Camera.main;
|
||||
}
|
||||
|
||||
if (brainCamera == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!basisInitialized)
|
||||
{
|
||||
camRight = brainCamera.transform.right;
|
||||
camForward = brainCamera.transform.forward;
|
||||
basisInitialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9b06ade88f2643a3b9f52a80e06081d
|
||||
@@ -0,0 +1,31 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace NightclubArcadia.Cinematics
|
||||
{
|
||||
/// <summary>
|
||||
/// Marker on NPCs and props that the POI director may bias toward.
|
||||
/// </summary>
|
||||
public sealed class CameraPointOfInterest : MonoBehaviour
|
||||
{
|
||||
[SerializeField] float weight = 1f;
|
||||
[SerializeField] float triggerRadius = 8f;
|
||||
[SerializeField] float maxBias = 2.5f;
|
||||
[SerializeField] float extraDistance = 1.5f;
|
||||
|
||||
public float Weight => weight;
|
||||
public float TriggerRadius => triggerRadius;
|
||||
public float MaxBias => maxBias;
|
||||
public float ExtraDistance => extraDistance;
|
||||
public Transform Target => transform;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
CameraPointOfInterestDirector.Register(this);
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
CameraPointOfInterestDirector.Unregister(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7862f80331f384d28b27600075971a77
|
||||
@@ -0,0 +1,161 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NightclubArcadia.Cinematics
|
||||
{
|
||||
/// <summary>
|
||||
/// Scores registered POIs and writes bias onto the camera anchor.
|
||||
/// </summary>
|
||||
public sealed class CameraPointOfInterestDirector : MonoBehaviour
|
||||
{
|
||||
static readonly List<CameraPointOfInterest> Registered = new List<CameraPointOfInterest>();
|
||||
|
||||
[SerializeField] CameraRigAnchor anchor;
|
||||
[SerializeField] CameraStateDirector stateDirector;
|
||||
[SerializeField] float poiSmoothTime = 1f;
|
||||
[SerializeField] float poiReleaseTime = 1.4f;
|
||||
[SerializeField] float maxHeadingAngle = 100f;
|
||||
|
||||
Vector3 poiVelocity;
|
||||
float distanceVelocity;
|
||||
CameraPointOfInterest activePoi;
|
||||
|
||||
public bool HasActivePoi => activePoi != null;
|
||||
|
||||
public static void Register(CameraPointOfInterest poi)
|
||||
{
|
||||
if (poi != null && !Registered.Contains(poi))
|
||||
{
|
||||
Registered.Add(poi);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Unregister(CameraPointOfInterest poi)
|
||||
{
|
||||
Registered.Remove(poi);
|
||||
}
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (anchor == null)
|
||||
{
|
||||
anchor = GetComponent<CameraRigAnchor>();
|
||||
}
|
||||
|
||||
if (stateDirector == null)
|
||||
{
|
||||
stateDirector = GetComponent<CameraStateDirector>();
|
||||
}
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
if (anchor == null || stateDirector == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var follow = anchor.FollowTarget;
|
||||
if (follow == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!stateDirector.AllowsPointOfInterest)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (stateDirector.Current == CameraState.ManualPan)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
activePoi = FindBestPoi(follow);
|
||||
if (activePoi == null)
|
||||
{
|
||||
ReleasePoi();
|
||||
return;
|
||||
}
|
||||
|
||||
var toPoi = activePoi.Target.position - follow.position;
|
||||
toPoi.y = 0f;
|
||||
var targetBias = Vector3.ClampMagnitude(toPoi, activePoi.MaxBias);
|
||||
|
||||
anchor.PoiBias = Vector3.SmoothDamp(
|
||||
anchor.PoiBias,
|
||||
targetBias,
|
||||
ref poiVelocity,
|
||||
poiSmoothTime);
|
||||
|
||||
anchor.DistanceBias = Mathf.SmoothDamp(
|
||||
anchor.DistanceBias,
|
||||
activePoi.ExtraDistance,
|
||||
ref distanceVelocity,
|
||||
poiSmoothTime);
|
||||
}
|
||||
|
||||
CameraPointOfInterest FindBestPoi(Transform follow)
|
||||
{
|
||||
CameraPointOfInterest best = null;
|
||||
var bestScore = 0f;
|
||||
var forward = follow.forward;
|
||||
|
||||
foreach (var poi in Registered)
|
||||
{
|
||||
if (poi == null || !poi.isActiveAndEnabled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var offset = poi.Target.position - follow.position;
|
||||
offset.y = 0f;
|
||||
var distance = offset.magnitude;
|
||||
if (distance > poi.TriggerRadius || distance <= 0.01f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var angle = Vector3.Angle(forward, offset);
|
||||
if (angle > maxHeadingAngle)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var score = poi.Weight * (1f - distance / poi.TriggerRadius);
|
||||
if (score > bestScore)
|
||||
{
|
||||
bestScore = score;
|
||||
best = poi;
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
void ReleasePoi()
|
||||
{
|
||||
if (anchor == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
anchor.PoiBias = Vector3.SmoothDamp(
|
||||
anchor.PoiBias,
|
||||
Vector3.zero,
|
||||
ref poiVelocity,
|
||||
poiReleaseTime);
|
||||
|
||||
anchor.DistanceBias = Mathf.SmoothDamp(
|
||||
anchor.DistanceBias,
|
||||
0f,
|
||||
ref distanceVelocity,
|
||||
poiReleaseTime);
|
||||
|
||||
if (anchor.PoiBias.sqrMagnitude < 0.0001f && Mathf.Abs(anchor.DistanceBias) < 0.01f)
|
||||
{
|
||||
activePoi = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3343417e1de440f095b73e55c6977b6
|
||||
@@ -0,0 +1,85 @@
|
||||
using Unity.Cinemachine;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NightclubArcadia.Cinematics
|
||||
{
|
||||
/// <summary>
|
||||
/// Proxy transform the follow camera tracks. Combines player follow,
|
||||
/// POI bias, manual pan offset, and distance bias.
|
||||
/// </summary>
|
||||
public sealed class CameraRigAnchor : MonoBehaviour
|
||||
{
|
||||
[SerializeField] Transform followTarget;
|
||||
[SerializeField] CinemachinePositionComposer composer;
|
||||
[SerializeField] float followSmoothTime = 0.12f;
|
||||
[SerializeField] float distanceSmoothTime = 0.8f;
|
||||
|
||||
float defaultCameraDistance = 10f;
|
||||
float distanceVelocity;
|
||||
Vector3 followVelocity;
|
||||
|
||||
public Vector3 PoiBias { get; set; }
|
||||
public Vector3 ManualPanOffset { get; set; }
|
||||
public float DistanceBias { get; set; }
|
||||
|
||||
public Transform FollowTarget => followTarget;
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (composer == null)
|
||||
{
|
||||
composer = FindFirstObjectByType<CinemachinePositionComposer>();
|
||||
}
|
||||
|
||||
if (composer != null)
|
||||
{
|
||||
defaultCameraDistance = composer.CameraDistance;
|
||||
}
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
if (followTarget == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var targetPosition = followTarget.position + PoiBias + ManualPanOffset;
|
||||
transform.position = Vector3.SmoothDamp(
|
||||
transform.position,
|
||||
targetPosition,
|
||||
ref followVelocity,
|
||||
followSmoothTime);
|
||||
|
||||
if (composer != null)
|
||||
{
|
||||
var targetDistance = defaultCameraDistance + DistanceBias;
|
||||
composer.CameraDistance = Mathf.SmoothDamp(
|
||||
composer.CameraDistance,
|
||||
targetDistance,
|
||||
ref distanceVelocity,
|
||||
distanceSmoothTime);
|
||||
}
|
||||
}
|
||||
|
||||
public void Nudge(Vector3 worldDelta)
|
||||
{
|
||||
ManualPanOffset += worldDelta;
|
||||
}
|
||||
|
||||
public void ClampManualPan(float maxDistance)
|
||||
{
|
||||
if (followTarget == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var offset = ManualPanOffset;
|
||||
offset.y = 0f;
|
||||
if (offset.magnitude > maxDistance)
|
||||
{
|
||||
ManualPanOffset = offset.normalized * maxDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16d6717a3703a4bb3a8c3bb0bb0392de
|
||||
@@ -0,0 +1,135 @@
|
||||
using NightclubArcadia.UI;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NightclubArcadia.Cinematics
|
||||
{
|
||||
public enum CameraState
|
||||
{
|
||||
Follow,
|
||||
ManualPan,
|
||||
PointOfInterest,
|
||||
Reveal,
|
||||
Dialogue,
|
||||
Menu
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves which camera subsystem may write each frame.
|
||||
/// </summary>
|
||||
public sealed class CameraStateDirector : MonoBehaviour
|
||||
{
|
||||
[SerializeField] CameraRigAnchor anchor;
|
||||
[SerializeField] float dialogueRecenterTime = 0.5f;
|
||||
|
||||
int activeRevealCount;
|
||||
float dialogueRecenterTimer;
|
||||
bool wasDialogueActive;
|
||||
bool wasMenuOpen;
|
||||
|
||||
public CameraState Current { get; private set; } = CameraState.Follow;
|
||||
|
||||
public bool AllowsManualPan =>
|
||||
Current != CameraState.Dialogue
|
||||
&& Current != CameraState.Menu
|
||||
&& Current != CameraState.Reveal;
|
||||
|
||||
public bool AllowsPointOfInterest =>
|
||||
Current == CameraState.Follow || Current == CameraState.PointOfInterest;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (anchor == null)
|
||||
{
|
||||
anchor = GetComponent<CameraRigAnchor>();
|
||||
}
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (UIStateController.Instance != null)
|
||||
{
|
||||
UIStateController.Instance.StateChanged += OnUiStateChanged;
|
||||
}
|
||||
|
||||
ResolveState(false);
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (UIStateController.Instance != null)
|
||||
{
|
||||
UIStateController.Instance.StateChanged -= OnUiStateChanged;
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
ResolveState(true);
|
||||
|
||||
if (Current == CameraState.Dialogue && anchor != null)
|
||||
{
|
||||
dialogueRecenterTimer += Time.deltaTime;
|
||||
var t = Mathf.Clamp01(dialogueRecenterTimer / dialogueRecenterTime);
|
||||
anchor.ManualPanOffset = Vector3.Lerp(anchor.ManualPanOffset, Vector3.zero, t);
|
||||
}
|
||||
}
|
||||
|
||||
void OnUiStateChanged() => ResolveState(true);
|
||||
|
||||
public void RegisterReveal() => activeRevealCount++;
|
||||
|
||||
public void UnregisterReveal() => activeRevealCount = Mathf.Max(0, activeRevealCount - 1);
|
||||
|
||||
void ResolveState(bool allowManualPanPromotion)
|
||||
{
|
||||
var ui = UIStateController.Instance;
|
||||
var menuOpen = ui != null && ui.IsMenuOpen;
|
||||
var dialogueActive = ui != null && ui.IsDialogueActive;
|
||||
|
||||
if (menuOpen)
|
||||
{
|
||||
Current = CameraState.Menu;
|
||||
}
|
||||
else if (dialogueActive)
|
||||
{
|
||||
if (!wasDialogueActive)
|
||||
{
|
||||
dialogueRecenterTimer = 0f;
|
||||
}
|
||||
|
||||
Current = CameraState.Dialogue;
|
||||
}
|
||||
else if (activeRevealCount > 0)
|
||||
{
|
||||
Current = CameraState.Reveal;
|
||||
}
|
||||
else if (allowManualPanPromotion && IsManualPanActive())
|
||||
{
|
||||
Current = CameraState.ManualPan;
|
||||
}
|
||||
else if (HasActivePointOfInterest())
|
||||
{
|
||||
Current = CameraState.PointOfInterest;
|
||||
}
|
||||
else
|
||||
{
|
||||
Current = CameraState.Follow;
|
||||
}
|
||||
|
||||
wasDialogueActive = dialogueActive;
|
||||
wasMenuOpen = menuOpen;
|
||||
}
|
||||
|
||||
bool IsManualPanActive()
|
||||
{
|
||||
var pan = GetComponent<CameraPanInput>();
|
||||
return pan != null && pan.IsManualPanActive;
|
||||
}
|
||||
|
||||
bool HasActivePointOfInterest()
|
||||
{
|
||||
var director = GetComponent<CameraPointOfInterestDirector>();
|
||||
return director != null && director.HasActivePoi;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ca0160e4d996040238dab18725a2e844
|
||||
@@ -0,0 +1,72 @@
|
||||
using NightclubArcadia.UI;
|
||||
using Unity.Cinemachine;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NightclubArcadia.Cinematics
|
||||
{
|
||||
/// <summary>
|
||||
/// Shifts CinemachinePositionComposer.Composition.ScreenPosition so the
|
||||
/// player is centred in the screen space left of the dialogue panel.
|
||||
/// </summary>
|
||||
public sealed class DialogueCameraFraming : MonoBehaviour
|
||||
{
|
||||
[SerializeField] CinemachinePositionComposer composer;
|
||||
[SerializeField] float dialogueScreenX = -0.17f;
|
||||
[SerializeField] bool suppressDeadZoneXInDialogue = true;
|
||||
|
||||
float defaultDeadZoneX = 0.2f;
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (composer == null)
|
||||
{
|
||||
composer = FindFirstObjectByType<CinemachinePositionComposer>();
|
||||
}
|
||||
|
||||
if (composer != null)
|
||||
{
|
||||
defaultDeadZoneX = composer.Composition.DeadZone.Size.x;
|
||||
}
|
||||
|
||||
if (UIStateController.Instance != null)
|
||||
{
|
||||
UIStateController.Instance.StateChanged += OnStateChanged;
|
||||
}
|
||||
|
||||
ApplyFraming();
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (UIStateController.Instance != null)
|
||||
{
|
||||
UIStateController.Instance.StateChanged -= OnStateChanged;
|
||||
}
|
||||
}
|
||||
|
||||
void OnStateChanged() => ApplyFraming();
|
||||
|
||||
void ApplyFraming()
|
||||
{
|
||||
if (composer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var active = UIStateController.Instance != null && UIStateController.Instance.IsDialogueActive;
|
||||
var target = active ? dialogueScreenX : 0f;
|
||||
|
||||
var composition = composer.Composition;
|
||||
composition.ScreenPosition = new Vector2(target, composition.ScreenPosition.y);
|
||||
|
||||
if (suppressDeadZoneXInDialogue)
|
||||
{
|
||||
var size = composition.DeadZone.Size;
|
||||
size.x = active ? 0f : defaultDeadZoneX;
|
||||
composition.DeadZone.Size = size;
|
||||
}
|
||||
|
||||
composer.Composition = composition;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 56142a5f8795401498df8e01dc74b258
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16d046b9603964894b61827ac87b5020
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,61 @@
|
||||
using Yarn.Unity;
|
||||
|
||||
namespace NightclubArcadia.Dialogue
|
||||
{
|
||||
/// <summary>
|
||||
/// Deterministically turns the player's real name into an unrelated club
|
||||
/// alias. Same input name always yields the same alias (hash-seeded pick
|
||||
/// from two word pools), but the alias carries no resemblance to the
|
||||
/// input — it's a stage name, not a nickname derived from it.
|
||||
///
|
||||
/// Used from Yarn as a smart variable (see $player_alias in Common.yarn)
|
||||
/// or directly: {alias_name($player_name)}
|
||||
/// </summary>
|
||||
public static class AliasNameGenerator
|
||||
{
|
||||
static readonly string[] FirstWords =
|
||||
{
|
||||
"Midnight", "Velvet", "Neon", "Crimson", "Static", "Amber",
|
||||
"Obsidian", "Electric", "Hollow", "Silver", "Violet", "Iron",
|
||||
"Phantom", "Copper", "Glass", "Ashen",
|
||||
};
|
||||
|
||||
static readonly string[] SecondWords =
|
||||
{
|
||||
"Vex", "Wilde", "Sable", "Rook", "Marlowe", "Cross",
|
||||
"Vane", "Thorn", "Rye", "Blaze", "Faye", "Quinn",
|
||||
"Storm", "Reyes", "Lark", "Doyle",
|
||||
};
|
||||
|
||||
[YarnFunction("alias_name")]
|
||||
public static string AliasName(string realName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(realName))
|
||||
{
|
||||
realName = "unknown";
|
||||
}
|
||||
|
||||
var hash = Fnv1aHash(realName);
|
||||
var first = FirstWords[hash % (uint)FirstWords.Length];
|
||||
var second = SecondWords[(hash / (uint)FirstWords.Length) % (uint)SecondWords.Length];
|
||||
return $"{first} {second}";
|
||||
}
|
||||
|
||||
// FNV-1a: simple, stable across platforms and .NET versions (unlike
|
||||
// string.GetHashCode, which is not guaranteed stable run-to-run).
|
||||
static uint Fnv1aHash(string value)
|
||||
{
|
||||
const uint offsetBasis = 2166136261;
|
||||
const uint prime = 16777619;
|
||||
|
||||
var hash = offsetBasis;
|
||||
foreach (var c in value)
|
||||
{
|
||||
hash ^= c;
|
||||
hash *= prime;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee985664120c84b41921806fadeccb34
|
||||
@@ -0,0 +1,69 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using Yarn.Unity;
|
||||
|
||||
namespace NightclubArcadia.Dialogue
|
||||
{
|
||||
/// <summary>
|
||||
/// Trigger-zone interactable: player walks in range, presses E, and this
|
||||
/// starts the given Yarn node. The reverse of DialogueTransitionController's
|
||||
/// <<enter_environment>> — this is the "step into dialogue" half of the loop.
|
||||
/// </summary>
|
||||
public class DialogueInteractable : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private DialogueRunner dialogueRunner;
|
||||
[SerializeField] private GameObject dialogueUI;
|
||||
[SerializeField] private string yarnNodeName = "Bartender_Chat";
|
||||
|
||||
private bool playerInRange;
|
||||
private InputAction interactAction;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
interactAction = new InputAction("Interact");
|
||||
interactAction.AddBinding("<Keyboard>/e");
|
||||
interactAction.AddBinding("<Gamepad>/buttonNorth");
|
||||
interactAction.performed += _ =>
|
||||
{
|
||||
if (playerInRange)
|
||||
{
|
||||
StartConversation();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void OnEnable() => interactAction?.Enable();
|
||||
void OnDisable() => interactAction?.Disable();
|
||||
|
||||
void OnTriggerEnter(Collider other)
|
||||
{
|
||||
if (other.CompareTag("Player"))
|
||||
{
|
||||
playerInRange = true;
|
||||
}
|
||||
}
|
||||
|
||||
void OnTriggerExit(Collider other)
|
||||
{
|
||||
if (other.CompareTag("Player"))
|
||||
{
|
||||
playerInRange = false;
|
||||
}
|
||||
}
|
||||
|
||||
async void StartConversation()
|
||||
{
|
||||
// Player controls are locked by DialogueTransitionController off the
|
||||
// runner's onDialogueStart event — nothing to do for that here.
|
||||
if (DialogueUIVisibility.Show(dialogueUI))
|
||||
{
|
||||
// Only reached if the UI was found deactivated; give TMP a
|
||||
// frame to re-cache its Canvas in OnEnable before any text is
|
||||
// generated.
|
||||
await Awaitable.NextFrameAsync();
|
||||
}
|
||||
|
||||
await dialogueRunner.StartDialogue(yarnNodeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 99afbbd28c8054d59a7a0dec94f5ec8f
|
||||
@@ -0,0 +1,85 @@
|
||||
using NightclubArcadia.UI;
|
||||
using UnityEngine;
|
||||
using Yarn.Unity;
|
||||
|
||||
namespace NightclubArcadia.Dialogue
|
||||
{
|
||||
/// <summary>
|
||||
/// Owns the hand-off between dialogue and the 3D environment: locks the
|
||||
/// player's controls for as long as a conversation is running, and registers
|
||||
/// the <<enter_environment>> Yarn command that hands control back.
|
||||
///
|
||||
/// The lock is driven by the DialogueRunner's own lifecycle events rather
|
||||
/// than by each interactable, so every entry point — auto-started nodes,
|
||||
/// NPCStandIn, DialogueInteractable — gets the same behaviour without
|
||||
/// having to remember to do it. Control locking is delegated to
|
||||
/// <see cref="PlayerControlLock"/> so the player menu can hold a second
|
||||
/// independent lock without re-enabling movement mid-conversation.
|
||||
/// </summary>
|
||||
public class DialogueTransitionController : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private DialogueRunner dialogueRunner;
|
||||
[SerializeField] private GameObject dialogueUI;
|
||||
[SerializeField] private GameObject playerArmature;
|
||||
[SerializeField] private PlayerControlLock controlLock;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (controlLock == null && playerArmature != null)
|
||||
{
|
||||
controlLock = playerArmature.GetComponent<PlayerControlLock>();
|
||||
}
|
||||
|
||||
// Subscribed in Awake rather than Start: the runner auto-starts its
|
||||
// first node from its own Start(), and script execution order
|
||||
// between the two is not guaranteed.
|
||||
dialogueRunner.onDialogueStart?.AddListener(OnDialogueStart);
|
||||
dialogueRunner.onDialogueComplete?.AddListener(OnDialogueComplete);
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (controlLock == null && playerArmature != null)
|
||||
{
|
||||
controlLock = playerArmature.GetComponent<PlayerControlLock>();
|
||||
}
|
||||
|
||||
// Dialogue is in progress as soon as this controller is active
|
||||
// (including auto-started nodes), so the player stays locked out
|
||||
// until <<enter_environment>>. The armature itself stays
|
||||
// active/visible — only its controls are disabled.
|
||||
controlLock?.Acquire(this);
|
||||
|
||||
dialogueRunner.AddCommandHandler("enter_environment", EnterEnvironment);
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
dialogueRunner.onDialogueStart?.RemoveListener(OnDialogueStart);
|
||||
dialogueRunner.onDialogueComplete?.RemoveListener(OnDialogueComplete);
|
||||
}
|
||||
|
||||
void OnDialogueStart()
|
||||
{
|
||||
DialogueUIVisibility.Show(dialogueUI);
|
||||
UIStateController.Instance?.SetDialogueActive(true);
|
||||
controlLock?.Acquire(this);
|
||||
}
|
||||
|
||||
// Safety net: a node that ends without <<enter_environment>> must still
|
||||
// give the player their controls back.
|
||||
void OnDialogueComplete() => EnterEnvironment();
|
||||
|
||||
void EnterEnvironment()
|
||||
{
|
||||
// Hide, don't deactivate: deactivating the Canvas breaks TMP's
|
||||
// cached Canvas reference and makes the next conversation throw.
|
||||
// See DialogueUIVisibility / PanelVisibility.
|
||||
DialogueUIVisibility.Hide(dialogueUI);
|
||||
UIStateController.Instance?.SetDialogueActive(false);
|
||||
// Release is idempotent on a HashSet — safe when both
|
||||
// <<enter_environment>> and onDialogueComplete fire.
|
||||
controlLock?.Release(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc43c99ef338a41ea8f7a18c283fe6df
|
||||
@@ -0,0 +1,28 @@
|
||||
using NightclubArcadia.UI;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NightclubArcadia.Dialogue
|
||||
{
|
||||
/// <summary>
|
||||
/// Shows and hides the dialogue UI *without* deactivating its GameObject.
|
||||
/// Forwards to <see cref="PanelVisibility"/> — kept as a thin wrapper so
|
||||
/// existing call sites (NPCStandIn, DialogueInteractable,
|
||||
/// DialogueTransitionController) continue to compile unchanged.
|
||||
/// </summary>
|
||||
public static class DialogueUIVisibility
|
||||
{
|
||||
/// <summary>
|
||||
/// Makes the dialogue UI visible and interactive. Returns true if the
|
||||
/// GameObject had to be re-activated (legacy scene state), in which
|
||||
/// case the caller must wait one frame before rendering text so that
|
||||
/// TMP's OnEnable can re-cache its Canvas.
|
||||
/// </summary>
|
||||
public static bool Show(GameObject dialogueUI) => PanelVisibility.Show(dialogueUI);
|
||||
|
||||
/// <summary>
|
||||
/// Hides the dialogue UI, leaving the hierarchy active. See
|
||||
/// <see cref="PanelVisibility"/>.
|
||||
/// </summary>
|
||||
public static void Hide(GameObject dialogueUI) => PanelVisibility.Hide(dialogueUI);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user