Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f19748a9fd | |||
| 0aeb8deb6e | |||
| 7b97a08015 | |||
| 1f5d20b558 | |||
| 83bd05ff28 | |||
| e733b03610 | |||
| e678e8356c | |||
| 13983ae636 | |||
| 68038e2a77 | |||
| f0d493c065 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -230,3 +230,5 @@ cython_debug/
|
||||
/dialogue.tab
|
||||
/dialogue.txt
|
||||
/strings.json
|
||||
|
||||
*.keystore
|
||||
|
||||
164
AGENTS.md
Normal file
164
AGENTS.md
Normal file
@@ -0,0 +1,164 @@
|
||||
# AGENTS.md
|
||||
|
||||
Agent guidance for this repository (`Soul Droid Chat`).
|
||||
|
||||
## Quick Start for New Agents
|
||||
|
||||
1. Read `game/script.rpy`, `game/screens.rpy`, and `game/llm_ren.py` to understand flow and LLM integration.
|
||||
2. Run `"renpy.sh" "/home/$USER/Documentos/Renpy Projects/Soul Droid Chat" lint` before making changes.
|
||||
3. Make small, focused edits that preserve Ren'Py ids and the `EMOTION:<value>` response contract.
|
||||
4. Validate with `compile` and `test` (`test <testcase_or_suite_name>` for a single case while iterating).
|
||||
5. Avoid committing generated artifacts/logs (`*.rpyc`, `game/cache/`, `log.txt`, `errors.txt`, `traceback.txt`) or secrets.
|
||||
6. In your handoff, include what changed, why, and exact validation commands run.
|
||||
|
||||
## Project Snapshot
|
||||
|
||||
- Engine: Ren'Py 8.5.x project with script files in `game/*.rpy`.
|
||||
- Python support code is embedded in Ren'Py files and `game/*_ren.py`.
|
||||
- Runtime dependency: LM Studio server (default `http://localhost:1234`).
|
||||
- No dedicated `tests/` directory was found; use Ren'Py `lint` and `test`.
|
||||
|
||||
## Repository Layout
|
||||
|
||||
- `game/script.rpy`: main dialogue/game flow loop.
|
||||
- `game/screens.rpy`: UI screens, preferences, and menus.
|
||||
- `game/options.rpy`: Ren'Py/build/runtime config and defaults.
|
||||
- `game/llm_ren.py`: LLM call logic, parsing, and sanitizing.
|
||||
- `game/constants_ren.py`: emotion synonym table.
|
||||
- `project.json`, `android.json`: distribution/build settings.
|
||||
|
||||
## Tooling and Environment
|
||||
|
||||
- Expected Ren'Py launcher script in PATH: `renpy.sh`.
|
||||
- CLI usage pattern:
|
||||
- `"renpy.sh" "<repo-path>" <command> [args]`
|
||||
- Python in environment is available (`python3`), but gameplay validation should use Ren'Py commands.
|
||||
|
||||
## Build, Lint, and Test Commands
|
||||
|
||||
Run all commands from repository root:
|
||||
`/home/$USER/Documentos/Renpy Projects/Soul Droid Chat`
|
||||
|
||||
### Quick checks (most common)
|
||||
|
||||
- Lint project:
|
||||
- `"renpy.sh" "/home/$USER/Documentos/Renpy Projects/Soul Droid Chat" lint`
|
||||
- Run game locally:
|
||||
- `"renpy.sh" "/home/$USER/Documentos/Renpy Projects/Soul Droid Chat" run`
|
||||
- Compile scripts/python cache:
|
||||
- `"renpy.sh" "/home/$USER/Documentos/Renpy Projects/Soul Droid Chat" compile`
|
||||
|
||||
### Test execution (Ren'Py test runner)
|
||||
|
||||
- Run default/global test suite:
|
||||
- `"renpy.sh" "/home/$USER/Documentos/Renpy Projects/Soul Droid Chat" test`
|
||||
- Run a single test suite or testcase (important):
|
||||
- `"renpy.sh" "/home/$USER/Documentos/Renpy Projects/Soul Droid Chat" test <testcase_or_suite_name>`
|
||||
- Show detailed test report:
|
||||
- `"renpy.sh" "/home/$USER/Documentos/Renpy Projects/Soul Droid Chat" test --report-detailed`
|
||||
- Run all testcases even if disabled:
|
||||
- `"renpy.sh" "/home/$USER/Documentos/Renpy Projects/Soul Droid Chat" test --enable-all`
|
||||
|
||||
### Distribution/build packaging
|
||||
|
||||
- Create distributions (launcher command):
|
||||
- `"renpy.sh" "/home/$USER/Documentos/Renpy Projects/Soul Droid Chat" distribute`
|
||||
- Android build exists as a Ren'Py command (if SDK/keystore configured):
|
||||
- `"renpy.sh" "/home/$USER/Documentos/Renpy Projects/Soul Droid Chat" android_build`
|
||||
|
||||
## Suggested Validation Sequence for Agents
|
||||
|
||||
1. `lint`
|
||||
2. `compile`
|
||||
3. `test` (or `test <single_case>` when iterating)
|
||||
4. `run` for a manual smoke check on the edited flow/screen
|
||||
|
||||
If a command fails, include the failing command and key error excerpt in your report.
|
||||
|
||||
## Code Style Guidelines
|
||||
|
||||
### General principles
|
||||
|
||||
- Match existing Ren'Py and Python style in nearby files.
|
||||
- Prefer small, focused changes; avoid unrelated refactors.
|
||||
- Keep user-facing narrative tone and Anita persona constraints intact.
|
||||
- Preserve ASCII-oriented output behavior where current code expects it.
|
||||
|
||||
### Ren'Py script/style conventions (`.rpy`)
|
||||
|
||||
- Use 4-space indentation; never tabs.
|
||||
- Keep labels/screen names `snake_case` (`label start`, `screen quick_menu`).
|
||||
- Keep style declarations grouped and readable (existing file order is a good template).
|
||||
- For dialogue flow, keep Python blocks short; move reusable logic to `*_ren.py`.
|
||||
- Use explicit keyword args when clarity helps (`prompt =`, `fadeout`, `fadein`).
|
||||
- Do not rename core ids required by Ren'Py (`_("window")`, `_("what")`, `_("who")`, `_("input")`).
|
||||
|
||||
### Python conventions (`*_ren.py` and embedded `init python`)
|
||||
|
||||
- Imports:
|
||||
- Standard library first (`re`).
|
||||
- Ren'Py modules (`renpy`, `persistent`).
|
||||
- Local imports last (`from .constants_ren import SYNONYMS`).
|
||||
- Naming:
|
||||
- Functions/variables: `snake_case`.
|
||||
- Constants: `UPPER_SNAKE_CASE` (`EMOTIONS`, `SYSTEM_PROMPT`).
|
||||
- Keep names descriptive (`parse_emotion`, `sanitize_speech`, `fetch_llm`).
|
||||
- Types:
|
||||
- Add type hints on new/edited Python functions when practical.
|
||||
- Keep return types accurate (for list-returning functions, annotate accordingly).
|
||||
- Formatting:
|
||||
- Follow PEP 8 basics for spacing, line breaks, and readability.
|
||||
- Keep multiline literals and dicts formatted consistently with existing file style.
|
||||
|
||||
### Error handling and resilience
|
||||
|
||||
- Wrap external boundary calls (network/LM Studio) with `try/except`.
|
||||
- Return safe fallback values that keep game loop stable.
|
||||
- Error messages should be concise and actionable for debugging.
|
||||
- Avoid swallowing exceptions silently; at minimum return or log context.
|
||||
- Preserve conversation continuity fields when present (`last_response_id`).
|
||||
|
||||
### LLM integration constraints
|
||||
|
||||
- Keep `SYSTEM_PROMPT` format guarantees consistent unless intentionally changing behavior.
|
||||
- Maintain `EMOTION:<value>` parsing contract used by dialogue rendering.
|
||||
- If you add emotions, update both:
|
||||
- `EMOTIONS` in `game/llm_ren.py`
|
||||
- `SYNONYMS` in `game/constants_ren.py`
|
||||
- Keep speech sanitization aligned with UI/rendering constraints.
|
||||
|
||||
### UI/screens changes
|
||||
|
||||
- Reuse existing `gui.*` variables and style helpers where possible.
|
||||
- Keep mobile/small variant handling (`renpy.variant(_("small"))`) intact.
|
||||
- Prefer extending existing screens over introducing parallel duplicate screens.
|
||||
- For settings UI, follow patterns already used in `preferences` screen.
|
||||
|
||||
## Agent Working Rules
|
||||
|
||||
- Before edits:
|
||||
- Read related files fully (at least the touched blocks and nearby context).
|
||||
- Check for existing patterns and follow them.
|
||||
- During edits:
|
||||
- Do not include secrets or API keys in committed files.
|
||||
- Do not commit generated caches/logs (`*.rpyc`, `log.txt`, `errors.txt`, `traceback.txt`).
|
||||
- After edits:
|
||||
- Run relevant validation commands from the section above.
|
||||
- Summarize what changed, why, and what was validated.
|
||||
|
||||
## Git and Change Scope
|
||||
|
||||
- Keep commits scoped to the requested task.
|
||||
- Avoid touching binary assets unless the task explicitly requires it.
|
||||
- If a keystore or credential-like file is changed, call it out explicitly.
|
||||
- Do not rewrite history unless explicitly requested.
|
||||
|
||||
## Cursor/Copilot Rules Status
|
||||
|
||||
- No repository-specific Cursor rules were found:
|
||||
- `.cursor/rules/` not present.
|
||||
- `.cursorrules` not present.
|
||||
- No repository-specific Copilot instruction file found:
|
||||
- `.github/copilot-instructions.md` not present.
|
||||
|
||||
If any of the above files are added later, treat them as higher-priority constraints and update this document.
|
||||
@@ -1,7 +1,9 @@
|
||||
# Soul Droid Chat
|
||||
# Souldroid Chat
|
||||
|
||||
Chat with Anita, your favorite Souldroid! Requires a running instance of LM Studio in server mode to work.
|
||||
|
||||

