Desert notes after retiring the alternative renders; mod documentation
This commit is contained in:
1 parent
c2a5074337
commit
36882e50cf
40 files changed
+1866
-69
No files matched your search
@@ -12,6 +12,7 @@ obj/
|
|||||||
gen/
|
gen/
|
||||||
obj/
|
obj/
|
||||||
bin/
|
bin/
|
||||||
|
art_sources/
|
||||||
|
|
||||||
*.tres~
|
*.tres~
|
||||||
*.tscn~
|
*.tscn~
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# Desert expansion
|
||||||
|
|
||||||
|
From the village, walk west along the existing horizontal road to cell (0,9).
|
||||||
|
This enters `rpg_desert` at (38,12). Its eastern edge at (39,12) returns to
|
||||||
|
village (1,9); the landing cells are outside the trigger cells.
|
||||||
|
|
||||||
|
The 40x24 map uses the separate `desert` tileset: sand and rocky surface patches,
|
||||||
|
an adobe house, a caravan shelter, a ruined tower, date palms, cacti and scrub.
|
||||||
|
Buildings are complete multi-cell compositions. Their occupied footprints
|
||||||
|
block movement; no interior map is provided. Door/notice interactions are at
|
||||||
|
the front of the buildings.
|
||||||
|
|
||||||
|
Five visible monsters use two new character templates:
|
||||||
|
`rpg_desert_scorpion` and `rpg_desert_beetle`. Interact with E/F to fight them.
|
||||||
|
Victories grant coins and small resources through normal battle loot and set
|
||||||
|
per-monster flags; defeated monsters stay gone across save/load.
|
||||||
|
|
||||||
|
Original barrel tiles were converted to actor events in the village and
|
||||||
|
elder's house. Two more supply barrels are beside village buildings and
|
||||||
|
three are in the desert. Every barrel has independent closed/open pages,
|
||||||
|
its own saved flag, a distinct open sprite, and one-time small rewards.
|
||||||
|
Reopening only displays the empty message. Existing chests are unchanged.
|
||||||
|
Barrel definitions: `map_events/<map>/barrels.json` and desert `events.json`.
|
||||||
|
|
||||||
|
## Art choices
|
||||||
|
|
||||||
|
The map uses the `desert` tileset (built-in image_gen source). Two alternative
|
||||||
|
ComfyUI renders with identical logical ids (`qwen_image_21_clean_00110_.png`,
|
||||||
|
`qwen_image_21_clean_00111_.png`) were used by no map and were removed from the
|
||||||
|
mod on 2026-10-29; their installed tilesets are archived in
|
||||||
|
`../art_sources/cthangover/village_pack/retired_tilesets_2026-10-29/`, and
|
||||||
|
`build_desert_v1.cjs` still builds them into `gen/desert_v1/<name>/` for comparison.
|
||||||
|
Installing one again as a tileset swaps the desert art without moving buildings or
|
||||||
|
changing event/collision coordinates.
|
||||||
|
|
||||||
|
The original ComfyUI PNGs and embedded workflows/prompts are archived under
|
||||||
|
`gen/desert_v1/`. Background extraction used built-in image_gen; its separate
|
||||||
|
outputs and exact extraction prompts are beside the originals. Full-resolution
|
||||||
|
selected sheets and individual alternative objects live in
|
||||||
|
`../art_sources/cthangover/village_pack/art_sources/desert_qwen110/` and `desert_qwen111/`.
|
||||||
|
The main source sheets are in `art_sources/desert_v1/`.
|
||||||
|
Prompts for the main generation are in `comfy/rpg_life/desert_v1_prompts.json`.
|
||||||
|
|
||||||
|
`comfy/rpg_life/build_desert_v1.cjs` assembles images, charsets, metadata and the
|
||||||
|
authored content (Node with `sharp` required). It also writes
|
||||||
|
`gen/desert_v1/map_preview.png` and `variants_preview.png`.
|
||||||
|
It is an authoring builder: rerunning restores its authored map placements.
|
||||||
|
It does not modify game collection definitions.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
`--itest=desert` / TestRunner `--test desert` checks one-time barrel rewards,
|
||||||
|
save/load, both transitions by real movement, both monster battles and loot,
|
||||||
|
and persistent defeated-monster/open-barrel states. Optional
|
||||||
|
`--shots=<directory>` captures the desert in a windowed run.
|
||||||
|
The test uses the reserved save slot `slot_9918` and removes it on success.
|
||||||
|
|
||||||
|
2026-10-28 host verification: desert 29/29, rpg_walk 34/34,
|
||||||
|
resource_atlas_rpg_plus 39/39, locale_atlas 89/89 (zero locale errors).
|
||||||
|
The existing shutdown RID diagnostics are separate from those passing assertions.
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
{
|
||||||
|
"Kind": "character",
|
||||||
|
"Items": [
|
||||||
|
{
|
||||||
|
"Id": "rpg_desert_scorpion",
|
||||||
|
"Name": "rpg_plus/desert/enemy/scorpion/name",
|
||||||
|
"Image": "characters/desert/scorpion",
|
||||||
|
"Behaviour": "base",
|
||||||
|
"Level": 2,
|
||||||
|
"Point": 1,
|
||||||
|
"Health": 16,
|
||||||
|
"Defence": 0,
|
||||||
|
"Attack": 4,
|
||||||
|
"Exp": 8,
|
||||||
|
"RecruitmentChance": 0,
|
||||||
|
"Actions": [
|
||||||
|
"physics/attack"
|
||||||
|
],
|
||||||
|
"Stats": {
|
||||||
|
"str": 4,
|
||||||
|
"vit": 3,
|
||||||
|
"agi": 6
|
||||||
|
},
|
||||||
|
"Loot": [
|
||||||
|
{
|
||||||
|
"ItemId": "money/coin",
|
||||||
|
"MinCount": 2,
|
||||||
|
"MaxCount": 5,
|
||||||
|
"Probability": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemId": "rpg_demo/stones",
|
||||||
|
"MinCount": 1,
|
||||||
|
"MaxCount": 2,
|
||||||
|
"Probability": 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "rpg_desert_beetle",
|
||||||
|
"Name": "rpg_plus/desert/enemy/beetle/name",
|
||||||
|
"Image": "characters/desert/beetle",
|
||||||
|
"Behaviour": "base",
|
||||||
|
"Level": 1,
|
||||||
|
"Point": 1,
|
||||||
|
"Health": 12,
|
||||||
|
"Defence": 1,
|
||||||
|
"Attack": 3,
|
||||||
|
"Exp": 5,
|
||||||
|
"RecruitmentChance": 0,
|
||||||
|
"Actions": [
|
||||||
|
"physics/attack"
|
||||||
|
],
|
||||||
|
"Stats": {
|
||||||
|
"str": 3,
|
||||||
|
"vit": 3,
|
||||||
|
"agi": 3
|
||||||
|
},
|
||||||
|
"Loot": [
|
||||||
|
{
|
||||||
|
"ItemId": "money/coin",
|
||||||
|
"MinCount": 2,
|
||||||
|
"MaxCount": 5,
|
||||||
|
"Probability": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemId": "rpg_plus/resin",
|
||||||
|
"MinCount": 1,
|
||||||
|
"MaxCount": 2,
|
||||||
|
"Probability": 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 136 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 131 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
+115
@@ -0,0 +1,115 @@
|
|||||||
|
# RPG Plus — a woodland RPG built from mods
|
||||||
|
|
||||||
|
RPG Plus is a complete small RPG: a visual-novel prologue where you choose who your hero was, then a tile-map village and forest, a mystery about the forest's lost light, side quests, a workbench, a merchant, a desert to the west and turn-based battles. The game is made of data files and scenarios only; its rules — stats, gear, checks, battles, crafting — come from separate mods that any other game can use too.
|
||||||
|
|
||||||
|
Start it from **Play → RPG Plus → New game**.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## The story and how to play
|
||||||
|
|
||||||
|
1. **The crossroads.** Choose your past: a **soldier** (warrior), a **mage's apprentice** (mage) or a **wanderer** (rogue). The choice sets your stats and your starting gear. You start with 25 coins.
|
||||||
|
2. **The village by the Grey Forest.** Walk with WASD or the arrows, run with Shift, use E or F to talk and interact. The **journal** always lists your concrete next step.
|
||||||
|
3. **The elder** asks about the missing light or sends you straight after the wolf at the eastern cave.
|
||||||
|
4. **The forest.** Gather brushwood and mushrooms; slimes drop living resin and coins. The wolf always drops an amber heart and enough fur for a mantle.
|
||||||
|
5. **The well workbench.** Make a woodland salve (mushroom + resin), a warden mantle (2 fur + 2 resin) or the ward lantern (amber + 2 resin + 2 branches). Put the mantle on from the bag: it changes your real battle numbers.
|
||||||
|
6. **The herbalist** by the western fence needs salve; helping her leads to a missing courier in the southern forest.
|
||||||
|
7. **The shrine.** Take the lantern through the cave behind the wolf to the ruined shrine. Return the light to the forest, or strike a pact for the village. Both endings finish the adventure, and the elder's last words follow your choice.
|
||||||
|
|
||||||
|
Other people in the village: the **trainer** takes you to the training yard (experience once, and a strength check to lift a log), and the **seer** reads your luck. The hearth by the well restores health and mana. Rewards cannot be collected twice.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### The desert
|
||||||
|
|
||||||
|
From the village, follow the main road west off the edge of the map to reach **the Dry March**: sand, an adobe house, a caravan shelter, a ruined tower, palms and cacti. Five scorpions and beetles wait to be fought, and supply barrels hold small one-time rewards. The east edge leads back to the village.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Your hero
|
||||||
|
|
||||||
|
Open the **character sheet** from the toolbar to see your class, level and experience, attributes (Strength, Agility, Intelligence, Vitality, Charisma, Luck), combat values and gear in the weapon, body and accessory slots. Equip things from the bag.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Battles use the command-style (FF) battle with damage computed by formulas. Some dialog options test an attribute against a d20 roll, such as the trainer's **[Strength 12]**.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## How the game is assembled
|
||||||
|
|
||||||
|
RPG Plus is the collection **RPG Plus**. Its blocks:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Story rpg_plus_game (this mod)
|
||||||
|
Village and forest rpg_demo_game: the maps, people and items this game extends
|
||||||
|
Assets and rules village_pack (art), classic_rpg_pack (stats, classes, gear)
|
||||||
|
Stats and checks stats_base, stat_checks
|
||||||
|
Tile world rpg
|
||||||
|
Items, gear, trade items_base, equipment, trading, crafting
|
||||||
|
2D engine shell2d_base, dialog_base, characters_base, audio_base
|
||||||
|
Battle turn_battle_base, ff_battle, battle_formulas
|
||||||
|
Card battle card_battle (optional)
|
||||||
|
Interface ui_base, menu_base
|
||||||
|
Tile map editor rpg_editor (optional)
|
||||||
|
Data editors schema_tools, items_editor, characters_editor (optional)
|
||||||
|
Tile renderer tile_topdown_base, tile_isometric_base
|
||||||
|
```
|
||||||
|
|
||||||
|
Settings of the collection: `battle.core` is `ff_battle`, checks roll `d20`, the hero's map sprite is `village/hero`, maps are drawn top-down. Change them in **Mods & Collections → Collections → RPG Plus → Edit scheme**.
|
||||||
|
|
||||||
|
## For authors: change it without code
|
||||||
|
|
||||||
|
```filestree
|
||||||
|
rpg_plus_game/
|
||||||
|
├── scenarios/rpg_plus/ prologue, training_yard, events/ (trainer, seer), woodland/, desert/
|
||||||
|
├── map_events/<map>/ new people, the workbench, cave door, barrels; same ids replace rpg_demo_game's
|
||||||
|
├── maps/ rpg_woodland_shrine.json, rpg_desert.json
|
||||||
|
├── scenes/ crossroads, training yard, shrine, desert
|
||||||
|
├── quests/woodland.json journal stages
|
||||||
|
├── items/, recipes/, stations/ materials, crafted things, the workbench's menu
|
||||||
|
├── patches/character.json monster loot added to rpg_demo_game's monsters
|
||||||
|
├── patches/shop.json more goods for the village merchant
|
||||||
|
└── characters/, charsets/, backgrounds/, locale/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Extend another mod instead of copying it.** The village, house and forest still come from `rpg_demo_game`. An event with the same id as one there replaces it (the elder gets new dialogs); new ids add people. Patches add loot to its monsters and goods to its shop.
|
||||||
|
|
||||||
|
**Checks and conditions in scenarios.** The seer tests Luck and then looks at your gear:
|
||||||
|
|
||||||
|
```scenario
|
||||||
|
text "— Want to know what awaits you? Give me your hand. Not that one — the one you don't steal with." key=rpg_plus/seer/hello
|
||||||
|
check luk 14 who=rpg_hero else=fail -> :ok
|
||||||
|
|
||||||
|
:ok
|
||||||
|
text "— The wolf at the cave fears fire more than steel. And strike where it has no shield. That part is free. The rest isn't." key=rpg_plus/seer/fortune
|
||||||
|
when "!equipped(rpg_hero, 'classic/clover_charm')" -> :charm
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
`when "item('rpg_plus/salve') >= 1"` checks the bag, `level(rpg_hero) >= 3` the hero's level; flags make one-time rewards safe. Lines are written in Russian with a `key=` each; the translations are in `locale/scenarios_<lang>.properties`.
|
||||||
|
|
||||||
|
**A recipe** is data in `recipes/woodland.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Id": "rpg_plus/salve",
|
||||||
|
"Name": "rpg_plus/recipe/salve/name",
|
||||||
|
"Type": "Cooking",
|
||||||
|
"Input": [
|
||||||
|
{ "Id": "rpg_demo/mushroom", "Count": 1 },
|
||||||
|
{ "Id": "rpg_plus/resin", "Count": 1 }
|
||||||
|
],
|
||||||
|
"Output": [{ "Id": "rpg_plus/salve", "Count": 1 }]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The optional **tile map editor** paints ground, collision and events, and the **Data Editor** edits items, recipes and characters. Turn them on in the game's scheme.
|
||||||
|
|
||||||
|
## If something goes wrong
|
||||||
|
|
||||||
|
1. **The wolf's amber is missing in an old save.** Go to the wolf's spot by the cave and press E/F: the amber can be picked up there once if you have neither it nor the lantern.
|
||||||
|
2. **Not sure what to do next.** Open the journal.
|
||||||
|
3. **A dialog option is missing.** Some options appear only after a quest stage or with a certain item.
|
||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
# RPG Plus — un RPG de bosque construido con mods
|
||||||
|
|
||||||
|
RPG Plus es un RPG pequeño y completo: un prólogo tipo novela visual donde eliges quién era tu héroe, después una aldea y un bosque sobre mapa de tiles, un misterio sobre la luz perdida del bosque, misiones secundarias, una mesa de trabajo, un mercader, un desierto al oeste y combates por turnos. El juego está hecho solo de archivos de datos y escenarios; sus reglas — atributos, equipo, pruebas, combates, crafteo — vienen de mods separados que cualquier otro juego también puede usar.
|
||||||
|
|
||||||
|
Se juega desde **Jugar → RPG Plus → Nueva partida**.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## La historia y cómo jugar
|
||||||
|
|
||||||
|
1. **El cruce de caminos.** Elige tu pasado: **soldado** (guerrero), **aprendiz de mago** (mago) o **vagabundo** (pícaro). La elección fija tus atributos y tu equipo inicial. Empiezas con 25 monedas.
|
||||||
|
2. **La aldea junto al Bosque Gris.** Camina con WASD o las flechas, corre con Shift, usa E o F para hablar e interactuar. El **diario** siempre anota tu siguiente paso concreto.
|
||||||
|
3. **El anciano** pregunta por la luz desaparecida o te manda directamente tras el lobo de la cueva del este.
|
||||||
|
4. **El bosque.** Recoge leña y setas; los limos sueltan resina viva y monedas. El lobo siempre suelta un corazón de ámbar y suficiente pelaje para un manto.
|
||||||
|
5. **La mesa de trabajo del pozo.** Prepara una pomada del bosque (seta + resina), un manto del guardián (2 pelajes + 2 resinas) o el farol guardián (ámbar + 2 resinas + 2 ramas). Ponte el manto desde la bolsa: cambia tus números reales de combate.
|
||||||
|
6. **La herborista**, junto a la valla occidental, necesita pomada; ayudarla lleva a un correo desaparecido en el bosque del sur.
|
||||||
|
7. **El santuario.** Lleva el farol por la cueva que está tras el lobo hasta el santuario en ruinas. Devuélvele la luz al bosque, o pacta por la aldea. Ambos finales cierran la aventura, y las últimas palabras del anciano siguen tu elección.
|
||||||
|
|
||||||
|
Otra gente de la aldea: el **instructor** te lleva al patio de entrenamiento (experiencia una vez, y una prueba de Fuerza para alzar un tronco), y la **adivina** lee tu suerte. El hogar junto al pozo restaura salud y maná. Las recompensas no se pueden cobrar dos veces.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### El desierto
|
||||||
|
|
||||||
|
Desde la aldea, sigue el camino principal hacia el oeste hasta salir del borde del mapa y llegarás a **la Marca Seca**: arena, una casa de adobe, un refugio de caravana, una torre en ruinas, palmeras y cactus. Cinco escorpiones y escarabajos esperan pelea, y los barriles de provisiones guardan pequeñas recompensas de una sola vez. El borde este devuelve a la aldea.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Tu héroe
|
||||||
|
|
||||||
|
Abre la **hoja de personaje** desde la barra de herramientas para ver tu clase, nivel y experiencia, los atributos (Fuerza, Agilidad, Inteligencia, Vitalidad, Carisma, Suerte), los valores de combate y el equipo en las ranuras de arma, cuerpo y accesorio. Equípate desde la bolsa.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Los combates usan el estilo de órdenes (FF) con daño calculado por fórmulas. Algunas opciones de diálogo enfrentan un atributo a una tirada de d20, como el **[Fuerza 12]** del instructor.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Cómo se monta el juego
|
||||||
|
|
||||||
|
RPG Plus es la colección **RPG Plus**. Sus bloques:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Historia rpg_plus_game (este mod)
|
||||||
|
Aldea y bosque rpg_demo_game: los mapas, la gente y los objetos que este juego amplía
|
||||||
|
Recursos y reglas village_pack (arte), classic_rpg_pack (atributos, clases, equipo)
|
||||||
|
Atributos y pruebas stats_base, stat_checks
|
||||||
|
Mundo de tiles rpg
|
||||||
|
Objetos, equipo, comercio items_base, equipment, trading, crafting
|
||||||
|
Motor 2D shell2d_base, dialog_base, characters_base, audio_base
|
||||||
|
Combate turn_battle_base, ff_battle, battle_formulas
|
||||||
|
Combate de cartas card_battle (opcional)
|
||||||
|
Interfaz ui_base, menu_base
|
||||||
|
Editor de mapas de tiles rpg_editor (opcional)
|
||||||
|
Editores de datos schema_tools, items_editor, characters_editor (opcional)
|
||||||
|
Render de tiles tile_topdown_base, tile_isometric_base
|
||||||
|
```
|
||||||
|
|
||||||
|
Ajustes de la colección: `battle.core` es `ff_battle`, las pruebas tiran `d20`, el sprite del héroe en el mapa es `village/hero`, los mapas se dibujan de arriba a abajo. Se cambian en **Mods & Collections → Colecciones → RPG Plus → Editar esquema**.
|
||||||
|
|
||||||
|
## Para autores: cámbialo sin código
|
||||||
|
|
||||||
|
```filestree
|
||||||
|
rpg_plus_game/
|
||||||
|
├── scenarios/rpg_plus/ prólogo, training_yard, events/ (instructor, adivina), woodland/, desert/
|
||||||
|
├── map_events/<map>/ gente nueva, la mesa de trabajo, la puerta de la cueva, barriles; los mismos id sustituyen a los de rpg_demo_game
|
||||||
|
├── maps/ rpg_woodland_shrine.json, rpg_desert.json
|
||||||
|
├── scenes/ cruce de caminos, patio de entrenamiento, santuario, desierto
|
||||||
|
├── quests/woodland.json etapas del diario
|
||||||
|
├── items/, recipes/, stations/ materiales, objetos fabricados, el menú de la mesa de trabajo
|
||||||
|
├── patches/character.json botín añadido a los monstruos de rpg_demo_game
|
||||||
|
├── patches/shop.json más mercancías para el mercader de la aldea
|
||||||
|
└── characters/, charsets/, backgrounds/, locale/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Amplía el mod de otro en vez de copiarlo.** La aldea, la casa y el bosque siguen viniendo de `rpg_demo_game`. Un evento con el mismo id que uno de allí lo sustituye (el anciano recibe diálogos nuevos); los id nuevos añaden gente. Los parches añaden botín a sus monstruos y mercancías a su tienda.
|
||||||
|
|
||||||
|
**Pruebas y condiciones en los escenarios.** La adivina prueba la Suerte y luego mira tu equipo:
|
||||||
|
|
||||||
|
```scenario
|
||||||
|
text "—¿Quieres saber qué te espera? Dame la mano. Esa no: la que no usas para robar." key=rpg_plus/seer/hello
|
||||||
|
check luk 14 who=rpg_hero else=fail -> :ok
|
||||||
|
|
||||||
|
:ok
|
||||||
|
text "—El lobo de la cueva teme más al fuego que al acero. Y golpea donde no tenga escudo. Esto es gratis. Lo demás, no." key=rpg_plus/seer/fortune
|
||||||
|
when "!equipped(rpg_hero, 'classic/clover_charm')" -> :charm
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
`when "item('rpg_plus/salve') >= 1"` mira la bolsa, `level(rpg_hero) >= 3` el nivel del héroe; las banderas hacen seguras las recompensas de una sola vez. Las líneas están escritas en ruso con un `key=` cada una; las traducciones están en `locale/scenarios_<lang>.properties`.
|
||||||
|
|
||||||
|
**Una receta** son datos en `recipes/woodland.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Id": "rpg_plus/salve",
|
||||||
|
"Name": "rpg_plus/recipe/salve/name",
|
||||||
|
"Type": "Cooking",
|
||||||
|
"Input": [
|
||||||
|
{ "Id": "rpg_demo/mushroom", "Count": 1 },
|
||||||
|
{ "Id": "rpg_plus/resin", "Count": 1 }
|
||||||
|
],
|
||||||
|
"Output": [{ "Id": "rpg_plus/salve", "Count": 1 }]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
El **editor de mapas de tiles** opcional pinta suelo, colisiones y eventos, y el **Editor de datos** edita objetos, recetas y personajes. Actívalos en el esquema del juego.
|
||||||
|
|
||||||
|
## Si algo falla
|
||||||
|
|
||||||
|
1. **Falta el ámbar del lobo en una partida guardada antigua.** Ve al punto del lobo junto a la cueva y pulsa E/F: el ámbar se puede recoger allí una vez si no tienes ni él ni el farol.
|
||||||
|
2. **No sabes qué hacer ahora.** Abre el diario.
|
||||||
|
3. **Falta una opción de diálogo.** Algunas opciones aparecen solo tras una etapa de misión o con cierto objeto.
|
||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
# RPG Plus — MOD で組み上げた森の RPG
|
||||||
|
|
||||||
|
RPG Plus は完成された小規模 RPG です。まず自分のヒーローの過去を選ぶビジュアルノベル形式のプロローグ、続いてタイルマップの村と森、森から消えた光の謎、サブクエスト、作業台、商人、西の砂漠、そしてターン制バトル。ゲームはデータファイルとシナリオだけでできており、ルール — ステータス、装備、判定、戦闘、クラフト — は別々の MOD が担います。どの MOD も他のゲームでも使えます。
|
||||||
|
|
||||||
|
開始は **プレイ → RPG Plus → ニューゲーム** から。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 物語と遊び方
|
||||||
|
|
||||||
|
1. **岐路。** 過去を選びます:**兵士**(ウォリアー)、**魔法使いの弟子**(メイジ)、**流浪人**(ローグ)。選択がステータスと初期装備を決めます。所持金は 25 硬貨から。
|
||||||
|
2. **灰色の森のそばの村。** WASD か矢印キーで歩き、Shift で走り、E か F で話しかけ・調べます。**ジャーナル**は常に、次の具体的な一歩を示してくれます。
|
||||||
|
3. **村長**は消えた光について尋ねてくるか、東の洞窟の狼のもとへすぐに送り出します。
|
||||||
|
4. **森。** 柴とキノコを集めます。スライムは生きた樹脂と硬貨を落とします。狼は必ず琥珀の心臓と、マント一枚ぶんの毛皮を落とします。
|
||||||
|
5. **井戸の作業台。** 森の軟膏(キノコ + 樹脂)、森の番人のマント(毛皮 2 + 樹脂 2)、護りの灯籠(琥珀 + 樹脂 2 + 柴 2)を作れます。マントはインベントリから装備します。実際の戦闘の数値が変わります。
|
||||||
|
6. 西の柵のそばの**薬草師**は軟膏を求めています。手伝うと、南の森で行方不明になった伝令へとつながります。
|
||||||
|
7. **祠。** 灯籠を持って、狼の奥の洞窟を抜け、荒れた祠へ。光を森に返すか、村のために契りを交わすか。どちらの結末でも冒険は終わり、村長の最期の言葉はあなたの選択に従います。
|
||||||
|
|
||||||
|
村のその他の人々:**師匠**は稽古場へ連れて行ってくれ(経験値は一度、丸太を持ち上げる力の判定つき)、**占い師**はあなたの運を読みます。井戸のそばの囲み炉は体力とマナを回復します。報酬は二度は受け取れません。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 砂漠
|
||||||
|
|
||||||
|
村から大通りを西へ、マップの端を出ていくと**乾いた辺境**に着きます。砂、日干しレンガの家、隊商の休憩小屋、崩れた塔、ヤシとサボテン。サソリと甲虫が五匹戦いを待ち、補給の樽には小さな一度きりの報酬が入っています。東の端から村へ戻れます。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## あなたのヒーロー
|
||||||
|
|
||||||
|
ツールバーから**キャラクターシート**を開くと、クラス、レベルと経験値、属性(力、敏捷、知力、体力、カリスマ、幸運)、戦闘値、武器・体・装飾品のスロットの装備が見られます。装備はインベントリから着けます。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
戦闘はコマンド式(FF)バトルで、ダメージは数式で計算されます。会話の選択肢の一部は属性を d20 ロールで判定します。師匠の **[力 12]** がその例です。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## ゲームの構成
|
||||||
|
|
||||||
|
RPG Plus の正体はコレクション **RPG Plus** です。そのブロック:
|
||||||
|
|
||||||
|
```text
|
||||||
|
物語 rpg_plus_game(この MOD)
|
||||||
|
村と森 rpg_demo_game:このゲームが拡張するマップ、人、アイテム
|
||||||
|
素材とルール village_pack(アート)、classic_rpg_pack(ステータス、クラス、装備)
|
||||||
|
ステータスと判定 stats_base, stat_checks
|
||||||
|
タイル世界 rpg
|
||||||
|
アイテム、装備、交易 items_base, equipment, trading, crafting
|
||||||
|
2D エンジン shell2d_base, dialog_base, characters_base, audio_base
|
||||||
|
戦闘 turn_battle_base, ff_battle, battle_formulas
|
||||||
|
カードバトル card_battle(任意)
|
||||||
|
インターフェース ui_base, menu_base
|
||||||
|
タイルマップエディター rpg_editor(任意)
|
||||||
|
データエディター schema_tools, items_editor, characters_editor(任意)
|
||||||
|
タイルレンダラー tile_topdown_base, tile_isometric_base
|
||||||
|
```
|
||||||
|
|
||||||
|
コレクションの設定:`battle.core` は `ff_battle`、判定は `d20` をロール、ヒーローのマップスプライトは `village/hero`、マップはトップダウン描画。**Mods & Collections → コレクション → RPG Plus → 構成を編集** から変えられます。
|
||||||
|
|
||||||
|
## 作者向け:コードなしで変える
|
||||||
|
|
||||||
|
```filestree
|
||||||
|
rpg_plus_game/
|
||||||
|
├── scenarios/rpg_plus/ プロローグ、training_yard、events/(師匠、占い師)、woodland/、desert/
|
||||||
|
├── map_events/<map>/ 新しい人々、作業台、洞窟の扉、樽。同じ id は rpg_demo_game のものを置き換える
|
||||||
|
├── maps/ rpg_woodland_shrine.json、rpg_desert.json
|
||||||
|
├── scenes/ 岐路、稽古場、祠、砂漠
|
||||||
|
├── quests/woodland.json ジャーナルの段階
|
||||||
|
├── items/, recipes/, stations/ 素材、作成品、作業台のメニュー
|
||||||
|
├── patches/character.json rpg_demo_game のモンスターに追加されたドロップ
|
||||||
|
├── patches/shop.json 村の商人の商品の追加
|
||||||
|
└── characters/, charsets/, backgrounds/, locale/
|
||||||
|
```
|
||||||
|
|
||||||
|
**コピーせず、他の MOD を拡張する。** 村も家も森も今も `rpg_demo_game` から来ます。そこと同じ id のイベントはそれを置き換え(村長は新しい会話を得る)、新しい id は人を増やします。パッチはモンスターのドロップと店の商品を足します。
|
||||||
|
|
||||||
|
**シナリオの判定と条件。** 占い師は幸運を試し、それからあなたの装備を見ます:
|
||||||
|
|
||||||
|
```scenario
|
||||||
|
text "「この先を知りたいかい?手をお出し。そっちじゃない——盗みに使わないほうの手だよ」" key=rpg_plus/seer/hello
|
||||||
|
check luk 14 who=rpg_hero else=fail -> :ok
|
||||||
|
|
||||||
|
:ok
|
||||||
|
text "「洞窟の狼は鋼より火を恐れる。それと盾のないところを打ちな。ここまでは無料。残りは有料だよ」" key=rpg_plus/seer/fortune
|
||||||
|
when "!equipped(rpg_hero, 'classic/clover_charm')" -> :charm
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
`when "item('rpg_plus/salve') >= 1"` はインベントリを、`level(rpg_hero) >= 3` はヒーローのレベルを見ます。フラグのおかげで、一度きりの報酬は安全です。台詞はロシア語で書かれ、各行に `key=` が付きます。翻訳は `locale/scenarios_<lang>.properties` にあります。
|
||||||
|
|
||||||
|
**レシピ**は `recipes/woodland.json` のデータです:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Id": "rpg_plus/salve",
|
||||||
|
"Name": "rpg_plus/recipe/salve/name",
|
||||||
|
"Type": "Cooking",
|
||||||
|
"Input": [
|
||||||
|
{ "Id": "rpg_demo/mushroom", "Count": 1 },
|
||||||
|
{ "Id": "rpg_plus/resin", "Count": 1 }
|
||||||
|
],
|
||||||
|
"Output": [{ "Id": "rpg_plus/salve", "Count": 1 }]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
任意の**タイルマップエディター**は地面・衝突・イベントを描き、**データエディター**はアイテム・レシピ・キャラクターを編集します。ゲームの構成で有効にしてください。
|
||||||
|
|
||||||
|
## うまくいかないとき
|
||||||
|
|
||||||
|
1. **古いセーブに狼の琥珀がない。** 洞窟のそばの狼の場所へ行って E/F を押してください。琥珀も灯籠も持っていなければ、そこで一度だけ拾えます。
|
||||||
|
2. **次に何をすればいいか分からない。** ジャーナルを開きましょう。
|
||||||
|
3. **会話の選択肢が出ない。** 一部の選択肢は、特定のクエスト段階の後か、特定のアイテム所持時だけ現れます。
|
||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
# RPG Plus — 모드로 지어 올린 숲의 RPG
|
||||||
|
|
||||||
|
RPG Plus는 완성된 소규모 RPG입니다. 영웅의 과거를 고르는 비주얼 노벨 프롤로그, 이어서 타일맵의 마을과 숲, 숲에서 사라진 빛의 미스터리, 사이드 퀘스트, 작업대, 상인, 서쪽의 사막, 그리고 턴제 전투. 게임은 데이터 파일과 시나리오만으로 이루어져 있고, 규칙 — 스탯, 장비, 판정, 전투, 제작 — 은 별개의 모드가 담당합니다. 어떤 모드든 다른 게임에서도 쓸 수 있습니다.
|
||||||
|
|
||||||
|
**플레이 → RPG Plus → 새 게임**에서 시작합니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 이야기와 진행 방법
|
||||||
|
|
||||||
|
1. **갈림길.** 과거를 고르세요: **군인**(전사), **마법사 견습생**(메이지), **방랑자**(로그). 선택이 스탯과 시작 장비를 정합니다. 시작 자금은 동전 25닢.
|
||||||
|
2. **잿빛 숲 옆 마을.** WASD나 방향키로 걷고, Shift로 뛰고, E나 F로 말하고 상호작용합니다. **저널**은 언제나 다음 구체적 한 걸음을 알려 줍니다.
|
||||||
|
3. **촌장**은 사라진 빛을 묻거나, 곧장 동쪽 동굴의 늑대에게 보냅니다.
|
||||||
|
4. **숲.** 땔감과 버섯을 모으세요. 슬라임은 산 수지와 동전을 남깁니다. 늑대는 언제나 호박 심장과 망토 한 벌分의 털을 남깁니다.
|
||||||
|
5. **우물 작업대.** 숲 연고(버섯 + 수지), 숲 수호자 망토(털 2 + 수지 2), 수호 등불(호박 + 수지 2 + 땔감 2)을 만들 수 있습니다. 망토는 가방에서 착용하세요. 실제 전투 수치가 바뀝니다.
|
||||||
|
6. 서쪽 울타리 옆 **약초상**은 연고가 필요합니다. 돕다 보면 남쪽 숲에서 실종된 전령으로 이어집니다.
|
||||||
|
7. **성소.** 등불을 들고 늑대 너머의 동굴을 지나 폐허가 된 성소로 가세요. 숲에 빛을 돌려주거나, 마을을 위해 계약을 맺거나. 두 결말 모두 모험을 끝내고, 촌장의 마지막 말은 당신의 선택을 따릅니다.
|
||||||
|
|
||||||
|
마을의 다른 사람들: **훈련관**은 수련장으로 안내해 주고(경험치는 한 번, 통나무 드는 힘 판정 포함), **점쟁이**는 당신의 행운을 읽습니다. 우물 곁 화덕은 체력과 마나를 회복합니다. 보상은 두 번 받을 수 없습니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 사막
|
||||||
|
|
||||||
|
마을에서 큰길을 따라 서쪽으로, 맵 끝을 벗어나면 **마른 변경**에 닿습니다. 모래, 흙벽돌 집, 상로막 쉼터, 무너진 탑, 야자수와 선인장. 전갈과 딱정벌레 다섯 마리가 싸움을 기다리고, 보급 통에는 작은 일회성 보상이 들어 있습니다. 동쪽 끝은 다시 마을로 돌아갑니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 당신의 영웅
|
||||||
|
|
||||||
|
툴바에서 **캐릭터 시트**를 열면 클래스, 레벨과 경험치, 속성(힘, 민첩, 지능, 생명력, 매력, 행운), 전투 수치, 그리고 무기·몸·장신구 슬롯의 장비를 볼 수 있습니다. 장비는 가방에서 착용합니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
전투는 커맨드식(FF) 전투로, 피해는 공식으로 계산됩니다. 일부 대화 선택지는 속성을 d20 굴림으로 판정합니다. 훈련관의 **[힘 12]**가 그 예입니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 게임의 구성
|
||||||
|
|
||||||
|
RPG Plus의 정체는 컬렉션 **RPG Plus**입니다. 그 블록:
|
||||||
|
|
||||||
|
```text
|
||||||
|
이야기 rpg_plus_game(이 모드)
|
||||||
|
마을과 숲 rpg_demo_game: 이 게임이 확장하는 맵, 사람, 아이템
|
||||||
|
에셋과 규칙 village_pack(그림), classic_rpg_pack(스탯, 클래스, 장비)
|
||||||
|
스탯과 판정 stats_base, stat_checks
|
||||||
|
타일 세계 rpg
|
||||||
|
아이템, 장비, 거래 items_base, equipment, trading, crafting
|
||||||
|
2D 엔진 shell2d_base, dialog_base, characters_base, audio_base
|
||||||
|
전투 turn_battle_base, ff_battle, battle_formulas
|
||||||
|
카드 배틀 card_battle(선택)
|
||||||
|
인터페이스 ui_base, menu_base
|
||||||
|
타일맵 편집기 rpg_editor(선택)
|
||||||
|
데이터 편집기 schema_tools, items_editor, characters_editor(선택)
|
||||||
|
타일 렌더러 tile_topdown_base, tile_isometric_base
|
||||||
|
```
|
||||||
|
|
||||||
|
컬렉션 설정: `battle.core`는 `ff_battle`, 판정은 `d20`을 굴리고, 영웅의 맵 스프라이트는 `village/hero`, 맵은 탑다운으로 그려집니다. **Mods & Collections → 컬렉션 → RPG Plus → 구성 편집**에서 바꿀 수 있습니다.
|
||||||
|
|
||||||
|
## 모드 작성자를 위해: 코드 없이 바꾸기
|
||||||
|
|
||||||
|
```filestree
|
||||||
|
rpg_plus_game/
|
||||||
|
├── scenarios/rpg_plus/ 프롤로그, training_yard, events/(훈련관, 점쟁이), woodland/, desert/
|
||||||
|
├── map_events/<map>/ 새 사람들, 작업대, 동굴 문, 통. 같은 id는 rpg_demo_game의 것을 대체
|
||||||
|
├── maps/ rpg_woodland_shrine.json, rpg_desert.json
|
||||||
|
├── scenes/ 갈림길, 수련장, 성소, 사막
|
||||||
|
├── quests/woodland.json 저널 단계
|
||||||
|
├── items/, recipes/, stations/ 재료, 제작품, 작업대 메뉴
|
||||||
|
├── patches/character.json rpg_demo_game 몬스터에 추가된 전리품
|
||||||
|
├── patches/shop.json 마을 상인의 물품 추가
|
||||||
|
└── characters/, charsets/, backgrounds/, locale/
|
||||||
|
```
|
||||||
|
|
||||||
|
**복사하지 말고 다른 모드를 확장하세요.** 마을도 집도 숲도 여전히 `rpg_demo_game`에서 옵니다. 거기와 같은 id의 이벤트는 그것을 대체하고(촌장은 새 대사를 얻음), 새 id는 사람을 더합니다. 패치는 그 몬스터의 전리품과 가게 물품을 더합니다.
|
||||||
|
|
||||||
|
**시나리오의 판정과 조건.** 점쟁이는 행운을 시험한 뒤 당신의 장비를 봅니다:
|
||||||
|
|
||||||
|
```scenario
|
||||||
|
text "“무엇이 기다리는지 알고 싶어? 손을 줘. 그거 말고 — 도둑질 안 하는 손.”" key=rpg_plus/seer/hello
|
||||||
|
check luk 14 who=rpg_hero else=fail -> :ok
|
||||||
|
|
||||||
|
:ok
|
||||||
|
text "“동굴의 늑대는 강철보다 불을 더 무서워해. 그리고 보호막이 없는 데를 쳐. 여기까진 공짜야. 나머진 아니고.”" key=rpg_plus/seer/fortune
|
||||||
|
when "!equipped(rpg_hero, 'classic/clover_charm')" -> :charm
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
`when "item('rpg_plus/salve') >= 1"`은 가방을, `level(rpg_hero) >= 3`은 영웅의 레벨을 봅니다. 플래그 덕분에 일회성 보상은 안전합니다. 대사는 러시아어로 쓰였고 각 줄에 `key=`가 있습니다. 번역은 `locale/scenarios_<lang>.properties`에 있습니다.
|
||||||
|
|
||||||
|
**레시피**는 `recipes/woodland.json`의 데이터입니다:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Id": "rpg_plus/salve",
|
||||||
|
"Name": "rpg_plus/recipe/salve/name",
|
||||||
|
"Type": "Cooking",
|
||||||
|
"Input": [
|
||||||
|
{ "Id": "rpg_demo/mushroom", "Count": 1 },
|
||||||
|
{ "Id": "rpg_plus/resin", "Count": 1 }
|
||||||
|
],
|
||||||
|
"Output": [{ "Id": "rpg_plus/salve", "Count": 1 }]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
선택인 **타일맵 편집기**는 지면·충돌·이벤트를 그리고, **데이터 편집기**는 아이템·레시피·캐릭터를 편집합니다. 게임의 구성에서 켜세요.
|
||||||
|
|
||||||
|
## 문제가 생겼을 때
|
||||||
|
|
||||||
|
1. **오래된 저장 파일에 늑대의 호박이 없습니다.** 동굴 옆 늑대 자리로 가서 E/F를 누르세요. 호박과 등불이 둘 다 없다면 그곳에서 한 번 주울 수 있습니다.
|
||||||
|
2. **다음에 뭘 해야 할지 모르겠습니다.** 저널을 여세요.
|
||||||
|
3. **대화 선택지가 사라졌습니다.** 어떤 선택지는 특정 퀘스트 단계 후에, 또는 특정 아이템이 있을 때만 나타납니다.
|
||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
# RPG Plus — лесная RPG, собранная из модов
|
||||||
|
|
||||||
|
RPG Plus — законченная небольшая RPG: пролог-новелла, где вы выбираете, кем был ваш герой, затем деревня и лес на тайловой карте, тайна пропавшего лесного света, побочные задания, верстак, торговец, пустыня на западе и пошаговые бои. Игра состоит только из файлов данных и сценариев; правила — характеристики, снаряжение, проверки, бои, крафт — приносят отдельные моды, которые может взять и любая другая игра.
|
||||||
|
|
||||||
|
Запуск — **Играть → RPG Plus → Новая игра**.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## История и как играть
|
||||||
|
|
||||||
|
1. **Перекрёсток.** Выберите своё прошлое: **солдат** (воин), **ученик мага** (маг) или **странник** (плут). Выбор задаёт характеристики и стартовое снаряжение. На старте у вас 25 монет.
|
||||||
|
2. **Деревня у Серого леса.** Ходите на WASD или стрелки, бегите на Shift, говорите и взаимодействуйте на E или F. **Журнал** всегда называет ваш конкретный следующий шаг.
|
||||||
|
3. **Староста** спрашивает о пропавшем свете или сразу отправляет вас за волком у восточной пещеры.
|
||||||
|
4. **Лес.** Собирайте хворост и грибы; слизни оставляют живую смолу и монеты. Волк всегда оставляет сердце янтаря и столько шерсти, что хватит на плащ.
|
||||||
|
5. **Верстак у колодца.** Сварите лесную мазь (гриб + смола), сшейте плащ лесного стража (2 шерсти + 2 смолы) или соберите сторожевой фонарь (янтарь + 2 смолы + 2 веток). Плащ надевается из сумки: он меняет ваши настоящие боевые числа.
|
||||||
|
6. **Травница** у западного забора просит мазь; помощь ей приводит к пропавшему курьеру в южном лесу.
|
||||||
|
7. **Святилище.** Пронесите фонарь через пещеру за волком к разрушенному святилищу. Верните лесу свет — или заключите сделку ради деревни. Обе концовки завершают приключение, а последние слова старосты зависят от вашего выбора.
|
||||||
|
|
||||||
|
Прочие жители деревни: **наставник** ведёт на тренировочную площадку (опыт один раз и проверка силы с бревном), **гадалка** читает вашу удачу. Очаг у колодца восстанавливает здоровье и ману. Награды нельзя собрать дважды.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Пустыня
|
||||||
|
|
||||||
|
От деревни пройдите по главной дороге на запад, за край карты, — попадёте в **Сухой предел**: песок, глинобитный дом, навес каравана, разрушенная башня, пальмы и кактусы. Пять скорпионов и жуков ждут боя, а бочки припасов хранят небольшие разовые награды. Восточный край ведёт обратно к деревне.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Ваш герой
|
||||||
|
|
||||||
|
Откройте **лист персонажа** с панели инструментов: класс, уровень и опыт, характеристики (Сила, Ловкость, Интеллект, Выносливость, Харизма, Удача), боевые значения и снаряжение в слотах оружия, брони и аксессуара. Надевайте вещи из сумки.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Бои идут в командном стиле (FF), урон считается по формулам. Часть вариантов диалога проверяет характеристику броском d20 — например, **[Сила 12]** у наставника.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Как игра собрана
|
||||||
|
|
||||||
|
RPG Plus — это коллекция **RPG Plus**. Её блоки:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Сюжет rpg_plus_game (этот мод)
|
||||||
|
Деревня и лес rpg_demo_game: карты, люди и предметы, которые игра расширяет
|
||||||
|
Ресурсы и правила village_pack (арт), classic_rpg_pack (характеристики, классы, снаряжение)
|
||||||
|
Характеристики и проверки stats_base, stat_checks
|
||||||
|
Тайловый мир rpg
|
||||||
|
Предметы, снаряжение, торговля items_base, equipment, trading, crafting
|
||||||
|
2D-движок shell2d_base, dialog_base, characters_base, audio_base
|
||||||
|
Бой turn_battle_base, ff_battle, battle_formulas
|
||||||
|
Карточный бой card_battle (опционально)
|
||||||
|
Интерфейс ui_base, menu_base
|
||||||
|
Редактор тайловых карт rpg_editor (опционально)
|
||||||
|
Редакторы данных schema_tools, items_editor, characters_editor (опционально)
|
||||||
|
Тайловый рендер tile_topdown_base, tile_isometric_base
|
||||||
|
```
|
||||||
|
|
||||||
|
Настройки коллекции: `battle.core` — `ff_battle`, проверки бросают `d20`, спрайт героя на карте — `village/hero`, карты рисуются сверху. Меняются в **Mods & Collections → Коллекции → RPG Plus → Изменить схему**.
|
||||||
|
|
||||||
|
## Авторам: меняйте без кода
|
||||||
|
|
||||||
|
```filestree
|
||||||
|
rpg_plus_game/
|
||||||
|
├── scenarios/rpg_plus/ пролог, training_yard, events/ (наставник, гадалка), woodland/, desert/
|
||||||
|
├── map_events/<map>/ новые люди, верстак, дверь пещеры, бочки; события с теми же id заменяют события rpg_demo_game
|
||||||
|
├── maps/ rpg_woodland_shrine.json, rpg_desert.json
|
||||||
|
├── scenes/ перекрёсток, тренировочная площадка, святилище, пустыня
|
||||||
|
├── quests/woodland.json этапы журнала
|
||||||
|
├── items/, recipes/, stations/ материалы, изделия, меню верстака
|
||||||
|
├── patches/character.json добыча, добавленная монстрам rpg_demo_game
|
||||||
|
├── patches/shop.json товары сверх обычных у деревенского торговца
|
||||||
|
└── characters/, charsets/, backgrounds/, locale/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Расширяйте чужой мод, а не копируйте его.** Деревня, дом и лес по-прежнему берутся из `rpg_demo_game`. Событие с тем же id, что там, заменяет его (староста получает новые диалоги); новые id добавляют людей. Патчи добавляют добычу его монстрам и товары его лавке.
|
||||||
|
|
||||||
|
**Проверки и условия в сценариях.** Гадалка испытывает Удачу, а потом смотрит на ваше снаряжение:
|
||||||
|
|
||||||
|
```scenario
|
||||||
|
text "— Хочешь знать, что тебя ждёт? Дай руку. Не эту — ту, которой не воруешь." key=rpg_plus/seer/hello
|
||||||
|
check luk 14 who=rpg_hero else=fail -> :ok
|
||||||
|
|
||||||
|
:ok
|
||||||
|
text "— Волк у пещеры боится огня больше, чем стали. И бей туда, где у него нет щита. Это бесплатно. Остальное — нет." key=rpg_plus/seer/fortune
|
||||||
|
when "!equipped(rpg_hero, 'classic/clover_charm')" -> :charm
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
`when "item('rpg_plus/salve') >= 1"` заглядывает в сумку, `level(rpg_hero) >= 3` — в уровень героя; флаги делают разовые награды безопасными. Строки написаны по-русски, у каждой есть `key=`; переводы лежат в `locale/scenarios_<lang>.properties`.
|
||||||
|
|
||||||
|
**Рецепт** — это данные в `recipes/woodland.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Id": "rpg_plus/salve",
|
||||||
|
"Name": "rpg_plus/recipe/salve/name",
|
||||||
|
"Type": "Cooking",
|
||||||
|
"Input": [
|
||||||
|
{ "Id": "rpg_demo/mushroom", "Count": 1 },
|
||||||
|
{ "Id": "rpg_plus/resin", "Count": 1 }
|
||||||
|
],
|
||||||
|
"Output": [{ "Id": "rpg_plus/salve", "Count": 1 }]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Опциональный **редактор тайловых карт** рисует землю, коллизии и события, а **редактор данных** правит предметы, рецепты и персонажей. Включите их в схеме игры.
|
||||||
|
|
||||||
|
## Если что-то пошло не так
|
||||||
|
|
||||||
|
1. **В старом сохранении нет янтаря волка.** Подойдите к месту волка у пещеры и нажмите E/F: янтарь можно подобрать там один раз, если у вас нет ни его, ни фонаря.
|
||||||
|
2. **Неясно, что делать дальше.** Откройте журнал.
|
||||||
|
3. **Пропал вариант диалога.** Некоторые варианты появляются только после определённого этапа задания или при нужном предмете.
|
||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
# RPG Plus — một RPG rừng rậm dựng từ các mod
|
||||||
|
|
||||||
|
RPG Plus là một trò RPG nhỏ mà hoàn chỉnh: phần mở đầu kiểu tiểu thuyết tương tác để bạn chọn quá khứ của anh hùng, rồi một làng và khu rừng trên bản đồ ô vuông, bí ẩn về ánh sáng lạc mất của khu rừng, nhiệm vụ phụ, bàn chế tạo, thương nhân, một hoang mạc phía tây và chiến đấu theo lượt. Trò chơi chỉ gồm tệp dữ liệu và kịch bản; còn luật chơi — chỉ số, trang bị, phép kiểm, chiến đấu, chế tạo — đến từ những mod riêng mà trò chơi khác cũng dùng được.
|
||||||
|
|
||||||
|
Chơi từ **Chơi → RPG Plus → Trò chơi mới**.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Cốt truyện và cách chơi
|
||||||
|
|
||||||
|
1. **Ngã ba.** Chọn quá khứ của bạn: **lính** (chiến binh), **đệ tử pháp sư** (pháp sư) hay **kẻ phiêu bạt** (rogue). Lựa chọn quyết định chỉ số và trang bị khởi đầu. Bạn bắt đầu với 25 đồng xu.
|
||||||
|
2. **Ngôi làng bên Rừng Xám.** Đi lại bằng WASD hoặc phím mũi tên, chạy bằng Shift, dùng E hoặc F để nói chuyện và tương tác. **Nhật ký** luôn ghi rõ bước cụ thể tiếp theo của bạn.
|
||||||
|
3. **Trưởng làng** hỏi về ánh sáng biến mất, hoặc phái bạn đi ngay theo con sói ở hang phía đông.
|
||||||
|
4. **Khu rừng.** Hái củi và nấm; slime rơi ra nhựa sống và đồng xu. Con sói luôn rơi trái tim hổ phách và đủ lông để may một chiếc áo choàng.
|
||||||
|
5. **Bàn chế tạo bên giếng.** Làm thuốc mỡ rừng (nấm + nhựa), áo choàng người canh rừng (2 lông + 2 nhựa) hoặc đèn lồng hộ mệnh (hổ phách + 2 nhựa + 2 củi). Mang áo choàng từ túi đồ: nó thay đổi con số chiến đấu thật của bạn.
|
||||||
|
6. **Bà thuốc** ở hàng rào phía tây cần thuốc mỡ; giúp bà dẫn tới người đưa thư mất tích trong rừng phương nam.
|
||||||
|
7. **Đền thờ.** Xách đèn qua hang động phía sau con sói tới đền thờ hoang tàn. Trả ánh sáng cho khu rừng, hoặc lập khế ước cho ngôi làng. Cả hai kết thúc đều khép lại cuộc phiêu lưu, và lời cuối của trưởng làng sẽ theo lựa chọn của bạn.
|
||||||
|
|
||||||
|
Một số người khác trong làng: **võ sư** dẫn bạn ra bãi tập (kinh nghiệm một lần, kèm phép kiểm Sức mạnh để nhấc khúc gỗ), và **bà thầy bói** đọc vận may của bạn. Lò sưởi bên giếng hồi máu và mana. Phần thưởng không nhận lại được lần thứ hai.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Hoang mạc
|
||||||
|
|
||||||
|
Từ làng, đi theo con đường chính về phía tây, ra khỏi mép bản đồ là tới **Vùng Biên Khô**: cát, một ngôi nhà bằng đất nặn, chỗ trú của đoàn thương xa, một tòa tháp hoang, cây cọ và xương rồng. Năm con bọ cạp và bọ hung chờ được đánh, và các thùng tiếp tế cất phần thưởng nhỏ một lần. Mép đông dẫn về lại làng.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Anh hùng của bạn
|
||||||
|
|
||||||
|
Mở **bảng nhân vật** từ thanh công cụ để xem class, cấp và kinh nghiệm, các chỉ số (Sức mạnh, Nhanh nhẹn, Trí tuệ, Sinh lực, Sức hút, May mắn), giá trị chiến đấu và trang bị trong ba ô vũ khí, thân thể và phụ kiện. Trang bị lấy từ túi đồ.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Trận đánh dùng lối chiến đấu ra lệnh (FF), sát thương tính theo công thức. Một số lựa chọn hội thoại đem chỉ số ra thử với một lần gieo d20, ví dụ **[Sức mạnh 12]** của võ sư.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Trò chơi được ghép thế nào
|
||||||
|
|
||||||
|
RPG Plus là bộ sưu tập **RPG Plus**. Các khối của nó:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Cốt truyện rpg_plus_game (mod này)
|
||||||
|
Làng và rừng rpg_demo_game: bản đồ, con người và vật phẩm mà trò này mở rộng
|
||||||
|
Tài nguyên và luật village_pack (hình ảnh), classic_rpg_pack (chỉ số, class, trang bị)
|
||||||
|
Chỉ số và phép kiểm stats_base, stat_checks
|
||||||
|
Thế giới ô vuông rpg
|
||||||
|
Vật phẩm, trang bị, mua bán items_base, equipment, trading, crafting
|
||||||
|
Engine 2D shell2d_base, dialog_base, characters_base, audio_base
|
||||||
|
Chiến đấu turn_battle_base, ff_battle, battle_formulas
|
||||||
|
Chiến đấu thẻ bài card_battle (tùy chọn)
|
||||||
|
Giao diện ui_base, menu_base
|
||||||
|
Trình soạn bản đồ ô rpg_editor (tùy chọn)
|
||||||
|
Trình soạn dữ liệu schema_tools, items_editor, characters_editor (tùy chọn)
|
||||||
|
Bộ dựng ô tile tile_topdown_base, tile_isometric_base
|
||||||
|
```
|
||||||
|
|
||||||
|
Cấu hình của bộ sưu tập: `battle.core` là `ff_battle`, phép kiểm gieo `d20`, hình anh hùng trên bản đồ là `village/hero`, bản đồ vẽ nhìn từ trên xuống. Đổi trong **Mods & Collections → Bộ sưu tập → RPG Plus → Sửa sơ đồ**.
|
||||||
|
|
||||||
|
## Dành cho tác giả: đổi nội dung không cần mã
|
||||||
|
|
||||||
|
```filestree
|
||||||
|
rpg_plus_game/
|
||||||
|
├── scenarios/rpg_plus/ phần mở đầu, training_yard, events/ (võ sư, thầy bói), woodland/, desert/
|
||||||
|
├── map_events/<map>/ người mới, bàn chế tạo, cửa hang, thùng hàng; id trùng sẽ thay của rpg_demo_game
|
||||||
|
├── maps/ rpg_woodland_shrine.json, rpg_desert.json
|
||||||
|
├── scenes/ ngã ba, bãi tập, đền thờ, hoang mạc
|
||||||
|
├── quests/woodland.json các chặng nhật ký
|
||||||
|
├── items/, recipes/, stations/ nguyên liệu, sản phẩm, trình đơn của bàn chế tạo
|
||||||
|
├── patches/character.json chiến lợi phẩm thêm cho quái của rpg_demo_game
|
||||||
|
├── patches/shop.json thêm hàng cho thương nhân trong làng
|
||||||
|
└── characters/, charsets/, backgrounds/, locale/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Hãy mở rộng mod của người khác thay vì copy nó.** Làng, nhà và rừng vẫn đến từ `rpg_demo_game`. Sự kiện trùng id với sự kiện ở đó sẽ thay thế nó (trưởng làng nhận hội thoại mới); id mới thêm người. Các bản vá thêm chiến lợi phẩm cho quái và thêm hàng cho cửa hàng của nó.
|
||||||
|
|
||||||
|
**Phép kiểm và điều kiện trong kịch bản.** Bà thầy bói thử May mắn rồi nhìn trang bị của bạn:
|
||||||
|
|
||||||
|
```scenario
|
||||||
|
text "“Muốn biết điều gì đang chờ ngươi? Đưa tay đây. Không phải tay đó — tay không dùng để trộm ấy.”" key=rpg_plus/seer/hello
|
||||||
|
check luk 14 who=rpg_hero else=fail -> :ok
|
||||||
|
|
||||||
|
:ok
|
||||||
|
text "“Con sói ở hang sợ lửa hơn thép. Và hãy đánh vào chỗ nó không có khiên. Phần này miễn phí. Phần còn lại thì không.”" key=rpg_plus/seer/fortune
|
||||||
|
when "!equipped(rpg_hero, 'classic/clover_charm')" -> :charm
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
`when "item('rpg_plus/salve') >= 1"` nhìn vào túi đồ, `level(rpg_hero) >= 3` nhìn cấp của anh hùng; nhờ các cờ mà phần thưởng một lần không bị nhận hai lần. Các câu viết bằng tiếng Nga, mỗi câu một `key=`; bản dịch nằm trong `locale/scenarios_<lang>.properties`.
|
||||||
|
|
||||||
|
**Một công thức** là dữ liệu trong `recipes/woodland.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Id": "rpg_plus/salve",
|
||||||
|
"Name": "rpg_plus/recipe/salve/name",
|
||||||
|
"Type": "Cooking",
|
||||||
|
"Input": [
|
||||||
|
{ "Id": "rpg_demo/mushroom", "Count": 1 },
|
||||||
|
{ "Id": "rpg_plus/resin", "Count": 1 }
|
||||||
|
],
|
||||||
|
"Output": [{ "Id": "rpg_plus/salve", "Count": 1 }]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Trình soạn bản đồ ô** tùy chọn vẽ nền, va chạm và sự kiện, còn **Trình soạn dữ liệu** sửa vật phẩm, công thức và nhân vật. Bật chúng trong sơ đồ của trò chơi.
|
||||||
|
|
||||||
|
## Nếu có trục trặc
|
||||||
|
|
||||||
|
1. **Hổ phách của con sói thiếu trong bản lưu cũ.** Ra chỗ con sói bên hang và nhấn E/F: nếu bạn chưa có hổ phách cũng như đèn, có thể nhặt nó ở đó một lần.
|
||||||
|
2. **Chưa biết làm gì tiếp.** Mở nhật ký.
|
||||||
|
3. **Một lựa chọn hội thoại mất tích.** Một số lựa chọn chỉ xuất hiện sau một chặng nhiệm vụ nhất định hoặc khi có vật phẩm nhất định.
|
||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
# RPG Plus — 用模组搭起来的森林 RPG
|
||||||
|
|
||||||
|
RPG Plus 是一款完整的小型 RPG:先是一段视觉小说序章,让你选择英雄的过去;然后是图块地图上的村庄与森林、关于森林失落的神秘之光的主线、支线任务、工作台、商人、西边的沙漠和回合制战斗。游戏只由数据文件和剧本组成;规则——属性、装备、检定、战斗、制作——则来自独立的模组,任何其他游戏同样可以复用。
|
||||||
|
|
||||||
|
从“开始游戏 → RPG Plus → 新游戏”进入。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 剧情与玩法
|
||||||
|
|
||||||
|
1. **岔路口。** 选择你的过去:**士兵**(战士)、**魔法师学徒**(法师)或**流浪者**(盗贼)。选择决定你的属性和初始装备。开局有 25 枚硬币。
|
||||||
|
2. **灰森林旁的村庄。** 用 WASD 或方向键行走,Shift 奔跑,E 或 F 交谈与互动。**日志**始终列着你接下来该做的具体一步。
|
||||||
|
3. **村长**会问起失踪的光,或者直接派你去东边洞穴对付那只狼。
|
||||||
|
4. **森林。** 采集柴火和蘑菇;史莱姆掉落活树脂和硬币。狼必定掉落琥珀之心,以及足够做一件斗篷的毛皮。
|
||||||
|
5. **井边工作台。** 制作林间药膏(蘑菇 + 树脂)、守林人斗篷(2 毛皮 + 2 树脂)或守护提灯(琥珀 + 2 树脂 + 2 柴火)。斗篷可以从背包直接穿上:它会改变你真实的战斗数值。
|
||||||
|
6. 西边栅栏旁的**草药师**需要药膏;帮她之后,会引出南方森林里失踪的信使。
|
||||||
|
7. **圣祠。** 带着提灯穿过狼身后的洞穴,抵达荒废的圣祠。把光还给森林,或者为村庄立下一纸契约。两个结局都会完结这段冒险,村长最后的嘱托也随你的选择而变。
|
||||||
|
|
||||||
|
村里还有别人:**教头**会带你去练武场(经验只有一次,还有一次举起原木的力量检定),**占卜师**为你解读运气。井边的炉火恢复生命和法力。奖励不能重复领取。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 沙漠
|
||||||
|
|
||||||
|
从村庄沿主路向西走出地图边缘,就到了**枯荒之地**:黄沙、一座土坯房、一处商队棚屋、一座残塔、棕榈和仙人掌。五只蝎子和甲虫等着开打,补给桶里装着小份的一次性奖励。东边边缘通回村庄。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 你的英雄
|
||||||
|
|
||||||
|
从工具栏打开**角色面板**,可以看到职业、等级和经验、属性(力量、敏捷、智力、体质、魅力、幸运)、战斗数值,以及武器、身体、饰品三个栏位里的装备。装备从背包里穿上。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
战斗采用指令式(FF),伤害由公式计算。部分对话选项会让属性对抗一次 d20 掷骰,比如教头的**[力量 12]**。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 游戏的组成
|
||||||
|
|
||||||
|
RPG Plus 就是合集 **RPG Plus**。它的分组:
|
||||||
|
|
||||||
|
```text
|
||||||
|
剧情 rpg_plus_game(本模组)
|
||||||
|
村庄与森林 rpg_demo_game:本游戏在其上扩展的地图、人物和物品
|
||||||
|
素材与规则 village_pack(美术)、classic_rpg_pack(属性、职业、装备)
|
||||||
|
属性与检定 stats_base, stat_checks
|
||||||
|
图块世界 rpg
|
||||||
|
物品、装备、交易 items_base, equipment, trading, crafting
|
||||||
|
2D 引擎 shell2d_base, dialog_base, characters_base, audio_base
|
||||||
|
战斗 turn_battle_base, ff_battle, battle_formulas
|
||||||
|
卡牌战斗 card_battle(可选)
|
||||||
|
界面 ui_base, menu_base
|
||||||
|
图块地图编辑器 rpg_editor(可选)
|
||||||
|
数据编辑器 schema_tools, items_editor, characters_editor(可选)
|
||||||
|
图块渲染器 tile_topdown_base, tile_isometric_base
|
||||||
|
```
|
||||||
|
|
||||||
|
合集的设置:`battle.core` 为 `ff_battle`,检定掷 `d20`,英雄的地图贴图为 `village/hero`,地图以俯视方式绘制。可在 **Mods & Collections → 合集 → RPG Plus → 编辑结构** 中修改。
|
||||||
|
|
||||||
|
## 给模组作者:不改代码就能改内容
|
||||||
|
|
||||||
|
```filestree
|
||||||
|
rpg_plus_game/
|
||||||
|
├── scenarios/rpg_plus/ 序章、training_yard、events/(教头、占卜师)、woodland/、desert/
|
||||||
|
├── map_events/<map>/ 新人物、工作台、洞穴门、木桶;相同 id 的事件会覆盖 rpg_demo_game 的
|
||||||
|
├── maps/ rpg_woodland_shrine.json, rpg_desert.json
|
||||||
|
├── scenes/ 岔路口、练武场、圣祠、沙漠
|
||||||
|
├── quests/woodland.json 日志的阶段
|
||||||
|
├── items/, recipes/, stations/ 材料、成品、工作台的菜单
|
||||||
|
├── patches/character.json 给 rpg_demo_game 的怪物追加的掉落
|
||||||
|
├── patches/shop.json 给村庄商人追加的商品
|
||||||
|
└── characters/, charsets/, backgrounds/, locale/
|
||||||
|
```
|
||||||
|
|
||||||
|
**去扩展别的模组,而不是复制它。** 村庄、房屋和森林仍然来自 `rpg_demo_game`。与那里相同 id 的事件会将其替换(村长得到新对话);新 id 则添加人物。补丁给它的怪物添加掉落,给它的商店添加商品。
|
||||||
|
|
||||||
|
**剧本中的检定与条件。** 占卜师先测试幸运,再打量你的装备:
|
||||||
|
|
||||||
|
```scenario
|
||||||
|
text "“想知道等着你的是什么?把手给我。不是这只——是你不用来偷东西的那只。”" key=rpg_plus/seer/hello
|
||||||
|
check luk 14 who=rpg_hero else=fail -> :ok
|
||||||
|
|
||||||
|
:ok
|
||||||
|
text "“洞口的狼怕火胜过怕钢。还有,往它没护盾的地方打。这些免费。其余的不免费。”" key=rpg_plus/seer/fortune
|
||||||
|
when "!equipped(rpg_hero, 'classic/clover_charm')" -> :charm
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
`when "item('rpg_plus/salve') >= 1"` 检查背包,`level(rpg_hero) >= 3` 检查英雄的等级;标志位让一次性奖励不会重复。台词以俄语写成,每行带一个 `key=`;翻译在 `locale/scenarios_<lang>.properties` 里。
|
||||||
|
|
||||||
|
**配方**就是 `recipes/woodland.json` 里的数据:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Id": "rpg_plus/salve",
|
||||||
|
"Name": "rpg_plus/recipe/salve/name",
|
||||||
|
"Type": "Cooking",
|
||||||
|
"Input": [
|
||||||
|
{ "Id": "rpg_demo/mushroom", "Count": 1 },
|
||||||
|
{ "Id": "rpg_plus/resin", "Count": 1 }
|
||||||
|
],
|
||||||
|
"Output": [{ "Id": "rpg_plus/salve", "Count": 1 }]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
可选的**图块地图编辑器**负责绘制地面、碰撞和事件,**数据编辑器**则编辑物品、配方和角色。在游戏的结构里打开它们。
|
||||||
|
|
||||||
|
## 出问题了怎么办
|
||||||
|
|
||||||
|
1. **旧存档里没有狼的琥珀。** 走到洞穴旁狼所在的位置按 E/F:只要你既没有琥珀也没有提灯,就能在那里捡到一次。
|
||||||
|
2. **不知道下一步做什么。** 打开日志。
|
||||||
|
3. **某个对话选项不见了。** 有些选项只有推进到某个任务阶段或持有特定物品后才会出现。
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
mod_rpg_plus_game_name = RPG Plus
|
mod_rpg_plus_game_name = RPG Plus
|
||||||
mod_rpg_plus_game_desc = The story of the RPG Plus sample, built on top of the RPG demo rather than beside it: a novel-style prologue at a crossroads with a class choice and a first charisma check, then the whole rpg_demo village and forest without a single edit — a trainer and a fortune teller are added by this mod's own map events file, a training yard scene, gear at the merchant's (a shop patch), a wolf with stats (a character patch). Only data and scenarios.
|
mod_rpg_plus_game_desc = A complete small RPG: a prologue that picks the class, a village and forest mystery, crafting, trade, a desert and turn-based battles — data and scenarios only.
|
||||||
|
|
||||||
rpg_plus/item/resin/name = Living resin
|
rpg_plus/item/resin/name = Living resin
|
||||||
rpg_plus/item/resin/desc = The slime left this instead of a will. Burns even in the rain.
|
rpg_plus/item/resin/desc = The slime left this instead of a will. Burns even in the rain.
|
||||||
@@ -33,3 +33,7 @@ rpg_plus/recipe/lantern/name = Ward lantern
|
|||||||
rpg_plus/recipe/lantern/desc = Amber, two resin and two branch bundles. Light needs a frame.
|
rpg_plus/recipe/lantern/desc = Amber, two resin and two branch bundles. Light needs a frame.
|
||||||
rpg_plus/station/name = The well workbench
|
rpg_plus/station/name = The well workbench
|
||||||
rpg_plus/map/shrine = The forest remembers
|
rpg_plus/map/shrine = The forest remembers
|
||||||
|
|
||||||
|
rpg_plus/desert/map/name=The Dry March
|
||||||
|
rpg_plus/desert/enemy/scorpion/name=Sand scorpion
|
||||||
|
rpg_plus/desert/enemy/beetle/name=Desert beetle
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
mod_rpg_plus_game_name = RPG Plus
|
mod_rpg_plus_game_name = RPG Plus
|
||||||
mod_rpg_plus_game_desc = La historia del ejemplo RPG Plus, construida encima de la demo RPG y no a su lado: un prólogo estilo novela en un cruce con elección de clase y una primera tirada de carisma, luego toda la aldea y el bosque de rpg_demo sin tocar nada — un maestro y una adivina añadidos con el propio archivo de eventos de mapa del mod, una escena del patio de entrenamiento, equipo en la tienda (un parche de la tienda), un lobo con estadísticas (un parche de personaje). Solo datos y escenarios.
|
mod_rpg_plus_game_desc = Un RPG pequeño y completo: un prólogo que elige la clase, el misterio de la aldea y el bosque, crafteo, comercio, un desierto y combates por turnos. Solo datos y escenarios.
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
mod_rpg_plus_game_name = RPG Plus
|
mod_rpg_plus_game_name = RPG Plus
|
||||||
mod_rpg_plus_game_desc = RPG Plus サンプルの物語。RPG デモの横ではなく上に作られている:十字路でのノベル風プロローグで職業を選び、最初のカリスマ判定。その後は rpg_demo の村と森をそのまま——師匠と占い師はこの MOD 自身のマップイベントファイルで追加、稽古場のシーン、商人の装備(店のパッチ)、ステータス付きの狼(キャラクターのパッチ)。データとシナリオのみ。
|
mod_rpg_plus_game_desc = 完成された小さな RPG:クラスを選ぶプロローグ、村と森の謎、クラフト、交易、砂漠、ターン制バトル。データとシナリオのみ。
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
mod_rpg_plus_game_name = RPG Plus
|
mod_rpg_plus_game_name = RPG Plus
|
||||||
mod_rpg_plus_game_desc = RPG Plus 샘플의 이야기. RPG 데모 옆이 아니라 그 위에 만들었습니다: 갈림길의 노벨식 프롤로그에서 직업을 고르고 첫 매력 판정을 하고, 이어서 rpg_demo의 마을과 숲 전체를 한 줄도 고치지 않고 — 스승과 점쟁이는 이 모드의 자체 맵 이벤트 파일로 추가, 수련장 장면, 상인의 장비(상점 패치), 능력치를 가진 늑대(캐릭터 패치). 데이터와 시나리오만.
|
mod_rpg_plus_game_desc = 완성된 소규모 RPG: 클래스를 고르는 프롤로그, 마을과 숲의 미스터리, 제작, 거래, 사막, 턴제 전투. 데이터와 시나리오만 있습니다.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
mod_rpg_plus_game_name = RPG Plus
|
mod_rpg_plus_game_name = RPG Plus
|
||||||
mod_rpg_plus_game_desc = Сюжет образца RPG Plus, построенный поверх RPG-демо, а не рядом с ним: пролог-новелла на перекрёстке с выбором класса и первой проверкой харизмы, затем вся деревня и лес rpg_demo без единой правки — наставник и гадалка добавлены собственным файлом событий карты, сцена тренировочной площадки, снаряжение у торговца (патч лавки), волк со статами (патч персонажа). Только данные и сценарии.
|
mod_rpg_plus_game_desc = Полная маленькая RPG: пролог с выбором класса, тайна деревни и леса, крафт, торговля, пустыня и пошаговые бои — только данные и сценарии.
|
||||||
|
|
||||||
rpg_plus/item/resin/name = Живая смола
|
rpg_plus/item/resin/name = Живая смола
|
||||||
rpg_plus/item/resin/desc = Слизень оставил её вместо завещания. Горит даже под дождём.
|
rpg_plus/item/resin/desc = Слизень оставил её вместо завещания. Горит даже под дождём.
|
||||||
@@ -33,3 +33,7 @@ rpg_plus/recipe/lantern/name = Сторожевой фонарь
|
|||||||
rpg_plus/recipe/lantern/desc = Янтарное сердце, две смолы, две связки веток. Свету нужен каркас.
|
rpg_plus/recipe/lantern/desc = Янтарное сердце, две смолы, две связки веток. Свету нужен каркас.
|
||||||
rpg_plus/station/name = Верстак у колодца
|
rpg_plus/station/name = Верстак у колодца
|
||||||
rpg_plus/map/shrine = Лес помнит
|
rpg_plus/map/shrine = Лес помнит
|
||||||
|
|
||||||
|
rpg_plus/desert/map/name=Сухой предел
|
||||||
|
rpg_plus/desert/enemy/scorpion/name=Песчаный скорпион
|
||||||
|
rpg_plus/desert/enemy/beetle/name=Пустынный жук
|
||||||
@@ -95,3 +95,21 @@ rpg_plus/woodland/lamplighter/3 = “Brighter forest. Darker village. At least t
|
|||||||
|
|
||||||
rpg_plus/woodland/elder/ready = “Built it? Then through the cave to the shrine. Keep the shutters closed until you decide whom the light serves.”
|
rpg_plus/woodland/elder/ready = “Built it? Then through the cave to the shrine. Keep the shutters closed until you decide whom the light serves.”
|
||||||
rpg_plus/woodland/amber/recovered = The amber heart was left by the cave. This time you take it with you.
|
rpg_plus/woodland/amber/recovered = The amber heart was left by the cave. This time you take it with you.
|
||||||
|
|
||||||
|
rpg_plus/desert/barrel/supply_rpg_village_19_11=You find a few supplies in the barrel.
|
||||||
|
rpg_plus/desert/barrel/supply_rpg_village_20_11=You find a few supplies in the barrel.
|
||||||
|
rpg_plus/desert/barrel/supply_rpg_village_4_7=You find a few supplies in the barrel.
|
||||||
|
rpg_plus/desert/barrel/supply_rpg_village_22_11=You find a few supplies in the barrel.
|
||||||
|
rpg_plus/desert/barrel/supply_rpg_elder_house_1_2=You find a few supplies in the barrel.
|
||||||
|
rpg_plus/desert/barrel/supply_rpg_elder_house_2_2=You find a few supplies in the barrel.
|
||||||
|
rpg_plus/desert/barrel/supply_rpg_elder_house_10_2=You find a few supplies in the barrel.
|
||||||
|
|
||||||
|
rpg_plus/desert/enter=Beyond the last tree, the shade ends. The scorpions here are larger than the local hopes.
|
||||||
|
rpg_plus/desert/barrel/desert_barrel_supply=You find a few supplies in the barrel.
|
||||||
|
rpg_plus/desert/barrel/empty=Empty. Even the rubbish has been taken.
|
||||||
|
rpg_plus/desert/barrel/desert_barrel_caravan=You find a few supplies in the barrel.
|
||||||
|
rpg_plus/desert/barrel/desert_barrel_ruins=You find a few supplies in the barrel.
|
||||||
|
rpg_plus/desert/house=A note on the door reads: Back when it cools down. The ink has faded.
|
||||||
|
rpg_plus/desert/caravan=The caravan has left. The prices remain. Nothing has become cheaper.
|
||||||
|
rpg_plus/desert/enemy/scorpion=The scorpion raises its tail. Negotiations will be brief.
|
||||||
|
rpg_plus/desert/enemy/beetle=The beetle scrapes the sand. It charges meat for passage.
|
||||||
@@ -95,3 +95,21 @@ rpg_plus/woodland/lamplighter/3 = — В лесу светлее. В дерев
|
|||||||
|
|
||||||
rpg_plus/woodland/elder/ready = — Собрал? Тогда через пещеру к святилищу. И не поднимай створки, пока не решишь, кому светить.
|
rpg_plus/woodland/elder/ready = — Собрал? Тогда через пещеру к святилищу. И не поднимай створки, пока не решишь, кому светить.
|
||||||
rpg_plus/woodland/amber/recovered = Сердце янтаря осталось у пещеры. На этот раз забираешь его с собой.
|
rpg_plus/woodland/amber/recovered = Сердце янтаря осталось у пещеры. На этот раз забираешь его с собой.
|
||||||
|
|
||||||
|
rpg_plus/desert/barrel/supply_rpg_village_19_11=В бочке нашлась мелочь: 2 монет(ы), 1 связка хвороста.
|
||||||
|
rpg_plus/desert/barrel/supply_rpg_village_20_11=В бочке нашлась мелочь: 2 монет(ы), 1 связка хвороста.
|
||||||
|
rpg_plus/desert/barrel/supply_rpg_village_4_7=В бочке нашлась мелочь: 2 монет(ы), 1 смола.
|
||||||
|
rpg_plus/desert/barrel/supply_rpg_village_22_11=В бочке нашлась мелочь: 2 монет(ы), 1 смола.
|
||||||
|
rpg_plus/desert/barrel/supply_rpg_elder_house_1_2=В бочке нашлась мелочь: 2 монет(ы), 1 связка хвороста.
|
||||||
|
rpg_plus/desert/barrel/supply_rpg_elder_house_2_2=В бочке нашлась мелочь: 2 монет(ы), 1 связка хвороста.
|
||||||
|
rpg_plus/desert/barrel/supply_rpg_elder_house_10_2=В бочке нашлась мелочь: 2 монет(ы), 1 связка хвороста.
|
||||||
|
|
||||||
|
rpg_plus/desert/enter=За последним деревом кончается тень. Скорпионы здесь крупнее местных надежд.
|
||||||
|
rpg_plus/desert/barrel/desert_barrel_supply=В бочке нашлась мелочь: 3 монет(ы), 1 эликсир.
|
||||||
|
rpg_plus/desert/barrel/empty=Пусто. Даже мусор уже забрали.
|
||||||
|
rpg_plus/desert/barrel/desert_barrel_caravan=В бочке нашлась мелочь: 4 монет(ы), 1 смола.
|
||||||
|
rpg_plus/desert/barrel/desert_barrel_ruins=В бочке нашлась мелочь: 2 монет(ы), 1 связка хвороста.
|
||||||
|
rpg_plus/desert/house=На двери записка: «Вернусь, когда станет прохладно». Чернила выцвели.
|
||||||
|
rpg_plus/desert/caravan=Караван ушёл. Цены остались. Дешевле от этого не стало.
|
||||||
|
rpg_plus/desert/enemy/scorpion=Скорпион поднимает хвост. Переговоры будут короткими.
|
||||||
|
rpg_plus/desert/enemy/beetle=Жук скребёт песок. За проход он берёт мясом.
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
mod_rpg_plus_game_name = RPG Plus
|
mod_rpg_plus_game_name = RPG Plus
|
||||||
mod_rpg_plus_game_desc = Câu chuyện của mẫu RPG Plus, xây trên bản demo RPG chứ không đặt cạnh nó: phần mở đầu kiểu tiểu thuyết ở ngã ba với lựa chọn lớp nhân vật và lần kiểm tra sức hút đầu tiên, rồi toàn bộ làng và rừng của rpg_demo không sửa một dòng — sư phụ và bà thầy bói được thêm bằng chính tệp sự kiện bản đồ của mod, cảnh bãi tập, trang bị ở cửa hàng (bản vá cửa hàng), con sói có chỉ số (bản vá nhân vật). Chỉ dữ liệu và kịch bản.
|
mod_rpg_plus_game_desc = Một RPG nhỏ nhưng trọn vẹn: phần mở đầu chọn class, bí ẩn của làng và khu rừng, chế tạo, mua bán, hoang mạc và chiến đấu theo lượt — chỉ dữ liệu và kịch bản.
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
mod_rpg_plus_game_name = RPG Plus
|
mod_rpg_plus_game_name = RPG Plus
|
||||||
mod_rpg_plus_game_desc = RPG Plus 示例的故事,建立在 RPG 演示之上而非之旁:十字路口的小说式序章,选择职业并进行第一次魅力检定;之后是 rpg_demo 的整个村庄和森林,一处未改——导师和占卜师由本模组自己的地图事件文件加入,练武场场景,商人处的装备(商店补丁),带属性的狼(角色补丁)。仅有数据和剧本。
|
mod_rpg_plus_game_desc = 一款完整的小型 RPG:选择职业的序章、村庄与森林之谜、制作、交易、沙漠和回合制战斗——只有数据和剧本。
|
||||||
+7
-1
@@ -3,7 +3,7 @@
|
|||||||
"name": "RPG Plus",
|
"name": "RPG Plus",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": "ct",
|
"author": "ct",
|
||||||
"description": "A woodland RPG with a class prologue, a village ward mystery, an apothecary and missing courier, a ruined shrine with two endings, monster loot and recipes. World and story are data and scenarios; crafting is a separate reusable mechanic.",
|
"description": "A complete small RPG: a prologue that picks the class, a village and forest mystery, crafting, trade, a desert and turn-based battles — data and scenarios only.",
|
||||||
"category": "story",
|
"category": "story",
|
||||||
"depends": [
|
"depends": [
|
||||||
"rpg_demo_game>=1",
|
"rpg_demo_game>=1",
|
||||||
@@ -31,6 +31,12 @@
|
|||||||
"activate": [
|
"activate": [
|
||||||
"--itest=woodland"
|
"--itest=woodland"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "desert",
|
||||||
|
"activate": [
|
||||||
|
"--itest=desert"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
{
|
||||||
|
"Events": [
|
||||||
|
{
|
||||||
|
"Id": "desert_to_village",
|
||||||
|
"X": 39,
|
||||||
|
"Y": 12,
|
||||||
|
"Tile": -1,
|
||||||
|
"Dir": "Down",
|
||||||
|
"Trigger": "Touch",
|
||||||
|
"Solid": false,
|
||||||
|
"Teleport": {
|
||||||
|
"Map": "rpg_village",
|
||||||
|
"X": 1,
|
||||||
|
"Y": 9,
|
||||||
|
"Dir": "Right"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "desert_barrel_supply",
|
||||||
|
"X": 17,
|
||||||
|
"Y": 8,
|
||||||
|
"Sprite": "desert/barrel_closed",
|
||||||
|
"Tile": -1,
|
||||||
|
"Dir": "Down",
|
||||||
|
"Trigger": "Action",
|
||||||
|
"Solid": true,
|
||||||
|
"Commands": [
|
||||||
|
"flag desert_barrel_supply_open 1",
|
||||||
|
"action item.add id=money/coin count=3",
|
||||||
|
"action item.add id=rpg_demo/elixir count=1",
|
||||||
|
"ptext \u0022\u0412 \u0431\u043E\u0447\u043A\u0435 \u043D\u0430\u0448\u043B\u0430\u0441\u044C \u043C\u0435\u043B\u043E\u0447\u044C: 3 \u043C\u043E\u043D\u0435\u0442(\u044B), 1 \u044D\u043B\u0438\u043A\u0441\u0438\u0440.\u0022 key=rpg_plus/desert/barrel/desert_barrel_supply"
|
||||||
|
],
|
||||||
|
"Pages": [
|
||||||
|
{
|
||||||
|
"Condition": "desert_barrel_supply_open == 1",
|
||||||
|
"Sprite": "desert/barrel_open",
|
||||||
|
"Commands": [
|
||||||
|
"ptext \u0022\u041F\u0443\u0441\u0442\u043E. \u0414\u0430\u0436\u0435 \u043C\u0443\u0441\u043E\u0440 \u0443\u0436\u0435 \u0437\u0430\u0431\u0440\u0430\u043B\u0438.\u0022 key=rpg_plus/desert/barrel/empty"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "desert_barrel_caravan",
|
||||||
|
"X": 25,
|
||||||
|
"Y": 8,
|
||||||
|
"Sprite": "desert/barrel_closed",
|
||||||
|
"Tile": -1,
|
||||||
|
"Dir": "Down",
|
||||||
|
"Trigger": "Action",
|
||||||
|
"Solid": true,
|
||||||
|
"Commands": [
|
||||||
|
"flag desert_barrel_caravan_open 1",
|
||||||
|
"action item.add id=money/coin count=4",
|
||||||
|
"action item.add id=rpg_plus/resin count=1",
|
||||||
|
"ptext \u0022\u0412 \u0431\u043E\u0447\u043A\u0435 \u043D\u0430\u0448\u043B\u0430\u0441\u044C \u043C\u0435\u043B\u043E\u0447\u044C: 4 \u043C\u043E\u043D\u0435\u0442(\u044B), 1 \u0441\u043C\u043E\u043B\u0430.\u0022 key=rpg_plus/desert/barrel/desert_barrel_caravan"
|
||||||
|
],
|
||||||
|
"Pages": [
|
||||||
|
{
|
||||||
|
"Condition": "desert_barrel_caravan_open == 1",
|
||||||
|
"Sprite": "desert/barrel_open",
|
||||||
|
"Commands": [
|
||||||
|
"ptext \u0022\u041F\u0443\u0441\u0442\u043E. \u0414\u0430\u0436\u0435 \u043C\u0443\u0441\u043E\u0440 \u0443\u0436\u0435 \u0437\u0430\u0431\u0440\u0430\u043B\u0438.\u0022 key=rpg_plus/desert/barrel/empty"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "desert_barrel_ruins",
|
||||||
|
"X": 10,
|
||||||
|
"Y": 20,
|
||||||
|
"Sprite": "desert/barrel_closed",
|
||||||
|
"Tile": -1,
|
||||||
|
"Dir": "Down",
|
||||||
|
"Trigger": "Action",
|
||||||
|
"Solid": true,
|
||||||
|
"Commands": [
|
||||||
|
"flag desert_barrel_ruins_open 1",
|
||||||
|
"action item.add id=money/coin count=2",
|
||||||
|
"action item.add id=rpg_demo/branches count=1",
|
||||||
|
"ptext \u0022\u0412 \u0431\u043E\u0447\u043A\u0435 \u043D\u0430\u0448\u043B\u0430\u0441\u044C \u043C\u0435\u043B\u043E\u0447\u044C: 2 \u043C\u043E\u043D\u0435\u0442(\u044B), 1 \u0441\u0432\u044F\u0437\u043A\u0430 \u0445\u0432\u043E\u0440\u043E\u0441\u0442\u0430.\u0022 key=rpg_plus/desert/barrel/desert_barrel_ruins"
|
||||||
|
],
|
||||||
|
"Pages": [
|
||||||
|
{
|
||||||
|
"Condition": "desert_barrel_ruins_open == 1",
|
||||||
|
"Sprite": "desert/barrel_open",
|
||||||
|
"Commands": [
|
||||||
|
"ptext \u0022\u041F\u0443\u0441\u0442\u043E. \u0414\u0430\u0436\u0435 \u043C\u0443\u0441\u043E\u0440 \u0443\u0436\u0435 \u0437\u0430\u0431\u0440\u0430\u043B\u0438.\u0022 key=rpg_plus/desert/barrel/empty"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "desert_house_door",
|
||||||
|
"X": 8,
|
||||||
|
"Y": 8,
|
||||||
|
"Tile": -1,
|
||||||
|
"Dir": "Down",
|
||||||
|
"Trigger": "Action",
|
||||||
|
"Solid": false,
|
||||||
|
"Commands": [
|
||||||
|
"ptext \u0022\u041D\u0430 \u0434\u0432\u0435\u0440\u0438 \u0437\u0430\u043F\u0438\u0441\u043A\u0430: \u00AB\u0412\u0435\u0440\u043D\u0443\u0441\u044C, \u043A\u043E\u0433\u0434\u0430 \u0441\u0442\u0430\u043D\u0435\u0442 \u043F\u0440\u043E\u0445\u043B\u0430\u0434\u043D\u043E\u00BB. \u0427\u0435\u0440\u043D\u0438\u043B\u0430 \u0432\u044B\u0446\u0432\u0435\u043B\u0438.\u0022 key=rpg_plus/desert/house"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "desert_caravan_notice",
|
||||||
|
"X": 21,
|
||||||
|
"Y": 8,
|
||||||
|
"Tile": -1,
|
||||||
|
"Dir": "Down",
|
||||||
|
"Trigger": "Action",
|
||||||
|
"Solid": false,
|
||||||
|
"Commands": [
|
||||||
|
"ptext \u0022\u041A\u0430\u0440\u0430\u0432\u0430\u043D \u0443\u0448\u0451\u043B. \u0426\u0435\u043D\u044B \u043E\u0441\u0442\u0430\u043B\u0438\u0441\u044C. \u0414\u0435\u0448\u0435\u0432\u043B\u0435 \u043E\u0442 \u044D\u0442\u043E\u0433\u043E \u043D\u0435 \u0441\u0442\u0430\u043B\u043E.\u0022 key=rpg_plus/desert/caravan"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "desert_scorpion_1",
|
||||||
|
"X": 27,
|
||||||
|
"Y": 12,
|
||||||
|
"Sprite": "desert/scorpion",
|
||||||
|
"Tile": -1,
|
||||||
|
"Dir": "Down",
|
||||||
|
"Trigger": "Action",
|
||||||
|
"Solid": true,
|
||||||
|
"Commands": [
|
||||||
|
"ptext \u0022\u0421\u043A\u043E\u0440\u043F\u0438\u043E\u043D \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0435\u0442 \u0445\u0432\u043E\u0441\u0442. \u041F\u0435\u0440\u0435\u0433\u043E\u0432\u043E\u0440\u044B \u0431\u0443\u0434\u0443\u0442 \u043A\u043E\u0440\u043E\u0442\u043A\u0438\u043C\u0438.\u0022 key=rpg_plus/desert/enemy/scorpion",
|
||||||
|
"action rpg.battle enemies=rpg_desert_scorpion win_flag=desert_scorpion_1_dead",
|
||||||
|
"end"
|
||||||
|
],
|
||||||
|
"Condition": "desert_scorpion_1_dead != 1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "desert_beetle_2",
|
||||||
|
"X": 22,
|
||||||
|
"Y": 13,
|
||||||
|
"Sprite": "desert/beetle",
|
||||||
|
"Tile": -1,
|
||||||
|
"Dir": "Left",
|
||||||
|
"Trigger": "Action",
|
||||||
|
"Solid": true,
|
||||||
|
"Commands": [
|
||||||
|
"ptext \u0022\u0416\u0443\u043A \u0441\u043A\u0440\u0435\u0431\u0451\u0442 \u043F\u0435\u0441\u043E\u043A. \u0417\u0430 \u043F\u0440\u043E\u0445\u043E\u0434 \u043E\u043D \u0431\u0435\u0440\u0451\u0442 \u043C\u044F\u0441\u043E\u043C.\u0022 key=rpg_plus/desert/enemy/beetle",
|
||||||
|
"action rpg.battle enemies=rpg_desert_beetle win_flag=desert_beetle_2_dead",
|
||||||
|
"end"
|
||||||
|
],
|
||||||
|
"Condition": "desert_beetle_2_dead != 1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "desert_scorpion_3",
|
||||||
|
"X": 13,
|
||||||
|
"Y": 14,
|
||||||
|
"Sprite": "desert/scorpion",
|
||||||
|
"Tile": -1,
|
||||||
|
"Dir": "Down",
|
||||||
|
"Trigger": "Action",
|
||||||
|
"Solid": true,
|
||||||
|
"Commands": [
|
||||||
|
"ptext \u0022\u0421\u043A\u043E\u0440\u043F\u0438\u043E\u043D \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0435\u0442 \u0445\u0432\u043E\u0441\u0442. \u041F\u0435\u0440\u0435\u0433\u043E\u0432\u043E\u0440\u044B \u0431\u0443\u0434\u0443\u0442 \u043A\u043E\u0440\u043E\u0442\u043A\u0438\u043C\u0438.\u0022 key=rpg_plus/desert/enemy/scorpion",
|
||||||
|
"action rpg.battle enemies=rpg_desert_scorpion win_flag=desert_scorpion_3_dead",
|
||||||
|
"end"
|
||||||
|
],
|
||||||
|
"Condition": "desert_scorpion_3_dead != 1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "desert_beetle_4",
|
||||||
|
"X": 11,
|
||||||
|
"Y": 10,
|
||||||
|
"Sprite": "desert/beetle",
|
||||||
|
"Tile": -1,
|
||||||
|
"Dir": "Left",
|
||||||
|
"Trigger": "Action",
|
||||||
|
"Solid": true,
|
||||||
|
"Commands": [
|
||||||
|
"ptext \u0022\u0416\u0443\u043A \u0441\u043A\u0440\u0435\u0431\u0451\u0442 \u043F\u0435\u0441\u043E\u043A. \u0417\u0430 \u043F\u0440\u043E\u0445\u043E\u0434 \u043E\u043D \u0431\u0435\u0440\u0451\u0442 \u043C\u044F\u0441\u043E\u043C.\u0022 key=rpg_plus/desert/enemy/beetle",
|
||||||
|
"action rpg.battle enemies=rpg_desert_beetle win_flag=desert_beetle_4_dead",
|
||||||
|
"end"
|
||||||
|
],
|
||||||
|
"Condition": "desert_beetle_4_dead != 1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "desert_beetle_5",
|
||||||
|
"X": 28,
|
||||||
|
"Y": 19,
|
||||||
|
"Sprite": "desert/beetle",
|
||||||
|
"Tile": -1,
|
||||||
|
"Dir": "Down",
|
||||||
|
"Trigger": "Action",
|
||||||
|
"Solid": true,
|
||||||
|
"Commands": [
|
||||||
|
"ptext \u0022\u0416\u0443\u043A \u0441\u043A\u0440\u0435\u0431\u0451\u0442 \u043F\u0435\u0441\u043E\u043A. \u0417\u0430 \u043F\u0440\u043E\u0445\u043E\u0434 \u043E\u043D \u0431\u0435\u0440\u0451\u0442 \u043C\u044F\u0441\u043E\u043C.\u0022 key=rpg_plus/desert/enemy/beetle",
|
||||||
|
"action rpg.battle enemies=rpg_desert_beetle win_flag=desert_beetle_5_dead",
|
||||||
|
"end"
|
||||||
|
],
|
||||||
|
"Condition": "desert_beetle_5_dead != 1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
{
|
||||||
|
"Events": [
|
||||||
|
{
|
||||||
|
"Id": "supply_rpg_elder_house_1_2",
|
||||||
|
"X": 1,
|
||||||
|
"Y": 2,
|
||||||
|
"Sprite": "desert/barrel_closed",
|
||||||
|
"Trigger": "Action",
|
||||||
|
"Solid": true,
|
||||||
|
"Commands": [
|
||||||
|
"flag supply_rpg_elder_house_1_2_open 1",
|
||||||
|
"action item.add id=money/coin count=2",
|
||||||
|
"action item.add id=rpg_demo/branches count=1",
|
||||||
|
"ptext \"В бочке нашлась мелочь: 2 монет(ы), 1 связка хвороста.\" key=rpg_plus/desert/barrel/supply_rpg_elder_house_1_2"
|
||||||
|
],
|
||||||
|
"Pages": [
|
||||||
|
{
|
||||||
|
"Condition": "supply_rpg_elder_house_1_2_open == 1",
|
||||||
|
"Sprite": "desert/barrel_open",
|
||||||
|
"Commands": [
|
||||||
|
"ptext \"Пусто. Даже мусор уже забрали.\" key=rpg_plus/desert/barrel/empty"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "supply_rpg_elder_house_2_2",
|
||||||
|
"X": 2,
|
||||||
|
"Y": 2,
|
||||||
|
"Sprite": "desert/barrel_closed",
|
||||||
|
"Trigger": "Action",
|
||||||
|
"Solid": true,
|
||||||
|
"Commands": [
|
||||||
|
"flag supply_rpg_elder_house_2_2_open 1",
|
||||||
|
"action item.add id=money/coin count=2",
|
||||||
|
"action item.add id=rpg_demo/branches count=1",
|
||||||
|
"ptext \"В бочке нашлась мелочь: 2 монет(ы), 1 связка хвороста.\" key=rpg_plus/desert/barrel/supply_rpg_elder_house_2_2"
|
||||||
|
],
|
||||||
|
"Pages": [
|
||||||
|
{
|
||||||
|
"Condition": "supply_rpg_elder_house_2_2_open == 1",
|
||||||
|
"Sprite": "desert/barrel_open",
|
||||||
|
"Commands": [
|
||||||
|
"ptext \"Пусто. Даже мусор уже забрали.\" key=rpg_plus/desert/barrel/empty"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "supply_rpg_elder_house_10_2",
|
||||||
|
"X": 10,
|
||||||
|
"Y": 2,
|
||||||
|
"Sprite": "desert/barrel_closed",
|
||||||
|
"Trigger": "Action",
|
||||||
|
"Solid": true,
|
||||||
|
"Commands": [
|
||||||
|
"flag supply_rpg_elder_house_10_2_open 1",
|
||||||
|
"action item.add id=money/coin count=2",
|
||||||
|
"action item.add id=rpg_demo/branches count=1",
|
||||||
|
"ptext \"В бочке нашлась мелочь: 2 монет(ы), 1 связка хвороста.\" key=rpg_plus/desert/barrel/supply_rpg_elder_house_10_2"
|
||||||
|
],
|
||||||
|
"Pages": [
|
||||||
|
{
|
||||||
|
"Condition": "supply_rpg_elder_house_10_2_open == 1",
|
||||||
|
"Sprite": "desert/barrel_open",
|
||||||
|
"Commands": [
|
||||||
|
"ptext \"Пусто. Даже мусор уже забрали.\" key=rpg_plus/desert/barrel/empty"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
{
|
||||||
|
"Events": [
|
||||||
|
{
|
||||||
|
"Id": "supply_rpg_village_19_11",
|
||||||
|
"X": 19,
|
||||||
|
"Y": 11,
|
||||||
|
"Sprite": "desert/barrel_closed",
|
||||||
|
"Trigger": "Action",
|
||||||
|
"Solid": true,
|
||||||
|
"Commands": [
|
||||||
|
"flag supply_rpg_village_19_11_open 1",
|
||||||
|
"action item.add id=money/coin count=2",
|
||||||
|
"action item.add id=rpg_demo/branches count=1",
|
||||||
|
"ptext \"В бочке нашлась мелочь: 2 монет(ы), 1 связка хвороста.\" key=rpg_plus/desert/barrel/supply_rpg_village_19_11"
|
||||||
|
],
|
||||||
|
"Pages": [
|
||||||
|
{
|
||||||
|
"Condition": "supply_rpg_village_19_11_open == 1",
|
||||||
|
"Sprite": "desert/barrel_open",
|
||||||
|
"Commands": [
|
||||||
|
"ptext \"Пусто. Даже мусор уже забрали.\" key=rpg_plus/desert/barrel/empty"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "supply_rpg_village_20_11",
|
||||||
|
"X": 20,
|
||||||
|
"Y": 11,
|
||||||
|
"Sprite": "desert/barrel_closed",
|
||||||
|
"Trigger": "Action",
|
||||||
|
"Solid": true,
|
||||||
|
"Commands": [
|
||||||
|
"flag supply_rpg_village_20_11_open 1",
|
||||||
|
"action item.add id=money/coin count=2",
|
||||||
|
"action item.add id=rpg_demo/branches count=1",
|
||||||
|
"ptext \"В бочке нашлась мелочь: 2 монет(ы), 1 связка хвороста.\" key=rpg_plus/desert/barrel/supply_rpg_village_20_11"
|
||||||
|
],
|
||||||
|
"Pages": [
|
||||||
|
{
|
||||||
|
"Condition": "supply_rpg_village_20_11_open == 1",
|
||||||
|
"Sprite": "desert/barrel_open",
|
||||||
|
"Commands": [
|
||||||
|
"ptext \"Пусто. Даже мусор уже забрали.\" key=rpg_plus/desert/barrel/empty"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "supply_rpg_village_4_7",
|
||||||
|
"X": 4,
|
||||||
|
"Y": 7,
|
||||||
|
"Sprite": "desert/barrel_closed",
|
||||||
|
"Trigger": "Action",
|
||||||
|
"Solid": true,
|
||||||
|
"Commands": [
|
||||||
|
"flag supply_rpg_village_4_7_open 1",
|
||||||
|
"action item.add id=money/coin count=2",
|
||||||
|
"action item.add id=rpg_plus/resin count=1",
|
||||||
|
"ptext \"В бочке нашлась мелочь: 2 монет(ы), 1 смола.\" key=rpg_plus/desert/barrel/supply_rpg_village_4_7"
|
||||||
|
],
|
||||||
|
"Pages": [
|
||||||
|
{
|
||||||
|
"Condition": "supply_rpg_village_4_7_open == 1",
|
||||||
|
"Sprite": "desert/barrel_open",
|
||||||
|
"Commands": [
|
||||||
|
"ptext \"Пусто. Даже мусор уже забрали.\" key=rpg_plus/desert/barrel/empty"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "supply_rpg_village_22_11",
|
||||||
|
"X": 22,
|
||||||
|
"Y": 11,
|
||||||
|
"Sprite": "desert/barrel_closed",
|
||||||
|
"Trigger": "Action",
|
||||||
|
"Solid": true,
|
||||||
|
"Commands": [
|
||||||
|
"flag supply_rpg_village_22_11_open 1",
|
||||||
|
"action item.add id=money/coin count=2",
|
||||||
|
"action item.add id=rpg_plus/resin count=1",
|
||||||
|
"ptext \"В бочке нашлась мелочь: 2 монет(ы), 1 смола.\" key=rpg_plus/desert/barrel/supply_rpg_village_22_11"
|
||||||
|
],
|
||||||
|
"Pages": [
|
||||||
|
{
|
||||||
|
"Condition": "supply_rpg_village_22_11_open == 1",
|
||||||
|
"Sprite": "desert/barrel_open",
|
||||||
|
"Commands": [
|
||||||
|
"ptext \"Пусто. Даже мусор уже забрали.\" key=rpg_plus/desert/barrel/empty"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"Events": [
|
||||||
|
{
|
||||||
|
"Id": "village_to_desert",
|
||||||
|
"X": 0,
|
||||||
|
"Y": 9,
|
||||||
|
"Trigger": "Touch",
|
||||||
|
"Solid": false,
|
||||||
|
"Teleport": {
|
||||||
|
"Map": "rpg_desert",
|
||||||
|
"X": 38,
|
||||||
|
"Y": 12,
|
||||||
|
"Dir": "Left"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -4,6 +4,8 @@
|
|||||||
"Id": "shrine_exit",
|
"Id": "shrine_exit",
|
||||||
"X": 1,
|
"X": 1,
|
||||||
"Y": 11,
|
"Y": 11,
|
||||||
|
"Tile": -1,
|
||||||
|
"Dir": "Down",
|
||||||
"Trigger": "Touch",
|
"Trigger": "Touch",
|
||||||
"Teleport": {
|
"Teleport": {
|
||||||
"Map": "rpg_forest",
|
"Map": "rpg_forest",
|
||||||
@@ -17,14 +19,12 @@
|
|||||||
"X": 12,
|
"X": 12,
|
||||||
"Y": 5,
|
"Y": 5,
|
||||||
"Tile": 36,
|
"Tile": 36,
|
||||||
|
"Dir": "Down",
|
||||||
|
"Trigger": "Action",
|
||||||
"Scenario": "rpg_plus/woodland/beacon",
|
"Scenario": "rpg_plus/woodland/beacon",
|
||||||
"Light": {
|
|
||||||
"Radius": 2,
|
|
||||||
"Color": "#699baf"
|
|
||||||
},
|
|
||||||
"Pages": [
|
"Pages": [
|
||||||
{
|
{
|
||||||
"Condition": "rpg_ward_done == 1 && rpg_ward_choice == 'pact'",
|
"Condition": "rpg_ward_done == 1 \u0026\u0026 rpg_ward_choice == \u0027pact\u0027",
|
||||||
"Tile": 36,
|
"Tile": 36,
|
||||||
"Light": {
|
"Light": {
|
||||||
"Radius": 6,
|
"Radius": 6,
|
||||||
@@ -32,14 +32,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Condition": "rpg_ward_done == 1 && rpg_ward_choice == 'free'",
|
"Condition": "rpg_ward_done == 1 \u0026\u0026 rpg_ward_choice == \u0027free\u0027",
|
||||||
"Tile": 38,
|
"Tile": 38,
|
||||||
"Light": {
|
"Light": {
|
||||||
"Radius": 6,
|
"Radius": 6,
|
||||||
"Color": "#9ce3c7"
|
"Color": "#9ce3c7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"Light": {
|
||||||
|
"Radius": 2,
|
||||||
|
"Color": "#699baf"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
{
|
||||||
|
"Kind": "rpg_map",
|
||||||
|
"Items": [
|
||||||
|
{
|
||||||
|
"Id": "rpg_desert",
|
||||||
|
"Name": "rpg_plus/desert/map/name",
|
||||||
|
"Tileset": "desert",
|
||||||
|
"Width": 40,
|
||||||
|
"Height": 24,
|
||||||
|
"Zoom": 2,
|
||||||
|
"Layers": {
|
||||||
|
"Back": [
|
||||||
|
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||||
|
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||||
|
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||||
|
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||||
|
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||||
|
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||||
|
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||||
|
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||||
|
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||||
|
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||||
|
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||||
|
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||||
|
"1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1",
|
||||||
|
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||||
|
"0 0 0 1 0 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||||
|
"0 0 0 1 1 0 1 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||||
|
"0 0 0 1 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||||
|
"0 0 0 0 1 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||||
|
"0 0 0 0 0 1 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||||
|
"0 0 0 1 0 0 1 1 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||||
|
"0 0 0 1 1 0 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||||
|
"0 0 0 1 1 1 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||||
|
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||||
|
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"
|
||||||
|
],
|
||||||
|
"Main": [
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . 244 245 . . . . . . . . . . 244 245 . . . . . . . . . . . . . . . . . . . . 252 253 . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229 230 231 . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . 124 125 126 127 128 129 . . . . . . 164 165 166 167 168 169 . . . . . . . 244 245 . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . 252 253 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . 252 253 . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . 252 253 . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . 244 245 . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . 196 197 198 199 . . . . . . 229 230 231 . . . . . . . . . . . . . . 229 230 231 . 252 253 . .",
|
||||||
|
" . . 252 253 . . . . . . . . . . . . . . . . . . . . . . . . 252 253 . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ."
|
||||||
|
],
|
||||||
|
"Front": [
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . 240 241 . . . . . . . . . . 240 241 . . . . . . . . . . . . . 220 221 222 . . . . . . . .",
|
||||||
|
" . . 242 243 . . . . . . . . . . 242 243 . . . . . . . . . . . . . 223 224 225 . . . . 250 251 . .",
|
||||||
|
" . . . . . . . 100 101 102 103 104 105 . . . . . . 140 141 142 143 144 145 . . . . 226 227 228 . . . . . . . .",
|
||||||
|
" . . . . . . . 106 107 108 109 110 111 . . . . . . 146 147 148 149 150 151 . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . 112 113 114 115 116 117 . . . . . . 152 153 154 155 156 157 . . . . . . . 240 241 . . . . . .",
|
||||||
|
" . . . . . . . 118 119 120 121 122 123 . . . . . . 158 159 160 161 162 163 . . . . . . . 242 243 . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . 250 251 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . 250 251 . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . 250 251 . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . 180 181 182 183 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . 184 185 186 187 . . . . . . 220 221 222 . . . . . . 240 241 . . . . . . 220 221 222 . . . . .",
|
||||||
|
" . . . . . 188 189 190 191 . . . . . . 223 224 225 . . . . . . 242 243 . . . . . . 223 224 225 . . . . .",
|
||||||
|
" . . . . . 192 193 194 195 . . . . . . 226 227 228 . . . . . . . . . . . . . . 226 227 228 . 250 251 . .",
|
||||||
|
" . . 250 251 . . . . . . . . . . . . . . . . . . . . . . . . 250 251 . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
|
" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Collision": [
|
||||||
|
"########################################",
|
||||||
|
"#......................................#",
|
||||||
|
"#......................................#",
|
||||||
|
"#......................................#",
|
||||||
|
"#......######......######..............#",
|
||||||
|
"#......######......######..............#",
|
||||||
|
"#......######......######..............#",
|
||||||
|
"#......######......######..............#",
|
||||||
|
"#......######......######..............#",
|
||||||
|
"#......................................#",
|
||||||
|
"#......................................#",
|
||||||
|
"#......................................#",
|
||||||
|
"#......................................o",
|
||||||
|
"#......................................#",
|
||||||
|
"#......................................#",
|
||||||
|
"#......................................#",
|
||||||
|
"#....####..............................#",
|
||||||
|
"#....####..............................#",
|
||||||
|
"#....####..............................#",
|
||||||
|
"#....####..............................#",
|
||||||
|
"#....####..............................#",
|
||||||
|
"#......................................#",
|
||||||
|
"#......................................#",
|
||||||
|
"########################################"
|
||||||
|
],
|
||||||
|
"Spawn": {
|
||||||
|
"X": 38,
|
||||||
|
"Y": 12,
|
||||||
|
"Dir": "Left"
|
||||||
|
},
|
||||||
|
"Ambient": "#fff0d8"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -8,69 +8,67 @@
|
|||||||
"Width": 24,
|
"Width": 24,
|
||||||
"Height": 16,
|
"Height": 16,
|
||||||
"Zoom": 2,
|
"Zoom": 2,
|
||||||
"Ambient": "#a3bbc1",
|
|
||||||
"Atmosphere": "woodland",
|
|
||||||
"Layers": {
|
"Layers": {
|
||||||
"Back": [
|
"Back": [
|
||||||
" 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24",
|
"24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24",
|
||||||
" 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24",
|
"24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24",
|
||||||
" 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24",
|
"24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24",
|
||||||
" 24 24 24 24 24 24 24 24 4 4 4 4 4 4 4 4 4 24 24 24 24 24 24 24",
|
"24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24",
|
||||||
" 24 24 24 24 24 24 24 24 4 6 6 6 6 6 6 6 4 24 24 24 24 24 24 24",
|
"24 24 24 24 24 24 24 24 4 4 4 4 4 4 4 4 4 24 24 24 24 24 24 24",
|
||||||
" 24 24 24 24 24 24 24 24 4 6 6 6 6 6 6 6 4 24 24 24 24 24 24 24",
|
"24 24 24 24 24 24 24 24 4 3 3 3 3 3 3 3 4 24 24 24 24 24 24 24",
|
||||||
" 24 24 24 24 24 24 24 24 4 6 6 6 6 6 6 6 4 24 24 24 24 24 24 24",
|
"24 24 24 24 24 24 24 24 4 3 3 3 3 3 3 3 4 24 24 24 24 24 24 24",
|
||||||
" 24 24 24 24 24 24 24 24 4 6 6 6 160 6 6 6 4 24 24 24 24 24 24 24",
|
"24 24 24 24 24 24 24 24 4 3 3 3 3 3 3 3 4 24 24 24 24 24 24 24",
|
||||||
" 24 24 24 24 24 24 24 24 4 4 4 4 170 4 4 4 4 24 24 24 24 24 24 24",
|
"24 24 24 24 24 24 24 24 4 4 4 4 3 4 4 4 4 24 24 24 24 24 24 24",
|
||||||
" 24 24 24 24 24 24 24 24 24 24 24 24 170 24 24 24 24 24 24 24 24 24 24 24",
|
"24 24 24 24 24 24 24 24 24 24 24 24 3 24 24 24 24 24 24 24 24 24 24 24",
|
||||||
" 24 24 24 24 24 24 24 24 24 24 24 24 170 24 24 24 24 24 24 24 24 24 24 24",
|
"24 24 24 24 24 24 24 24 24 24 24 24 3 24 24 24 24 24 24 24 24 24 24 24",
|
||||||
" 24 173 165 165 165 165 165 165 165 165 165 165 166 24 24 24 24 24 24 24 24 24 24 24",
|
"24 3 3 3 3 3 3 3 3 3 3 3 3 24 24 24 24 24 24 24 24 24 24 24",
|
||||||
" 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24",
|
"24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24",
|
||||||
" 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24",
|
"24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24",
|
||||||
" 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24",
|
"24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24",
|
||||||
" 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24"
|
"24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24"
|
||||||
],
|
],
|
||||||
"Main": [
|
"Main": [
|
||||||
" . . . . . . . . . . . . . . . . . . . . . . . .",
|
". . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
" . . . . . . . . . . . . . . . . . . . . . . . .",
|
". . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
" 70 71 72 70 71 72 70 71 72 . . . . . . 70 71 72 70 71 72 70 71 72",
|
"70 71 72 70 71 72 70 71 72 . . . . . . 70 71 72 70 71 72 70 71 72",
|
||||||
" . . . . . . . . . . . . . . . . . . . . . . . .",
|
". . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
" . . . . . . . . 37 . . . . . . . 37 . . . . . . .",
|
". . . . . . . . 37 . 124 125 126 127 . . 37 . . . . . . .",
|
||||||
" 70 71 72 70 71 72 . . . . . . . . . . . . 70 71 72 70 71 72",
|
"70 71 72 70 71 72 . . . . . . . . . . . . 70 71 72 70 71 72",
|
||||||
" . . . . . . . . . . . . . . . . . . . . . . . .",
|
". . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
" . . . . . . . . . . . . . . . . . . . . . . . .",
|
". . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
" 70 71 72 . . . . . 37 . . . . . . . 37 . . . . 70 71 72",
|
"70 71 72 . . . . . 37 . . . . . . . 37 . . . . 70 71 72",
|
||||||
" . . . . 70 71 72 . . . . . . . . . . . . . . . . .",
|
". . . . 70 71 72 . . . . . . . . . . . . . . . . .",
|
||||||
" . . . . . . . . . . . . . . . . . . . . . . . .",
|
". . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
" . . . . . . . . . . . . . . . . . . . . . 70 71 72",
|
". . . . . . . . . . . . . . . . . . . . . 70 71 72",
|
||||||
" . . . . . . . . . . . . . . . . . . . . . . . .",
|
". . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
" . . . . . . . . . . . . . . . . . . . . . . . .",
|
". . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
" . . . . . . . . . . . . . . . . . . . . . . . .",
|
". . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
" 70 71 72 70 71 72 70 71 72 70 71 72 70 71 72 70 71 72 70 71 72 70 71 72"
|
"70 71 72 70 71 72 70 71 72 70 71 72 70 71 72 70 71 72 70 71 72 70 71 72"
|
||||||
],
|
],
|
||||||
"Front": [
|
"Front": [
|
||||||
" 64 65 66 64 65 66 64 65 66 . . . . . . 64 65 66 64 65 66 64 65 66",
|
"64 65 66 64 65 66 64 65 66 . . . . . . 64 65 66 64 65 66 64 65 66",
|
||||||
" 67 68 69 67 68 69 67 68 69 . 112 113 114 115 . 67 68 69 67 68 69 67 68 69",
|
"67 68 69 67 68 69 67 68 69 . 112 113 114 115 . 67 68 69 67 68 69 67 68 69",
|
||||||
" . . . . . . . . . . 116 117 118 119 . . . . . . . . . .",
|
". . . . . . . . . . 116 117 118 119 . . . . . . . . . .",
|
||||||
" 64 65 66 64 65 66 . . . . 120 121 122 123 . . . . 64 65 66 64 65 66",
|
"64 65 66 64 65 66 . . . . 120 121 122 123 . . . . 64 65 66 64 65 66",
|
||||||
" 67 68 69 67 68 69 . . . . 124 125 126 127 . . . . 67 68 69 67 68 69",
|
"67 68 69 67 68 69 . . . . . . . . . . . . 67 68 69 67 68 69",
|
||||||
" . . . . . . . . . . . . . . . . . . . . . . . .",
|
". . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
" 64 65 66 . . . . . . . . . . . . . . . . . . 64 65 66",
|
"64 65 66 . . . . . . . . . . . . . . . . . . 64 65 66",
|
||||||
" 67 68 69 . 64 65 66 . . . . . . . . . . . . . . 67 68 69",
|
"67 68 69 . 64 65 66 . . . . . . . . . . . . . . 67 68 69",
|
||||||
" . . . . 67 68 69 . . . . . . . . . . . . . . . . .",
|
". . . . 67 68 69 . . . . . . . . . . . . . . . . .",
|
||||||
" . . . . . . . . . . . . . . . . . . . . . 64 65 66",
|
". . . . . . . . . . . . . . . . . . . . . 64 65 66",
|
||||||
" . . . . . . . . . . . . . . . . . . . . . 67 68 69",
|
". . . . . . . . . . . . . . . . . . . . . 67 68 69",
|
||||||
" . . . . . . . . . . . . . . . . . . . . . . . .",
|
". . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
" . . . . . . . . . . . . . . . . . . . . . . . .",
|
". . . . . . . . . . . . . . . . . . . . . . . .",
|
||||||
" 64 65 66 64 65 66 64 65 66 64 65 66 64 65 66 64 65 66 64 65 66 64 65 66",
|
"64 65 66 64 65 66 64 65 66 64 65 66 64 65 66 64 65 66 64 65 66 64 65 66",
|
||||||
" 67 68 69 67 68 69 67 68 69 67 68 69 67 68 69 67 68 69 67 68 69 67 68 69",
|
"67 68 69 67 68 69 67 68 69 67 68 69 67 68 69 67 68 69 67 68 69 67 68 69",
|
||||||
" . . . . . . . . . . . . . . . . . . . . . . . ."
|
". . . . . . . . . . . . . . . . . . . . . . . ."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Collision": [
|
"Collision": [
|
||||||
"########################",
|
"########################",
|
||||||
"#......................#",
|
"#......................#",
|
||||||
"#......................#",
|
"#......................#",
|
||||||
"#.........#..#.........#",
|
"#.........####.........#",
|
||||||
"#.........####.........#",
|
"#.........####.........#",
|
||||||
"#......................#",
|
"#......................#",
|
||||||
"#......................#",
|
"#......................#",
|
||||||
@@ -88,7 +86,9 @@
|
|||||||
"X": 2,
|
"X": 2,
|
||||||
"Y": 11,
|
"Y": 11,
|
||||||
"Dir": "Right"
|
"Dir": "Right"
|
||||||
}
|
},
|
||||||
|
"Ambient": "#a3bbc1",
|
||||||
|
"Atmosphere": "woodland"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
scene: rpg_desert
|
||||||
|
priority: 10
|
||||||
|
is_one_run: true
|
||||||
|
save_allowed: true
|
||||||
|
---
|
||||||
|
ptext "За последним деревом кончается тень. Скорпионы здесь крупнее местных надежд." key=rpg_plus/desert/enter
|
||||||
|
end
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "rpg_desert",
|
||||||
|
"defaultBackground": "desert/outpost",
|
||||||
|
"defaultAmbient": ""
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "Desert and supply barrel test",
|
||||||
|
"description": "West/east transitions, one-time barrel loot, save/load and both desert monster battles.",
|
||||||
|
"sources": ["src/*.cs"],
|
||||||
|
"depends": ["rpg_plus_game>=1", "ff_battle>=1"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
namespace Cthangover.Mods.RpgPlus.Tests
|
||||||
|
{
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Cthangover.Core.Battle;
|
||||||
|
using Cthangover.Core.Bootstrap;
|
||||||
|
using Cthangover.Core.Games;
|
||||||
|
using Cthangover.Core.Items;
|
||||||
|
using Cthangover.Core.Scenes;
|
||||||
|
using Cthangover.Core.Settings;
|
||||||
|
using Cthangover.Core.UI;
|
||||||
|
using Cthangover.Core.Utils;
|
||||||
|
using Cthangover.Mods.Dialog;
|
||||||
|
using Cthangover.Mods.Interface.View;
|
||||||
|
using Cthangover.Mods.Rpg;
|
||||||
|
using Cthangover.FFBattle.UI;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exercises authored data through real interaction, battle rewards and save/load.
|
||||||
|
/// Enemy HP is reduced only after the battle starts, keeping the UI victory/loot path.
|
||||||
|
/// </summary>
|
||||||
|
public partial class DesertTestDriver : Node
|
||||||
|
{
|
||||||
|
private const string Slot = "slot_9918";
|
||||||
|
private const string VillageBarrel = "supply_rpg_village_19_11";
|
||||||
|
private readonly Queue<int> choices = new(new[] { 0, 1 });
|
||||||
|
private readonly string shots;
|
||||||
|
private int step, ticks, passed, failed, coins, branches, elixirs, stones, resin;
|
||||||
|
private double elapsed;
|
||||||
|
private ulong nextAttack;
|
||||||
|
private BattleSceneContext testedBattle;
|
||||||
|
private Key? releaseKey;
|
||||||
|
private static RpgWorldView View => RpgWorldHost.Current?.View;
|
||||||
|
private static DialogBox Box => RpgInput.FindDialogBox();
|
||||||
|
private static IInventory Bag => GameData.Instance.Runtime.Inventory;
|
||||||
|
|
||||||
|
public DesertTestDriver(string shots)
|
||||||
|
{
|
||||||
|
this.shots = shots;
|
||||||
|
Name = "DesertTestDriver";
|
||||||
|
var timer = new Timer { WaitTime = 0.1, Autostart = true };
|
||||||
|
timer.Timeout += Tick;
|
||||||
|
AddChild(timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Tick()
|
||||||
|
{
|
||||||
|
if (step < 0) return;
|
||||||
|
if (releaseKey.HasValue)
|
||||||
|
{
|
||||||
|
var key = releaseKey.Value;
|
||||||
|
Input.ParseInputEvent(new InputEventKey { PhysicalKeycode = key, Keycode = key, Pressed = false });
|
||||||
|
releaseKey = null;
|
||||||
|
}
|
||||||
|
elapsed += 0.1;
|
||||||
|
if (elapsed > 180) { Check(false, $"timeout at step {step}"); Finish(); return; }
|
||||||
|
try { RunStep(); }
|
||||||
|
catch (Exception ex) { Check(false, ex.ToString()); Finish(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunStep()
|
||||||
|
{
|
||||||
|
var scene = GetTree().Root.GetNodeOrNull<SceneManager>("SceneManager")?.CurrentSceneName;
|
||||||
|
if (!Boot.IsReady || string.IsNullOrEmpty(scene)) return;
|
||||||
|
if (Box?.IsRunning == true)
|
||||||
|
{
|
||||||
|
if (Box.IsWaitingAnswer) Box.TrySelectVariant(choices.Count > 0 ? choices.Dequeue() : 0);
|
||||||
|
else Box.NextAction();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (GodotSceneService.IsTransitioning || ++ticks < 3) return;
|
||||||
|
switch (step)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
if (View?.Map.ID != "rpg_village") return;
|
||||||
|
Check(View.FindEvent("village_to_desert")?.Current.Teleport.Map == "rpg_desert", "west exit points to desert");
|
||||||
|
Check(View.CanEnter(new Vector2I(0, 9)), "west village border is passable");
|
||||||
|
coins = Bag.CheckCount("money/coin"); branches = Bag.CheckCount("rpg_demo/branches");
|
||||||
|
View.PlacePlayer(19, 12, RpgDirection.Up);
|
||||||
|
Check(View.Interact(), "closed supply barrel can be opened");
|
||||||
|
Next(); return;
|
||||||
|
case 1:
|
||||||
|
Check(Bag.CheckCount("money/coin") == coins + 2 && Bag.CheckCount("rpg_demo/branches") == branches + 1, "barrel grants the authored small loot");
|
||||||
|
Check(View.FindEvent(VillageBarrel)?.Current.Sprite == "desert/barrel_open", "barrel switches to open graphic");
|
||||||
|
View.Interact(); Next(); return;
|
||||||
|
case 2:
|
||||||
|
Check(Bag.CheckCount("money/coin") == coins + 2 && Bag.CheckCount("rpg_demo/branches") == branches + 1, "opening twice does not duplicate loot");
|
||||||
|
SaveService.Save(Slot);
|
||||||
|
Check(GameSession.LoadAndEnter(this, Slot), "barrel save reloads");
|
||||||
|
Next(); return;
|
||||||
|
case 3:
|
||||||
|
if (View?.Map.ID != "rpg_village") return;
|
||||||
|
Check(View.FindEvent(VillageBarrel)?.PageIndex == 1, "open barrel survives reload");
|
||||||
|
Check(Bag.CheckCount("money/coin") == coins + 2, "barrel coins survive reload without duplication");
|
||||||
|
View.PlacePlayer(1, 9, RpgDirection.Left); Tap(Key.Left); Next(); return;
|
||||||
|
case 4:
|
||||||
|
if (View?.Map.ID != "rpg_desert") return;
|
||||||
|
Check(View.Player.Cell == new Vector2I(38, 12), "walking west arrives at the east desert entrance");
|
||||||
|
Check(View.Tileset.ID == "desert", "desert uses its own art and sand surface");
|
||||||
|
Check(View.FindEvent("desert_scorpion_1")?.Visible == true && View.FindEvent("desert_beetle_2")?.Visible == true, "both desert monster species are present");
|
||||||
|
Check(View.CanEnter(new Vector2I(39, 12)), "return passage is open");
|
||||||
|
Shot("desert");
|
||||||
|
coins = Bag.CheckCount("money/coin"); elixirs = Bag.CheckCount("rpg_demo/elixir");
|
||||||
|
View.PlacePlayer(17, 9, RpgDirection.Up); Check(View.Interact(), "desert supply barrel opens"); Next(); return;
|
||||||
|
case 5:
|
||||||
|
Check(Bag.CheckCount("money/coin") == coins + 3 && Bag.CheckCount("rpg_demo/elixir") == elixirs + 1, "desert barrel grants coins and an elixir");
|
||||||
|
View.Interact(); Next(); return;
|
||||||
|
case 6:
|
||||||
|
Check(Bag.CheckCount("money/coin") == coins + 3 && Bag.CheckCount("rpg_demo/elixir") == elixirs + 1, "desert barrel is repeat safe");
|
||||||
|
stones = Bag.CheckCount("rpg_demo/stones");
|
||||||
|
View.PlacePlayer(26, 12, RpgDirection.Right);
|
||||||
|
Check(View.Interact(), "scorpion interaction starts its battle"); Next(); return;
|
||||||
|
case 7:
|
||||||
|
if (!Win("rpg_desert_scorpion")) return;
|
||||||
|
Next(); return;
|
||||||
|
case 8:
|
||||||
|
if (!ContinueVictory()) return;
|
||||||
|
Next(); return;
|
||||||
|
case 9:
|
||||||
|
if (View?.Map.ID != "rpg_desert") return;
|
||||||
|
Check(View.FindEvent("desert_scorpion_1")?.Visible == false, "defeated scorpion disappears");
|
||||||
|
Check(Bag.CheckCount("rpg_demo/stones") >= stones + 1, "scorpion victory grants its loot");
|
||||||
|
resin = Bag.CheckCount("rpg_plus/resin");
|
||||||
|
View.PlacePlayer(22, 14, RpgDirection.Up);
|
||||||
|
Check(View.Interact(), "beetle interaction starts its battle"); Next(); return;
|
||||||
|
case 10:
|
||||||
|
if (!Win("rpg_desert_beetle")) return;
|
||||||
|
Next(); return;
|
||||||
|
case 11:
|
||||||
|
if (!ContinueVictory()) return;
|
||||||
|
Next(); return;
|
||||||
|
case 12:
|
||||||
|
if (View?.Map.ID != "rpg_desert") return;
|
||||||
|
Check(View.FindEvent("desert_beetle_2")?.Visible == false, "defeated beetle disappears");
|
||||||
|
Check(Bag.CheckCount("rpg_plus/resin") >= resin + 1, "beetle grants its loot");
|
||||||
|
SaveService.Save(Slot);
|
||||||
|
Check(GameSession.LoadAndEnter(this, Slot), "desert progress reloads");
|
||||||
|
Next(); return;
|
||||||
|
case 13:
|
||||||
|
if (View?.Map.ID != "rpg_desert") return;
|
||||||
|
Check(View.FindEvent("desert_scorpion_1")?.Visible == false && View.FindEvent("desert_beetle_2")?.Visible == false, "defeated monsters stay gone after reload");
|
||||||
|
Check(View.FindEvent("desert_barrel_supply")?.PageIndex == 1, "desert barrel stays open after reload");
|
||||||
|
View.PlacePlayer(38, 12, RpgDirection.Right); Tap(Key.Right); Next(); return;
|
||||||
|
case 14:
|
||||||
|
if (View?.Map.ID != "rpg_village") return;
|
||||||
|
Check(View.Player.Cell == new Vector2I(1, 9), "east desert exit returns inside west village edge");
|
||||||
|
Check(View.FindEvent(VillageBarrel)?.PageIndex == 1, "village barrel remains open after the round trip");
|
||||||
|
SaveService.DeleteSave(Slot); Finish(); return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Win(string id)
|
||||||
|
{
|
||||||
|
if (GetTree().CurrentScene is not BattleSceneContext battle) return false;
|
||||||
|
if (battle.IsDestroyed) return true;
|
||||||
|
if (battle.ActiveBattleCore is not IBattleAutoPlayable auto || !auto.IsWaitingForPlayerInput || Time.GetTicksMsec() < nextAttack) return false;
|
||||||
|
var enemies = battle.FindChild("FFEnemyPanel", true, false) as FFEnemyPanel;
|
||||||
|
var players = battle.FindChild("FFPlayerPanel", true, false) as FFPlayerPanel;
|
||||||
|
var menu = battle.FindChildren("*", "", true, false).OfType<FFMenuPanel>().FirstOrDefault();
|
||||||
|
if (enemies == null || players == null || menu == null) return false;
|
||||||
|
if (testedBattle != battle)
|
||||||
|
{
|
||||||
|
testedBattle = battle;
|
||||||
|
Check(enemies.Widgets.Count == 1 && enemies.Widgets[0].Card.ID == id, "battle contains " + id);
|
||||||
|
foreach (var enemy in enemies.Widgets) { enemy.Card.Attributes.Defence.Value = 0; enemy.Card.Attributes.Health.Value = 1; }
|
||||||
|
}
|
||||||
|
var target = enemies.Widgets.FirstOrDefault(w => !w.IsDead);
|
||||||
|
var player = players.Widgets.FirstOrDefault(w => !w.IsDead);
|
||||||
|
if (target == null || player == null) return false;
|
||||||
|
player.Card.Attributes.Point.Value = 100;
|
||||||
|
Click(player);
|
||||||
|
menu.SelectedIndex = menu.Entries.FindIndex(e => e.Key == "attack"); menu.ConfirmSelection();
|
||||||
|
menu.SelectedIndex = menu.Entries.FindIndex(e => e.Data is Cthangover.Core.Characters.ActionCharacter action && action.Type == Cthangover.Core.Characters.ActionCharacterType.ToEnemy);
|
||||||
|
menu.ConfirmSelection(); Click(target); nextAttack = Time.GetTicksMsec() + 1600;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ContinueVictory()
|
||||||
|
{
|
||||||
|
if (GetTree().Root.FindChild("ContinueButton", true, false) is not Button button || !button.IsVisibleInTree()) return false;
|
||||||
|
button.EmitSignal(BaseButton.SignalName.Pressed); return true;
|
||||||
|
}
|
||||||
|
private static void Click(Control c) => c.EmitSignal(Control.SignalName.GuiInput, new InputEventMouseButton { ButtonIndex = MouseButton.Left, Pressed = true });
|
||||||
|
private void Tap(Key key)
|
||||||
|
{
|
||||||
|
// Movement samples held input in _Process; retain the press until
|
||||||
|
// the next timer tick so at least one real simulation frame sees it.
|
||||||
|
Input.ParseInputEvent(new InputEventKey { PhysicalKeycode = key, Keycode = key, Pressed = true });
|
||||||
|
releaseKey = key;
|
||||||
|
}
|
||||||
|
private void Shot(string name)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(shots) || DisplayServer.GetName() == "headless") return;
|
||||||
|
DirAccess.MakeDirRecursiveAbsolute(shots);
|
||||||
|
ViewportCapture.SavePng(GetViewport(), shots.TrimEnd('/') + "/" + name + ".png");
|
||||||
|
}
|
||||||
|
private void Next() { step++; ticks = 0; }
|
||||||
|
private void Check(bool ok, string message)
|
||||||
|
{
|
||||||
|
if (ok) passed++; else failed++;
|
||||||
|
GameLogger.Log("TEST", (ok ? "PASS " : "FAIL ") + message);
|
||||||
|
}
|
||||||
|
private void Finish()
|
||||||
|
{
|
||||||
|
step = -1;
|
||||||
|
GameLogger.Log("TEST", $"[TEST_RESULT] name=desert passed={passed} failed={failed}");
|
||||||
|
GetTree().Quit(failed == 0 ? 0 : 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
namespace Cthangover.Mods.RpgPlus.Tests
|
||||||
|
{
|
||||||
|
using System.Linq;
|
||||||
|
using Cthangover.Core.Mods;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
/// <summary>Mounts the opt-in desert playthrough without adding production gameplay code.</summary>
|
||||||
|
public class DesertTestInitializer : IModInitializer
|
||||||
|
{
|
||||||
|
private static bool queued;
|
||||||
|
public void OnModLoaded(string id) { }
|
||||||
|
public void OnModResourcesReady()
|
||||||
|
{
|
||||||
|
var args = OS.GetCmdlineUserArgs();
|
||||||
|
if (queued || !args.Contains("--itest=desert")) return;
|
||||||
|
queued = true;
|
||||||
|
string shots = null;
|
||||||
|
foreach (var arg in args)
|
||||||
|
if (arg.StartsWith("--shots=")) shots = arg.Substring(8);
|
||||||
|
ModRoot.AddDeferred(new DesertTestDriver(shots));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -419,9 +419,9 @@ namespace Cthangover.Mods.RpgPlus.Tests
|
|||||||
nightPhase++;
|
nightPhase++;
|
||||||
return false;
|
return false;
|
||||||
case 1:
|
case 1:
|
||||||
var material = View.GetNodeOrNull<TileMapLayer>("Main")?.Material as ShaderMaterial;
|
var material = View.Presentation?.SharedMaterial;
|
||||||
Check(material != null, "the tiles have the map's light material");
|
Check(material != null, "the tiles have the map's light material");
|
||||||
Check(material != null && View.Player.Material == material, "the player shares it");
|
Check(material != null && View.Presentation.ActorsShareMaterial, "the player shares it");
|
||||||
var weights = material?.GetShaderParameter("time_weights").AsVector4() ?? Vector4.Zero;
|
var weights = material?.GetShaderParameter("time_weights").AsVector4() ?? Vector4.Zero;
|
||||||
Check(Mathf.IsEqualApprox(weights.W, 1f), "23:00 lights the map as night");
|
Check(Mathf.IsEqualApprox(weights.W, 1f), "23:00 lights the map as night");
|
||||||
Check((material?.GetShaderParameter("light_count").AsInt32() ?? 0) >= 4, "the player, the lamp and windows give light");
|
Check((material?.GetShaderParameter("light_count").AsInt32() ?? 0) >= 4, "the player, the lamp and windows give light");
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ namespace Cthangover.Mods.RpgPlus.Tests
|
|||||||
private readonly Queue<int> choices = new();
|
private readonly Queue<int> choices = new();
|
||||||
private readonly bool pact;
|
private readonly bool pact;
|
||||||
private readonly string shots;
|
private readonly string shots;
|
||||||
|
private readonly bool terrainReview;
|
||||||
private int step;
|
private int step;
|
||||||
private int ticks;
|
private int ticks;
|
||||||
private int passed;
|
private int passed;
|
||||||
@@ -60,6 +61,7 @@ namespace Cthangover.Mods.RpgPlus.Tests
|
|||||||
{
|
{
|
||||||
this.pact = pact;
|
this.pact = pact;
|
||||||
this.shots = shots;
|
this.shots = shots;
|
||||||
|
terrainReview = OS.GetCmdlineUserArgs().Contains("--terrain-review");
|
||||||
Name = "WoodlandTestDriver";
|
Name = "WoodlandTestDriver";
|
||||||
var timer = new Timer { WaitTime = 0.1, Autostart = true };
|
var timer = new Timer { WaitTime = 0.1, Autostart = true };
|
||||||
timer.Timeout += Tick;
|
timer.Timeout += Tick;
|
||||||
@@ -204,6 +206,7 @@ namespace Cthangover.Mods.RpgPlus.Tests
|
|||||||
Go("rpg_elder_house", 6, 4, RpgDirection.Up); Next(); return;
|
Go("rpg_elder_house", 6, 4, RpgDirection.Up); Next(); return;
|
||||||
case 21:
|
case 21:
|
||||||
if (View?.Map.ID != "rpg_elder_house") return;
|
if (View?.Map.ID != "rpg_elder_house") return;
|
||||||
|
Shot("interior");
|
||||||
Tap(Key.E); Next(); return;
|
Tap(Key.E); Next(); return;
|
||||||
case 22:
|
case 22:
|
||||||
Check(Quest("ward").Data.Status == 2, "elder supplies the lantern recipe objective");
|
Check(Quest("ward").Data.Status == 2, "elder supplies the lantern recipe objective");
|
||||||
@@ -423,12 +426,49 @@ namespace Cthangover.Mods.RpgPlus.Tests
|
|||||||
if (villageShotPhase == 1)
|
if (villageShotPhase == 1)
|
||||||
{
|
{
|
||||||
Shot("village");
|
Shot("village");
|
||||||
|
if (terrainReview)
|
||||||
|
CheckWaterMaterials();
|
||||||
|
else
|
||||||
time.SetTime(shotTime);
|
time.SetTime(shotTime);
|
||||||
villageShotPhase = 2;
|
villageShotPhase = 2;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CheckWaterMaterials()
|
||||||
|
{
|
||||||
|
var tileset = View.Tileset;
|
||||||
|
var textures = new RpgTextures();
|
||||||
|
Check(tileset.Image == "village/objects" && tileset.SurfaceImage == "village/surfaces",
|
||||||
|
"objects and surfaces use separate atlases");
|
||||||
|
Check(tileset.TileOf(40)?.Surface != null && tileset.AutotileOf(40) == null,
|
||||||
|
"water uses a base material with runtime diffusion");
|
||||||
|
Check(tileset.Tiles.Count(t => t.Surface != null) == 7 &&
|
||||||
|
tileset.Tiles.All(t => t.Index < 192 || t.Index >= 1000 && t.Index < 1120),
|
||||||
|
"only seven base surfaces remain, with no baked phase or transition ids");
|
||||||
|
var roomTop = tileset.TileOf(1000)?.AtlasIndex ?? -1;
|
||||||
|
var roomPacked = roomTop >= 0 && roomTop % tileset.Columns + 12 <= tileset.Columns;
|
||||||
|
for (var cell = 0; cell < 120; cell++)
|
||||||
|
roomPacked &= tileset.TileOf(1000 + cell)?.AtlasIndex == roomTop + cell / 12 * tileset.Columns + cell % 12;
|
||||||
|
Check(roomPacked, "the authored room occupies a connected 12x10 atlas block");
|
||||||
|
Check(View.Presentation?.SurfaceChunkCount > 0,
|
||||||
|
"the world renders the computed surface layer");
|
||||||
|
var caveTop = tileset.TileOf(128)?.AtlasIndex ?? -1;
|
||||||
|
Check(caveTop >= 0 && caveTop % tileset.Columns + 1 < tileset.Columns &&
|
||||||
|
tileset.TileOf(129)?.AtlasIndex == caveTop + 1 &&
|
||||||
|
tileset.TileOf(27)?.AtlasIndex == caveTop + tileset.Columns &&
|
||||||
|
tileset.TileOf(130)?.AtlasIndex == caveTop + tileset.Columns + 1,
|
||||||
|
"the cave occupies a complete 2x2 atlas rectangle");
|
||||||
|
var lampTop = tileset.TileOf(29)?.AtlasIndex ?? -1;
|
||||||
|
Check(lampTop >= 0 && tileset.TileOf(28)?.AtlasIndex == lampTop + tileset.Columns,
|
||||||
|
"the streetlamp head and foot form one vertical atlas rectangle");
|
||||||
|
var grid = RpgMapGrid.Parse(View.Map);
|
||||||
|
var generated = RpgSurfaceRenderer.Build(grid, RpgLayer.Back, tileset, textures);
|
||||||
|
Check(generated?.GetWidth() == grid.Width * tileset.TileSize,
|
||||||
|
"diffusion is generated from the current map at runtime");
|
||||||
|
generated?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
private void Shot(string name)
|
private void Shot(string name)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(shots) || DisplayServer.GetName() == "headless") return;
|
if (string.IsNullOrEmpty(shots) || DisplayServer.GetName() == "headless") return;
|
||||||
|
|||||||
Reference in new issue
Block a user