Shared character defaults, fixed paths and names, one global font
This commit is contained in:
1 parent
4962eac7a8
commit
7dbb4cc154
22 files changed
+434
-567
No files matched your search
+35
-44
@@ -14,13 +14,13 @@ If you'd like a quick primer on how to use the editor, check either of [these](h
|
|||||||
|
|
||||||
The game plays spritesheets, regions, layers, nulls, layer groups, events, trigger sounds, and the position, crop, pivot, scale, rotation, tint, color offset, visibility and easing of keyframes. It doesn't play skew, depth, blur, custom shaders or animation layers. Animation groups (the editor's folders) are fine; the game just looks animations up by name, so keep those unique.
|
The game plays spritesheets, regions, layers, nulls, layer groups, events, trigger sounds, and the position, crop, pivot, scale, rotation, tint, color offset, visibility and easing of keyframes. It doesn't play skew, depth, blur, custom shaders or animation layers. Animation groups (the editor's folders) are fine; the game just looks animations up by name, so keep those unique.
|
||||||
|
|
||||||
Stage-specific character animations are controlled by `animationSuffix` on each stage in `character.json`. The base stage uses no suffix. Later stages append that stage's suffix.
|
Stage-specific character animations end with the stage's name. The first stage uses no ending.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
- stage 1: `Look`
|
- stage 1: `Look`
|
||||||
- stage 2 with `"animationSuffix": "Stage2"`: `LookStage2`
|
- stage 2: `LookStage2`
|
||||||
- stage 5 with `"animationSuffix": "Stage5"`: `LookStage5`
|
- stage 5: `LookStage5`
|
||||||
|
|
||||||
Animation names in the JSON files should still use just the base name like `Look`, `Neutral`, or `Eat`. The game appends the current stage's suffix automatically.
|
Animation names in the JSON files should still use just the base name like `Look`, `Neutral`, or `Eat`. The game appends the current stage's suffix automatically.
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ Know that despite Anm2Ed supporting sounds for animations, don't expect these to
|
|||||||
|
|
||||||
There's two folders inside resources; "characters" and "font".
|
There's two folders inside resources; "characters" and "font".
|
||||||
|
|
||||||
"font" is just a font containing the font used in the selection screen. That's literally it.
|
"font" holds the one font the whole game uses (Mali, under the SIL Open Font License; see its LICENSE.txt).
|
||||||
|
|
||||||
"common" holds files characters share (sounds, particles, the cursor and so on). Whenever a character names a file, the game looks in the character's folder first, then in "common", so a character can use anything there as is, or replace it by having its own file at the same path. This includes the taskbar's icons, `gfx/ui/icons.anm2`: one region per icon, named after its button (`talk`, `mood`, `spawn`, `autofeed`, `home`, `stats`, `catalog`, `settings`, `cheats`, `help`, plus whatever `icon`s the interaction types name). A character with its own `gfx/ui/icons.anm2` gets its own icons.
|
"common" holds files characters share (sounds, particles, the cursor and so on). Whenever a character names a file, the game looks in the character's folder first, then in "common", so a character can use anything there as is, or replace it by having its own file at the same path. This includes the taskbar's icons, `gfx/ui/icons.anm2`: one region per icon, named after its button (`talk`, `mood`, `spawn`, `autofeed`, `home`, `stats`, `catalog`, `settings`, `cheats`, `help`, plus whatever `icon`s the interaction types name). A character with its own `gfx/ui/icons.anm2` gets its own icons.
|
||||||
|
|
||||||
@@ -50,20 +50,34 @@ Wherever sounds are listed, one is picked at random each time. A list can also b
|
|||||||
|
|
||||||
Times are in seconds and chances in percent. The game warns (in its log) about keys it doesn't know, which are usually typos, and files it can't find.
|
Times are in seconds and chances in percent. The game warns (in its log) about keys it doesn't know, which are usually typos, and files it can't find.
|
||||||
|
|
||||||
If you're making your own character, your best bet would probably be just to copy the Snivy character and edit it based on your needs, just as a helpful start.
|
If you're making your own character, your best bet would probably be just to copy the Mini character and edit it based on your needs, just as a helpful start.
|
||||||
|
|
||||||
|
## Fixed files and names
|
||||||
|
Some things aren't set in the data, they're always found at the same place or by the same name:
|
||||||
|
|
||||||
|
| What | Where |
|
||||||
|
|---|---|
|
||||||
|
| The character's anm2 | `gfx/character/character.anm2` |
|
||||||
|
| Select screen render and portrait | `gfx/preview/render.png` and `gfx/preview/portrait.png` (both optional) |
|
||||||
|
| The background | `gfx/areas/area.anm2`, with the world's bounds in its `Bounds` null and where items spawn in its `ItemSpawn` null |
|
||||||
|
| The cursor | `gfx/cursor/cursor.anm2`, with the animations `Default`, `Click`, `Hover` (over an item), `Grab` (an item), `Pan` (with middle mouse), `Zoom` (with the mouse wheel) and `Dispose` (right clicking an item) |
|
||||||
|
| Stage names | Stages are named `Stage1`, `Stage2` and so on. Past the first, the stage's name is added to every animation's name, and each stage's "How are you feeling?" dialogue is the pool of its name. |
|
||||||
|
| Dialogue when over capacity | An interaction plays its pool with `Full` added (e.g. `BellyRubFull`) instead, if there is one. |
|
||||||
|
|
||||||
# character.json
|
# character.json
|
||||||
|
|
||||||
This is the main character file where much of the functionality is stored.
|
This is the main character file where much of the functionality is stored. It's read over `common/character.json`, which holds the defaults (the body's numbers, sounds, particles, the interaction types and so on), so a character only writes what it changes. Objects merge key by key (e.g. `"blink": {"destination": "Head"}` keeps the default `source`, and `"interactTypes": {"Smack": {"scaleAmplitude": 5}}` keeps the rest of Smack), while lists are replaced whole.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"name": "Snivy",
|
"name": "Princess Mini",
|
||||||
"anm2": "gfx/character/snivy.anm2",
|
"color": [0.188, 0.651, 0.424],
|
||||||
"weight": 8.1,
|
"weight": 40,
|
||||||
"stages": [{"weight": 10, "dialoguePool": "Stage2", "animationSuffix": "Stage2"}],
|
"weightMax": 100,
|
||||||
"animations": {"start": "Appear", "idle": "Neutral", "idleFull": "NeutralFull", "stageUp": "StageUp"},
|
"capacity": 1250,
|
||||||
"sounds": {"digest": ["sfx/digest1.ogg"], "gurgle": ["sfx/gurgle1.ogg", "sfx/gurgle2.ogg"]}
|
"stages": [45, 60, 70, 90],
|
||||||
|
"expandAreas": [{"layer": "Torso", "null": "Belly", "scaleMax": 2.5}],
|
||||||
|
"interactAreas": [{"type": "Rub", "null": "Head", "dialoguePool": "HeadRub"}]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -73,14 +87,11 @@ This is the main character file where much of the functionality is stored.
|
|||||||
| `name` | string | Name of the character; will appear in dialogue, stats, etc. Defaults to the folder name. |
|
| `name` | string | Name of the character; will appear in dialogue, stats, etc. Defaults to the folder name. |
|
||||||
| `description` | string | A general description of the character; will show in the Select screen. |
|
| `description` | string | A general description of the character; will show in the Select screen. |
|
||||||
| `credits` | string | Credits text shown in the Select screen's Credits tab. |
|
| `credits` | string | Credits text shown in the Select screen's Credits tab. |
|
||||||
| `render` | path | Texture for the character's "render" (i.e., a typical full-body display), will show in the Select screen. |
|
|
||||||
| `portrait` | path | Texture for the character's "portrait" (i.e., a cropped profile view), will show in the Select screen. |
|
|
||||||
| `anm2` | path | The character's anm2 file; should have all the character's animations. |
|
|
||||||
| `color` | [r, g, b], 0-1 | UI accent color for the character. |
|
| `color` | [r, g, b], 0-1 | UI accent color for the character. |
|
||||||
| `weight` | float, kg | The character's starting weight. |
|
| `weight` | float, kg | The character's starting weight. |
|
||||||
| `weightMax` | float, kg | Maximum weight the character can reach. |
|
| `weightMax` | float, kg | Maximum weight the character can reach. |
|
||||||
| `capacity` | float, kcal | The character's starting capacity. |
|
| `capacity` | float, kcal | The character's starting capacity, and the least it can go down to. |
|
||||||
| `capacityMin`, `capacityMax` | float, kcal | The range capacity stays within. |
|
| `capacityMax` | float, kcal | The most capacity can grow to. |
|
||||||
| `overstuffedMultiplier` | float | The character can eat past capacity up to capacity times this. |
|
| `overstuffedMultiplier` | float | The character can eat past capacity up to capacity times this. |
|
||||||
| `overstuffedDigestBonus` | float | Additional capacity gained on digest when the character was over capacity (scaled by how far over). |
|
| `overstuffedDigestBonus` | float | Additional capacity gained on digest when the character was over capacity (scaled by how far over). |
|
||||||
| `caloriesToKilogram` | float | How many kilograms each calorie digested becomes. |
|
| `caloriesToKilogram` | float | How many kilograms each calorie digested becomes. |
|
||||||
@@ -90,7 +101,6 @@ This is the main character file where much of the functionality is stored.
|
|||||||
| `blinkChance` | float, % | Chance each tick to blink. |
|
| `blinkChance` | float, % | Chance each tick to blink. |
|
||||||
| `gurgleChance` | float, % | Chance each tick of a gurgle (see `sounds`), scaled by how full the character is. |
|
| `gurgleChance` | float, % | Chance each tick of a gurgle (see `sounds`), scaled by how full the character is. |
|
||||||
| `gurgleCapacityMultiplier` | float | Multiplies the gurgle chance at full capacity. Higher capacity = higher gurgle chance. |
|
| `gurgleCapacityMultiplier` | float | Multiplies the gurgle chance at full capacity. Higher capacity = higher gurgle chance. |
|
||||||
| `dialoguePool` | string | The character's base dialogue pool for "How are you feeling?". This is effectively "Stage 1"'s dialogue; each stage should have its own dialogue pool. |
|
|
||||||
|
|
||||||
### alternate
|
### alternate
|
||||||
The alternate spritesheet of the character, if applicable (in Pokemon terms, the "shiny").
|
The alternate spritesheet of the character, if applicable (in Pokemon terms, the "shiny").
|
||||||
@@ -103,14 +113,7 @@ The alternate spritesheet of the character, if applicable (in Pokemon terms, the
|
|||||||
| `chance` | float, % | Chance of rolling the alternate spritesheet when starting a new game. |
|
| `chance` | float, % | Chance of rolling the alternate spritesheet when starting a new game. |
|
||||||
|
|
||||||
### stages
|
### stages
|
||||||
A "stage" represents each visual change of the character as the weight increases. By default, there's always one stage; each entry in this list adds another.
|
A "stage" represents each visual change of the character as the weight increases. There's always a first stage (`Stage1`). This is a list of the weights (in kg) at which each stage after it (`Stage2`, `Stage3`...) is reached.
|
||||||
|
|
||||||
| Key | Type | Meaning |
|
|
||||||
|---|---|---|
|
|
||||||
| `weight` | float, kg | The weight threshold to reach this stage. |
|
|
||||||
| `dialoguePool` | string | The stage's dialogue pool for "How are you feeling?". |
|
|
||||||
| `animationSuffix` | string | Suffix appended to base animation names while this stage is active. Usually `Stage2`, `Stage3`, etc. |
|
|
||||||
| `area` | int | Switches to this area (from `areas.list`) from this stage on. |
|
|
||||||
|
|
||||||
### animations
|
### animations
|
||||||
The character's animations. Know that a lot of character animations are typically easily played through dialogue; this is just for animations that aren't reliant on that system. These are just the base names.
|
The character's animations. Know that a lot of character animations are typically easily played through dialogue; this is just for animations that aren't reliant on that system. These are just the base names.
|
||||||
@@ -149,7 +152,7 @@ The areas which will expand based on how full the character is and their progres
|
|||||||
| `scaleMax` | The scale (in percent) that will be added at maximum. Know that a scale of 100 is the normal scale of an animation. |
|
| `scaleMax` | The scale (in percent) that will be added at maximum. Know that a scale of 100 is the normal scale of an animation. |
|
||||||
|
|
||||||
### interactTypes, interactAreas
|
### interactTypes, interactAreas
|
||||||
`interactTypes` is an object of interaction types by name (each gets a button on the taskbar), giving each type's defaults. `interactAreas` is a list of the concrete areas on a character which can be interacted with; usually for belly rubs, kisses, etc. Each area names its `type` and takes all of that type's settings, changing whichever it gives itself.
|
`interactTypes` is an object of interaction types by name, giving each type's defaults. Rub, Smack, Kiss and Turn Around come from common. A type gets a button on the taskbar only if one of the character's areas uses it. `interactAreas` is a list of the concrete areas on a character which can be interacted with; usually for belly rubs, kisses, etc. Each area names its `type` and takes all of that type's settings, changing whichever it gives itself.
|
||||||
|
|
||||||
| Key | Type | Meaning |
|
| Key | Type | Meaning |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -162,8 +165,7 @@ The areas which will expand based on how full the character is and their progres
|
|||||||
| `cursorActive` | string | The animation the cursor will play when interacting. |
|
| `cursorActive` | string | The animation the cursor will play when interacting. |
|
||||||
| `animation` | string | A character animation to play on interacting, when the area has no dialogue pool. |
|
| `animation` | string | A character animation to play on interacting, when the area has no dialogue pool. |
|
||||||
| `idleOnEnd` | string | The idle animation the character uses after interacting (e.g. turning around). |
|
| `idleOnEnd` | string | The idle animation the character uses after interacting (e.g. turning around). |
|
||||||
| `dialoguePool` | string | The dialogue pool which will be drawn from when interacting. |
|
| `dialoguePool` | string | The dialogue pool which will be drawn from when interacting. Over capacity, the same pool with `Full` added is used, if there is one. |
|
||||||
| `dialoguePoolFull` | string | Dialogue pool used when the character is over capacity. |
|
|
||||||
| `dialogueCooldown` | float, seconds | Time before interacting can start new dialogue again. |
|
| `dialogueCooldown` | float, seconds | Time before interacting can start new dialogue again. |
|
||||||
| `digestionClick` | float, % | Digestion added each click. |
|
| `digestionClick` | float, % | Digestion added each click. |
|
||||||
| `digestionHover` | float, %/second | Digestion added while the cursor moves, held. |
|
| `digestionHover` | float, %/second | Digestion added while the cursor moves, held. |
|
||||||
@@ -179,33 +181,22 @@ The areas which will expand based on how full the character is and their progres
|
|||||||
| `digest` | Plays when the character begins digesting (digestion bar full). |
|
| `digest` | Plays when the character begins digesting (digestion bar full). |
|
||||||
| `gurgle` | Ambient sounds that play randomly based on capacity; see `gurgleChance`. |
|
| `gurgle` | Ambient sounds that play randomly based on capacity; see `gurgleChance`. |
|
||||||
|
|
||||||
### areas
|
### area
|
||||||
"Areas" refers to the backgrounds of the game. Stages can switch between them (see `stages`).
|
How items move in the background (`gfx/areas/area.anm2`): `gravity` (applied to items' velocities per tick), `friction` (applied when items are grounded or hit walls) and `airResistance`.
|
||||||
|
|
||||||
| Key | Type | Meaning |
|
|
||||||
|---|---|---|
|
|
||||||
| `anm2` | path | The anm2 the areas use. |
|
|
||||||
| `boundsNull` | string | The null setting the world's bounds. Defaults to `Bounds`. |
|
|
||||||
| `spawnNull` | string | The null items spawn at random points within. If omitted or missing, `boundsNull` is used. |
|
|
||||||
| `list` | list | The areas. Each can set its own `anm2`, its `gravity` (applied to items' velocities per tick), `friction` (applied when items are grounded or hit walls) and `airResistance`. |
|
|
||||||
|
|
||||||
### cursor
|
### cursor
|
||||||
Determines the cursor appearance and behavior. Without one, the system cursor is used.
|
The cursor's sounds and particles. Its look is `gfx/cursor/cursor.anm2` (see "Fixed files and names").
|
||||||
|
|
||||||
| Key | Meaning |
|
| Key | Meaning |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `anm2` | The anm2 file the cursor will use. |
|
|
||||||
| `animations` | `idle`, `click`, `hover` (over an item), `grab` (an item), `pan` (with middle mouse), `zoom` (with the mouse wheel) and `dispose` (right clicking an item). |
|
|
||||||
| `sounds` | `grab` (grabbing an item), `release` (letting go) and `throw` (letting go while dragging quickly). |
|
| `sounds` | `grab` (grabbing an item), `release` (letting go) and `throw` (letting go while dragging quickly). |
|
||||||
| `particles` | `grab`: particles spawned when grabbing an item. |
|
| `particles` | `grab`: particles spawned when grabbing an item. |
|
||||||
|
|
||||||
### menu
|
### menu
|
||||||
Determines menu and general UI appearance and behavior.
|
The UI's sounds.
|
||||||
|
|
||||||
| Key | Meaning |
|
| Key | Meaning |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `font` | The font the menu will use. |
|
|
||||||
| `rounding` | The rounding of corners the UI will use. |
|
|
||||||
| `sounds` | `open` and `close` (the taskbar or one of its windows), `hover` and `select` (a widget), and `cheats` (after entering a special code to activate cheats). |
|
| `sounds` | `open` and `close` (the taskbar or one of its windows), `hover` and `select` (a widget), and `cheats` (after entering a special code to activate cheats). |
|
||||||
|
|
||||||
### particles
|
### particles
|
||||||
|
|||||||
@@ -2,123 +2,35 @@
|
|||||||
"name": "Braixen",
|
"name": "Braixen",
|
||||||
"description": "A fire-wielding, enigmatic Fox Pokémon. You're her apprentice!",
|
"description": "A fire-wielding, enigmatic Fox Pokémon. You're her apprentice!",
|
||||||
"credits": "Art, Animation, Dialogue: ShweetMagnet Sound Effects: Pokemon, skunkwhoplunks Commissioned by @FatGirIsHMU on Twitter",
|
"credits": "Art, Animation, Dialogue: ShweetMagnet Sound Effects: Pokemon, skunkwhoplunks Commissioned by @FatGirIsHMU on Twitter",
|
||||||
"render": "gfx/preview/render.png",
|
|
||||||
"portrait": "gfx/preview/portrait.png",
|
|
||||||
"anm2": "gfx/character/braixen.anm2",
|
|
||||||
"color": [0.901961, 0.156863, 0.160784],
|
"color": [0.901961, 0.156863, 0.160784],
|
||||||
"weight": 32,
|
"weight": 32,
|
||||||
"weightMax": 250,
|
"weightMax": 250,
|
||||||
"capacity": 1000,
|
"capacity": 1000,
|
||||||
"capacityMin": 1000,
|
"stages": [35, 45, 55, 70, 95, 125, 160, 200, 250],
|
||||||
"capacityMax": 10000,
|
"animations": {"idle": "Idle"},
|
||||||
"overstuffedMultiplier": 1.5,
|
"expandAreas": [{"layer": "Torso", "null": "Belly", "scaleMax": 1.25}],
|
||||||
"overstuffedDigestBonus": 0.2,
|
"interactTypes": {"Smack": {"dialogueCooldown": 5, "digestionClick": 0.25}},
|
||||||
"caloriesToKilogram": 0.001,
|
|
||||||
"digestionRate": 1.5,
|
|
||||||
"digestionRateMax": 4.5,
|
|
||||||
"digestionTime": 2,
|
|
||||||
"blinkChance": 0.5,
|
|
||||||
"gurgleChance": 0.05,
|
|
||||||
"gurgleCapacityMultiplier": 10,
|
|
||||||
"dialoguePool": "Stage1",
|
|
||||||
"stages": [
|
|
||||||
{"weight": 35, "dialoguePool": "Stage2", "animationSuffix": "Stage2"},
|
|
||||||
{"weight": 45, "dialoguePool": "Stage3", "animationSuffix": "Stage3"},
|
|
||||||
{"weight": 55, "dialoguePool": "Stage4", "animationSuffix": "Stage4"},
|
|
||||||
{"weight": 70, "dialoguePool": "Stage5", "animationSuffix": "Stage5"},
|
|
||||||
{"weight": 95, "dialoguePool": "Stage6", "animationSuffix": "Stage6"},
|
|
||||||
{"weight": 125, "dialoguePool": "Stage7", "animationSuffix": "Stage7"},
|
|
||||||
{"weight": 160, "dialoguePool": "Stage8", "animationSuffix": "Stage8"},
|
|
||||||
{"weight": 200, "dialoguePool": "Stage9", "animationSuffix": "Stage9"},
|
|
||||||
{"weight": 250, "dialoguePool": "Stage10", "animationSuffix": "Stage10"}
|
|
||||||
],
|
|
||||||
"animations": {"start": "Appear", "idle": "Idle", "idleFull": "NeutralFull", "stageUp": "StageUp"},
|
|
||||||
"talk": {"source": "Talk", "destination": "Mouth"},
|
|
||||||
"blink": {"source": "Blink", "destination": "Eye"},
|
|
||||||
"eatAreas": [
|
|
||||||
{"null": "Mouth", "animation": "Eat", "event": "Eat"}
|
|
||||||
],
|
|
||||||
"expandAreas": [
|
|
||||||
{"layer": "Torso", "null": "Belly", "scaleMax": 1.25}
|
|
||||||
],
|
|
||||||
"sounds": {"digest": "sfx/digest*.ogg", "gurgle": "sfx/gurgle*.ogg"},
|
|
||||||
"interactTypes": {
|
|
||||||
"Rub": {
|
|
||||||
"icon": "rub",
|
|
||||||
"isHold": true,
|
|
||||||
"cursorHover": "Hover",
|
|
||||||
"cursorActive": "Rub",
|
|
||||||
"sounds": ["sfx/rub.ogg"],
|
|
||||||
"particles": ["Circle"]
|
|
||||||
},
|
|
||||||
"Smack": {
|
|
||||||
"icon": "smack",
|
|
||||||
"cursorHover": "SmackHover",
|
|
||||||
"cursorActive": "Smack",
|
|
||||||
"dialogueCooldown": 5,
|
|
||||||
"digestionClick": 0.25,
|
|
||||||
"scaleAmplitude": 1,
|
|
||||||
"scaleCycles": 3,
|
|
||||||
"sounds": "sfx/smack*.ogg",
|
|
||||||
"particles": ["Circle"]
|
|
||||||
},
|
|
||||||
"Kiss": {
|
|
||||||
"icon": "kiss",
|
|
||||||
"cursorHover": "KissHover",
|
|
||||||
"cursorActive": "Kiss",
|
|
||||||
"sounds": "sfx/kiss*.ogg",
|
|
||||||
"particles": ["Circle"]
|
|
||||||
},
|
|
||||||
"Turn Around": {
|
|
||||||
"icon": "turn",
|
|
||||||
"sounds": ["sfx/turn.ogg"],
|
|
||||||
"particles": ["Circle"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"interactAreas": [
|
"interactAreas": [
|
||||||
{"type": "Rub", "null": "Head", "dialoguePool": "HeadRub", "dialoguePoolFull": "HeadRubFull"},
|
{"type": "Rub", "null": "Head", "dialoguePool": "HeadRub"},
|
||||||
{"type": "Rub", "null": "Belly", "cursorActive": "Grab", "dialoguePool": "BellyRub", "dialoguePoolFull": "BellyRubFull", "digestionHover": 6},
|
{"type": "Rub", "null": "Belly", "cursorActive": "Grab", "dialoguePool": "BellyRub", "digestionHover": 6},
|
||||||
{"type": "Kiss", "null": "Mouth", "dialoguePool": "MouthKiss", "dialoguePoolFull": "MouthKiss"},
|
{"type": "Kiss", "null": "Mouth", "dialoguePool": "MouthKiss"},
|
||||||
{"type": "Kiss", "null": "Belly", "dialoguePool": "BellyKiss", "dialoguePoolFull": "BellyKiss"},
|
{"type": "Kiss", "null": "Belly", "dialoguePool": "BellyKiss"},
|
||||||
{"type": "Smack", "null": "Belly", "layer": "Torso", "dialoguePool": "BellySmack", "dialoguePoolFull": "BellySmack"},
|
{"type": "Smack", "null": "Belly", "layer": "Torso", "dialoguePool": "BellySmack"},
|
||||||
{"type": "Smack", "null": "ButtFront", "layer": "LegFront", "dialoguePool": "ButtSmack"},
|
{"type": "Smack", "null": "ButtFront", "layer": "LegFront", "dialoguePool": "ButtSmack"},
|
||||||
{"type": "Smack", "null": "ButtBack", "layer": "LegBack", "idleOnEnd": "IdleBack", "dialoguePool": "ButtSmack"},
|
{"type": "Smack", "null": "ButtBack", "layer": "LegBack", "idleOnEnd": "IdleBack", "dialoguePool": "ButtSmack"},
|
||||||
{"type": "Turn Around", "null": "FullBody", "animation": "IdleBack", "idleOnEnd": "IdleBack", "cursorHover": "TurnAround"},
|
{
|
||||||
{"type": "Turn Around", "null": "FullBodyBack", "animation": "Idle", "idleOnEnd": "Idle", "cursorHover": "TurnAroundBack"}
|
"type": "Turn Around",
|
||||||
],
|
"null": "FullBody",
|
||||||
"areas": {
|
"animation": "IdleBack",
|
||||||
"anm2": "gfx/areas/area.anm2",
|
"idleOnEnd": "IdleBack",
|
||||||
"boundsNull": "Bounds",
|
"cursorHover": "TurnAround"
|
||||||
"spawnNull": "ItemSpawn",
|
|
||||||
"list": [
|
|
||||||
{"gravity": 0.95, "friction": 0.75, "airResistance": 0.975}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"cursor": {
|
|
||||||
"anm2": "gfx/cursor/cursor.anm2",
|
|
||||||
"animations": {"idle": "Default", "click": "Click", "hover": "Hover", "grab": "Grab", "pan": "Pan", "zoom": "Zoom", "dispose": "Dispose"},
|
|
||||||
"sounds": {
|
|
||||||
"grab": ["sfx/grab.ogg"],
|
|
||||||
"release": ["sfx/release.ogg"],
|
|
||||||
"throw": "sfx/throw*.ogg"
|
|
||||||
},
|
},
|
||||||
"particles": {
|
{
|
||||||
"grab": ["Circle"]
|
"type": "Turn Around",
|
||||||
|
"null": "FullBodyBack",
|
||||||
|
"animation": "Idle",
|
||||||
|
"idleOnEnd": "Idle",
|
||||||
|
"cursorHover": "TurnAroundBack"
|
||||||
}
|
}
|
||||||
},
|
]
|
||||||
"menu": {
|
|
||||||
"font": "font/font.ttf",
|
|
||||||
"rounding": 10.0,
|
|
||||||
"sounds": {
|
|
||||||
"open": ["sfx/open.ogg"],
|
|
||||||
"close": ["sfx/close.ogg"],
|
|
||||||
"hover": "sfx/hover*.ogg",
|
|
||||||
"select": ["sfx/select.ogg"],
|
|
||||||
"cheats": ["sfx/cheatsActivated.ogg"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"particles": {
|
|
||||||
"Circle": {"anm2": "gfx/particle/circle.anm2", "animation": "Main"},
|
|
||||||
"Puff": {"anm2": "gfx/particle/puff.anm2", "animation": "Main"}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Binary file not shown.
File renamed without changes.
@@ -2,109 +2,18 @@
|
|||||||
"name": "Princess Mini",
|
"name": "Princess Mini",
|
||||||
"description": "A sprightly, pampered, petite princess. Don't call her \"Mint\"!",
|
"description": "A sprightly, pampered, petite princess. Don't call her \"Mint\"!",
|
||||||
"credits": "Art, Animation, Dialogue: ShweetMagnet Character: 2x/MerchantAnon Sound Effects: JC Sounds (CC-BY 4.0), artisticdude (CC0), Kronbits (CC0), Vehicle (CC0), pixabay.org, ShweetMagnet Font: DPComic",
|
"credits": "Art, Animation, Dialogue: ShweetMagnet Character: 2x/MerchantAnon Sound Effects: JC Sounds (CC-BY 4.0), artisticdude (CC0), Kronbits (CC0), Vehicle (CC0), pixabay.org, ShweetMagnet Font: DPComic",
|
||||||
"render": "gfx/preview/render.png",
|
|
||||||
"portrait": "gfx/preview/portrait.png",
|
|
||||||
"anm2": "gfx/character/mini.anm2",
|
|
||||||
"color": [0.188, 0.651, 0.424],
|
"color": [0.188, 0.651, 0.424],
|
||||||
"weight": 40,
|
"weight": 40,
|
||||||
"weightMax": 100,
|
"weightMax": 100,
|
||||||
"capacity": 1250,
|
"capacity": 1250,
|
||||||
"capacityMin": 1250,
|
"stages": [45, 60, 70, 90],
|
||||||
"capacityMax": 10000,
|
"expandAreas": [{"layer": "Torso", "null": "Belly", "scaleMax": 2.5}],
|
||||||
"overstuffedMultiplier": 1.5,
|
"interactTypes": {"Smack": {"digestionClick": 0.25}},
|
||||||
"overstuffedDigestBonus": 0.2,
|
|
||||||
"caloriesToKilogram": 0.001,
|
|
||||||
"digestionRate": 1.5,
|
|
||||||
"digestionRateMax": 4.5,
|
|
||||||
"digestionTime": 2,
|
|
||||||
"blinkChance": 0.5,
|
|
||||||
"gurgleChance": 0.05,
|
|
||||||
"gurgleCapacityMultiplier": 10,
|
|
||||||
"dialoguePool": "Stage1",
|
|
||||||
"stages": [
|
|
||||||
{"weight": 45, "dialoguePool": "Stage2", "animationSuffix": "Stage2"},
|
|
||||||
{"weight": 60, "dialoguePool": "Stage3", "animationSuffix": "Stage3"},
|
|
||||||
{"weight": 70, "dialoguePool": "Stage4", "animationSuffix": "Stage4"},
|
|
||||||
{"weight": 90, "dialoguePool": "Stage5", "animationSuffix": "Stage5"}
|
|
||||||
],
|
|
||||||
"animations": {"start": "Appear", "idle": "Neutral", "idleFull": "NeutralFull", "stageUp": "StageUp"},
|
|
||||||
"talk": {"source": "Talk", "destination": "Mouth"},
|
|
||||||
"blink": {"source": "Blink", "destination": "Eye"},
|
|
||||||
"eatAreas": [
|
|
||||||
{"null": "Mouth", "animation": "Eat", "event": "Eat"}
|
|
||||||
],
|
|
||||||
"expandAreas": [
|
|
||||||
{"layer": "Torso", "null": "Belly", "scaleMax": 2.5}
|
|
||||||
],
|
|
||||||
"sounds": {"digest": "sfx/digest*.ogg", "gurgle": "sfx/gurgle*.ogg"},
|
|
||||||
"interactTypes": {
|
|
||||||
"Rub": {
|
|
||||||
"icon": "rub",
|
|
||||||
"isHold": true,
|
|
||||||
"cursorHover": "Hover",
|
|
||||||
"cursorActive": "Rub",
|
|
||||||
"sounds": ["sfx/rub.ogg"],
|
|
||||||
"particles": ["Circle"]
|
|
||||||
},
|
|
||||||
"Smack": {
|
|
||||||
"icon": "smack",
|
|
||||||
"cursorHover": "SmackHover",
|
|
||||||
"cursorActive": "Smack",
|
|
||||||
"dialogueCooldown": 0.6667,
|
|
||||||
"digestionClick": 0.25,
|
|
||||||
"scaleAmplitude": 1,
|
|
||||||
"scaleCycles": 3,
|
|
||||||
"sounds": "sfx/smack*.ogg",
|
|
||||||
"particles": ["Circle"]
|
|
||||||
},
|
|
||||||
"Kiss": {
|
|
||||||
"icon": "kiss",
|
|
||||||
"cursorHover": "KissHover",
|
|
||||||
"cursorActive": "Kiss",
|
|
||||||
"sounds": "sfx/kiss*.ogg",
|
|
||||||
"particles": ["Circle"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"interactAreas": [
|
"interactAreas": [
|
||||||
{"type": "Rub", "null": "Head", "dialoguePool": "HeadRub", "dialoguePoolFull": "HeadRubFull"},
|
{"type": "Rub", "null": "Head", "dialoguePool": "HeadRub"},
|
||||||
{"type": "Rub", "null": "Belly", "cursorActive": "Grab", "dialoguePool": "BellyRub", "dialoguePoolFull": "BellyRubFull", "digestionHover": 6},
|
{"type": "Rub", "null": "Belly", "cursorActive": "Grab", "dialoguePool": "BellyRub", "digestionHover": 6},
|
||||||
{"type": "Kiss", "null": "Mouth", "dialoguePool": "MouthKiss", "dialoguePoolFull": "MouthKiss"},
|
{"type": "Kiss", "null": "Mouth", "dialoguePool": "MouthKiss"},
|
||||||
{"type": "Kiss", "null": "Belly", "dialoguePool": "BellyKiss", "dialoguePoolFull": "BellyKiss"},
|
{"type": "Kiss", "null": "Belly", "dialoguePool": "BellyKiss"},
|
||||||
{"type": "Smack", "null": "Belly", "layer": "Torso", "dialoguePool": "BellySmack", "dialoguePoolFull": "BellySmack"}
|
{"type": "Smack", "null": "Belly", "layer": "Torso", "dialoguePool": "BellySmack"}
|
||||||
],
|
]
|
||||||
"areas": {
|
|
||||||
"anm2": "gfx/areas/area.anm2",
|
|
||||||
"boundsNull": "Bounds",
|
|
||||||
"spawnNull": "ItemSpawn",
|
|
||||||
"list": [
|
|
||||||
{"gravity": 0.95, "friction": 0.75, "airResistance": 0.975}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"cursor": {
|
|
||||||
"anm2": "gfx/cursor/cursor.anm2",
|
|
||||||
"animations": {"idle": "Default", "click": "Click", "hover": "Hover", "grab": "Grab", "pan": "Pan", "zoom": "Zoom", "dispose": "Dispose"},
|
|
||||||
"sounds": {
|
|
||||||
"grab": ["sfx/grab.ogg"],
|
|
||||||
"release": ["sfx/release.ogg"],
|
|
||||||
"throw": "sfx/throw*.ogg"
|
|
||||||
},
|
|
||||||
"particles": {
|
|
||||||
"grab": ["Circle"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"menu": {
|
|
||||||
"font": "font/font.ttf",
|
|
||||||
"rounding": 10.0,
|
|
||||||
"sounds": {
|
|
||||||
"open": ["sfx/open.ogg"],
|
|
||||||
"close": ["sfx/close.ogg"],
|
|
||||||
"hover": "sfx/hover*.ogg",
|
|
||||||
"select": ["sfx/select.ogg"],
|
|
||||||
"cheats": ["sfx/cheatsActivated.ogg"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"particles": {
|
|
||||||
"Circle": {"anm2": "gfx/particle/circle.anm2", "animation": "Main"},
|
|
||||||
"Puff": {"anm2": "gfx/particle/puff.anm2", "animation": "Main"}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Binary file not shown.
File renamed without changes.
@@ -2,128 +2,48 @@
|
|||||||
"name": "Snivy",
|
"name": "Snivy",
|
||||||
"description": "A Grass Snake Pokémon with rather large ambitions.",
|
"description": "A Grass Snake Pokémon with rather large ambitions.",
|
||||||
"credits": "Art, Animation, Dialogue: ShweetMagnet Character - Pokémon Sound Effects - Pokémon; ShweetMagnet",
|
"credits": "Art, Animation, Dialogue: ShweetMagnet Character - Pokémon Sound Effects - Pokémon; ShweetMagnet",
|
||||||
"render": "gfx/preview/render.png",
|
|
||||||
"portrait": "gfx/preview/portrait.png",
|
|
||||||
"anm2": "gfx/character/snivy.anm2",
|
|
||||||
"color": [0.12, 0.515, 0.115],
|
"color": [0.12, 0.515, 0.115],
|
||||||
"weight": 8.1,
|
"weight": 8.1,
|
||||||
"weightMax": 200,
|
"weightMax": 200,
|
||||||
"capacity": 300,
|
"capacity": 300,
|
||||||
"capacityMin": 300,
|
|
||||||
"capacityMax": 10000,
|
|
||||||
"overstuffedMultiplier": 1.5,
|
|
||||||
"overstuffedDigestBonus": 0.25,
|
"overstuffedDigestBonus": 0.25,
|
||||||
"caloriesToKilogram": 0.0005,
|
"caloriesToKilogram": 0.0005,
|
||||||
"digestionRate": 1.5,
|
|
||||||
"digestionRateMax": 4.5,
|
|
||||||
"digestionTime": 2,
|
|
||||||
"blinkChance": 0.5,
|
|
||||||
"gurgleChance": 0.05,
|
|
||||||
"gurgleCapacityMultiplier": 10,
|
|
||||||
"dialoguePool": "Stage1",
|
|
||||||
"alternate": {"texture": "gfx/character/snivy_alt.png", "sound": "sfx/shiny.ogg", "chance": 0.001},
|
"alternate": {"texture": "gfx/character/snivy_alt.png", "sound": "sfx/shiny.ogg", "chance": 0.001},
|
||||||
"stages": [
|
"stages": [10, 12.5, 17.5, 27.5, 40, 55, 75, 100, 130],
|
||||||
{"weight": 10, "dialoguePool": "Stage2", "animationSuffix": "Stage2"},
|
"blink": {"destination": "Head"},
|
||||||
{"weight": 12.5, "dialoguePool": "Stage3", "animationSuffix": "Stage3"},
|
"expandAreas": [{"layer": "Torso", "null": "Belly", "scaleMax": 7.5}, {"layer": "Tail", "null": "Tail", "scaleMax": 7.5}],
|
||||||
{"weight": 17.5, "dialoguePool": "Stage4", "animationSuffix": "Stage4"},
|
"interactTypes": {"Smack": {"scaleAmplitude": 5}},
|
||||||
{"weight": 27.5, "dialoguePool": "Stage5", "animationSuffix": "Stage5"},
|
|
||||||
{"weight": 40, "dialoguePool": "Stage6", "animationSuffix": "Stage6"},
|
|
||||||
{"weight": 55, "dialoguePool": "Stage7", "animationSuffix": "Stage7"},
|
|
||||||
{"weight": 75, "dialoguePool": "Stage8", "animationSuffix": "Stage8"},
|
|
||||||
{"weight": 100, "dialoguePool": "Stage9", "animationSuffix": "Stage9"},
|
|
||||||
{"weight": 130, "dialoguePool": "Stage10", "animationSuffix": "Stage10"}
|
|
||||||
],
|
|
||||||
"animations": {"start": "Appear", "idle": "Neutral", "idleFull": "NeutralFull", "stageUp": "StageUp"},
|
|
||||||
"talk": {"source": "Talk", "destination": "Mouth"},
|
|
||||||
"blink": {"source": "Blink", "destination": "Head"},
|
|
||||||
"eatAreas": [
|
|
||||||
{"null": "Mouth", "animation": "Eat", "event": "Eat"}
|
|
||||||
],
|
|
||||||
"expandAreas": [
|
|
||||||
{"layer": "Torso", "null": "Belly", "scaleMax": 7.5},
|
|
||||||
{"layer": "Tail", "null": "Tail", "scaleMax": 7.5}
|
|
||||||
],
|
|
||||||
"sounds": {"digest": "sfx/digest*.ogg", "gurgle": "sfx/gurgle*.ogg"},
|
|
||||||
"interactTypes": {
|
|
||||||
"Rub": {
|
|
||||||
"icon": "rub",
|
|
||||||
"isHold": true,
|
|
||||||
"cursorHover": "Hover",
|
|
||||||
"cursorActive": "Rub",
|
|
||||||
"sounds": ["sfx/rub.ogg"],
|
|
||||||
"particles": ["Circle"]
|
|
||||||
},
|
|
||||||
"Smack": {
|
|
||||||
"icon": "smack",
|
|
||||||
"cursorHover": "SmackHover",
|
|
||||||
"cursorActive": "Smack",
|
|
||||||
"dialogueCooldown": 0.6667,
|
|
||||||
"scaleAmplitude": 5,
|
|
||||||
"scaleCycles": 3,
|
|
||||||
"sounds": "sfx/smack*.ogg",
|
|
||||||
"particles": ["Circle"]
|
|
||||||
},
|
|
||||||
"Kiss": {
|
|
||||||
"icon": "kiss",
|
|
||||||
"cursorHover": "KissHover",
|
|
||||||
"cursorActive": "Kiss",
|
|
||||||
"sounds": "sfx/kiss*.ogg",
|
|
||||||
"particles": ["Circle"]
|
|
||||||
},
|
|
||||||
"Turn Around": {
|
|
||||||
"icon": "turn",
|
|
||||||
"sounds": ["sfx/turn.ogg"],
|
|
||||||
"particles": ["Circle"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"interactAreas": [
|
"interactAreas": [
|
||||||
{"type": "Rub", "null": "Head", "dialoguePool": "HeadRub", "dialoguePoolFull": "HeadRubFull"},
|
{"type": "Rub", "null": "Head", "dialoguePool": "HeadRub"},
|
||||||
{"type": "Rub", "null": "HeadBack", "idleOnEnd": "NeutralBack", "dialoguePool": "HeadRubBack", "dialoguePoolFull": "HeadRubBackFull"},
|
{"type": "Rub", "null": "HeadBack", "idleOnEnd": "NeutralBack", "dialoguePool": "HeadRubBack"},
|
||||||
{"type": "Rub", "null": "Belly", "cursorActive": "Grab", "dialoguePool": "BellyRub", "dialoguePoolFull": "BellyRubFull", "digestionHover": 6},
|
{"type": "Rub", "null": "Belly", "cursorActive": "Grab", "dialoguePool": "BellyRub", "digestionHover": 6},
|
||||||
{"type": "Rub", "null": "Tail", "cursorActive": "Grab", "dialoguePool": "TailRub", "dialoguePoolFull": "TailRubFull"},
|
{"type": "Rub", "null": "Tail", "cursorActive": "Grab", "dialoguePool": "TailRub"},
|
||||||
{"type": "Rub", "null": "TailBack", "idleOnEnd": "NeutralBack", "cursorActive": "Grab", "dialoguePool": "TailRubBack", "dialoguePoolFull": "TailRubBackFull"},
|
{
|
||||||
{"type": "Kiss", "null": "Mouth", "dialoguePool": "MouthKiss", "dialoguePoolFull": "MouthKiss"},
|
"type": "Rub",
|
||||||
{"type": "Kiss", "null": "Belly", "dialoguePool": "BellyKiss", "dialoguePoolFull": "BellyKiss"},
|
"null": "TailBack",
|
||||||
{"type": "Kiss", "null": "Tail", "dialoguePool": "TailKiss", "dialoguePoolFull": "TailKiss"},
|
"idleOnEnd": "NeutralBack",
|
||||||
{"type": "Smack", "null": "Belly", "layer": "Torso", "dialoguePool": "BellySmack", "dialoguePoolFull": "BellySmack", "digestionClick": 0.25},
|
"cursorActive": "Grab",
|
||||||
{"type": "Smack", "null": "Tail", "layer": "Tail", "dialoguePool": "TailSmack", "dialoguePoolFull": "TailSmack"},
|
"dialoguePool": "TailRubBack"
|
||||||
{"type": "Smack", "null": "TailBack", "layer": "Tail", "idleOnEnd": "NeutralBack", "dialoguePool": "TailSmackBack", "dialoguePoolFull": "TailSmackBack"},
|
|
||||||
{"type": "Turn Around", "null": "FullBody", "animation": "NeutralBack", "idleOnEnd": "NeutralBack", "cursorHover": "TurnAround"},
|
|
||||||
{"type": "Turn Around", "null": "FullBodyBack", "animation": "Neutral", "idleOnEnd": "Neutral", "cursorHover": "TurnAroundBack"}
|
|
||||||
],
|
|
||||||
"areas": {
|
|
||||||
"anm2": "gfx/areas/area.anm2",
|
|
||||||
"boundsNull": "Bounds",
|
|
||||||
"spawnNull": "ItemSpawn",
|
|
||||||
"list": [
|
|
||||||
{"gravity": 0.95, "friction": 0.75, "airResistance": 0.975}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"cursor": {
|
|
||||||
"anm2": "gfx/cursor/cursor.anm2",
|
|
||||||
"animations": {"idle": "Default", "click": "Click", "hover": "Hover", "grab": "Grab", "pan": "Pan", "zoom": "Zoom", "dispose": "Dispose"},
|
|
||||||
"sounds": {
|
|
||||||
"grab": ["sfx/grab.ogg"],
|
|
||||||
"release": ["sfx/release.ogg"],
|
|
||||||
"throw": "sfx/throw*.ogg"
|
|
||||||
},
|
},
|
||||||
"particles": {
|
{"type": "Kiss", "null": "Mouth", "dialoguePool": "MouthKiss"},
|
||||||
"grab": ["Circle"]
|
{"type": "Kiss", "null": "Belly", "dialoguePool": "BellyKiss"},
|
||||||
|
{"type": "Kiss", "null": "Tail", "dialoguePool": "TailKiss"},
|
||||||
|
{"type": "Smack", "null": "Belly", "layer": "Torso", "dialoguePool": "BellySmack", "digestionClick": 0.25},
|
||||||
|
{"type": "Smack", "null": "Tail", "layer": "Tail", "dialoguePool": "TailSmack"},
|
||||||
|
{"type": "Smack", "null": "TailBack", "layer": "Tail", "idleOnEnd": "NeutralBack", "dialoguePool": "TailSmackBack"},
|
||||||
|
{
|
||||||
|
"type": "Turn Around",
|
||||||
|
"null": "FullBody",
|
||||||
|
"animation": "NeutralBack",
|
||||||
|
"idleOnEnd": "NeutralBack",
|
||||||
|
"cursorHover": "TurnAround"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Turn Around",
|
||||||
|
"null": "FullBodyBack",
|
||||||
|
"animation": "Neutral",
|
||||||
|
"idleOnEnd": "Neutral",
|
||||||
|
"cursorHover": "TurnAroundBack"
|
||||||
}
|
}
|
||||||
},
|
]
|
||||||
"menu": {
|
|
||||||
"font": "font/font.ttf",
|
|
||||||
"rounding": 10.0,
|
|
||||||
"sounds": {
|
|
||||||
"open": ["sfx/open.ogg"],
|
|
||||||
"close": ["sfx/close.ogg"],
|
|
||||||
"hover": "sfx/hover*.ogg",
|
|
||||||
"select": ["sfx/select.ogg"],
|
|
||||||
"cheats": ["sfx/cheatsActivated.ogg"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"particles": {
|
|
||||||
"Circle": {"anm2": "gfx/particle/circle.anm2", "animation": "Main"},
|
|
||||||
"Puff": {"anm2": "gfx/particle/puff.anm2", "animation": "Main"}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Binary file not shown.
File renamed without changes.
@@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"capacityMax": 10000,
|
||||||
|
"overstuffedMultiplier": 1.5,
|
||||||
|
"overstuffedDigestBonus": 0.2,
|
||||||
|
"caloriesToKilogram": 0.001,
|
||||||
|
"digestionRate": 1.5,
|
||||||
|
"digestionRateMax": 4.5,
|
||||||
|
"digestionTime": 2,
|
||||||
|
"blinkChance": 0.5,
|
||||||
|
"gurgleChance": 0.05,
|
||||||
|
"gurgleCapacityMultiplier": 10,
|
||||||
|
"animations": {"start": "Appear", "idle": "Neutral", "idleFull": "NeutralFull", "stageUp": "StageUp"},
|
||||||
|
"talk": {"source": "Talk", "destination": "Mouth"},
|
||||||
|
"blink": {"source": "Blink", "destination": "Eye"},
|
||||||
|
"eatAreas": [{"animation": "Eat", "event": "Eat", "null": "Mouth"}],
|
||||||
|
"sounds": {"digest": "sfx/digest*.ogg", "gurgle": "sfx/gurgle*.ogg"},
|
||||||
|
"interactTypes": {
|
||||||
|
"Rub": {
|
||||||
|
"icon": "rub",
|
||||||
|
"isHold": true,
|
||||||
|
"cursorHover": "Hover",
|
||||||
|
"cursorActive": "Rub",
|
||||||
|
"sounds": ["sfx/rub.ogg"],
|
||||||
|
"particles": ["Circle"]
|
||||||
|
},
|
||||||
|
"Smack": {
|
||||||
|
"icon": "smack",
|
||||||
|
"cursorHover": "SmackHover",
|
||||||
|
"cursorActive": "Smack",
|
||||||
|
"dialogueCooldown": 0.6667,
|
||||||
|
"scaleAmplitude": 1,
|
||||||
|
"scaleCycles": 3,
|
||||||
|
"sounds": "sfx/smack*.ogg",
|
||||||
|
"particles": ["Circle"]
|
||||||
|
},
|
||||||
|
"Kiss": {
|
||||||
|
"icon": "kiss",
|
||||||
|
"cursorHover": "KissHover",
|
||||||
|
"cursorActive": "Kiss",
|
||||||
|
"sounds": "sfx/kiss*.ogg",
|
||||||
|
"particles": ["Circle"]
|
||||||
|
},
|
||||||
|
"Turn Around": {"icon": "turn", "sounds": ["sfx/turn.ogg"], "particles": ["Circle"]}
|
||||||
|
},
|
||||||
|
"cursor": {
|
||||||
|
"sounds": {"grab": ["sfx/grab.ogg"], "release": ["sfx/release.ogg"], "throw": "sfx/throw*.ogg"},
|
||||||
|
"particles": {"grab": ["Circle"]}
|
||||||
|
},
|
||||||
|
"menu": {
|
||||||
|
"sounds": {
|
||||||
|
"open": ["sfx/open.ogg"],
|
||||||
|
"close": ["sfx/close.ogg"],
|
||||||
|
"hover": "sfx/hover*.ogg",
|
||||||
|
"select": ["sfx/select.ogg"],
|
||||||
|
"cheats": ["sfx/cheatsActivated.ogg"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"particles": {
|
||||||
|
"Circle": {"anm2": "gfx/particle/circle.anm2", "animation": "Main"},
|
||||||
|
"Puff": {"anm2": "gfx/particle/puff.anm2", "animation": "Main"}
|
||||||
|
},
|
||||||
|
"area": {"gravity": 0.95, "friction": 0.75, "airResistance": 0.975}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
Copyright 2018 The Mali Project Authors (https://github.com/cadsondemak/Mali)
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
Binary file not shown.
+7
-7
@@ -44,7 +44,7 @@ void Body::set(Character& played, Audio& sound, Game game)
|
|||||||
void Body::stage_set(int index)
|
void Body::stage_set(int index)
|
||||||
{
|
{
|
||||||
stage = index;
|
stage = index;
|
||||||
suffix = stage > 0 && stage <= (int)character->stages.size() ? character->stages[stage - 1].animationSuffix : "";
|
suffix = stage > 0 ? character->stage_name_get(stage) : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
float Body::capacity_max_get()
|
float Body::capacity_max_get()
|
||||||
@@ -65,21 +65,21 @@ bool Body::is_over_capacity()
|
|||||||
|
|
||||||
int Body::stage_get(float weight)
|
int Body::stage_get(float weight)
|
||||||
{
|
{
|
||||||
std::vector<Stage>& stages = character->stages;
|
std::vector<float>& stages = character->stages;
|
||||||
if (stages.empty() || weight <= character->traits.weight)
|
if (stages.empty() || weight <= character->traits.weight)
|
||||||
return 0;
|
return 0;
|
||||||
for (int i = 0; i < (int)stages.size(); i++)
|
for (int i = 0; i < (int)stages.size(); i++)
|
||||||
if (weight < stages[i].weight)
|
if (weight < stages[i])
|
||||||
return i;
|
return i;
|
||||||
return (int)stages.size();
|
return (int)stages.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
float Body::stage_threshold_get(int index)
|
float Body::stage_threshold_get(int index)
|
||||||
{
|
{
|
||||||
std::vector<Stage>& stages = character->stages;
|
std::vector<float>& stages = character->stages;
|
||||||
if (index <= 0 || stages.empty())
|
if (index <= 0 || stages.empty())
|
||||||
return character->traits.weight;
|
return character->traits.weight;
|
||||||
return stages[std::min(index, (int)stages.size()) - 1].weight;
|
return stages[std::min(index, (int)stages.size()) - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
float Body::stage_progress_get()
|
float Body::stage_progress_get()
|
||||||
@@ -132,7 +132,7 @@ void Body::values_clamp()
|
|||||||
{
|
{
|
||||||
Traits& traits = character->traits;
|
Traits& traits = character->traits;
|
||||||
stats.weight = value_clamp(stats.weight, traits.weight, traits.weightMax);
|
stats.weight = value_clamp(stats.weight, traits.weight, traits.weightMax);
|
||||||
stats.capacity = value_clamp(stats.capacity, traits.capacityMin, traits.capacityMax);
|
stats.capacity = value_clamp(stats.capacity, traits.capacity, traits.capacityMax);
|
||||||
stats.digestionRate = value_clamp(stats.digestionRate, traits.digestionRateMin, traits.digestionRateMax);
|
stats.digestionRate = value_clamp(stats.digestionRate, traits.digestionRateMin, traits.digestionRateMax);
|
||||||
stats.calories = value_clamp(stats.calories, 0, std::max(0.0f, capacity_max_get()));
|
stats.calories = value_clamp(stats.calories, 0, std::max(0.0f, capacity_max_get()));
|
||||||
stats.digestionProgress = value_clamp(stats.digestionProgress, 0, DIGESTION_MAX);
|
stats.digestionProgress = value_clamp(stats.digestionProgress, 0, DIGESTION_MAX);
|
||||||
@@ -195,7 +195,7 @@ void Body::update(std::vector<Event>& events)
|
|||||||
float over = stats.calories - stats.capacity;
|
float over = stats.calories - stats.capacity;
|
||||||
float overPercent = over / (capacity_max_get() - stats.capacity);
|
float overPercent = over / (capacity_max_get() - stats.capacity);
|
||||||
stats.capacity = value_clamp(stats.capacity + overPercent * traits.overstuffedDigestBonus * over,
|
stats.capacity = value_clamp(stats.capacity + overPercent * traits.overstuffedDigestBonus * over,
|
||||||
traits.capacityMin, traits.capacityMax);
|
traits.capacity, traits.capacityMax);
|
||||||
}
|
}
|
||||||
stats.calories = 0;
|
stats.calories = 0;
|
||||||
if (int nextStage = stage_get(nextWeight); nextStage > stage_get(stats.weight))
|
if (int nextStage = stage_get(nextWeight); nextStage > stage_get(stats.weight))
|
||||||
|
|||||||
+124
-122
@@ -1,10 +1,16 @@
|
|||||||
#include "character.h"
|
#include "character.h"
|
||||||
#include "json.h"
|
#include "json.h"
|
||||||
#include <imgui.h>
|
|
||||||
|
|
||||||
constexpr float FONT_SIZE = 20;
|
constexpr const char* AREA_PATH = "gfx/areas/area.anm2";
|
||||||
|
constexpr const char* BOUNDS_NULL = "Bounds";
|
||||||
constexpr const char* COMMON_FOLDER = "common/";
|
constexpr const char* COMMON_FOLDER = "common/";
|
||||||
|
constexpr const char* CURSOR_PATH = "gfx/cursor/cursor.anm2";
|
||||||
|
constexpr const char* DATA_FILE = "character.json";
|
||||||
constexpr const char* ICONS_PATH = "gfx/ui/icons.anm2";
|
constexpr const char* ICONS_PATH = "gfx/ui/icons.anm2";
|
||||||
|
constexpr const char* PORTRAIT_PATH = "gfx/preview/portrait.png";
|
||||||
|
constexpr const char* RENDER_PATH = "gfx/preview/render.png";
|
||||||
|
constexpr const char* SPAWN_NULL = "ItemSpawn";
|
||||||
|
constexpr const char* STAGE_PREFIX = "Stage";
|
||||||
constexpr const char* TAG_NAMES[] = {"animation", "big", "color", "delay", "screenshake",
|
constexpr const char* TAG_NAMES[] = {"animation", "big", "color", "delay", "screenshake",
|
||||||
"shake", "silent", "small", "sound", "wave"};
|
"shake", "silent", "small", "sound", "wave"};
|
||||||
constexpr const char* USE_NAMES[] = {"", "edible", "tool"};
|
constexpr const char* USE_NAMES[] = {"", "edible", "tool"};
|
||||||
@@ -69,7 +75,7 @@ int Catalog::item_get(const std::string& id)
|
|||||||
void Character::preview_load(const std::string& path, Renderer& renderer)
|
void Character::preview_load(const std::string& path, Renderer& renderer)
|
||||||
{
|
{
|
||||||
Json json;
|
Json json;
|
||||||
json.load(path + "character.json");
|
json.load(path + DATA_FILE);
|
||||||
if (!(isValid = json.isValid))
|
if (!(isValid = json.isValid))
|
||||||
return;
|
return;
|
||||||
object root = json.root;
|
object root = json.root;
|
||||||
@@ -83,15 +89,17 @@ void Character::preview_load(const std::string& path, Renderer& renderer)
|
|||||||
credits = json_string(root, "credits");
|
credits = json_string(root, "credits");
|
||||||
color = json_color(root, "color", color);
|
color = json_color(root, "color", color);
|
||||||
traits.weight = json_float(root, "weight");
|
traits.weight = json_float(root, "weight");
|
||||||
anm2Path = json_string(root, "anm2");
|
auto texture_get = [&](const char* relative) {
|
||||||
portrait = root["portrait"].error() ? nullptr : renderer.texture_get(file_find(roots, json_string(root, "portrait")));
|
std::string file = folder + relative;
|
||||||
render = root["render"].error() ? nullptr : renderer.texture_get(file_find(roots, json_string(root, "render")));
|
return SDL_GetPathInfo(file.c_str(), nullptr) ? renderer.texture_get(file) : nullptr;
|
||||||
|
};
|
||||||
|
portrait = texture_get(PORTRAIT_PATH);
|
||||||
|
render = texture_get(RENDER_PATH);
|
||||||
for (element entry : json_array(root, "stages"))
|
for (element entry : json_array(root, "stages"))
|
||||||
{
|
{
|
||||||
object fields;
|
double weight;
|
||||||
if (!entry.get(fields))
|
if (!entry.get(weight))
|
||||||
stages.push_back({json_string(fields, "animationSuffix"), json_string(fields, "dialoguePool"),
|
stages.push_back((float)weight);
|
||||||
json_float(fields, "weight"), json_int(fields, "area", -1)});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,14 +120,13 @@ static void interact_parse(object source, InteractArea& area, Character& charact
|
|||||||
{
|
{
|
||||||
json_keys_check(source,
|
json_keys_check(source,
|
||||||
{"type", "null", "layer", "icon", "isHold", "animation", "idleOnEnd", "cursorHover", "cursorActive",
|
{"type", "null", "layer", "icon", "isHold", "animation", "idleOnEnd", "cursorHover", "cursorActive",
|
||||||
"dialoguePool", "dialoguePoolFull", "dialogueCooldown", "digestionClick", "digestionHover",
|
"dialoguePool", "dialogueCooldown", "digestionClick", "digestionHover",
|
||||||
"scaleAmplitude", "scaleCycles", "scaleTime", "sounds", "particles"},
|
"scaleAmplitude", "scaleCycles", "scaleTime", "sounds", "particles"},
|
||||||
"an interact area");
|
"an interact area");
|
||||||
area.animation = json_string(source, "animation", area.animation);
|
area.animation = json_string(source, "animation", area.animation);
|
||||||
area.cursorActive = json_string(source, "cursorActive", area.cursorActive);
|
area.cursorActive = json_string(source, "cursorActive", area.cursorActive);
|
||||||
area.cursorHover = json_string(source, "cursorHover", area.cursorHover);
|
area.cursorHover = json_string(source, "cursorHover", area.cursorHover);
|
||||||
area.dialoguePool = json_string(source, "dialoguePool", area.dialoguePool);
|
area.dialoguePool = json_string(source, "dialoguePool", area.dialoguePool);
|
||||||
area.dialoguePoolFull = json_string(source, "dialoguePoolFull", area.dialoguePoolFull);
|
|
||||||
area.icon = json_string(source, "icon", area.icon);
|
area.icon = json_string(source, "icon", area.icon);
|
||||||
area.idleOnEnd = json_string(source, "idleOnEnd", area.idleOnEnd);
|
area.idleOnEnd = json_string(source, "idleOnEnd", area.idleOnEnd);
|
||||||
area.digestionClick = json_float(source, "digestionClick", area.digestionClick);
|
area.digestionClick = json_float(source, "digestionClick", area.digestionClick);
|
||||||
@@ -142,30 +149,29 @@ static void interact_parse(object source, InteractArea& area, Character& charact
|
|||||||
static void character_parse(object root, Character& character, Renderer& renderer, Audio& audio)
|
static void character_parse(object root, Character& character, Renderer& renderer, Audio& audio)
|
||||||
{
|
{
|
||||||
json_keys_check(root,
|
json_keys_check(root,
|
||||||
{"id", "name", "description", "credits", "render", "portrait", "anm2", "color", "weight", "weightMax",
|
{"id", "name", "description", "credits", "color", "weight", "weightMax", "capacity", "capacityMax",
|
||||||
"capacity", "capacityMin", "capacityMax", "overstuffedMultiplier", "overstuffedDigestBonus",
|
"overstuffedMultiplier", "overstuffedDigestBonus", "caloriesToKilogram", "digestionRate",
|
||||||
"caloriesToKilogram", "digestionRate", "digestionRateMin", "digestionRateMax", "digestionTime",
|
"digestionRateMin", "digestionRateMax", "digestionTime", "blinkChance", "gurgleChance",
|
||||||
"blinkChance", "gurgleChance", "gurgleCapacityMultiplier", "dialoguePool", "alternate", "stages",
|
"gurgleCapacityMultiplier", "alternate", "stages", "animations", "talk", "blink", "eatAreas",
|
||||||
"animations", "talk", "blink", "eatAreas", "expandAreas", "sounds", "interactTypes",
|
"expandAreas", "sounds", "interactTypes", "interactAreas", "area", "cursor", "menu", "particles"},
|
||||||
"interactAreas", "areas", "cursor", "menu", "particles"},
|
|
||||||
"character.json");
|
"character.json");
|
||||||
character.anm2 = character.anm2_get(character.anm2Path, renderer, audio);
|
|
||||||
Traits& traits = character.traits;
|
Traits& traits = character.traits;
|
||||||
traits.weightMax = json_float(root, "weightMax", traits.weight);
|
auto float_read = [&](object source, const char* key, float& value, float scale = 1) {
|
||||||
traits.capacity = json_float(root, "capacity");
|
value = json_float(source, key, value * scale) / scale;
|
||||||
traits.capacityMin = json_float(root, "capacityMin");
|
};
|
||||||
traits.capacityMax = json_float(root, "capacityMax", traits.capacity);
|
float_read(root, "weightMax", traits.weightMax);
|
||||||
traits.overstuffedMultiplier = json_float(root, "overstuffedMultiplier", 1);
|
float_read(root, "capacity", traits.capacity);
|
||||||
traits.overstuffedDigestBonus = json_float(root, "overstuffedDigestBonus");
|
float_read(root, "capacityMax", traits.capacityMax);
|
||||||
traits.caloriesToKilogram = json_float(root, "caloriesToKilogram");
|
float_read(root, "overstuffedMultiplier", traits.overstuffedMultiplier);
|
||||||
traits.digestionRate = json_float(root, "digestionRate") / TICK_RATE;
|
float_read(root, "overstuffedDigestBonus", traits.overstuffedDigestBonus);
|
||||||
traits.digestionRateMin = json_float(root, "digestionRateMin") / TICK_RATE;
|
float_read(root, "caloriesToKilogram", traits.caloriesToKilogram);
|
||||||
traits.digestionRateMax = json_float(root, "digestionRateMax", traits.digestionRate * TICK_RATE) / TICK_RATE;
|
float_read(root, "digestionRate", traits.digestionRate, TICK_RATE);
|
||||||
traits.digestionTime = ticks_get(json_float(root, "digestionTime"));
|
float_read(root, "digestionRateMin", traits.digestionRateMin, TICK_RATE);
|
||||||
traits.blinkChance = json_float(root, "blinkChance");
|
float_read(root, "digestionRateMax", traits.digestionRateMax, TICK_RATE);
|
||||||
traits.gurgleChance = json_float(root, "gurgleChance");
|
float_read(root, "blinkChance", traits.blinkChance);
|
||||||
traits.gurgleCapacityMultiplier = json_float(root, "gurgleCapacityMultiplier");
|
float_read(root, "gurgleChance", traits.gurgleChance);
|
||||||
character.dialoguePool = json_string(root, "dialoguePool");
|
float_read(root, "gurgleCapacityMultiplier", traits.gurgleCapacityMultiplier);
|
||||||
|
traits.digestionTime = ticks_get(json_float(root, "digestionTime", traits.digestionTime / TICK_RATE));
|
||||||
|
|
||||||
object alternate = json_object(root, "alternate");
|
object alternate = json_object(root, "alternate");
|
||||||
json_keys_check(alternate, {"texture", "sound", "chance", "spritesheet"}, "alternate");
|
json_keys_check(alternate, {"texture", "sound", "chance", "spritesheet"}, "alternate");
|
||||||
@@ -173,13 +179,14 @@ static void character_parse(object root, Character& character, Renderer& rendere
|
|||||||
character.alternate.texture = renderer.texture_get(file_find(character.roots, json_string(alternate, "texture")));
|
character.alternate.texture = renderer.texture_get(file_find(character.roots, json_string(alternate, "texture")));
|
||||||
if (!alternate["sound"].error())
|
if (!alternate["sound"].error())
|
||||||
character.alternate.sound = audio.sound_get(file_find(character.roots, json_string(alternate, "sound")));
|
character.alternate.sound = audio.sound_get(file_find(character.roots, json_string(alternate, "sound")));
|
||||||
character.alternate.chance = json_float(alternate, "chance");
|
float_read(alternate, "chance", character.alternate.chance);
|
||||||
character.alternate.sheet = json_int(alternate, "spritesheet");
|
character.alternate.sheet = json_int(alternate, "spritesheet", character.alternate.sheet);
|
||||||
|
|
||||||
object animations = json_object(root, "animations");
|
object animations = json_object(root, "animations");
|
||||||
json_keys_check(animations, {"start", "idle", "idleFull", "stageUp"}, "animations");
|
json_keys_check(animations, {"start", "idle", "idleFull", "stageUp"}, "animations");
|
||||||
character.animations = {json_string(animations, "idle"), json_string(animations, "idleFull"),
|
Animations& names = character.animations;
|
||||||
json_string(animations, "stageUp"), json_string(animations, "start")};
|
names = {json_string(animations, "idle", names.idle), json_string(animations, "idleFull", names.idleFull),
|
||||||
|
json_string(animations, "stageUp", names.stageUp), json_string(animations, "start", names.start)};
|
||||||
|
|
||||||
Anm2* anm2 = character.anm2;
|
Anm2* anm2 = character.anm2;
|
||||||
auto layer_get = [&](object source, const char* key) {
|
auto layer_get = [&](object source, const char* key) {
|
||||||
@@ -188,20 +195,17 @@ static void character_parse(object root, Character& character, Renderer& rendere
|
|||||||
auto null_get = [&](object source, const char* key) {
|
auto null_get = [&](object source, const char* key) {
|
||||||
return anm2 ? anm2->null_get(json_string(source, key)) : -1;
|
return anm2 ? anm2->null_get(json_string(source, key)) : -1;
|
||||||
};
|
};
|
||||||
auto swap_get = [&](const char* key) {
|
auto swap_read = [&](const char* key, Swap& swap) {
|
||||||
object swap = json_object(root, key);
|
object fields = json_object(root, key);
|
||||||
json_keys_check(swap, {"source", "destination"}, key);
|
json_keys_check(fields, {"source", "destination"}, key);
|
||||||
return Swap{layer_get(swap, "destination"), layer_get(swap, "source")};
|
swap.source = fields["source"].error() ? swap.source : layer_get(fields, "source");
|
||||||
|
swap.destination = fields["destination"].error() ? swap.destination : layer_get(fields, "destination");
|
||||||
};
|
};
|
||||||
character.talk = swap_get("talk");
|
swap_read("talk", character.talk);
|
||||||
character.blink = swap_get("blink");
|
swap_read("blink", character.blink);
|
||||||
for (element entry : json_array(root, "stages"))
|
|
||||||
{
|
|
||||||
object fields;
|
|
||||||
if (!entry.get(fields))
|
|
||||||
json_keys_check(fields, {"weight", "dialoguePool", "animationSuffix", "area"}, "a stage");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!root["eatAreas"].error())
|
||||||
|
character.eatAreas.clear();
|
||||||
for (element entry : json_array(root, "eatAreas"))
|
for (element entry : json_array(root, "eatAreas"))
|
||||||
{
|
{
|
||||||
object fields;
|
object fields;
|
||||||
@@ -211,6 +215,8 @@ static void character_parse(object root, Character& character, Renderer& rendere
|
|||||||
character.eatAreas.push_back({json_string(fields, "animation"),
|
character.eatAreas.push_back({json_string(fields, "animation"),
|
||||||
anm2 ? anm2->event_get(json_string(fields, "event")) : -1, null_get(fields, "null")});
|
anm2 ? anm2->event_get(json_string(fields, "event")) : -1, null_get(fields, "null")});
|
||||||
}
|
}
|
||||||
|
if (!root["expandAreas"].error())
|
||||||
|
character.expandAreas.clear();
|
||||||
for (element entry : json_array(root, "expandAreas"))
|
for (element entry : json_array(root, "expandAreas"))
|
||||||
{
|
{
|
||||||
object fields;
|
object fields;
|
||||||
@@ -221,81 +227,47 @@ static void character_parse(object root, Character& character, Renderer& rendere
|
|||||||
{json_float(fields, "scaleMax"), layer_get(fields, "layer"), null_get(fields, "null")});
|
{json_float(fields, "scaleMax"), layer_get(fields, "layer"), null_get(fields, "null")});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> typeNames;
|
std::vector<InteractArea>& types = character.interactTypes;
|
||||||
|
auto type_find = [&](std::string_view name) {
|
||||||
|
return std::find_if(types.begin(), types.end(), [&](InteractArea& type) { return type.name == name; });
|
||||||
|
};
|
||||||
for (auto [name, value] : json_object(root, "interactTypes"))
|
for (auto [name, value] : json_object(root, "interactTypes"))
|
||||||
{
|
{
|
||||||
object fields;
|
object fields;
|
||||||
if (value.get(fields))
|
if (value.get(fields))
|
||||||
continue;
|
continue;
|
||||||
InteractArea& type = character.interactTypes.emplace_back();
|
auto found = type_find(name);
|
||||||
|
InteractArea& type = found == types.end() ? types.emplace_back() : *found;
|
||||||
interact_parse(fields, type, character, audio);
|
interact_parse(fields, type, character, audio);
|
||||||
type.name = name;
|
type.name = name;
|
||||||
type.type = (int)typeNames.size();
|
type.type = (int)(&type - types.data());
|
||||||
typeNames.emplace_back(name);
|
|
||||||
}
|
}
|
||||||
|
if (!root["interactAreas"].error())
|
||||||
|
character.interactAreas.clear();
|
||||||
for (element entry : json_array(root, "interactAreas"))
|
for (element entry : json_array(root, "interactAreas"))
|
||||||
{
|
{
|
||||||
object fields;
|
object fields;
|
||||||
if (entry.get(fields))
|
if (entry.get(fields))
|
||||||
continue;
|
continue;
|
||||||
auto type = std::find(typeNames.begin(), typeNames.end(), json_string(fields, "type"));
|
auto type = type_find(json_string(fields, "type"));
|
||||||
InteractArea area = type == typeNames.end() ? InteractArea{} : character.interactTypes[type - typeNames.begin()];
|
InteractArea area = type == types.end() ? InteractArea{} : *type;
|
||||||
interact_parse(fields, area, character, audio);
|
interact_parse(fields, area, character, audio);
|
||||||
if (area.null >= 0)
|
if (area.null >= 0)
|
||||||
character.interactAreas.push_back(area);
|
character.interactAreas.push_back(area);
|
||||||
}
|
}
|
||||||
|
|
||||||
object areas = json_object(root, "areas");
|
object area = json_object(root, "area");
|
||||||
json_keys_check(areas, {"anm2", "boundsNull", "spawnNull", "list"}, "areas");
|
json_keys_check(area, {"gravity", "friction", "airResistance"}, "area");
|
||||||
Anm2* areaAnm2 = character.anm2_get(json_string(areas, "anm2"), renderer, audio);
|
float_read(area, "gravity", character.area.gravity);
|
||||||
std::string boundsNull = json_string(areas, "boundsNull", "Bounds");
|
float_read(area, "friction", character.area.friction);
|
||||||
std::string spawnNull = json_string(areas, "spawnNull", boundsNull);
|
float_read(area, "airResistance", character.area.airResistance);
|
||||||
for (element entry : json_array(areas, "list"))
|
|
||||||
{
|
|
||||||
object fields;
|
|
||||||
if (entry.get(fields))
|
|
||||||
continue;
|
|
||||||
json_keys_check(fields, {"anm2", "gravity", "friction", "airResistance"}, "an area");
|
|
||||||
Area& area = character.areas.emplace_back();
|
|
||||||
area.anm2 = fields["anm2"].error() ? areaAnm2 : character.anm2_get(json_string(fields, "anm2"), renderer, audio);
|
|
||||||
area.gravity = json_float(fields, "gravity", area.gravity);
|
|
||||||
area.friction = json_float(fields, "friction", area.friction);
|
|
||||||
area.airResistance = json_float(fields, "airResistance", area.airResistance);
|
|
||||||
if (!area.anm2)
|
|
||||||
continue;
|
|
||||||
Actor actor(area.anm2);
|
|
||||||
area.bounds = actor.null_rect_get(area.anm2->null_get(boundsNull), -1, 0);
|
|
||||||
area.spawn = actor.null_rect_get(area.anm2->null_get(spawnNull), -1, 0);
|
|
||||||
area.spawn = is_rect_valid(area.spawn) ? area.spawn : area.bounds;
|
|
||||||
}
|
|
||||||
|
|
||||||
object cursor = json_object(root, "cursor");
|
object cursor = json_object(root, "cursor");
|
||||||
object cursorAnimations = json_object(cursor, "animations");
|
object cursorParticles = json_object(cursor, "particles");
|
||||||
json_keys_check(cursor, {"anm2", "animations", "sounds", "particles"}, "cursor");
|
json_keys_check(cursor, {"sounds", "particles"}, "cursor");
|
||||||
json_keys_check(cursorAnimations, {"idle", "click", "hover", "grab", "pan", "zoom", "dispose"}, "cursor animations");
|
if (!cursorParticles["grab"].error())
|
||||||
auto cursor_get = [&](const char* key) {
|
character.cursor.grabParticles = json_strings(cursorParticles, "grab");
|
||||||
return json_string(cursorAnimations, key);
|
json_keys_check(json_object(root, "menu"), {"sounds"}, "menu");
|
||||||
};
|
|
||||||
character.cursor = {cursor_get("click"),
|
|
||||||
cursor_get("dispose"),
|
|
||||||
cursor_get("grab"),
|
|
||||||
cursor_get("hover"),
|
|
||||||
cursor_get("idle"),
|
|
||||||
cursor_get("pan"),
|
|
||||||
cursor_get("zoom"),
|
|
||||||
json_strings(json_object(cursor, "particles"), "grab"),
|
|
||||||
character.anm2_get(json_string(cursor, "anm2"), renderer, audio)};
|
|
||||||
|
|
||||||
object menu = json_object(root, "menu");
|
|
||||||
json_keys_check(menu, {"font", "rounding", "sounds"}, "menu");
|
|
||||||
auto font_get = [&](const char* key) {
|
|
||||||
std::string path = json_string(menu, key);
|
|
||||||
return path.empty() ? nullptr
|
|
||||||
: ImGui::GetIO().Fonts->AddFontFromFileTTF(file_find(character.roots, path).c_str(),
|
|
||||||
FONT_SIZE);
|
|
||||||
};
|
|
||||||
character.menu = {font_get("font"), json_float(menu, "rounding")};
|
|
||||||
character.icons = character.anm2_get(ICONS_PATH, renderer, audio);
|
|
||||||
|
|
||||||
for (auto [name, value] : json_object(root, "particles"))
|
for (auto [name, value] : json_object(root, "particles"))
|
||||||
{
|
{
|
||||||
@@ -308,6 +280,27 @@ static void character_parse(object root, Character& character, Renderer& rendere
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Drops the interaction types no area uses, so a character only gets buttons for what it has.
|
||||||
|
static void interact_types_trim(Character& character)
|
||||||
|
{
|
||||||
|
std::vector<InteractArea> used;
|
||||||
|
for (InteractArea& type : character.interactTypes)
|
||||||
|
{
|
||||||
|
bool isUsed = false;
|
||||||
|
for (InteractArea& area : character.interactAreas)
|
||||||
|
if (area.type == type.type)
|
||||||
|
{
|
||||||
|
area.type = (int)used.size();
|
||||||
|
isUsed = true;
|
||||||
|
}
|
||||||
|
if (!isUsed)
|
||||||
|
continue;
|
||||||
|
used.push_back(type);
|
||||||
|
used.back().type = (int)used.size() - 1;
|
||||||
|
}
|
||||||
|
character.interactTypes = used;
|
||||||
|
}
|
||||||
|
|
||||||
static void items_parse(object root, Character& character, Renderer& renderer, Audio& audio)
|
static void items_parse(object root, Character& character, Renderer& renderer, Audio& audio)
|
||||||
{
|
{
|
||||||
json_keys_check(root,
|
json_keys_check(root,
|
||||||
@@ -503,8 +496,9 @@ static void sounds_parse(object root, DataFile file, Character& character, Audio
|
|||||||
{
|
{
|
||||||
const SoundSource& source = SOUND_SOURCES[i];
|
const SoundSource& source = SOUND_SOURCES[i];
|
||||||
object section = *source.section ? json_object(root, source.section) : root;
|
object section = *source.section ? json_object(root, source.section) : root;
|
||||||
if (source.file == file)
|
object sounds = json_object(section, "sounds");
|
||||||
character.sounds[i] = audio.sounds_get(character.roots, json_strings(json_object(section, "sounds"), source.key));
|
if (source.file == file && !sounds[source.key].error())
|
||||||
|
character.sounds[i] = audio.sounds_get(character.roots, json_strings(sounds, source.key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -513,13 +507,27 @@ void Character::load(Renderer& renderer, Audio& audio)
|
|||||||
if (isLoaded)
|
if (isLoaded)
|
||||||
return;
|
return;
|
||||||
isLoaded = true;
|
isLoaded = true;
|
||||||
Json json;
|
anm2 = anm2_get(ANM2_PATH, renderer, audio);
|
||||||
json.load(folder + "character.json");
|
cursor.anm2 = anm2_get(CURSOR_PATH, renderer, audio);
|
||||||
if (json.isValid)
|
icons = anm2_get(ICONS_PATH, renderer, audio);
|
||||||
|
area.anm2 = anm2_get(AREA_PATH, renderer, audio);
|
||||||
|
if (area.anm2)
|
||||||
{
|
{
|
||||||
|
Actor actor(area.anm2);
|
||||||
|
area.bounds = actor.null_rect_get(area.anm2->null_get(BOUNDS_NULL), -1, 0);
|
||||||
|
area.spawn = actor.null_rect_get(area.anm2->null_get(SPAWN_NULL), -1, 0);
|
||||||
|
area.spawn = is_rect_valid(area.spawn) ? area.spawn : area.bounds;
|
||||||
|
}
|
||||||
|
Json json;
|
||||||
|
for (const std::string& root : {roots.back(), folder})
|
||||||
|
{
|
||||||
|
json.load(root + DATA_FILE);
|
||||||
|
if (!json.isValid)
|
||||||
|
continue;
|
||||||
character_parse(json.root, *this, renderer, audio);
|
character_parse(json.root, *this, renderer, audio);
|
||||||
sounds_parse(json.root, FILE_CHARACTER, *this, audio);
|
sounds_parse(json.root, FILE_CHARACTER, *this, audio);
|
||||||
}
|
}
|
||||||
|
interact_types_trim(*this);
|
||||||
json.load(folder + "items.json");
|
json.load(folder + "items.json");
|
||||||
if (json.isValid)
|
if (json.isValid)
|
||||||
{
|
{
|
||||||
@@ -543,23 +551,17 @@ Actor Character::item_actor_get(int item)
|
|||||||
return actor;
|
return actor;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The area a stage plays in: the last one a stage up to it names, or the first.
|
|
||||||
int Character::area_get(int stage)
|
|
||||||
{
|
|
||||||
if (areas.empty())
|
|
||||||
return -1;
|
|
||||||
int area = 0;
|
|
||||||
for (int i = 0; i < stage && i < (int)stages.size(); i++)
|
|
||||||
if (stages[i].area >= 0 && stages[i].area < (int)areas.size())
|
|
||||||
area = stages[i].area;
|
|
||||||
return area;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Character::save_path_get()
|
std::string Character::save_path_get()
|
||||||
{
|
{
|
||||||
return pref_path_get("saves/" + id + ".txt");
|
return pref_path_get("saves/" + id + ".txt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A stage's name, which its animations end with (past the first) and its dialogue pool is called.
|
||||||
|
std::string Character::stage_name_get(int stage)
|
||||||
|
{
|
||||||
|
return STAGE_PREFIX + std::to_string(stage + 1);
|
||||||
|
}
|
||||||
|
|
||||||
// Every folder in resources/characters holding a character.json, in name order, with its preview loaded.
|
// Every folder in resources/characters holding a character.json, in name order, with its preview loaded.
|
||||||
std::vector<Character> characters_load(Renderer& renderer)
|
std::vector<Character> characters_load(Renderer& renderer)
|
||||||
{
|
{
|
||||||
|
|||||||
+15
-34
@@ -5,8 +5,6 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
struct ImFont;
|
|
||||||
|
|
||||||
enum DataFile
|
enum DataFile
|
||||||
{
|
{
|
||||||
FILE_CHARACTER,
|
FILE_CHARACTER,
|
||||||
@@ -64,23 +62,23 @@ enum Hook
|
|||||||
HOOK_COUNT
|
HOOK_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Stage
|
constexpr const char* ANM2_PATH = "gfx/character/character.anm2";
|
||||||
{
|
constexpr const char* CURSOR_ANIMATION_CLICK = "Click";
|
||||||
std::string animationSuffix;
|
constexpr const char* CURSOR_ANIMATION_DISPOSE = "Dispose";
|
||||||
std::string dialoguePool;
|
constexpr const char* CURSOR_ANIMATION_GRAB = "Grab";
|
||||||
float weight = 0;
|
constexpr const char* CURSOR_ANIMATION_HOVER = "Hover";
|
||||||
int area = -1;
|
constexpr const char* CURSOR_ANIMATION_IDLE = "Default";
|
||||||
};
|
constexpr const char* CURSOR_ANIMATION_PAN = "Pan";
|
||||||
|
constexpr const char* CURSOR_ANIMATION_ZOOM = "Zoom";
|
||||||
|
|
||||||
// A spot on the character the cursor acts on, starting from its type's settings. Types are kept the same way, for
|
// A spot on the character the cursor acts on, starting from its type's settings. Types are kept the same way, for
|
||||||
// their tools and defaults.
|
// their buttons and defaults. Over capacity, the pool named with "Full" after it plays instead, if there is one.
|
||||||
struct InteractArea
|
struct InteractArea
|
||||||
{
|
{
|
||||||
std::string animation;
|
std::string animation;
|
||||||
std::string cursorActive;
|
std::string cursorActive;
|
||||||
std::string cursorHover;
|
std::string cursorHover;
|
||||||
std::string dialoguePool;
|
std::string dialoguePool;
|
||||||
std::string dialoguePoolFull;
|
|
||||||
std::string icon;
|
std::string icon;
|
||||||
std::string idleOnEnd;
|
std::string idleOnEnd;
|
||||||
std::string name;
|
std::string name;
|
||||||
@@ -112,7 +110,7 @@ struct ExpandArea
|
|||||||
int null = -1;
|
int null = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
// A background, with the world's bounds and where items spawn taken from its nulls.
|
// The background, with the world's bounds and where items spawn taken from its nulls.
|
||||||
struct Area
|
struct Area
|
||||||
{
|
{
|
||||||
Anm2* anm2 = nullptr;
|
Anm2* anm2 = nullptr;
|
||||||
@@ -246,7 +244,6 @@ struct Traits
|
|||||||
float caloriesToKilogram = 0;
|
float caloriesToKilogram = 0;
|
||||||
float capacity = 0;
|
float capacity = 0;
|
||||||
float capacityMax = 0;
|
float capacityMax = 0;
|
||||||
float capacityMin = 0;
|
|
||||||
float digestionRate = 0;
|
float digestionRate = 0;
|
||||||
float digestionRateMax = 0;
|
float digestionRateMax = 0;
|
||||||
float digestionRateMin = 0;
|
float digestionRateMin = 0;
|
||||||
@@ -284,23 +281,10 @@ struct Swap
|
|||||||
|
|
||||||
struct Cursor
|
struct Cursor
|
||||||
{
|
{
|
||||||
std::string click;
|
|
||||||
std::string dispose;
|
|
||||||
std::string grab;
|
|
||||||
std::string hover;
|
|
||||||
std::string idle;
|
|
||||||
std::string pan;
|
|
||||||
std::string zoom;
|
|
||||||
std::vector<std::string> grabParticles;
|
std::vector<std::string> grabParticles;
|
||||||
Anm2* anm2 = nullptr;
|
Anm2* anm2 = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Menu
|
|
||||||
{
|
|
||||||
ImFont* font = nullptr;
|
|
||||||
float rounding = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// The items a character can be given, their rarities, and how often they spawn.
|
// The items a character can be given, their rarities, and how often they spawn.
|
||||||
struct Catalog
|
struct Catalog
|
||||||
{
|
{
|
||||||
@@ -316,35 +300,32 @@ struct Catalog
|
|||||||
int item_get(const std::string& id);
|
int item_get(const std::string& id);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Everything a character folder holds. The preview (what the select screen shows) loads at startup, the rest when
|
// Everything a character folder holds, over the defaults in common's character.json. The preview (what the select
|
||||||
// played.
|
// screen shows) loads at startup, the rest when played. Stages are the weights each one starts at.
|
||||||
class Character
|
class Character
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::array<Sounds, SOUND_COUNT> sounds;
|
std::array<Sounds, SOUND_COUNT> sounds;
|
||||||
std::string anm2Path;
|
|
||||||
std::string credits;
|
std::string credits;
|
||||||
std::string description;
|
std::string description;
|
||||||
std::string dialoguePool;
|
|
||||||
std::string folder;
|
std::string folder;
|
||||||
std::string id;
|
std::string id;
|
||||||
std::string key;
|
std::string key;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::unordered_map<std::string, Anm2> anm2s;
|
std::unordered_map<std::string, Anm2> anm2s;
|
||||||
std::unordered_map<std::string, Particle> particles;
|
std::unordered_map<std::string, Particle> particles;
|
||||||
std::vector<Area> areas;
|
|
||||||
std::vector<EatArea> eatAreas;
|
std::vector<EatArea> eatAreas;
|
||||||
std::vector<ExpandArea> expandAreas;
|
std::vector<ExpandArea> expandAreas;
|
||||||
std::vector<InteractArea> interactAreas;
|
std::vector<InteractArea> interactAreas;
|
||||||
std::vector<InteractArea> interactTypes;
|
std::vector<InteractArea> interactTypes;
|
||||||
std::vector<Stage> stages;
|
std::vector<float> stages;
|
||||||
Roots roots;
|
Roots roots;
|
||||||
Alternate alternate;
|
Alternate alternate;
|
||||||
|
Area area;
|
||||||
Animations animations;
|
Animations animations;
|
||||||
Catalog catalog;
|
Catalog catalog;
|
||||||
Cursor cursor;
|
Cursor cursor;
|
||||||
Dialogue dialogue;
|
Dialogue dialogue;
|
||||||
Menu menu;
|
|
||||||
Swap blink;
|
Swap blink;
|
||||||
Swap talk;
|
Swap talk;
|
||||||
Traits traits;
|
Traits traits;
|
||||||
@@ -360,8 +341,8 @@ public:
|
|||||||
void load(Renderer& renderer, Audio& audio);
|
void load(Renderer& renderer, Audio& audio);
|
||||||
Anm2* anm2_get(const std::string& path, Renderer& renderer, Audio& audio);
|
Anm2* anm2_get(const std::string& path, Renderer& renderer, Audio& audio);
|
||||||
Actor item_actor_get(int item);
|
Actor item_actor_get(int item);
|
||||||
int area_get(int stage);
|
|
||||||
std::string save_path_get();
|
std::string save_path_get();
|
||||||
|
std::string stage_name_get(int stage);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<Character> characters_load(Renderer& renderer);
|
std::vector<Character> characters_load(Renderer& renderer);
|
||||||
+6
-3
@@ -1,5 +1,7 @@
|
|||||||
#include "play.h"
|
#include "play.h"
|
||||||
|
|
||||||
|
constexpr const char* FULL_SUFFIX = "Full";
|
||||||
|
|
||||||
constexpr float AUTO_ACCELERATION = 0.45f;
|
constexpr float AUTO_ACCELERATION = 0.45f;
|
||||||
constexpr float AUTO_ANGLE_STEP = 0.08f;
|
constexpr float AUTO_ANGLE_STEP = 0.08f;
|
||||||
constexpr float AUTO_BRAKING = 2;
|
constexpr float AUTO_BRAKING = 2;
|
||||||
@@ -64,8 +66,9 @@ void Interactions::update(Play& play, Pointer pointer)
|
|||||||
bool isReady = cooldowns[i] <= 0;
|
bool isReady = cooldowns[i] <= 0;
|
||||||
cooldowns[i] = std::max(0, entry.dialogueCooldown);
|
cooldowns[i] = std::max(0, entry.dialogueCooldown);
|
||||||
Dialogue& dialogue = character.dialogue;
|
Dialogue& dialogue = character.dialogue;
|
||||||
bool isFull = body.is_over_capacity() && dialogue.pools.contains(entry.dialoguePoolFull);
|
std::string pool = entry.dialoguePool;
|
||||||
const std::string& pool = isFull ? entry.dialoguePoolFull : entry.dialoguePool;
|
if (body.is_over_capacity() && dialogue.pools.contains(pool + FULL_SUFFIX))
|
||||||
|
pool += FULL_SUFFIX;
|
||||||
if (dialogue.pools.contains(pool))
|
if (dialogue.pools.contains(pool))
|
||||||
{
|
{
|
||||||
if (text.is_interruptible() && isReady)
|
if (text.is_interruptible() && isReady)
|
||||||
@@ -92,7 +95,7 @@ void Interactions::update(Play& play, Pointer pointer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((wasInteracting && !isInteracting) || (wasHovering && !isHovering))
|
if ((wasInteracting && !isInteracting) || (wasHovering && !isHovering))
|
||||||
active.play(character.cursor.idle);
|
active.play(CURSOR_ANIMATION_IDLE);
|
||||||
if (wasHolding && !isHolding && !body.actor.pending)
|
if (wasHolding && !isHolding && !body.actor.pending)
|
||||||
body.idle_play();
|
body.idle_play();
|
||||||
pointerPrevious = pointer.position;
|
pointerPrevious = pointer.position;
|
||||||
|
|||||||
+8
-9
@@ -87,8 +87,7 @@ void Items::update(Play& play, Pointer pointer)
|
|||||||
Stats& stats = body.stats;
|
Stats& stats = body.stats;
|
||||||
Actor& active = play.pointer_actor_get();
|
Actor& active = play.pointer_actor_get();
|
||||||
Audio& audio = *play.audio;
|
Audio& audio = *play.audio;
|
||||||
int areaIndex = character.area_get(body.stage);
|
Area& area = character.area;
|
||||||
Area area = areaIndex >= 0 ? character.areas[areaIndex] : Area{};
|
|
||||||
Rect bounds = play.bounds;
|
Rect bounds = play.bounds;
|
||||||
bool isOver = body.is_over_capacity();
|
bool isOver = body.is_over_capacity();
|
||||||
auto text_play = [&](Hook hook) {
|
auto text_play = [&](Hook hook) {
|
||||||
@@ -97,7 +96,7 @@ void Items::update(Play& play, Pointer pointer)
|
|||||||
};
|
};
|
||||||
auto release = [&](bool isThrown) {
|
auto release = [&](bool isThrown) {
|
||||||
drop();
|
drop();
|
||||||
active.play(character.cursor.idle);
|
active.play(CURSOR_ANIMATION_IDLE);
|
||||||
if (!isThrown && !body.actor.pending)
|
if (!isThrown && !body.actor.pending)
|
||||||
body.idle_play();
|
body.idle_play();
|
||||||
};
|
};
|
||||||
@@ -122,7 +121,7 @@ void Items::update(Play& play, Pointer pointer)
|
|||||||
spawnDelay--;
|
spawnDelay--;
|
||||||
for (int id : spawnQueue)
|
for (int id : spawnQueue)
|
||||||
{
|
{
|
||||||
Rect spawn = areaIndex >= 0 ? area.spawn : bounds;
|
Rect spawn = area.anm2 ? area.spawn : bounds;
|
||||||
Vec2 position = {spawn.x + random_get(0, spawn.w), spawn.y + random_get(0, spawn.h)};
|
Vec2 position = {spawn.x + random_get(0, spawn.w), spawn.y + random_get(0, spawn.h)};
|
||||||
if (!discovered[id])
|
if (!discovered[id])
|
||||||
{
|
{
|
||||||
@@ -172,7 +171,7 @@ void Items::update(Play& play, Pointer pointer)
|
|||||||
stats.calories += item.calories;
|
stats.calories += item.calories;
|
||||||
stats.totalCalories += item.calories;
|
stats.totalCalories += item.calories;
|
||||||
stats.capacity =
|
stats.capacity =
|
||||||
std::clamp(stats.capacity + item.capacityBonus, traits.capacityMin, traits.capacityMax);
|
std::clamp(stats.capacity + item.capacityBonus, traits.capacity, traits.capacityMax);
|
||||||
stats.digestionRate = std::clamp(stats.digestionRate + item.digestionBonus,
|
stats.digestionRate = std::clamp(stats.digestionRate + item.digestionBonus,
|
||||||
traits.digestionRateMin, traits.digestionRateMax);
|
traits.digestionRateMin, traits.digestionRateMax);
|
||||||
stats.totalConsumed++;
|
stats.totalConsumed++;
|
||||||
@@ -244,7 +243,7 @@ void Items::update(Play& play, Pointer pointer)
|
|||||||
{
|
{
|
||||||
isHovered = true;
|
isHovered = true;
|
||||||
tooltip = held == i || pointer.isAutomated ? -1 : entity.id;
|
tooltip = held == i || pointer.isAutomated ? -1 : entity.id;
|
||||||
active.play(character.cursor.hover, TRANSITION_IF_IDLE);
|
active.play(CURSOR_ANIMATION_HOVER, TRANSITION_IF_IDLE);
|
||||||
play.cursorState = CURSOR_HOVER;
|
play.cursorState = CURSOR_HOVER;
|
||||||
if (pointer.isClicked)
|
if (pointer.isClicked)
|
||||||
{
|
{
|
||||||
@@ -254,14 +253,14 @@ void Items::update(Play& play, Pointer pointer)
|
|||||||
}
|
}
|
||||||
if (pointer.isDown)
|
if (pointer.isDown)
|
||||||
{
|
{
|
||||||
active.play(character.cursor.grab);
|
active.play(CURSOR_ANIMATION_GRAB);
|
||||||
play.cursorState = CURSOR_ACTION;
|
play.cursorState = CURSOR_ACTION;
|
||||||
held = moved = i;
|
held = moved = i;
|
||||||
}
|
}
|
||||||
if (pointer.isRightClicked)
|
if (pointer.isRightClicked)
|
||||||
{
|
{
|
||||||
audio.play(character.sounds[SOUND_DISPOSE]);
|
audio.play(character.sounds[SOUND_DISPOSE]);
|
||||||
active.play(character.cursor.dispose, TRANSITION_FORCE, INTERRUPT_NEVER);
|
active.play(CURSOR_ANIMATION_DISPOSE, TRANSITION_FORCE, INTERRUPT_NEVER);
|
||||||
play.particles_spawn(character.catalog.disposeParticles, position);
|
play.particles_spawn(character.catalog.disposeParticles, position);
|
||||||
held = held == i ? -1 : held > i ? held - 1 : held;
|
held = held == i ? -1 : held > i ? held - 1 : held;
|
||||||
moved = moved == i ? -1 : moved > i ? moved - 1 : moved;
|
moved = moved == i ? -1 : moved > i ? moved - 1 : moved;
|
||||||
@@ -313,6 +312,6 @@ void Items::update(Play& play, Pointer pointer)
|
|||||||
held = (int)list.size() - 1;
|
held = (int)list.size() - 1;
|
||||||
}
|
}
|
||||||
if (wasHovered && !isHovered)
|
if (wasHovered && !isHovered)
|
||||||
active.play(character.cursor.idle);
|
active.play(CURSOR_ANIMATION_IDLE);
|
||||||
pointerPrevious = pointer.position;
|
pointerPrevious = pointer.position;
|
||||||
}
|
}
|
||||||
+2
-6
@@ -40,7 +40,6 @@ struct App
|
|||||||
Renderer renderer;
|
Renderer renderer;
|
||||||
Settings settings;
|
Settings settings;
|
||||||
Ui ui;
|
Ui ui;
|
||||||
ImFont* font = nullptr;
|
|
||||||
SDL_GLContext context = nullptr;
|
SDL_GLContext context = nullptr;
|
||||||
SDL_Window* window = nullptr;
|
SDL_Window* window = nullptr;
|
||||||
Uint64 tickPrevious = 0;
|
Uint64 tickPrevious = 0;
|
||||||
@@ -65,7 +64,6 @@ static void play_exit()
|
|||||||
return;
|
return;
|
||||||
app.play.save();
|
app.play.save();
|
||||||
app.settings.lastCharacter.clear();
|
app.settings.lastCharacter.clear();
|
||||||
ImGui::GetIO().FontDefault = app.font;
|
|
||||||
style_set(app.settings.color);
|
style_set(app.settings.color);
|
||||||
app.screen = SCREEN_SELECT;
|
app.screen = SCREEN_SELECT;
|
||||||
}
|
}
|
||||||
@@ -80,8 +78,7 @@ static void play_start(const std::string& key, Game game)
|
|||||||
app.ui.characterIndex = index;
|
app.ui.characterIndex = index;
|
||||||
app.play.set(character, game, app.renderer, app.audio);
|
app.play.set(character, game, app.renderer, app.audio);
|
||||||
app.settings.lastCharacter = character.key;
|
app.settings.lastCharacter = character.key;
|
||||||
ImGui::GetIO().FontDefault = character.menu.font ? character.menu.font : app.font;
|
style_set(app.settings.isCharacterColor ? character.color : app.settings.color);
|
||||||
style_set(app.settings.isCharacterColor ? character.color : app.settings.color, character.menu.rounding);
|
|
||||||
app.screen = SCREEN_PLAY;
|
app.screen = SCREEN_PLAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,8 +130,7 @@ static void init()
|
|||||||
ImGui::StyleColorsDark();
|
ImGui::StyleColorsDark();
|
||||||
ImGui_ImplSDL3_InitForOpenGL(app.window, app.context);
|
ImGui_ImplSDL3_InitForOpenGL(app.window, app.context);
|
||||||
ImGui_ImplOpenGL3_Init(GLSL_VERSION);
|
ImGui_ImplOpenGL3_Init(GLSL_VERSION);
|
||||||
app.font = io.Fonts->AddFontFromFileTTF(path_get("font/font.ttf").c_str(), FONT_NORMAL);
|
io.FontDefault = io.Fonts->AddFontFromFileTTF(path_get("font/font.ttf").c_str(), FONT_NORMAL);
|
||||||
io.FontDefault = app.font;
|
|
||||||
style_set(app.settings.color);
|
style_set(app.settings.color);
|
||||||
|
|
||||||
app.ui.audio = &app.audio;
|
app.ui.audio = &app.audio;
|
||||||
|
|||||||
+8
-8
@@ -115,10 +115,10 @@ void Play::save()
|
|||||||
// Shows the area of the body's stage, with its bounds.
|
// Shows the area of the body's stage, with its bounds.
|
||||||
void Play::area_set()
|
void Play::area_set()
|
||||||
{
|
{
|
||||||
int index = character->area_get(body.stage);
|
Area& world = character->area;
|
||||||
if (index >= 0 && area.anm2 != character->areas[index].anm2)
|
if (world.anm2 && area.anm2 != world.anm2)
|
||||||
area = Actor(character->areas[index].anm2);
|
area = Actor(world.anm2);
|
||||||
bounds = index >= 0 && is_rect_valid(character->areas[index].bounds) ? character->areas[index].bounds : WORLD_BOUNDS;
|
bounds = is_rect_valid(world.bounds) ? world.bounds : WORLD_BOUNDS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Play::toast_add(const std::string& message)
|
void Play::toast_add(const std::string& message)
|
||||||
@@ -233,7 +233,7 @@ void Play::update(Vec2 screenSize)
|
|||||||
|
|
||||||
if (cursor.anm2 && !autofeed.isActive && pointer.isClicked && !items.isHovered && !interactions.isHovering &&
|
if (cursor.anm2 && !autofeed.isActive && pointer.isClicked && !items.isHovered && !interactions.isHovering &&
|
||||||
cursorState == CURSOR_DEFAULT)
|
cursorState == CURSOR_DEFAULT)
|
||||||
cursor.play(character->cursor.click, TRANSITION_FORCE, INTERRUPT_ALWAYS);
|
cursor.play(CURSOR_ANIMATION_CLICK, TRANSITION_FORCE, INTERRUPT_ALWAYS);
|
||||||
|
|
||||||
body.update(events);
|
body.update(events);
|
||||||
events_handle(screenSize);
|
events_handle(screenSize);
|
||||||
@@ -250,7 +250,7 @@ void Play::update(Vec2 screenSize)
|
|||||||
{
|
{
|
||||||
bool isActive = (each == &autoCursor) == autofeed.isActive;
|
bool isActive = (each == &autoCursor) == autofeed.isActive;
|
||||||
if ((!isActive || cursorState == CURSOR_DEFAULT) && !each->pending)
|
if ((!isActive || cursorState == CURSOR_DEFAULT) && !each->pending)
|
||||||
each->play(character->cursor.idle, TRANSITION_IF_IDLE);
|
each->play(CURSOR_ANIMATION_IDLE, TRANSITION_IF_IDLE);
|
||||||
each->update(*audio);
|
each->update(*audio);
|
||||||
}
|
}
|
||||||
cursorState = CURSOR_DEFAULT;
|
cursorState = CURSOR_DEFAULT;
|
||||||
@@ -323,14 +323,14 @@ void Camera::update(Play& play, Vec2 screenSize)
|
|||||||
if (ImGui::IsMouseDown(ImGuiMouseButton_Middle) ||
|
if (ImGui::IsMouseDown(ImGuiMouseButton_Middle) ||
|
||||||
(ImGui::IsMouseDown(ImGuiMouseButton_Left) && ImGui::IsKeyDown(ImGuiMod_Ctrl)))
|
(ImGui::IsMouseDown(ImGuiMouseButton_Left) && ImGui::IsKeyDown(ImGuiMod_Ctrl)))
|
||||||
{
|
{
|
||||||
play.cursor.play(cursor.pan);
|
play.cursor.play(CURSOR_ANIMATION_PAN);
|
||||||
pan = pan - Vec2{io.MouseDelta.x, io.MouseDelta.y} / (zoom / PERCENT);
|
pan = pan - Vec2{io.MouseDelta.x, io.MouseDelta.y} / (zoom / PERCENT);
|
||||||
}
|
}
|
||||||
if (io.MouseWheel != 0)
|
if (io.MouseWheel != 0)
|
||||||
{
|
{
|
||||||
Vec2 mouse = {io.MousePos.x, io.MousePos.y};
|
Vec2 mouse = {io.MousePos.x, io.MousePos.y};
|
||||||
Vec2 world = world_get(mouse);
|
Vec2 world = world_get(mouse);
|
||||||
play.cursor.play(cursor.zoom);
|
play.cursor.play(CURSOR_ANIMATION_ZOOM);
|
||||||
int index = std::clamp(zoom_index_get(zoom) + (io.MouseWheel > 0 ? 1 : -1), 0,
|
int index = std::clamp(zoom_index_get(zoom) + (io.MouseWheel > 0 ? 1 : -1), 0,
|
||||||
(int)std::size(ZOOM_LADDER) - 1);
|
(int)std::size(ZOOM_LADDER) - 1);
|
||||||
zoom = ZOOM_LADDER[index];
|
zoom = ZOOM_LADDER[index];
|
||||||
|
|||||||
+7
-9
@@ -24,6 +24,7 @@ constexpr float INDICATOR_BOB = 0.12f;
|
|||||||
constexpr float INDICATOR_SIZE = 0.35f;
|
constexpr float INDICATOR_SIZE = 0.35f;
|
||||||
constexpr float INDICATOR_SPEED = 2;
|
constexpr float INDICATOR_SPEED = 2;
|
||||||
constexpr float ITEM_SIZE = 96;
|
constexpr float ITEM_SIZE = 96;
|
||||||
|
constexpr float ROUNDING = 10;
|
||||||
constexpr float BAR_WIDTH_MIN = 120;
|
constexpr float BAR_WIDTH_MIN = 120;
|
||||||
constexpr float BUTTON_SIZE_MAX = 96;
|
constexpr float BUTTON_SIZE_MAX = 96;
|
||||||
constexpr float BUTTON_SIZE_MIN = 56;
|
constexpr float BUTTON_SIZE_MIN = 56;
|
||||||
@@ -108,10 +109,10 @@ static Palette palette_get(Color color)
|
|||||||
{ImGuiCol_ChildBg, shade(COLOR_BACKGROUND, 0)}}};
|
{ImGuiCol_ChildBg, shade(COLOR_BACKGROUND, 0)}}};
|
||||||
}
|
}
|
||||||
|
|
||||||
void style_set(Color color, float rounding)
|
void style_set(Color color)
|
||||||
{
|
{
|
||||||
ImGuiStyle& style = ImGui::GetStyle();
|
ImGuiStyle& style = ImGui::GetStyle();
|
||||||
style.WindowRounding = style.FrameRounding = style.GrabRounding = style.PopupRounding = rounding;
|
style.WindowRounding = style.FrameRounding = style.GrabRounding = style.PopupRounding = ROUNDING;
|
||||||
style.ScrollbarSize = SCROLLBAR_SIZE;
|
style.ScrollbarSize = SCROLLBAR_SIZE;
|
||||||
style.FontSizeBase = FONT_NORMAL;
|
style.FontSizeBase = FONT_NORMAL;
|
||||||
for (auto [id, value] : palette_get(color))
|
for (auto [id, value] : palette_get(color))
|
||||||
@@ -461,9 +462,7 @@ void Ui::taskbar_draw(Play& play, ImVec2 screen)
|
|||||||
Items& items = play.items;
|
Items& items = play.items;
|
||||||
Dialogue& dialogue = data.dialogue;
|
Dialogue& dialogue = data.dialogue;
|
||||||
ImGuiStyle& style = ImGui::GetStyle();
|
ImGuiStyle& style = ImGui::GetStyle();
|
||||||
std::string moodPool = body.stage <= 0
|
std::string moodPool = data.stage_name_get(body.stage);
|
||||||
? data.dialoguePool
|
|
||||||
: data.stages[std::min(body.stage, (int)data.stages.size()) - 1].dialoguePool;
|
|
||||||
|
|
||||||
const char* labels[] = {"Weight", "Capacity", "Digestion"};
|
const char* labels[] = {"Weight", "Capacity", "Digestion"};
|
||||||
bool isMax = body.stage >= (int)data.stages.size();
|
bool isMax = body.stage >= (int)data.stages.size();
|
||||||
@@ -777,8 +776,7 @@ void Ui::settings_draw(Settings& settings)
|
|||||||
isColorChanged = true;
|
isColorChanged = true;
|
||||||
}
|
}
|
||||||
if (isColorChanged)
|
if (isColorChanged)
|
||||||
style_set(character && settings.isCharacterColor ? character->color : settings.color,
|
style_set(character && settings.isCharacterColor ? character->color : settings.color);
|
||||||
character ? character->menu.rounding : 0);
|
|
||||||
if (!character)
|
if (!character)
|
||||||
return;
|
return;
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
@@ -837,7 +835,7 @@ void Ui::cheats_draw(Play& play)
|
|||||||
Stats& stats = body.stats;
|
Stats& stats = body.stats;
|
||||||
Traits& traits = data.traits;
|
Traits& traits = data.traits;
|
||||||
is_fx(ImGui::SliderFloat("Calories", &stats.calories, 0, body.capacity_max_get(), "%0.0f kcal"));
|
is_fx(ImGui::SliderFloat("Calories", &stats.calories, 0, body.capacity_max_get(), "%0.0f kcal"));
|
||||||
is_fx(ImGui::SliderFloat("Capacity", &stats.capacity, traits.capacityMin, traits.capacityMax, "%0.0f kcal"));
|
is_fx(ImGui::SliderFloat("Capacity", &stats.capacity, traits.capacity, traits.capacityMax, "%0.0f kcal"));
|
||||||
if (is_fx(ImGui::SliderFloat("Weight", &stats.weight, traits.weight, traits.weightMax, "%0.2f kg")))
|
if (is_fx(ImGui::SliderFloat("Weight", &stats.weight, traits.weight, traits.weightMax, "%0.2f kg")))
|
||||||
{
|
{
|
||||||
body.stage_set(body.stage_get(stats.weight));
|
body.stage_set(body.stage_get(stats.weight));
|
||||||
@@ -1043,7 +1041,7 @@ void Ui::select_update(std::vector<Character>& characters, Settings& settings)
|
|||||||
}
|
}
|
||||||
if (is_fx(ImGui::BeginTabItem("In Game")))
|
if (is_fx(ImGui::BeginTabItem("In Game")))
|
||||||
{
|
{
|
||||||
Anm2* anm2 = selected->anm2_get(selected->anm2Path, *renderer, *audio);
|
Anm2* anm2 = selected->anm2_get(ANM2_PATH, *renderer, *audio);
|
||||||
if (previewActor.anm2 != anm2 || !anm2)
|
if (previewActor.anm2 != anm2 || !anm2)
|
||||||
previewActor = Actor(anm2);
|
previewActor = Actor(anm2);
|
||||||
previewActor.update(*audio);
|
previewActor.update(*audio);
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ struct Command
|
|||||||
|
|
||||||
using Commands = std::vector<Command>;
|
using Commands = std::vector<Command>;
|
||||||
|
|
||||||
void style_set(Color color, float rounding = 0);
|
void style_set(Color color);
|
||||||
|
|
||||||
// The taskbar, its windows, the dialogue box, toasts and the select screen.
|
// The taskbar, its windows, the dialogue box, toasts and the select screen.
|
||||||
class Ui
|
class Ui
|
||||||
|
|||||||
Reference in new issue
Block a user