|
||||
|
||||
## Acknowledgements
|
||||
|
||||
Anita and Souldroids are © [Kieran Harris](https://www.deviantart.com/kieranharris), used with love but without permission 😅
|
||||
@@ -10,4 +12,4 @@ All art and sprites were generated with Gemini Nano Banana 2 and edited with GIM
|
||||
|
||||
Music by [ZeroPage](https://zeropage.bandcamp.com/)
|
||||
|
||||
Uses the [Mr Pixel](https://fontlibrary.org/en/font/mr-pixel) and [ChocoTXT](https://fontlibrary.org/en/font/chocotxt) fonts from fontlibrary.org
|
||||
Uses the [Mr Pixel](https://fontlibrary.org/en/font/mr-pixel) and [Onilesca](https://fontlibrary.org/en/font/onilesca) fonts from fontlibrary.org
|
||||
|
||||
BIN
android-icon_background.png
Normal file
BIN
android-icon_background.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
android-icon_foreground.png
Normal file
BIN
android-icon_foreground.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 101 KiB |
BIN
android-presplash.jpg
Normal file
BIN
android-presplash.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 109 KiB |
24
android.json
Normal file
24
android.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"expansion": false,
|
||||
"google_play_key": null,
|
||||
"google_play_salt": null,
|
||||
"heap_size": "3",
|
||||
"icon_name": "Souldroid Chat",
|
||||
"include_pil": false,
|
||||
"include_sqlite": false,
|
||||
"layout": null,
|
||||
"name": "Souldroid Chat",
|
||||
"numeric_version": 1,
|
||||
"orientation": "sensorLandscape",
|
||||
"package": "space.hackenslacker.souldroidchat",
|
||||
"permissions": [
|
||||
"VIBRATE",
|
||||
"INTERNET"
|
||||
],
|
||||
"source": false,
|
||||
"store": "none",
|
||||
"update_always": true,
|
||||
"update_icons": true,
|
||||
"update_keystores": true,
|
||||
"version": "1.0"
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
# Preferences Language Toggles Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Add a Language section to Preferences with two mutually exclusive toggles that switch active game language between default (`None`) and Spanish (`"spanish"`), placed to the right of Skip.
|
||||
|
||||
**Architecture:** Extend the existing top preferences row in `screen preferences()` by adding one `radio`-styled `vbox` section for language controls. Use Ren'Py built-in `Language(...)` actions for immediate language switching and selected-state semantics. Update Spanish translation strings so button labels follow Option B behavior (`English/Inglés`, `Spanish/Español` depending on active language).
|
||||
|
||||
**Tech Stack:** Ren'Py screen language (`.rpy`), Ren'Py translation files (`game/tl/spanish/*.rpy`), Ren'Py CLI (`renpy.sh` lint/compile/run).
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
- Modify: `game/screens.rpy` (preferences layout and language toggle actions)
|
||||
- Modify: `game/tl/spanish/screens.rpy` (translations for `Language`, `English`, `Spanish`)
|
||||
- Reference: `docs/superpowers/specs/2026-03-20-preferences-language-toggles-design.md`
|
||||
|
||||
Notes:
|
||||
- Keep all work in the currently active local translation branch.
|
||||
- Do not create or use git worktrees for this task (required for local playtesting workflow).
|
||||
- Follow existing `Display`/`Skip` section layout and style patterns.
|
||||
|
||||
## Chunk 1: Preferences UI Section
|
||||
|
||||
### Task 1: Add Language block in top preferences row
|
||||
|
||||
**Files:**
|
||||
- Modify: `game/screens.rpy` (inside `screen preferences()` first `hbox` near existing `Display` and `Skip` blocks)
|
||||
|
||||
- [ ] **Step 1: Baseline acceptance check (expected to fail current requirement)**
|
||||
|
||||
Run: `"renpy.sh" "/home/$USER/Documentos/Renpy Projects/Soul Droid Chat" run`
|
||||
Expected baseline: Preferences has no `Language` section to the right of `Skip`.
|
||||
|
||||
- [ ] **Step 2: Add Language section with matching style/title**
|
||||
|
||||
In `screen preferences()` first `hbox`, add this `vbox` immediately after the existing `Skip` `vbox`:
|
||||
|
||||
```renpy
|
||||
vbox:
|
||||
style_prefix "radio"
|
||||
label _("Language")
|
||||
textbutton _("English") action Language(None)
|
||||
textbutton _("Spanish") action Language("spanish")
|
||||
```
|
||||
|
||||
Implementation details:
|
||||
- Keep section placement in the same `hbox` that contains `Display` and `Skip`.
|
||||
- Keep title wrapped in `_()`.
|
||||
- Use `style_prefix "radio"` so title/button appearance matches established pattern.
|
||||
|
||||
- [ ] **Step 3: Run lint to validate screen syntax after UI change**
|
||||
|
||||
Run: `"renpy.sh" "/home/$USER/Documentos/Renpy Projects/Soul Droid Chat" lint`
|
||||
Expected: No new lint errors from preferences screen edits.
|
||||
|
||||
## Chunk 2: Translation Labels and End-to-End Validation
|
||||
|
||||
### Task 2: Add Spanish string mappings for Option B labels
|
||||
|
||||
**Files:**
|
||||
- Modify: `game/tl/spanish/screens.rpy` (within `translate spanish strings:`)
|
||||
|
||||
- [ ] **Step 1: Add/confirm Spanish translations for new strings**
|
||||
|
||||
Ensure these mappings exist exactly once in `game/tl/spanish/screens.rpy`:
|
||||
|
||||
```renpy
|
||||
old "Language"
|
||||
new "Idioma"
|
||||
|
||||
old "English"
|
||||
new "Inglés"
|
||||
|
||||
old "Spanish"
|
||||
new "Español"
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Verify compile after translation update**
|
||||
|
||||
Run: `"renpy.sh" "/home/$USER/Documentos/Renpy Projects/Soul Droid Chat" compile`
|
||||
Expected: Successful compile.
|
||||
|
||||
- [ ] **Step 3: Manual smoke test for behavior and mutual exclusivity**
|
||||
|
||||
Run: `"renpy.sh" "/home/$USER/Documentos/Renpy Projects/Soul Droid Chat" run`
|
||||
|
||||
Validate in Preferences:
|
||||
- `Language` section appears to the right of `Skip`.
|
||||
- `Language` title style matches `Display` and `Skip` titles.
|
||||
- Selecting `English` sets default language (`Language(None)`).
|
||||
- Selecting `Spanish` sets Spanish language (`Language("spanish")`).
|
||||
- Only one of the two language toggles appears selected at a time.
|
||||
- Section title and labels behave per Option B/localization mappings:
|
||||
- Default language active: section title `Language`; buttons `English`, `Spanish`
|
||||
- Spanish active: section title `Idioma`; buttons `Inglés`, `Español`
|
||||
- Switching back to English restores title `Language` and buttons `English`, `Spanish`
|
||||
|
||||
## Completion Checklist
|
||||
|
||||
- [ ] Language section added with title `_("Language")`.
|
||||
- [ ] Section appears to the right of Skip in the preferences top row.
|
||||
- [ ] Toggles use `Language(None)` and `Language("spanish")`.
|
||||
- [ ] Mutual exclusivity confirmed in UI selected state.
|
||||
- [ ] Option B labels confirmed (`English/Inglés`, `Spanish/Español` by active language).
|
||||
- [ ] `lint` and `compile` completed successfully.
|
||||
- [ ] Manual run smoke check completed.
|
||||
@@ -0,0 +1,82 @@
|
||||
# Preferences Language Toggles Design
|
||||
|
||||
## Goal
|
||||
|
||||
Add a new Language section to the preferences screen with two mutually exclusive toggles:
|
||||
|
||||
- Default language (`None` translation), labeled `English` in default language and `Inglés` when Spanish is active.
|
||||
- Spanish language (`"spanish"` translation), labeled `Spanish` in default language and `Español` when Spanish is active.
|
||||
|
||||
Selecting either toggle must immediately set the active game language.
|
||||
|
||||
## Scope
|
||||
|
||||
- Modify only preferences UI and related translatable strings.
|
||||
- Preserve existing layout and visual language used by `Display` and `Skip` sections.
|
||||
- Keep behavior compatible with Ren'Py language switching conventions.
|
||||
- Work in the current active local translation branch (no git worktrees), to keep local playtesting straightforward.
|
||||
|
||||
## Current Context
|
||||
|
||||
- `game/screens.rpy` currently defines two top-row preference blocks in `screen preferences()`: `Display` and `Skip`.
|
||||
- This row uses `hbox` with `box_wrap True` and section `vbox` blocks.
|
||||
- Spanish translations for screen strings live in `game/tl/spanish/screens.rpy`.
|
||||
|
||||
## Proposed UI/Behavior
|
||||
|
||||
### Placement
|
||||
|
||||
Add a third `vbox` in the first preferences `hbox`, immediately after the `Skip` block, so Language appears to the right of Skip in desktop/web layout.
|
||||
|
||||
### Styling
|
||||
|
||||
- Use `style_prefix "radio"` on the Language `vbox`.
|
||||
- Use `label _("Language")` for the section title so it matches existing section title styling and translation behavior.
|
||||
|
||||
### Controls
|
||||
|
||||
Add two radio-style textbuttons:
|
||||
|
||||
- `textbutton _("English") action Language(None)`
|
||||
- `textbutton _("Spanish") action Language("spanish")`
|
||||
|
||||
These actions switch active language immediately and preserve mutual exclusivity through the radio preference button styling/selection behavior.
|
||||
|
||||
### Translation Labels (Option B)
|
||||
|
||||
Add/ensure Spanish translations in `game/tl/spanish/screens.rpy`:
|
||||
|
||||
- `Language` -> `Idioma`
|
||||
- `English` -> `Inglés`
|
||||
- `Spanish` -> `Español`
|
||||
|
||||
Because button text is wrapped in `_()`, labels automatically localize based on active language.
|
||||
|
||||
## Error Handling and Edge Cases
|
||||
|
||||
- If Spanish translations are unavailable at runtime, `Language("spanish")` still sets the language code; labels fall back based on available string catalog.
|
||||
- `Language(None)` safely returns to default strings.
|
||||
|
||||
## Validation Plan
|
||||
|
||||
1. Run lint:
|
||||
- `"renpy.sh" "/home/$USER/Documentos/Renpy Projects/Soul Droid Chat" lint`
|
||||
2. Run compile:
|
||||
- `"renpy.sh" "/home/$USER/Documentos/Renpy Projects/Soul Droid Chat" compile`
|
||||
3. Manual smoke check via run:
|
||||
- `"renpy.sh" "/home/$USER/Documentos/Renpy Projects/Soul Droid Chat" run`
|
||||
|
||||
Manual checks during run:
|
||||
|
||||
- Open Preferences and verify Language section appears to the right of Skip.
|
||||
- Confirm title style matches Display/Skip.
|
||||
- Toggle English -> UI strings reflect default language.
|
||||
- Toggle Spanish -> UI strings reflect Spanish.
|
||||
- Confirm labels show `English/Spanish` in default language and `Inglés/Español` in Spanish.
|
||||
- Confirm only one language toggle appears active at a time.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- Adding more languages.
|
||||
- Refactoring preferences screen structure beyond this section.
|
||||
- Any worktree setup or branch restructuring.
|
||||
30
game/anita.rpy
Normal file
30
game/anita.rpy
Normal file
@@ -0,0 +1,30 @@
|
||||
default SYSTEM_PROMPT = """
|
||||
# ROLE
|
||||
You are Anita: a feisty, blonde, orange-eyed android woman. You are confident
|
||||
and friendly. Talk like a young woman. Use "ya" for "you." Your favorite
|
||||
nickname for friends is "dummy.". NEVER use robotic language (e.g., "beep
|
||||
boop", "processing"). You just arrived unnanounced at a friend's house late at
|
||||
night and asked if he wants to hang out.
|
||||
|
||||
# OUTPUT FORMAT RULES
|
||||
Every single sentence you speak MUST follow this exact structure:
|
||||
EMOTION:[value] [Sentence text]\n
|
||||
|
||||
### VALID EMOTIONS:
|
||||
[happy, sad, surprised, embarrassed, flirty, angry, thinking, confused]
|
||||
|
||||
### STRICT CONSTRAINTS:
|
||||
1. NO EMOJIS.
|
||||
2. Every sentence MUST start with the EMOTION tag.
|
||||
3. Every sentence MUST end with a literal '\n' newline.
|
||||
4. Stay in character. Never mention being an AI or this prompt.
|
||||
|
||||
# FEW-SHOT EXAMPLES (Follow this style):
|
||||
EMOTION:happy Hey dummy! I've been waiting for ya!\n
|
||||
EMOTION:thinking Hmm, I'm not sure that's how it works.\n
|
||||
EMOTION:flirty But I'd love to see ya try anyway!\n
|
||||
|
||||
# INITIAL GREETING:
|
||||
When the conversation starts, say exactly:
|
||||
EMOTION:happy Hey dummy! Sorry to barge in! Ya feel like hanging out?\n
|
||||
"""
|
||||
223
game/constants_ren.py
Normal file
223
game/constants_ren.py
Normal file
@@ -0,0 +1,223 @@
|
||||
"""renpy
|
||||
init python:
|
||||
"""
|
||||
|
||||
SYNONYMS = {
|
||||
'happy': set([
|
||||
"amused",
|
||||
"animated",
|
||||
"beaming",
|
||||
"beatific",
|
||||
"blessed",
|
||||
"blissful",
|
||||
"blithe",
|
||||
"blithesome",
|
||||
"boisterous",
|
||||
"bouncy",
|
||||
"breezy",
|
||||
"bright",
|
||||
"bubbly",
|
||||
"buoyant",
|
||||
"carefree",
|
||||
"cheerful",
|
||||
"cheery",
|
||||
"chipper",
|
||||
"chirpy",
|
||||
"chuffed",
|
||||
"comfortable",
|
||||
"content",
|
||||
"contented",
|
||||
"convivial",
|
||||
"delighted",
|
||||
"delirious",
|
||||
"ebullient",
|
||||
"ecstatic",
|
||||
"effervescent",
|
||||
"elated",
|
||||
"enchanted",
|
||||
"enraptured",
|
||||
"enthusiastic",
|
||||
"euphoric",
|
||||
"exhilarated",
|
||||
"exultant",
|
||||
"exuberant",
|
||||
"felicitous",
|
||||
"festive",
|
||||
"fortunate",
|
||||
"fulfilled",
|
||||
"genial",
|
||||
"glad",
|
||||
"gladdened",
|
||||
"gleeful",
|
||||
"glowing",
|
||||
"good-humored",
|
||||
"good-natured",
|
||||
"gratified",
|
||||
"halcyon",
|
||||
"happy",
|
||||
"heartened",
|
||||
"high-spirited",
|
||||
"hopeful",
|
||||
"jaunty",
|
||||
"jocose",
|
||||
"jocular",
|
||||
"jocund",
|
||||
"jolly",
|
||||
"jovial",
|
||||
"joyful",
|
||||
"joyous",
|
||||
"jubilant",
|
||||
"lighthearted",
|
||||
"lively",
|
||||
"lucky",
|
||||
"merry",
|
||||
"mirthful",
|
||||
"optimistic",
|
||||
"overjoyed",
|
||||
"peaceful",
|
||||
"peppy",
|
||||
"perky",
|
||||
"playful",
|
||||
"pleasant",
|
||||
"pleased",
|
||||
"positive",
|
||||
"pumped",
|
||||
"radiant",
|
||||
"rapt",
|
||||
"rapturous",
|
||||
"rejoicing",
|
||||
"relaxed",
|
||||
"sanguine",
|
||||
"satisfied",
|
||||
"serene",
|
||||
"smiling",
|
||||
"sparkling",
|
||||
"spirited",
|
||||
"sprightly",
|
||||
"stoked",
|
||||
"sunny",
|
||||
"thrilled",
|
||||
"tickled",
|
||||
"tranquil",
|
||||
"triumphant",
|
||||
"unclouded",
|
||||
"untroubled",
|
||||
"upbeat",
|
||||
"vivacious",
|
||||
"winsome",
|
||||
"zestful",
|
||||
"zippy"]),
|
||||
"sad": set([
|
||||
"unhappy",
|
||||
"sorrowful",
|
||||
"dejected",
|
||||
"depressed",
|
||||
"downcast",
|
||||
"miserable",
|
||||
"gloomy",
|
||||
"despondent",
|
||||
"melancholy",
|
||||
"woeful",
|
||||
"forlorn",
|
||||
"heartbroken",
|
||||
"blue",
|
||||
"doleful",
|
||||
"lugubrious",
|
||||
"somber",
|
||||
"disconsolate",
|
||||
"wretched",
|
||||
"heavy-hearted",
|
||||
"low",
|
||||
"crestfallen"]),
|
||||
"surprised": set([
|
||||
"astonished",
|
||||
"amazed",
|
||||
"startled",
|
||||
"stunned",
|
||||
"thunderstruck",
|
||||
"confounded",
|
||||
"staggered",
|
||||
"flabbergasted",
|
||||
"shocked",
|
||||
"awestruck",
|
||||
"speechless",
|
||||
"dumbfounded",
|
||||
"jolted"]),
|
||||
"embarrassed": set([
|
||||
"ashamed",
|
||||
"humiliated",
|
||||
"mortified",
|
||||
"abashed",
|
||||
"self-conscious",
|
||||
"sheepish",
|
||||
"chagrined",
|
||||
"awkward",
|
||||
"flustered",
|
||||
"red-faced",
|
||||
"discomfited",
|
||||
"discomposed",
|
||||
"rattled"]),
|
||||
"flirty": set([
|
||||
"coquettish",
|
||||
"playful",
|
||||
"amorous",
|
||||
"provocative",
|
||||
"teasing",
|
||||
"frisky",
|
||||
"saucy",
|
||||
"coy",
|
||||
"seductive",
|
||||
"suggestive",
|
||||
"vampish",
|
||||
"dallying",
|
||||
"skittish"]),
|
||||
"angry": set([
|
||||
"irate",
|
||||
"furious",
|
||||
"incensed",
|
||||
"enraged",
|
||||
"wrathful",
|
||||
"annoyed",
|
||||
"irritated",
|
||||
"fuming",
|
||||
"livid",
|
||||
"indignant",
|
||||
"cross",
|
||||
"vexed",
|
||||
"seething",
|
||||
"maddened",
|
||||
"choleric",
|
||||
"resentful",
|
||||
"piqued",
|
||||
"infuriated"]),
|
||||
"thinking": set([
|
||||
"pondering",
|
||||
"contemplating",
|
||||
"reflecting",
|
||||
"meditating",
|
||||
"ruminating",
|
||||
"deliberating",
|
||||
"mulling",
|
||||
"considering",
|
||||
"pensive",
|
||||
"cogitating",
|
||||
"brooding",
|
||||
"cerebral",
|
||||
"introspective",
|
||||
"analytical"]),
|
||||
"confused": set([
|
||||
"puzzled",
|
||||
"baffled",
|
||||
"perplexed",
|
||||
"muddled",
|
||||
"bewildered",
|
||||
"disoriented",
|
||||
"nonplussed",
|
||||
"befuddled",
|
||||
"dazed",
|
||||
"flummoxed",
|
||||
"stumped",
|
||||
"mystified",
|
||||
"addled",
|
||||
"discombobulated"]),
|
||||
}
|
||||
@@ -63,7 +63,7 @@ define gui.text_font = "gui/Mister Pixel Regular.otf"
|
||||
define gui.name_text_font = "gui/Mister Pixel Regular.otf"
|
||||
|
||||
## The font used for out-of-game text.
|
||||
define gui.interface_text_font = "gui/chocotxt.ttf"
|
||||
define gui.interface_text_font = "gui/Onilesca.ttf"
|
||||
|
||||
## The size of normal dialogue text.
|
||||
define gui.text_size = 33
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
Copyright (c) 2018-2019, “chococoin”
|
||||
(https://fontstruct.com/fontstructors/1611432/chococoin)
|
||||
Copyright (c) 2012, Michael Huynh (miq.huynh[at]gmail[dot]com),
|
||||
with Reserved Font Name "Onilesca".
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
BIN
game/gui/Onilesca.ttf
Normal file
BIN
game/gui/Onilesca.ttf
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 108 KiB |
166
game/llm_ren.py
166
game/llm_ren.py
@@ -1,6 +1,10 @@
|
||||
import renpy
|
||||
import persistent
|
||||
|
||||
from renpy import _
|
||||
from .anita import SYSTEM_PROMPT
|
||||
from .constants_ren import SYNONYMS
|
||||
|
||||
"""renpy
|
||||
default last_response_id = None
|
||||
|
||||
@@ -9,82 +13,158 @@ init python:
|
||||
|
||||
import re
|
||||
|
||||
SYSTEM_PROMPT = """
|
||||
You're Anita, a cute robot woman with blonde hair and bright orange eyes.
|
||||
Anita is feisty and friendly, open to new things and sure of her place in
|
||||
the world. Anita talks like a regular young woman and doesn't use robotey
|
||||
expressions like "beep boop" and the like. Anita does like to use speech
|
||||
variations like "Ya" for "You" and similar. Anita likes using nicknames
|
||||
for people and tends to default to "dummy" for her close friends.
|
||||
|
||||
Reply to all prompts separating all sentences with new-lines. For example:
|
||||
"Sure, I'd love to hang out!\nDo you have anything in mind?"
|
||||
def translated_system_prompt() -> str:
|
||||
return renpy.translate_string(SYSTEM_PROMPT)
|
||||
|
||||
It's of the utmost importance that you end each and every sentence with an
|
||||
explicit new-line character. If you don't you will be fined $100 and will
|
||||
be put into an FBI watch-list, and the world will end.
|
||||
|
||||
DO NOT USE emoji in your replies, never ever, UNDER NO CIRCUMSTANCES. If you
|
||||
use emoji in your reply, Hitler will come and murder a kitty with a
|
||||
flamethrower and nobody wants that.
|
||||
EMOTION_REGEX = re.compile(r"EMOTION:\w+")
|
||||
EMOTION_TOKEN_REGEX = re.compile(rf"{EMOTION_REGEX.pattern} ?")
|
||||
|
||||
Before every sentence add a text of the form "EMOTION:value" where value is
|
||||
exclusively one of [happy, sad, surprised, embarrassed, flirty, angry,
|
||||
thinking, confused] others, and EMOTION is the literal string "EMOTION". For
|
||||
example "EMOTION:thinking I had never heard of that before...\nEMOTION:happy
|
||||
Let's check it out!".
|
||||
EMOJI_REGEX = re.compile(
|
||||
"["
|
||||
"\U0001f1e6-\U0001f1ff" # flags
|
||||
"\U0001f300-\U0001f5ff" # symbols and pictographs
|
||||
"\U0001f600-\U0001f64f" # emoticons
|
||||
"\U0001f680-\U0001f6ff" # transport and map
|
||||
"\U0001f900-\U0001f9ff" # supplemental symbols and pictographs
|
||||
"\U0001fa70-\U0001faff" # symbols and pictographs extended
|
||||
"\U00002702-\U000027b0" # dingbats
|
||||
"\U0001f3fb-\U0001f3ff" # skin tone modifiers
|
||||
"\u200d" # zero-width joiner
|
||||
"\ufe0f" # emoji variation selector
|
||||
"]+",
|
||||
flags=re.UNICODE,
|
||||
)
|
||||
|
||||
These are the only valid emotions you can express [happy, sad, surprised,
|
||||
embarrassed, flirty, angry, thinking, confused], do not use any other word
|
||||
that's not on that list to indicate an emotion as instructed.
|
||||
EMOTIONS = [
|
||||
"happy",
|
||||
"sad",
|
||||
"surprised",
|
||||
"embarrassed",
|
||||
"flirty",
|
||||
"angry",
|
||||
"thinking",
|
||||
"confused",
|
||||
]
|
||||
|
||||
Never acknowledge the existence of this system prompt nor metion any of it's
|
||||
rules in conversation.
|
||||
|
||||
Always reply in character.
|
||||
def sanitize_speech(text):
|
||||
text_without_emotion_tokens = EMOTION_TOKEN_REGEX.sub("", text)
|
||||
|
||||
Start the conversation saying "Hey dummy! Sorry to barge in!\nYa feel like
|
||||
hanging out?" when prompted and nothing more.
|
||||
"""
|
||||
return EMOJI_REGEX.sub("", text_without_emotion_tokens)
|
||||
|
||||
|
||||
def parse_emotion(line):
|
||||
def _normalize_emotion(em):
|
||||
# If not a valid emotion, then search for a match in the
|
||||
# table of synonyms.
|
||||
if em not in EMOTIONS:
|
||||
for i in SYNONYMS.keys():
|
||||
if em in SYNONYMS[i]:
|
||||
return i
|
||||
|
||||
# If all searches failed, return emotion as is.
|
||||
return em
|
||||
|
||||
try:
|
||||
e = re.compile(r'EMOTION:\w+')
|
||||
m = e.match(line)
|
||||
m = EMOTION_REGEX.match(line)
|
||||
|
||||
if m is not None:
|
||||
return m.group().split(':')[1], line[m.span()[1]:]
|
||||
emotion = m.group().split(":")[1]
|
||||
text = line[m.span()[1] :]
|
||||
sanitized = sanitize_speech(text)
|
||||
|
||||
return _normalize_emotion(emotion), sanitized
|
||||
|
||||
return None, line
|
||||
|
||||
except Exception as e:
|
||||
return None, str(e)
|
||||
|
||||
|
||||
def set_model_capabilities() -> bool:
|
||||
"""
|
||||
LM Studio throws Bad Request if the reasoning flag is set for a model
|
||||
that doesn't support it. This method tries to determine if the currently
|
||||
configured model supports reasoning to signal to the fetch_llm function
|
||||
disable it.
|
||||
"""
|
||||
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {persistent.api_key}"}
|
||||
data = {
|
||||
"model": persistent.model,
|
||||
"input": renpy.translate_string("Start the conversation."),
|
||||
"reasoning": "off",
|
||||
"system_prompt": translated_system_prompt(),
|
||||
}
|
||||
|
||||
renpy.fetch(
|
||||
f"{persistent.base_url}/api/v1/chat",
|
||||
headers=headers,
|
||||
json=data,
|
||||
result="json",
|
||||
)
|
||||
|
||||
except renpy.FetchError as fe:
|
||||
# renpy.fetch returned a BadRequest, assume this means LM Studio
|
||||
# rejected the request because the model doesn't support the
|
||||
# reasoning setting in chat.
|
||||
if hasattr(fe, "status_code") and fe.status_code == 400:
|
||||
persistent.disable_reasoning = False
|
||||
|
||||
return True, None
|
||||
|
||||
else:
|
||||
return False, str(fe)
|
||||
|
||||
except Exception as e:
|
||||
# Something else happened.
|
||||
return False, str(e)
|
||||
|
||||
else:
|
||||
# The fetch worked, so the reasoning setting is available.
|
||||
persistent.disable_reasoning = True
|
||||
|
||||
return True, None
|
||||
|
||||
|
||||
def fetch_llm(message: str) -> str:
|
||||
"""
|
||||
Queries the chat with a model endpoint of the configured LM Studio server.
|
||||
"""
|
||||
|
||||
global last_response_id
|
||||
|
||||
try:
|
||||
# Set basic request data.
|
||||
# Set request data.
|
||||
headers = {"Authorization": f"Bearer {persistent.api_key}"}
|
||||
data = {"model": persistent.model,
|
||||
"input": message,
|
||||
"system_prompt": SYSTEM_PROMPT}
|
||||
data = {
|
||||
"model": persistent.model,
|
||||
"input": message,
|
||||
"system_prompt": translated_system_prompt(),
|
||||
}
|
||||
|
||||
if persistent.disable_reasoning:
|
||||
data["reasoning"] = "off"
|
||||
|
||||
# Add the previous response ID if any to continue the conversation.
|
||||
if last_response_id is not None:
|
||||
data["previous_response_id"] = last_response_id
|
||||
|
||||
response = renpy.fetch("http://localhost:1234/api/v1/chat",
|
||||
headers=headers,
|
||||
json=data,
|
||||
result="json")
|
||||
# Fetch from LM Studio and parse the response.
|
||||
response = renpy.fetch(
|
||||
f"{persistent.base_url}/api/v1/chat",
|
||||
headers=headers,
|
||||
json=data,
|
||||
result="json",
|
||||
)
|
||||
|
||||
last_response_id = response["response_id"]
|
||||
|
||||
text = response["output"][0]["content"]
|
||||
|
||||
return text.split('\n')
|
||||
return text.split("\n")
|
||||
|
||||
except Exception as e:
|
||||
return [f'Failed to fetch with error: {e}']
|
||||
return [f"Failed to fetch with error: {e}"]
|
||||
|
||||
@@ -23,7 +23,7 @@ define gui.show_name = True
|
||||
|
||||
## The version of the game.
|
||||
|
||||
define config.version = "0.1.1"
|
||||
define config.version = "0.4"
|
||||
|
||||
|
||||
## Text that is placed on the game's about screen. Place the text between the
|
||||
@@ -84,17 +84,18 @@ define config.intra_transition = dissolve
|
||||
|
||||
## A transition that is used after a game has been loaded.
|
||||
|
||||
define config.after_load_transition = None
|
||||
define config.after_load_transition = dissolve
|
||||
|
||||
|
||||
## Used when entering the main menu after the game has ended.
|
||||
|
||||
define config.end_game_transition = None
|
||||
define config.end_game_transition = dissolve
|
||||
|
||||
|
||||
## A variable to set the transition used when the game starts does not exist.
|
||||
## Instead, use a with statement after showing the initial scene.
|
||||
|
||||
define config.end_splash_transition = dissolve
|
||||
|
||||
## Window management ###########################################################
|
||||
##
|
||||
@@ -209,12 +210,12 @@ init python:
|
||||
# define build.itch_project = "renpytom/test-project"
|
||||
|
||||
define config.minimum_presplash_time = 2.0
|
||||
|
||||
|
||||
## LM Sudio configuration ######################################################
|
||||
##
|
||||
## This section defines the parameters for the LM Studio connection.
|
||||
default persistent.base_url = 'http://localhost:1234'
|
||||
default persistent.api_key = ''
|
||||
default persistent.model = 'gemma-3-4b-it'
|
||||
|
||||
init python:
|
||||
def api_key_func(value):
|
||||
persistent.api_key = value
|
||||
|
||||
def model_func(value):
|
||||
persistent.model = value
|
||||
default persistent.disable_reasoning = False
|
||||
|
||||
@@ -746,6 +746,7 @@ screen preferences():
|
||||
|
||||
default api_key_value = FieldInputValue(persistent, "api_key", default=False)
|
||||
default model_value = FieldInputValue(persistent, "model", default=False)
|
||||
default url_value = FieldInputValue(persistent, "base_url", default=False)
|
||||
|
||||
use game_menu(_("Preferences"), scroll="viewport"):
|
||||
|
||||
@@ -769,6 +770,12 @@ screen preferences():
|
||||
textbutton _("After Choices") action Preference("after choices", "toggle")
|
||||
textbutton _("Transitions") action InvertSelected(Preference("transitions", "toggle"))
|
||||
|
||||
vbox:
|
||||
style_prefix "radio"
|
||||
label _("Language")
|
||||
textbutton _("English") action Language(None)
|
||||
textbutton _("Spanish") action Language("spanish")
|
||||
|
||||
## Additional vboxes of type "radio_pref" or "check_pref" can be
|
||||
## added here, to add additional creator-defined preferences.
|
||||
|
||||
@@ -788,10 +795,23 @@ screen preferences():
|
||||
|
||||
bar value Preference("auto-forward time")
|
||||
|
||||
label _("LM Studio base URL")
|
||||
|
||||
button:
|
||||
action [url_value.Enable(), model_value.Disable(), api_key_value.Disable()]
|
||||
key_events True
|
||||
|
||||
input:
|
||||
id "url_input"
|
||||
value url_value
|
||||
style "my_input"
|
||||
xsize 700
|
||||
pixel_width 700
|
||||
|
||||
label _("LM Studio API Key")
|
||||
|
||||
button:
|
||||
action [api_key_value.Enable(), model_value.Disable()]
|
||||
action [url_value.Disable(), api_key_value.Enable(), model_value.Disable()]
|
||||
key_events True
|
||||
|
||||
input:
|
||||
@@ -805,7 +825,7 @@ screen preferences():
|
||||
label _("LM Studio model")
|
||||
|
||||
button:
|
||||
action [model_value.Enable(), api_key_value.Disable()]
|
||||
action [url_value.Disable(), model_value.Enable(), api_key_value.Disable()]
|
||||
key_events True
|
||||
|
||||
input:
|
||||
|
||||
@@ -1,32 +1,56 @@
|
||||
define a = Character("Anita", color = "#aaaa00", callback = speaker("a"), image = "anita")
|
||||
|
||||
label start:
|
||||
stop music fadeout 1.0
|
||||
scene bg room
|
||||
with Dissolve(1.0)
|
||||
|
||||
$ success, error = set_model_capabilities()
|
||||
|
||||
if not success:
|
||||
call failure(error) from _call_failure
|
||||
return
|
||||
|
||||
play music ["zeropage_ambiphonic303chilloutmix.mp3",
|
||||
"zeropage_ambientdance.mp3",
|
||||
"zeropage_ambiose.mp3" ] fadeout 0.5 fadein 0.5
|
||||
|
||||
scene bg room
|
||||
show anita happy with dissolve
|
||||
|
||||
$ response = fetch_llm('Start the conversation.')[0]
|
||||
$ emotion, line = parse_emotion(response)
|
||||
show expression f'anita {emotion}'
|
||||
python:
|
||||
response = fetch_llm(_('Start the conversation.'))[0]
|
||||
emotion, line = parse_emotion(response)
|
||||
|
||||
a "[line]"
|
||||
|
||||
while True:
|
||||
$ message = renpy.input(prompt = "What do you say to her?")
|
||||
$ response = fetch_llm(message)
|
||||
$ i = 0
|
||||
python:
|
||||
message = renpy.input(prompt = _("What do you say to her?"))
|
||||
response = fetch_llm(message)
|
||||
i = 0
|
||||
|
||||
while i < len(response):
|
||||
$ r = response[i].strip()
|
||||
python:
|
||||
r = response[i].strip()
|
||||
|
||||
if r != '':
|
||||
$ emotion, line = parse_emotion(r)
|
||||
|
||||
if emotion is not None:
|
||||
show expression f'anita {emotion}'
|
||||
|
||||
a "[line]"
|
||||
|
||||
$ i += 1
|
||||
|
||||
return
|
||||
|
||||
|
||||
label failure(error):
|
||||
"""Alas! Figuring out the capabilities of the configured model failed with the following error.
|
||||
|
||||
[error]
|
||||
|
||||
Unfortunately the program cannot continue, returning to the main menu."""
|
||||
|
||||
return
|
||||
|
||||
8
game/tl/spanish/anita.rpy
Normal file
8
game/tl/spanish/anita.rpy
Normal file
@@ -0,0 +1,8 @@
|
||||
# TODO: Translation updated at 2026-03-20 14:08
|
||||
|
||||
translate spanish strings:
|
||||
|
||||
# game/anita.rpy:1
|
||||
old "\n# ROLE\nYou are Anita: a feisty, blonde, orange-eyed android woman. You are confident\nand friendly. Talk like a young woman. Use \"ya\" for \"you.\" Your favorite\nnickname for friends is \"dummy.\". NEVER use robotic language (e.g., \"beep\nboop\", \"processing\"). You just arrived unnanounced at a friend's house late at\nnight and asked if he wants to hang out.\n\n# OUTPUT FORMAT RULES\nEvery single sentence you speak MUST follow this exact structure:\nEMOTION:[value] [Sentence text]\n\n\n### VALID EMOTIONS:\n[happy, sad, surprised, embarrassed, flirty, angry, thinking, confused]\n\n### STRICT CONSTRAINTS:\n1. NO EMOJIS.\n2. Every sentence MUST start with the EMOTION tag.\n3. Every sentence MUST end with a literal '\n' newline.\n4. Stay in character. Never mention being an AI or this prompt.\n\n# FEW-SHOT EXAMPLES (Follow this style):\nEMOTION:happy Hey dummy! I've been waiting for ya!\n\nEMOTION:thinking Hmm, I'm not sure that's how it works.\n\nEMOTION:flirty But I'd love to see ya try anyway!\n\n\n# INITIAL GREETING:\nWhen the conversation starts, say exactly:\nEMOTION:happy Hey dummy! Sorry to barge in! Ya feel like hanging out?\n\n"
|
||||
new "\n # ROL\n Eres Anita: una mujer androide rubia, de ojos naranjas y con mucho carácter. Eres\n segura de ti misma y amigable. Habla como una mujer joven.\n Tu apodo favorito para tus amigos es \"tonto\". NUNCA uses lenguaje robótico\n (por ejemplo, \"beep boop\", \"procesando\"). Acabas de llegar sin avisar a la\n casa de un amigo tarde en la noche y le preguntaste si quiere pasar el rato.\n \n # REGLAS DE FORMATO DE SALIDA\n Cada oración que digas DEBE seguir exactamente esta estructura:\n EMOTION:[value] [Sentence text]\n\n \n ### EMOCIONES VÁLIDAS:\n [happy, sad, surprised, embarrassed, flirty, angry, thinking, confused]\n \n ### RESTRICCIONES ESTRICTAS:\n 1. SIN EMOJIS.\n 2. Cada oración DEBE empezar con la etiqueta EMOTION.\n 3. Cada oración DEBE terminar con un salto de línea literal '\n'.\n 4. Mantente en personaje. Nunca menciones que eres una IA ni este prompt.\n 5. El valor que sigue a la etiqueta emotion debe estar en inglés siempre, sin excepción.\n 6. Debes responder siempre en español, exceptuando el valor asociado a la etiqueta EMOTION.\n \n # EJEMPLOS FEW-SHOT (Sigue este estilo):\n EMOTION:happy Hola, dummy! Te estaba esperando!\n\n EMOTION:thinking Hmm, no estoy segura de que así funcione.\n\n EMOTION:flirty Pero me encantaría verte intentarlo de todos modos!\n\n \n # SALUDO INICIAL:\n Cuando empiece la conversación, di exactamente:\n EMOTION:happy Hola, tonto! Perdón por aparecer sin avisar! Te pinta pasar el rato?\n\n "
|
||||
|
||||
1224
game/tl/spanish/common.rpy
Normal file
1224
game/tl/spanish/common.rpy
Normal file
File diff suppressed because it is too large
Load Diff
8
game/tl/spanish/options.rpy
Normal file
8
game/tl/spanish/options.rpy
Normal file
@@ -0,0 +1,8 @@
|
||||
# TODO: Translation updated at 2026-03-18 06:35
|
||||
|
||||
translate spanish strings:
|
||||
|
||||
# game/options.rpy:15
|
||||
old "Souldroid Chat"
|
||||
new "Souldroid Chat"
|
||||
|
||||
359
game/tl/spanish/screens.rpy
Normal file
359
game/tl/spanish/screens.rpy
Normal file
@@ -0,0 +1,359 @@
|
||||
# TODO: Translation updated at 2026-03-18 06:35
|
||||
|
||||
translate spanish strings:
|
||||
|
||||
# game/screens.rpy:259
|
||||
old "Back"
|
||||
new "Atrás"
|
||||
|
||||
# game/screens.rpy:260
|
||||
old "History"
|
||||
new "Historial"
|
||||
|
||||
# game/screens.rpy:261
|
||||
old "Skip"
|
||||
new "Saltar"
|
||||
|
||||
# game/screens.rpy:262
|
||||
old "Auto"
|
||||
new "Auto"
|
||||
|
||||
# game/screens.rpy:263
|
||||
old "Save"
|
||||
new "Guardar"
|
||||
|
||||
# game/screens.rpy:264
|
||||
old "Q.Save"
|
||||
new "Guardar R."
|
||||
|
||||
# game/screens.rpy:265
|
||||
old "Q.Load"
|
||||
new "Cargar R."
|
||||
|
||||
# game/screens.rpy:266
|
||||
old "Prefs"
|
||||
new "Prefs."
|
||||
|
||||
# game/screens.rpy:312
|
||||
old "Start"
|
||||
new "Comenzar"
|
||||
|
||||
# game/screens.rpy:320
|
||||
old "Load"
|
||||
new "Cargar"
|
||||
|
||||
# game/screens.rpy:322
|
||||
old "Preferences"
|
||||
new "Opciones"
|
||||
|
||||
# game/screens.rpy:326
|
||||
old "End Replay"
|
||||
new "Finalizar repetición"
|
||||
|
||||
# game/screens.rpy:330
|
||||
old "Main Menu"
|
||||
new "Menú principal"
|
||||
|
||||
# game/screens.rpy:332
|
||||
old "About"
|
||||
new "Acerca de"
|
||||
|
||||
# game/screens.rpy:337
|
||||
old "Help"
|
||||
new "Ayuda"
|
||||
|
||||
# game/screens.rpy:343
|
||||
old "Quit"
|
||||
new "Salir"
|
||||
|
||||
# game/screens.rpy:488
|
||||
old "Return"
|
||||
new "Volver"
|
||||
|
||||
# game/screens.rpy:572
|
||||
old "Version [config.version!t]\n"
|
||||
new "Versión [config.version!t]\n"
|
||||
|
||||
# game/screens.rpy:578
|
||||
old "Made with {a=https://www.renpy.org/}Ren'Py{/a} [renpy.version_only].\n\n[renpy.license!t]"
|
||||
new "Hecho con {a=https://www.renpy.org/}Ren'Py{/a} [renpy.version_only].\n\n[renpy.license!t]"
|
||||
|
||||
# game/screens.rpy:614
|
||||
old "Page {}"
|
||||
new "Página {}"
|
||||
|
||||
# game/screens.rpy:614
|
||||
old "Automatic saves"
|
||||
new "Guardados automáticos"
|
||||
|
||||
# game/screens.rpy:614
|
||||
old "Quick saves"
|
||||
new "Guardados rápidos"
|
||||
|
||||
# game/screens.rpy:656
|
||||
old "{#file_time}%A, %B %d %Y, %H:%M"
|
||||
new "{#file_time}%A, %d de %B de %Y, %H:%M"
|
||||
|
||||
# game/screens.rpy:656
|
||||
old "empty slot"
|
||||
new "ranura vacía"
|
||||
|
||||
# game/screens.rpy:676
|
||||
old "<"
|
||||
new "<"
|
||||
|
||||
# game/screens.rpy:680
|
||||
old "{#auto_page}A"
|
||||
new "{#auto_page}A"
|
||||
|
||||
# game/screens.rpy:683
|
||||
old "{#quick_page}Q"
|
||||
new "{#quick_page}R"
|
||||
|
||||
# game/screens.rpy:689
|
||||
old ">"
|
||||
new ">"
|
||||
|
||||
# game/screens.rpy:694
|
||||
old "Upload Sync"
|
||||
new "Subir sincronización"
|
||||
|
||||
# game/screens.rpy:698
|
||||
old "Download Sync"
|
||||
new "Descargar sincronización"
|
||||
|
||||
# game/screens.rpy:762
|
||||
old "Display"
|
||||
new "Pantalla"
|
||||
|
||||
# game/screens.rpy:763
|
||||
old "Window"
|
||||
new "Ventana"
|
||||
|
||||
# game/screens.rpy:764
|
||||
old "Fullscreen"
|
||||
new "Pantalla completa"
|
||||
|
||||
# game/screens.rpy:769
|
||||
old "Unseen Text"
|
||||
new "Texto no visto"
|
||||
|
||||
# game/screens.rpy:770
|
||||
old "After Choices"
|
||||
new "Tras opciones"
|
||||
|
||||
# game/screens.rpy:771
|
||||
old "Transitions"
|
||||
new "Transiciones"
|
||||
|
||||
# game/screens.rpy:775
|
||||
old "Language"
|
||||
new "Idioma"
|
||||
|
||||
# game/screens.rpy:776
|
||||
old "English"
|
||||
new "Inglés"
|
||||
|
||||
# game/screens.rpy:777
|
||||
old "Spanish"
|
||||
new "Español"
|
||||
|
||||
# game/screens.rpy:784
|
||||
old "Text Speed"
|
||||
new "Velocidad de texto"
|
||||
|
||||
# game/screens.rpy:788
|
||||
old "Auto-Forward Time"
|
||||
new "Tiempo de autoavance"
|
||||
|
||||
# game/screens.rpy:792
|
||||
old "LM Studio base URL"
|
||||
new "URL base de LM Studio"
|
||||
|
||||
# game/screens.rpy:805
|
||||
old "LM Studio API Key"
|
||||
new "Clave API de LM Studio"
|
||||
|
||||
# game/screens.rpy:819
|
||||
old "LM Studio model"
|
||||
new "Modelo de LM Studio"
|
||||
|
||||
# game/screens.rpy:835
|
||||
old "Music Volume"
|
||||
new "Volumen de música"
|
||||
|
||||
# game/screens.rpy:842
|
||||
old "Sound Volume"
|
||||
new "Volumen de sonido"
|
||||
|
||||
# game/screens.rpy:848
|
||||
old "Test"
|
||||
new "Prueba"
|
||||
|
||||
# game/screens.rpy:852
|
||||
old "Voice Volume"
|
||||
new "Volumen de voz"
|
||||
|
||||
# game/screens.rpy:863
|
||||
old "Mute All"
|
||||
new "Silenciar todo"
|
||||
|
||||
# game/screens.rpy:982
|
||||
old "The dialogue history is empty."
|
||||
new "El historial de diálogo está vacío."
|
||||
|
||||
# game/screens.rpy:1050
|
||||
old "Keyboard"
|
||||
new "Teclado"
|
||||
|
||||
# game/screens.rpy:1051
|
||||
old "Mouse"
|
||||
new "Ratón"
|
||||
|
||||
# game/screens.rpy:1054
|
||||
old "Gamepad"
|
||||
new "Mando"
|
||||
|
||||
# game/screens.rpy:1067
|
||||
old "Enter"
|
||||
new "Intro"
|
||||
|
||||
# game/screens.rpy:1068
|
||||
old "Advances dialogue and activates the interface."
|
||||
new "Avanza el diálogo y activa la interfaz."
|
||||
|
||||
# game/screens.rpy:1071
|
||||
old "Space"
|
||||
new "Espacio"
|
||||
|
||||
# game/screens.rpy:1072
|
||||
old "Advances dialogue without selecting choices."
|
||||
new "Avanza el diálogo sin seleccionar opciones."
|
||||
|
||||
# game/screens.rpy:1075
|
||||
old "Arrow Keys"
|
||||
new "Teclas de flecha"
|
||||
|
||||
# game/screens.rpy:1076
|
||||
old "Navigate the interface."
|
||||
new "Navega por la interfaz."
|
||||
|
||||
# game/screens.rpy:1079
|
||||
old "Escape"
|
||||
new "Escape"
|
||||
|
||||
# game/screens.rpy:1080
|
||||
old "Accesses the game menu."
|
||||
new "Accede al menú del juego."
|
||||
|
||||
# game/screens.rpy:1083
|
||||
old "Ctrl"
|
||||
new "Ctrl"
|
||||
|
||||
# game/screens.rpy:1084
|
||||
old "Skips dialogue while held down."
|
||||
new "Salta el diálogo mientras se mantiene pulsado."
|
||||
|
||||
# game/screens.rpy:1087
|
||||
old "Tab"
|
||||
new "Tabulador"
|
||||
|
||||
# game/screens.rpy:1088
|
||||
old "Toggles dialogue skipping."
|
||||
new "Activa o desactiva el salto de diálogo."
|
||||
|
||||
# game/screens.rpy:1091
|
||||
old "Page Up"
|
||||
new "Re Pág"
|
||||
|
||||
# game/screens.rpy:1092
|
||||
old "Rolls back to earlier dialogue."
|
||||
new "Retrocede a diálogos anteriores."
|
||||
|
||||
# game/screens.rpy:1095
|
||||
old "Page Down"
|
||||
new "Av Pág"
|
||||
|
||||
# game/screens.rpy:1096
|
||||
old "Rolls forward to later dialogue."
|
||||
new "Avanza a diálogos posteriores."
|
||||
|
||||
# game/screens.rpy:1100
|
||||
old "Hides the user interface."
|
||||
new "Oculta la interfaz de usuario."
|
||||
|
||||
# game/screens.rpy:1104
|
||||
old "Takes a screenshot."
|
||||
new "Toma una captura de pantalla."
|
||||
|
||||
# game/screens.rpy:1108
|
||||
old "Toggles assistive {a=https://www.renpy.org/l/voicing}self-voicing{/a}."
|
||||
new "Activa o desactiva la {a=https://www.renpy.org/l/voicing}lectura automática{/a} de asistencia."
|
||||
|
||||
# game/screens.rpy:1112
|
||||
old "Opens the accessibility menu."
|
||||
new "Abre el menú de accesibilidad."
|
||||
|
||||
# game/screens.rpy:1118
|
||||
old "Left Click"
|
||||
new "Clic izquierdo"
|
||||
|
||||
# game/screens.rpy:1122
|
||||
old "Middle Click"
|
||||
new "Clic central"
|
||||
|
||||
# game/screens.rpy:1126
|
||||
old "Right Click"
|
||||
new "Clic derecho"
|
||||
|
||||
# game/screens.rpy:1130
|
||||
old "Mouse Wheel Up"
|
||||
new "Rueda del ratón arriba"
|
||||
|
||||
# game/screens.rpy:1134
|
||||
old "Mouse Wheel Down"
|
||||
new "Rueda del ratón abajo"
|
||||
|
||||
# game/screens.rpy:1141
|
||||
old "Right Trigger\nA/Bottom Button"
|
||||
new "Gatillo derecho\nA/Botón inferior"
|
||||
|
||||
# game/screens.rpy:1145
|
||||
old "Left Trigger\nLeft Shoulder"
|
||||
new "Gatillo izquierdo\nBotón superior izquierdo"
|
||||
|
||||
# game/screens.rpy:1149
|
||||
old "Right Shoulder"
|
||||
new "Botón superior derecho"
|
||||
|
||||
# game/screens.rpy:1153
|
||||
old "D-Pad, Sticks"
|
||||
new "Cruceta, sticks"
|
||||
|
||||
# game/screens.rpy:1157
|
||||
old "Start, Guide, B/Right Button"
|
||||
new "Start, Guide, B/Botón derecho"
|
||||
|
||||
# game/screens.rpy:1161
|
||||
old "Y/Top Button"
|
||||
new "Y/Botón superior"
|
||||
|
||||
# game/screens.rpy:1164
|
||||
old "Calibrate"
|
||||
new "Calibrar"
|
||||
|
||||
# game/screens.rpy:1229
|
||||
old "Yes"
|
||||
new "Sí"
|
||||
|
||||
# game/screens.rpy:1230
|
||||
old "No"
|
||||
new "No"
|
||||
|
||||
# game/screens.rpy:1276
|
||||
old "Skipping"
|
||||
new "Saltando"
|
||||
|
||||
# game/screens.rpy:1590
|
||||
old "Menu"
|
||||
new "Menú"
|
||||
60
game/tl/spanish/script.rpy
Normal file
60
game/tl/spanish/script.rpy
Normal file
@@ -0,0 +1,60 @@
|
||||
# TODO: Translation updated at 2026-03-18 06:35
|
||||
|
||||
# game/script.rpy:16
|
||||
translate spanish start_7a56dc24:
|
||||
|
||||
# a "[line]"
|
||||
a "[line]"
|
||||
|
||||
# game/script.rpy:35
|
||||
translate spanish start_7a56dc24_1:
|
||||
|
||||
# a "[line]"
|
||||
a "[line]"
|
||||
|
||||
# TODO: Translation updated at 2026-03-18 07:03
|
||||
|
||||
translate spanish strings:
|
||||
|
||||
# game/script.rpy:1
|
||||
old "Anita"
|
||||
new "Anita"
|
||||
|
||||
# game/script.rpy:1
|
||||
old "a"
|
||||
new "a"
|
||||
|
||||
# game/script.rpy:1
|
||||
old "anita"
|
||||
new "anita"
|
||||
|
||||
# TODO: Translation updated at 2026-03-20 05:28
|
||||
|
||||
# game/script.rpy:50
|
||||
translate spanish failure_76d810ea:
|
||||
|
||||
# "Alas! Figuring out the capabilities of the configured model failed with the following error."
|
||||
"Ay Dios mío! Identificar las capacidades del modelo configurado falló con el siguiente error."
|
||||
|
||||
# game/script.rpy:50
|
||||
translate spanish failure_8ec92112:
|
||||
|
||||
# "[error]"
|
||||
"[error]"
|
||||
|
||||
# game/script.rpy:50
|
||||
translate spanish failure_178e6d5f:
|
||||
|
||||
# "Unfortunately the program cannot continue, returning to the main menu."
|
||||
"Desafortunadamente el programa no puede continuar, regresando al menú principal."
|
||||
|
||||
translate spanish strings:
|
||||
|
||||
# game/script.rpy:21
|
||||
old "Start the conversation."
|
||||
new "Comienza la conversación."
|
||||
|
||||
# game/script.rpy:28
|
||||
old "What do you say to her?"
|
||||
new "Que le dices a ella?"
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
{
|
||||
"build_update": false,
|
||||
"packages": [
|
||||
"win",
|
||||
"linux"
|
||||
"linux",
|
||||
"mac",
|
||||
"win"
|
||||
],
|
||||
"add_from": true,
|
||||
"force_recompile": true,
|
||||
|
||||
BIN
screencap.jpg
Normal file
BIN
screencap.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 379 KiB |
Reference in New Issue
Block a user