Compare commits
1 Commits
7eb353677a
...
testing
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c442261d9 |
@@ -37,10 +37,6 @@ https://youtu.be/3F8C713XUj8
|
||||
Wayward Ghouls:
|
||||
http://teknoaxe.com/Link_Code_3.php?q=1469
|
||||
|
||||
Spider's Domain
|
||||
Alexey Kasyanchuk (DEgITx) [ https://degitx.com/links ]
|
||||
https://creativecommons.org/licenses/by/4.0/
|
||||
|
||||
Sound Effects:
|
||||
|
||||
_finish_level.ogg uses Shock Stab 08 by nomiqbomi from freesound. License Creative Commons 0.
|
||||
|
||||
@@ -35,9 +35,6 @@ Alt+Home: Ambience volume increase.
|
||||
Alt+Delete: Game sounds volume decrease.
|
||||
Alt+Insert: Game sounds volume increase.
|
||||
Backspace: Pause or resume the game.
|
||||
F1: Navigate to previous speech message in history.
|
||||
F2: Repeat current speech message.
|
||||
F3: Navigate to next speech message in history.
|
||||
|
||||
Notes
|
||||
|
||||
@@ -54,8 +51,7 @@ The game automatically saves your progress when you have collected 200 bone dust
|
||||
|
||||
Game Modes
|
||||
|
||||
Story Mode: Traditional level-by-level progression through predefined stages. Your progress is saved, and you can collect extra lives by gathering 100 bone dust.
|
||||
At 200 bone dust the game will save allowing you to load from the current level.
|
||||
Story Mode: Traditional level-by-level progression through predefined stages. Your progress is saved, and you can collect extra lives by gathering bone dust.
|
||||
|
||||
Survival Mode: An endless challenge where you face wave after wave of increasingly difficult enemies. Each wave gets progressively harder with stronger enemies, faster spawn rates, and longer levels. Level length starts at 300 units and increases by 20 units per wave (capped at 500 units). In survival mode:
|
||||
No saving or loading - each run is a fresh start
|
||||
|
||||
872
levels/README.md
872
levels/README.md
@@ -1,91 +1,41 @@
|
||||
# Creating Custom Levels for Wicked Quest
|
||||
|
||||
Want to add your own levels? It's simple! Every level is a JSON file, and you can create as many as you want up to 998. Level 999 is reserved for survival mode.
|
||||
Want to add your own levels? It's pretty simple! Every level is a JSON file, and you can create as many as you want.
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Create a directory under `levels/` with your level pack name:
|
||||
```
|
||||
levels/My Cool Levels/
|
||||
```
|
||||
1. Make a directory under levels/ with your level pack name:
|
||||
levels/My Cool Levels/
|
||||
|
||||
2. Start with `1.json` in your new directory. Each level after that is numbered in order: `2.json`, `3.json`, etc. You can also add instructions.txt and credits.txt to the level pack. These items will be shown in the menu for the level pack, e.g. Story Mode, Survival Mode, Instructions, Credits.
|
||||
2. Start with 1.json in your new directory. Each level after that is numbered in order: 2.json, 3.json, etc.
|
||||
|
||||
## Basic Level Structure
|
||||
|
||||
Every level needs these essential properties:
|
||||
Every level needs these things:
|
||||
|
||||
```json
|
||||
{
|
||||
"level_id": 1,
|
||||
"name": "My Level",
|
||||
"description": "Set the scene for your level.",
|
||||
"locked": false,
|
||||
"player_start": {
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"boundaries": {
|
||||
"left": 0,
|
||||
"right": 200
|
||||
},
|
||||
"ambience": "Graveyard Blitz.ogg",
|
||||
"footstep_sound": "footstep_stone"
|
||||
}
|
||||
```
|
||||
|
||||
If you set `locked` to `true`, the player cannot leave the level until all enemies have been defeated. Drop custom ambience files (e.g., music or creepy soundtracks) in `sounds/ambience/`. Add custom footstep sounds into the `sounds/` directory.
|
||||
|
||||
### Dialogue System
|
||||
|
||||
Instead of using a simple `"description"` field, levels can now include interactive dialogue sequences with the new `"dialog"` property. This creates immersive story-driven introductions with character conversations:
|
||||
|
||||
```json
|
||||
{
|
||||
"level_id": 1,
|
||||
"name": "Story Level",
|
||||
"dialog": {
|
||||
"allow_skip": true,
|
||||
"entries": [
|
||||
{
|
||||
"speaker": "Character Name",
|
||||
"text": "This is what the character says."
|
||||
},
|
||||
{
|
||||
"speaker": "Billy Bones",
|
||||
"text": "This is the player character's response."
|
||||
},
|
||||
{
|
||||
"text": "A narrative description of what happens.",
|
||||
"narrative": true
|
||||
},
|
||||
{
|
||||
"speaker": "Character Name",
|
||||
"text": "More dialogue with optional sound effect.",
|
||||
"sound": "character_sound"
|
||||
}
|
||||
]
|
||||
},
|
||||
"player_start": {
|
||||
"x": 0,
|
||||
"y": 0
|
||||
{
|
||||
"level_id": 1,
|
||||
"name": "My Level",
|
||||
"description": "Set the scene for your level.",
|
||||
"locked": false,
|
||||
"player_start": {
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"boundaries": {
|
||||
"left": 0,
|
||||
"right": 200
|
||||
},
|
||||
"ambience": "Graveyard Blitz.ogg",
|
||||
"footstep_sound": "footstep_stone"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Dialogue Properties:**
|
||||
- `allow_skip`: Set to `true` to let players skip the dialogue sequence
|
||||
- `entries`: Array of dialogue entries that play in sequence
|
||||
- `speaker`: Name of the character speaking (optional for narrative entries)
|
||||
- `text`: The dialogue text to be spoken
|
||||
- `narrative`: Set to `true` for descriptive text entries (no speaker)
|
||||
- `sound`: Optional sound file to play with this dialogue entry. If no sound is specified, the system will automatically play `sounds/dialogue.ogg` if it exists.
|
||||
If you set locked to true, the player may not leave the level until all enemies have been defeated. Drop custom ambience files, e.g. music or creepy sound track in sounds/ambience. Add custom footstep sounds into the sounds directory.
|
||||
|
||||
**Note:** Levels can include both `"description"` and `"dialog"`. When both are present, the dialogue plays first, followed by the standard level description message format ("Level X, Name. Description.").
|
||||
|
||||
## Adding Objects
|
||||
|
||||
All objects go in an `"objects"` list. Here are examples of what you can add:
|
||||
All objects go in an "objects" list. Here are some examples of what you can add:
|
||||
|
||||
### Object Positioning
|
||||
|
||||
@@ -98,104 +48,81 @@ The `y` coordinate determines the vertical layer:
|
||||
- `y: 3` - Elevated level (bone dust, coffins)
|
||||
- `y: 12` - High level (skull storms)
|
||||
|
||||
### Collectibles
|
||||
### Bone Dust
|
||||
|
||||
#### Bone Dust
|
||||
```json
|
||||
{
|
||||
"x_range": [5, 8],
|
||||
"y": 3,
|
||||
"sound": "bone_dust",
|
||||
"collectible": true,
|
||||
"static": true
|
||||
}
|
||||
```
|
||||
{
|
||||
"x_range": [5, 8],
|
||||
"y": 3,
|
||||
"sound": "bone_dust",
|
||||
"collectible": true,
|
||||
"static": true
|
||||
}
|
||||
|
||||
The `static` property means objects don't move - they stay in their fixed positions. Static objects like bone dust and graves remain stationary, while enemies without this property can move and patrol.
|
||||
|
||||
### Interactive Objects
|
||||
### Coffins
|
||||
|
||||
#### Coffins
|
||||
```json
|
||||
{
|
||||
"x": 15,
|
||||
"y": 3,
|
||||
"sound": "coffin",
|
||||
"type": "coffin",
|
||||
"item": "extra_life"
|
||||
}
|
||||
```
|
||||
{
|
||||
"x": 15,
|
||||
"y": 3,
|
||||
"sound": "coffin",
|
||||
"type": "coffin",
|
||||
"item": "extra_life"
|
||||
}
|
||||
|
||||
**Items available for coffins:**
|
||||
- `"extra_life"` - Grants an extra life
|
||||
- `"hand_of_glory"` - Provides temporary invincibility
|
||||
- `"jack_o_lantern"` - Throwable projectile
|
||||
- `"guts"` - Increases max health or restores health if at max
|
||||
- `"cauldron"` - Special item
|
||||
- `"witch_broom"` - Weapon upgrade
|
||||
- `"shin_bone"` - Currency for extra lives (100 needed)
|
||||
- `"guts"` - Health upgrade/restoration
|
||||
- `"random"` - Randomly selects from eligible items (default if not specified)
|
||||
Items are optional, can be extra_life, hand_of_glory, jack_o_lantern, or anything from graves.
|
||||
|
||||
**Coffin Behavior:** When broken with any weapon, coffins drop their specified item 1-2 tiles away in a random direction. The item bounces and can be collected by walking over it.
|
||||
|
||||
#### Graves
|
||||
```json
|
||||
{
|
||||
"x": 35,
|
||||
"y": 0,
|
||||
"type": "grave",
|
||||
"sound": "grave",
|
||||
"static": true,
|
||||
"zombie_spawn_chance": 20,
|
||||
"item": "shin_bone"
|
||||
}
|
||||
```
|
||||
### Graves
|
||||
|
||||
**Grave items available:**
|
||||
- Any item available for coffins
|
||||
{
|
||||
"x": 35,
|
||||
"y": 0,
|
||||
"type": "grave",
|
||||
"sound": "grave",
|
||||
"static": true,
|
||||
"zombie_spawn_chance": 20,
|
||||
"item": "shin_bone"
|
||||
}
|
||||
|
||||
Zombie spawn chance is 0-100, higher means more zombies. Item is also optional, can be shin_bone, guts, or any item from coffin.
|
||||
|
||||
**Grave behaviors:**
|
||||
- `zombie_spawn_chance`: 0-100, higher means more zombies spawn when walked over
|
||||
- **Item collection:** Duck while walking (not running) with the rusty shovel equipped to safely collect items
|
||||
- **Grave filling:** Duck with shovel over empty graves (no item) to fill them and remove the hazard
|
||||
|
||||
### Enemies
|
||||
|
||||
```json
|
||||
{
|
||||
"x_range": [20, 35],
|
||||
"y": 0,
|
||||
"enemy_type": "goblin",
|
||||
"health": 4,
|
||||
"damage": 2,
|
||||
"attack_range": 1.5,
|
||||
"attack_pattern": {
|
||||
"type": "hunter",
|
||||
"turn_threshold": 5
|
||||
{
|
||||
"x_range": [20, 35], // patrol or hunting range
|
||||
"y": 0,
|
||||
"enemy_type": "goblin",
|
||||
"health": 4,
|
||||
"damage": 2,
|
||||
"attack_range": 1.5,
|
||||
"attack_pattern": {
|
||||
"type": "hunter",
|
||||
"turn_threshold": 5
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Attack patterns:**
|
||||
- `"patrol"`: Enemy moves back and forth within their x_range
|
||||
- `"hunter"`: Enemy will leave their area to pursue the player once entered
|
||||
- `turn_threshold`: How quickly hunting enemies turn around to attack (lower = more aggressive)
|
||||
Attacks can be "hunter" or "patrol". The "patrol" option does not use the "turn_threshold" option. The "turn_threshold" option is how quickly the hunting enemy will turn around to attack the player. Hunters will leave their area to pursue the player once he has entered the enemy's range.
|
||||
|
||||
#### Advanced Enemy Behaviors
|
||||
|
||||
##### Vulnerability System (Ghost-like Enemies)
|
||||
```json
|
||||
#### Special Enemy Behaviors
|
||||
|
||||
Enemies can have special behaviors regardless of their type. Here are some examples:
|
||||
|
||||
##### incorporeal Goblin
|
||||
|
||||
``` json
|
||||
{
|
||||
"x_range": [400, 415],
|
||||
"y": 0,
|
||||
"enemy_type": "ghost",
|
||||
"health": 60,
|
||||
"enemy_type": "goblin",
|
||||
"health": 30,
|
||||
"damage": 2,
|
||||
"attack_range": 1,
|
||||
"has_vulnerability": true,
|
||||
"is_vulnerable": false,
|
||||
"vulnerability_duration": 3000,
|
||||
"vulnerability_duration": 1000,
|
||||
"invulnerability_duration": 5000,
|
||||
"speed_multiplier": 0.8,
|
||||
"attack_cooldown": 1200,
|
||||
@@ -206,618 +133,99 @@ The `static` property means objects don't move - they stay in their fixed positi
|
||||
}
|
||||
```
|
||||
|
||||
**Vulnerability system:** Enemy alternates between vulnerable and invulnerable states. They can only be damaged when vulnerable (plays `enemy_is_vulnerable.ogg` sound).
|
||||
##### Spawning Other Enemies (like revenants)
|
||||
|
||||
##### Enemy Spawning System
|
||||
```json
|
||||
{
|
||||
"x_range": [420, 470],
|
||||
"y": 0,
|
||||
"enemy_type": "revenant",
|
||||
"health": 80,
|
||||
"damage": 1,
|
||||
"attack_range": 1,
|
||||
"attack_pattern": {
|
||||
"type": "patrol"
|
||||
},
|
||||
"can_spawn": true,
|
||||
"spawn_type": "zombie",
|
||||
"spawn_cooldown": 2500,
|
||||
"spawn_chance": 75,
|
||||
"spawn_distance": 5
|
||||
}
|
||||
```
|
||||
You can mix and match these behaviors. For an example of a witch who spawns black cats, see "Wicked Quest/13.json"
|
||||
|
||||
**Spawning properties:**
|
||||
- `can_spawn`: Enable spawning behavior
|
||||
- `spawn_type`: Type of enemy to spawn (e.g., "zombie", "black_cat")
|
||||
- `spawn_cooldown`: Milliseconds between spawn attempts
|
||||
- `spawn_chance`: 0-100 probability of spawning when cooldown expires
|
||||
- `spawn_distance`: How far from the spawner to place new enemies
|
||||
|
||||
**Example combinations:**
|
||||
- **Witch spawning black cats:** High health witch that periodically spawns fast black cats
|
||||
- **Revenant spawning zombies:** Tough enemy that creates zombie minions
|
||||
- **Any enemy type can spawn any other enemy type**
|
||||
## Sound Requirements for Special Behaviors
|
||||
|
||||
When adding special behaviors to enemies, you'll need corresponding sound files:
|
||||
|
||||
For vulnerability system:
|
||||
- enemy_is_vulnerable.ogg - Sound when enemy becomes vulnerable
|
||||
|
||||
For spawning behavior:
|
||||
- enemy_spawn.ogg (optional) - Sound when spawning new enemies
|
||||
|
||||
## Tips for Custom Enemies
|
||||
|
||||
- Balance special behaviors carefully
|
||||
- Test enemy combinations thoroughly
|
||||
- Consider providing audio cues for special behaviors
|
||||
- Remember faster enemies should generally do less damage
|
||||
- Vulnerability systems work best with higher health values
|
||||
- Spawning enemies should have lower health to compensate
|
||||
|
||||
|
||||
### Hazards
|
||||
|
||||
#### Skull Storm
|
||||
```json
|
||||
{
|
||||
"x_range": [40, 60],
|
||||
"y": 12,
|
||||
"type": "skull_storm",
|
||||
"damage": 4,
|
||||
"maximum_skulls": 2,
|
||||
"frequency": {
|
||||
"min": 1,
|
||||
"max": 4
|
||||
|
||||
{
|
||||
"x_range": [40, 60],
|
||||
"y": 12,
|
||||
"type": "skull_storm",
|
||||
"damage": 4,
|
||||
"maximum_skulls": 2,
|
||||
"frequency": {
|
||||
"min": 1,
|
||||
"max": 4
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `maximum_skulls` setting controls how many skulls can be falling simultaneously. `frequency` is the number of seconds that can elapse before the next skull falls.
|
||||
The maximum setting is how many skulls can be falling at once. Frequence is the number of seconds that can expire before the next skull falls.
|
||||
|
||||
**Skull Storm Sound Overrides:**
|
||||
```json
|
||||
{
|
||||
"x_range": [40, 60],
|
||||
"y": 12,
|
||||
"type": "skull_storm",
|
||||
"damage": 4,
|
||||
"maximum_skulls": 2,
|
||||
"frequency": {"min": 1, "max": 4},
|
||||
"sound_overrides": {
|
||||
"skull_storm": "reindeer",
|
||||
"falling_skull": "falling_poop",
|
||||
"skull_lands": "poop_splat",
|
||||
"end_message": "The reindeer fly away.",
|
||||
"hit_message": "Hit by reindeer poop!"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Override Properties:**
|
||||
- `skull_storm`: Sound when entering the skull storm area
|
||||
- `falling_skull`: Sound of skulls falling (system automatically uses numbered variants: falling_poop1, falling_poop2, etc. if they exist)
|
||||
- `skull_lands`: Sound when skulls hit the ground
|
||||
- `end_message`: TTS message when leaving the skull storm area
|
||||
- `hit_message`: TTS message when hit by a falling skull
|
||||
|
||||
#### Catapult
|
||||
```json
|
||||
{
|
||||
"x": 55,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 4000,
|
||||
"range": 25
|
||||
}
|
||||
```
|
||||
|
||||
**Properties:**
|
||||
- `fireInterval`: Milliseconds between shots (note: camelCase, not snake_case)
|
||||
- `range`: How far the catapult can shoot
|
||||
{
|
||||
"x": 55,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fire_interval": 4000, // milliseconds between shots
|
||||
"range": 25
|
||||
}
|
||||
|
||||
#### Spider Web
|
||||
```json
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 15,
|
||||
"y": 3,
|
||||
"sound_overrides": {
|
||||
"base": "floating_trap",
|
||||
"hit_sound": "trap_trigger",
|
||||
"spawn_enemy": "elf"
|
||||
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 15,
|
||||
"y": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Spider Web Mechanics:**
|
||||
Spider webs create interactive traps that can slow player movement and attack speed temporarily when triggered. They automatically spawn an enemy at the web location. When triggered, the player bounces back 3 tiles and may receive a 15-second penalty (half movement speed, double attack time) depending on the slowdown setting.
|
||||
|
||||
**Y-Position Based Behavior:**
|
||||
- **`"y": 0` (Ground Level)** - Ground trap that triggers when walking/running. **Avoid by jumping over it.**
|
||||
- **`"y": > 0` (Air Level)** - Air trap that triggers when walking or jumping. **Avoid by ducking under it.**
|
||||
|
||||
**Sound Override Properties:**
|
||||
- `"base"`: Override the ambient web sound (e.g., "floating_trap", "bear_trap")
|
||||
- `"hit_sound"`: Override the trigger sound (e.g., "trap_trigger", "snap")
|
||||
- `"spawn_enemy"`: Override spawned enemy type ("spider", "elf", "witch")
|
||||
- `"enemy_stats"`: Override spawned enemy stats (health, damage, speed_multiplier, attack_range)
|
||||
- `"slowdown"`: Override slowdown effect (true/false, defaults to true)
|
||||
|
||||
**Level Design Guidelines:**
|
||||
- **For traditional spider webs, always use `"y": > 0`** (air placement) unless you're specifically creating a themed ground trap
|
||||
- **Ground webs** (`"y": 0`) work well for: bear traps, tripwires, pressure plates, snowdrifts, present piles
|
||||
- **Air webs** (`"y": > 0`) work well for: hanging webs, floating pods, magical traps, dangling ornaments
|
||||
- **Slowdown considerations**: Use `"slowdown": false` for traps where the slowdown effect doesn't make thematic sense (stepping on a snowdrift shouldn't entangle you like a web would)
|
||||
|
||||
**Example Themed Variations:** note that in true json you cannot use comments like the // lines here for explanations.
|
||||
```json
|
||||
// Christmas snowdrift (ground trap - jump to avoid)
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 50,
|
||||
"y": 0,
|
||||
"sound_overrides": {
|
||||
"base": "snow_drift",
|
||||
"hit_sound": "snow_drift_hit",
|
||||
"spawn_enemy": "abominable_snowman",
|
||||
"enemy_stats": {
|
||||
"health": 12,
|
||||
"damage": 3,
|
||||
"speed_multiplier": 0.8,
|
||||
"attack_range": 1
|
||||
},
|
||||
"slowdown": false
|
||||
}
|
||||
}
|
||||
|
||||
// Floating snow cloud (air trap - duck to avoid)
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 75,
|
||||
"y": 4,
|
||||
"sound_overrides": {
|
||||
"base": "floating_snow_cloud",
|
||||
"hit_sound": "cloud_burst",
|
||||
"spawn_enemy": "witch",
|
||||
"slowdown": true
|
||||
}
|
||||
}
|
||||
|
||||
// Traditional spider web (default behavior)
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 100,
|
||||
"y": 2
|
||||
}
|
||||
```
|
||||
|
||||
#### Grasping Hands
|
||||
```json
|
||||
{
|
||||
"x_range": [40, 60],
|
||||
"y": 0,
|
||||
"type": "grasping_hands",
|
||||
"delay": 1000,
|
||||
"crumble_speed": 0.065
|
||||
}
|
||||
```
|
||||
|
||||
{
|
||||
"x_range": [40, 60],
|
||||
"y": 0,
|
||||
"type": "grasping_hands",
|
||||
"delay": 1000,
|
||||
"crumble_speed": 0.065
|
||||
}
|
||||
|
||||
The ground crumbles beneath the player as undead hands reach up. The `delay` is in milliseconds before crumbling starts, and `crumble_speed` controls how fast the crumbling catches up to the player.
|
||||
|
||||
## Level System Flexibility
|
||||
## Creating New Enemies
|
||||
|
||||
The level system supports complex interactions and behaviors:
|
||||
|
||||
### Dynamic Item Drops
|
||||
- **Monsters spawning items:** Use enemy spawning system to create enemies that drop items when killed
|
||||
- **Graves with random loot:** Each grave can contain different items collected via specific mechanics
|
||||
|
||||
### Creative Design Techniques
|
||||
|
||||
#### Coffins That Release Enemies
|
||||
While not directly supported, you can create the illusion of coffins that release enemies when touched:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 25,
|
||||
"y": 3,
|
||||
"sound_overrides": {
|
||||
"base": "coffin",
|
||||
"hit_sound": "coffin_shatter",
|
||||
"spawn_enemy": "elf"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Modern Approach (Recommended):**
|
||||
Use sound overrides directly in your JSON for cleaner, more maintainable theming. The above creates a "coffin" that shatters and spawns an enemy when touched.
|
||||
|
||||
**Legacy Approach:**
|
||||
You can still replace sounds in your level pack's sound directory:
|
||||
- Replace `spiderweb.ogg` with `coffin.ogg` (ambient coffin sound)
|
||||
- Replace `hit_spiderweb.ogg` with `coffin_shatter.ogg` (break sound)
|
||||
- Replace `spider.ogg` with `mummy.ogg` (spawned enemy sound)
|
||||
- Replace `spider_dies.ogg` with `mummy_dies.ogg` (death sound)
|
||||
|
||||
**Result:** Players hear a coffin, when they touch it they hear it shatter, get slowed down (representing the enemy emerging), and a "mummy" automatically spawns from the coffin. Since this uses `"y": 3` (air level), players must duck to avoid triggering it - creating the "low hanging coffin" effect you described.
|
||||
|
||||
### Complex Enemy Combinations
|
||||
- **Spawner + Vulnerability:** Create a tough ghost that spawns minions while alternating between vulnerable states
|
||||
- **Multi-layered encounters:** Combine patrol enemies with hunters and spawners for dynamic battles
|
||||
- **Progressive difficulty:** Later waves can spawn stronger enemy types
|
||||
|
||||
### Environmental Storytelling
|
||||
- **Themed hazard combinations:** Skull storms over graveyards, catapults defending strategic chokepoints
|
||||
- **Sound design integration:** Custom footsteps and ambience per level
|
||||
|
||||
## Creating New Enemy Types
|
||||
|
||||
Want to add a new enemy type? You'll need two sound files in the `sounds/` directory:
|
||||
- `enemy.ogg` - The sound the enemy makes while alive
|
||||
- `enemy_dies.ogg` - The death sound
|
||||
Want to add a new enemy type? You'll need two sound files in the sounds directory:
|
||||
- enemy.ogg - The sound the enemy makes while alive
|
||||
- enemy_dies.ogg - The death sound
|
||||
|
||||
For example, to add a werewolf enemy:
|
||||
- Add `werewolf.ogg` and `werewolf_dies.ogg` to the sounds directory
|
||||
- Use `"werewolf"` as the `enemy_type` in your level file
|
||||
- Add werewolf.ogg and werewolf_dies.ogg to the sounds directory
|
||||
- Use "werewolf" as the enemy_type in your level file
|
||||
|
||||
## Level Design Tips
|
||||
## Tips
|
||||
|
||||
### Balance Guidelines
|
||||
- **Add at least 33 bone dust per level** (for extra life economy)
|
||||
- **Space out hazards** to give players a chance to react
|
||||
- **Enemy health recommendations:**
|
||||
- Add at least 33 bone dust per level
|
||||
- Space out hazards to give players a chance
|
||||
- Enemy health:
|
||||
- Regular enemies: 4-6 HP
|
||||
- Mini-bosses: 8-15 HP
|
||||
- Mini-bosses: 8 HP
|
||||
- Bosses: 40+ HP
|
||||
- **Lock boss levels** with `"locked": true`
|
||||
|
||||
### Advanced Design Patterns
|
||||
- **Layered challenges:** Combine moving enemies with static hazards
|
||||
- **Risk/reward decisions:** Place valuable items near dangerous enemies
|
||||
- **Tactical positioning:** Use terrain and enemy placement to create strategic choices
|
||||
- **Escalating difficulty:** Gradually introduce new mechanics and combinations
|
||||
|
||||
## Custom Level Pack Structure
|
||||
|
||||
You can keep your levels and sounds separate from the main game. Create a directory structure like this for a pack called "Samhain Showdown":
|
||||
|
||||
### Directory Structure
|
||||
```
|
||||
levels/Samhain Showdown/
|
||||
├── 1.json
|
||||
├── 2.json
|
||||
├── 3.json
|
||||
└── end.ogg
|
||||
|
||||
sounds/Samhain Showdown/
|
||||
├── custom_footstep.ogg
|
||||
├── werewolf.ogg
|
||||
└── werewolf_dies.ogg
|
||||
|
||||
sounds/Samhain Showdown/ambience
|
||||
├── werewolf_hunting.ogg
|
||||
├── howling_winds.ogg
|
||||
```
|
||||
|
||||
### Advanced Sound Override System
|
||||
|
||||
Beyond simple file replacement, Wicked Quest now supports granular sound overrides directly in your level JSON files. This allows thematic consistency where a catapult becomes a "snowball launcher" or grasping hands become an "avalanche" - same mechanics, different sounds and feel.
|
||||
|
||||
#### Weapon Sound Overrides
|
||||
|
||||
Override weapon sounds and names globally for an entire level:
|
||||
|
||||
```json
|
||||
{
|
||||
"level_id": 1,
|
||||
"name": "Winter Wonderland",
|
||||
"weapon_sound_overrides": {
|
||||
"rusty_shovel": {
|
||||
"name": "rusty snow shovel",
|
||||
"attack_sound": "player_snow_shovel_attack",
|
||||
"hit_sound": "player_snow_shovel_hit"
|
||||
},
|
||||
"nunchucks": {
|
||||
"name": "ice sickles",
|
||||
"attack_sound": "player_ice_sickles_attack",
|
||||
"hit_sound": "player_ice_sickles_hit"
|
||||
},
|
||||
"witch_broom": {
|
||||
"name": "snow broom",
|
||||
"attack_sound": "player_snow_broom_attack",
|
||||
"hit_sound": "player_snow_broom_hit"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Weapon Override Properties:**
|
||||
- `name`: Display name for the weapon (e.g., "ice sickles" instead of "nunchucks")
|
||||
- `attack_sound`: Sound played when attacking
|
||||
- `hit_sound`: Sound played when hitting an enemy
|
||||
|
||||
**Weapon Override Persistence:**
|
||||
Weapon overrides set in **level 1** automatically persist throughout the entire level pack! You only need to define `weapon_sound_overrides` in your first level (1.json) and all subsequent levels will inherit these themed weapon names.
|
||||
|
||||
```json
|
||||
// In levels/Wicked Christmas/1.json - Sets theme for entire pack
|
||||
{
|
||||
"level_id": 1,
|
||||
"weapon_sound_overrides": {
|
||||
"rusty_shovel": { "name": "rusty snow shovel" },
|
||||
"witch_broom": { "name": "snow broom" },
|
||||
"nunchucks": { "name": "ice sickles" }
|
||||
}
|
||||
}
|
||||
|
||||
// In levels/Wicked Christmas/2.json - No overrides needed!
|
||||
{
|
||||
"level_id": 2,
|
||||
// Weapons automatically use themed names from level 1
|
||||
}
|
||||
```
|
||||
|
||||
**How it works:**
|
||||
- Level 1 overrides persist across all levels in the same play session
|
||||
- Switching to a different level pack resets weapons to their original names
|
||||
- Later levels can still override weapons if you want to change themes mid-pack
|
||||
- The system stores the original weapon names internally for proper lookups
|
||||
|
||||
#### Object Sound Overrides
|
||||
|
||||
Override sounds for individual objects in your level:
|
||||
|
||||
```json
|
||||
{
|
||||
"x": 25,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fire_interval": 3000,
|
||||
"range": 30,
|
||||
"sound_overrides": {
|
||||
"base": "snowball_launcher",
|
||||
"launch": "snowball_launcher_launch"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"x_range": [40, 60],
|
||||
"y": 0,
|
||||
"type": "grasping_hands",
|
||||
"delay": 1000,
|
||||
"sound_overrides": {
|
||||
"base": "avalanche"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"x": 35,
|
||||
"y": 0,
|
||||
"type": "grave",
|
||||
"item": "shin_bone",
|
||||
"sound_overrides": {
|
||||
"base": "snow_mound",
|
||||
"item": "candy_cane"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Object Sound Override Properties:**
|
||||
- `base`: Override the main ambient sound (e.g., "catapult" → "snowball_launcher")
|
||||
- `launch`: Override launch sound for catapults (e.g., "catapult_launch" → "snowball_launcher_launch")
|
||||
- `item`: Override pickup sound for grave items (e.g., "get_shin_bone.ogg" → "get_candy_cane.ogg")
|
||||
- `warning_message`: Override warning message for grasping hands (e.g., "The ground crumbles as snow begins to avalanche!")
|
||||
- `death_message`: Override death message for grasping hands (e.g., "You vanish under tons of snow!")
|
||||
|
||||
#### Themed Item Equivalents
|
||||
|
||||
The sound override system includes intelligent item mapping for crafting consistency. Certain themed items automatically behave like their original counterparts:
|
||||
|
||||
**Christmas Theme:**
|
||||
- `"candy_cane"` → Functions as `"shin_bone"` (increments shin bone count)
|
||||
- `"reindeer_guts"` → Functions as `"guts"` (enables nunchucks crafting)
|
||||
|
||||
**Result:** Collecting 2 candy canes + reindeer guts = nunchucks (can be renamed to "ice sickles")
|
||||
|
||||
This system allows complete thematic consistency where players collect "2 Candy Canes + Reindeer Guts = Ice Sickles" while preserving all original game mechanics. The mapping works automatically across any level pack - simply use themed item names and they'll function correctly.
|
||||
|
||||
**Adding New Themed Equivalents:**
|
||||
To add your own themed items, modify the `themed_mappings` in `src/powerup.py`:
|
||||
```python
|
||||
themed_mappings = {
|
||||
"your_bone_item": "shin_bone",
|
||||
"your_guts_item": "guts",
|
||||
}
|
||||
```
|
||||
|
||||
#### Complete Thematic Example
|
||||
|
||||
Here's how to transform a Halloween level into a Christmas level using sound overrides:
|
||||
|
||||
```json
|
||||
{
|
||||
"level_id": 1,
|
||||
"name": "Winter Siege",
|
||||
"description": "Santa's workshop is under attack by snow witches!",
|
||||
"weapon_sound_overrides": {
|
||||
"rusty_shovel": {
|
||||
"name": "snow shovel",
|
||||
"attack_sound": "player_snow_shovel_attack",
|
||||
"hit_sound": "player_snow_shovel_hit"
|
||||
}
|
||||
},
|
||||
"objects": [
|
||||
{
|
||||
"x": 25,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"sound_overrides": {
|
||||
"base": "snowball_launcher",
|
||||
"launch": "snowball_launcher_launch"
|
||||
}
|
||||
},
|
||||
{
|
||||
"x_range": [40, 60],
|
||||
"y": 12,
|
||||
"type": "skull_storm",
|
||||
"sound_overrides": {
|
||||
"base": "snowball_storm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"x": 75,
|
||||
"y": 0,
|
||||
"type": "grave",
|
||||
"item": "shin_bone",
|
||||
"sound_overrides": {
|
||||
"item": "candy_cane"
|
||||
}
|
||||
},
|
||||
{
|
||||
"x_range": [90, 110],
|
||||
"y": 0,
|
||||
"type": "grasping_hands",
|
||||
"sound_overrides": {
|
||||
"base": "avalanche",
|
||||
"warning_message": "The ground crumbles as snow begins to avalanche!",
|
||||
"death_message": "You vanish under tons of snow!"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Result:**
|
||||
- Weapons sound winter-themed when attacking
|
||||
- "Catapult" becomes "Snowball Launcher" with appropriate launch sounds
|
||||
- "Skull Storm" becomes "Snowball Storm"
|
||||
- "Graves" containing "Candy Canes" instead of "Shin Bones" (sound override only - base grave sound unchanged)
|
||||
- "Grasping Hands" becomes "Avalanche" with snow-themed death messages
|
||||
- All mechanics remain identical - only audio and messaging changes
|
||||
|
||||
#### Legacy Sound Override System
|
||||
- **Custom ambience:** Place in `sounds/[Pack Name]/ambience/`
|
||||
- **Custom enemy sounds:** Place in `sounds/[Pack Name]/`
|
||||
- **Custom footsteps:** Reference in level JSON as `"footstep_sound"`
|
||||
- **Ending scene:** Add `end.ogg` in the level pack directory
|
||||
|
||||
This legacy system allows complete audio customization through file replacement. For example, skull storms could become firestorms just by replacing the skull storm sounds in your pack's sound directory.
|
||||
|
||||
## Complete Example Level
|
||||
|
||||
Here's a comprehensive example showing multiple advanced features:
|
||||
|
||||
```json
|
||||
{
|
||||
"level_id": 5,
|
||||
"name": "The Witch's Domain",
|
||||
"dialog": {
|
||||
"allow_skip": true,
|
||||
"entries": [
|
||||
{
|
||||
"speaker": "Ancient Spirit",
|
||||
"text": "Mortal skeleton, you dare enter the witch's sacred domain?",
|
||||
"sound": "ancient_spirit"
|
||||
},
|
||||
{
|
||||
"speaker": "Billy Bones",
|
||||
"text": "I'm looking for trouble, and I think I found it."
|
||||
},
|
||||
{
|
||||
"text": "The air grows thick with dark magic as ancient runes begin to glow.",
|
||||
"narrative": true,
|
||||
"sound": "magic_ambience"
|
||||
},
|
||||
{
|
||||
"speaker": "Ancient Spirit",
|
||||
"text": "Then face the consequences of your boldness. The witch's minions will not show mercy.",
|
||||
"sound": "evil_laughter"
|
||||
},
|
||||
{
|
||||
"speaker": "Billy Bones",
|
||||
"text": "Good thing I don't need any."
|
||||
}
|
||||
]
|
||||
},
|
||||
"locked": true,
|
||||
"player_start": {"x": 0, "y": 0},
|
||||
"boundaries": {"left": 0, "right": 300},
|
||||
"ambience": "witch_theme.ogg",
|
||||
"footstep_sound": "footstep_mud",
|
||||
"objects": [
|
||||
{
|
||||
"x_range": [5, 15],
|
||||
"y": 3,
|
||||
"sound": "bone_dust",
|
||||
"collectible": true,
|
||||
"static": true
|
||||
},
|
||||
{
|
||||
"x": 25,
|
||||
"y": 3,
|
||||
"type": "coffin",
|
||||
"item": "hand_of_glory"
|
||||
},
|
||||
{
|
||||
"x": 40,
|
||||
"y": 0,
|
||||
"type": "grave",
|
||||
"zombie_spawn_chance": 50,
|
||||
"item": "guts"
|
||||
},
|
||||
{
|
||||
"x_range": [60, 80],
|
||||
"y": 0,
|
||||
"type": "grasping_hands",
|
||||
"delay": 500,
|
||||
"crumble_speed": 0.08
|
||||
},
|
||||
{
|
||||
"x": 100,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 3000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
"x_range": [120, 140],
|
||||
"y": 12,
|
||||
"type": "skull_storm",
|
||||
"damage": 3,
|
||||
"maximum_skulls": 3,
|
||||
"frequency": {"min": 2, "max": 5}
|
||||
},
|
||||
{
|
||||
"x_range": [200, 250],
|
||||
"y": 0,
|
||||
"enemy_type": "ghost",
|
||||
"health": 40,
|
||||
"damage": 2,
|
||||
"attack_range": 1.5,
|
||||
"has_vulnerability": true,
|
||||
"vulnerability_duration": 2000,
|
||||
"invulnerability_duration": 4000,
|
||||
"can_spawn": true,
|
||||
"spawn_type": "zombie",
|
||||
"spawn_cooldown": 3000,
|
||||
"spawn_chance": 60,
|
||||
"spawn_distance": 4,
|
||||
"attack_pattern": {"type": "hunter", "turn_threshold": 3}
|
||||
},
|
||||
{
|
||||
"x_range": [260, 290],
|
||||
"y": 0,
|
||||
"enemy_type": "witch",
|
||||
"health": 60,
|
||||
"damage": 3,
|
||||
"attack_range": 2,
|
||||
"can_spawn": true,
|
||||
"spawn_type": "black_cat",
|
||||
"spawn_cooldown": 4000,
|
||||
"spawn_chance": 80,
|
||||
"spawn_distance": 6,
|
||||
"attack_pattern": {"type": "patrol"}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
This example demonstrates:
|
||||
- **Environmental hazards** (grasping hands, skull storm, catapult)
|
||||
- **Interactive elements** (coffin with invincibility, grave with health)
|
||||
- **Advanced enemies** (vulnerable zombie-spawning ghost, cat-spawning witch)
|
||||
- **Custom audio** (themed ambience and footsteps)
|
||||
- **Strategic design** (safe zones, risk/reward placement)
|
||||
|
||||
Check out the existing Wicked Quest levels for more examples and inspiration!
|
||||
- Lock boss levels with "locked": true
|
||||
- Test your levels thoroughly!
|
||||
|
||||
Check out the Wicked Quest levels for more examples.
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
"x": 135,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 4000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -254,7 +254,7 @@
|
||||
"x": 310,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 4000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 65,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 75,
|
||||
@@ -130,7 +130,7 @@
|
||||
"x": 155,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 4000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -143,7 +143,7 @@
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 180,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 190,
|
||||
@@ -265,7 +265,7 @@
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 355,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x_range": [365, 380],
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"x": 50,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 3000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -38,7 +38,7 @@
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 70,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x_range": [75, 85],
|
||||
@@ -55,7 +55,7 @@
|
||||
"x": 100,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 3000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -85,7 +85,7 @@
|
||||
"x": 150,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 3000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -117,7 +117,7 @@
|
||||
"x": 200,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 3000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -142,13 +142,13 @@
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 220,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 250,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 3000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -169,7 +169,7 @@
|
||||
"x": 300,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 3000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -194,7 +194,7 @@
|
||||
"x": 350,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 3000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -220,7 +220,7 @@
|
||||
"x": 400,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 3000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -245,7 +245,7 @@
|
||||
"x": 450,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 3000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -266,7 +266,7 @@
|
||||
"x": 500,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 3000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
|
||||
@@ -26,13 +26,13 @@
|
||||
"x": 50,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 3000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 35,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x_range": [55, 60],
|
||||
@@ -54,7 +54,7 @@
|
||||
"x": 100,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 3000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -91,13 +91,13 @@
|
||||
"x": 150,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 3000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 135,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x_range": [155, 160],
|
||||
@@ -110,7 +110,7 @@
|
||||
"x": 200,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 3000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -154,7 +154,7 @@
|
||||
"x": 300,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 3000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -174,13 +174,13 @@
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 335,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 350,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 3000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -202,7 +202,7 @@
|
||||
"x": 400,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 3000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -241,7 +241,7 @@
|
||||
"x": 500,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 3000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -254,13 +254,13 @@
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 525,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 550,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 3000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -281,7 +281,7 @@
|
||||
"x": 600,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 3000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
"x": 120,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 5000,
|
||||
"range": 15
|
||||
},
|
||||
{
|
||||
|
||||
@@ -229,7 +229,7 @@
|
||||
"x": 175,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 4500,
|
||||
"range": 20
|
||||
},
|
||||
{
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
"x": 90,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 4500,
|
||||
"range": 20
|
||||
},
|
||||
{
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
"x": 55,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 4000,
|
||||
"range": 25
|
||||
},
|
||||
{
|
||||
@@ -198,7 +198,7 @@
|
||||
"x": 175,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 4000,
|
||||
"range": 25
|
||||
},
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 15,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x_range": [20, 35],
|
||||
@@ -75,7 +75,7 @@
|
||||
"x": 60,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 4000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -99,7 +99,7 @@
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 90,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x_range": [95, 98],
|
||||
@@ -194,7 +194,7 @@
|
||||
"x": 175,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 4000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -214,7 +214,7 @@
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 205,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x_range": [215, 230],
|
||||
@@ -281,7 +281,7 @@
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 290,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x_range": [295, 298],
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 10,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 15,
|
||||
@@ -57,7 +57,7 @@
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 40,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 45,
|
||||
@@ -126,7 +126,7 @@
|
||||
"x": 100,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 4000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -147,7 +147,7 @@
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 120,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x_range": [125, 145],
|
||||
@@ -196,7 +196,7 @@
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 175,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x_range": [180, 200],
|
||||
@@ -256,13 +256,13 @@
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 220,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 225,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 4000,
|
||||
"range": 20
|
||||
},
|
||||
{
|
||||
@@ -293,7 +293,7 @@
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 255,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x_range": [260, 275],
|
||||
@@ -334,7 +334,7 @@
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 305,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x_range": [310, 325],
|
||||
@@ -371,7 +371,7 @@
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 355,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x_range": [360, 375],
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
"x": 115,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 4000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -217,7 +217,7 @@
|
||||
"x": 270,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 4000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 15,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x_range": [25, 45],
|
||||
@@ -111,13 +111,13 @@
|
||||
{
|
||||
"type": "spider_web",
|
||||
"x": 140,
|
||||
"y": 1
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 150,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 4000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
@@ -244,7 +244,7 @@
|
||||
"x": 330,
|
||||
"y": 0,
|
||||
"type": "catapult",
|
||||
"fireInterval": 5000,
|
||||
"fire_interval": 4000,
|
||||
"range": 30
|
||||
},
|
||||
{
|
||||
|
||||
Submodule libstormgames updated: 0493edf8e6...0190fa3a06
BIN
sounds/_edge.ogg
LFS
BIN
sounds/_edge.ogg
LFS
Binary file not shown.
BIN
sounds/_finish_level.ogg
LFS
BIN
sounds/_finish_level.ogg
LFS
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
sounds/black_cat.ogg
LFS
BIN
sounds/black_cat.ogg
LFS
Binary file not shown.
BIN
sounds/black_cat_dies.ogg
LFS
BIN
sounds/black_cat_dies.ogg
LFS
Binary file not shown.
BIN
sounds/bone_dust.ogg
LFS
BIN
sounds/bone_dust.ogg
LFS
Binary file not shown.
BIN
sounds/boogie_man.ogg
LFS
BIN
sounds/boogie_man.ogg
LFS
Binary file not shown.
BIN
sounds/boogie_man_dies.ogg
LFS
BIN
sounds/boogie_man_dies.ogg
LFS
Binary file not shown.
BIN
sounds/catapult.ogg
LFS
BIN
sounds/catapult.ogg
LFS
Binary file not shown.
BIN
sounds/catapult_launch.ogg
LFS
BIN
sounds/catapult_launch.ogg
LFS
Binary file not shown.
BIN
sounds/coffin.ogg
LFS
BIN
sounds/coffin.ogg
LFS
Binary file not shown.
BIN
sounds/coffin_shatter.ogg
LFS
BIN
sounds/coffin_shatter.ogg
LFS
Binary file not shown.
BIN
sounds/dialogue.ogg
LFS
BIN
sounds/dialogue.ogg
LFS
Binary file not shown.
BIN
sounds/duck.ogg
LFS
BIN
sounds/duck.ogg
LFS
Binary file not shown.
BIN
sounds/edge.ogg
LFS
BIN
sounds/edge.ogg
LFS
Binary file not shown.
Binary file not shown.
BIN
sounds/end_of_level.ogg
LFS
BIN
sounds/end_of_level.ogg
LFS
Binary file not shown.
BIN
sounds/falling_skull1.ogg
LFS
BIN
sounds/falling_skull1.ogg
LFS
Binary file not shown.
BIN
sounds/falling_skull2.ogg
LFS
BIN
sounds/falling_skull2.ogg
LFS
Binary file not shown.
BIN
sounds/footstep.ogg
LFS
BIN
sounds/footstep.ogg
LFS
Binary file not shown.
BIN
sounds/footstep_dirt_road.ogg
LFS
BIN
sounds/footstep_dirt_road.ogg
LFS
Binary file not shown.
BIN
sounds/footstep_stone.ogg
LFS
BIN
sounds/footstep_stone.ogg
LFS
Binary file not shown.
BIN
sounds/footstep_tall_grass.ogg
LFS
BIN
sounds/footstep_tall_grass.ogg
LFS
Binary file not shown.
BIN
sounds/game_over.ogg
LFS
BIN
sounds/game_over.ogg
LFS
Binary file not shown.
BIN
sounds/get_bone_dust.ogg
LFS
BIN
sounds/get_bone_dust.ogg
LFS
Binary file not shown.
BIN
sounds/get_cauldron.ogg
LFS
BIN
sounds/get_cauldron.ogg
LFS
Binary file not shown.
BIN
sounds/get_extra_life.ogg
LFS
BIN
sounds/get_extra_life.ogg
LFS
Binary file not shown.
BIN
sounds/get_guts.ogg
LFS
BIN
sounds/get_guts.ogg
LFS
Binary file not shown.
BIN
sounds/get_hand_of_glory.ogg
LFS
BIN
sounds/get_hand_of_glory.ogg
LFS
Binary file not shown.
BIN
sounds/get_jack_o_lantern.ogg
LFS
BIN
sounds/get_jack_o_lantern.ogg
LFS
Binary file not shown.
BIN
sounds/get_nunchucks.ogg
LFS
BIN
sounds/get_nunchucks.ogg
LFS
Binary file not shown.
BIN
sounds/get_shin_bone.ogg
LFS
BIN
sounds/get_shin_bone.ogg
LFS
Binary file not shown.
BIN
sounds/get_witch_broom.ogg
LFS
BIN
sounds/get_witch_broom.ogg
LFS
Binary file not shown.
BIN
sounds/ghost_is_vulnerable.ogg
LFS
BIN
sounds/ghost_is_vulnerable.ogg
LFS
Binary file not shown.
BIN
sounds/ghoul.ogg
LFS
BIN
sounds/ghoul.ogg
LFS
Binary file not shown.
BIN
sounds/ghoul_dies.ogg
LFS
BIN
sounds/ghoul_dies.ogg
LFS
Binary file not shown.
BIN
sounds/goblin.ogg
LFS
BIN
sounds/goblin.ogg
LFS
Binary file not shown.
BIN
sounds/goblin_dies.ogg
LFS
BIN
sounds/goblin_dies.ogg
LFS
Binary file not shown.
BIN
sounds/grasping_hands.ogg
LFS
BIN
sounds/grasping_hands.ogg
LFS
Binary file not shown.
BIN
sounds/grasping_hands_end.ogg
LFS
BIN
sounds/grasping_hands_end.ogg
LFS
Binary file not shown.
BIN
sounds/grasping_hands_start.ogg
LFS
BIN
sounds/grasping_hands_start.ogg
LFS
Binary file not shown.
BIN
sounds/grave.ogg
LFS
BIN
sounds/grave.ogg
LFS
Binary file not shown.
BIN
sounds/headless_horseman.ogg
LFS
BIN
sounds/headless_horseman.ogg
LFS
Binary file not shown.
Binary file not shown.
BIN
sounds/hit_spiderweb.ogg
LFS
BIN
sounds/hit_spiderweb.ogg
LFS
Binary file not shown.
BIN
sounds/item_bounce.ogg
LFS
BIN
sounds/item_bounce.ogg
LFS
Binary file not shown.
BIN
sounds/jump.ogg
LFS
BIN
sounds/jump.ogg
LFS
Binary file not shown.
BIN
sounds/lose_a_life.ogg
LFS
BIN
sounds/lose_a_life.ogg
LFS
Binary file not shown.
BIN
sounds/menu-move.ogg
LFS
BIN
sounds/menu-move.ogg
LFS
Binary file not shown.
BIN
sounds/menu-select.ogg
LFS
BIN
sounds/menu-select.ogg
LFS
Binary file not shown.
BIN
sounds/music_menu.ogg
LFS
BIN
sounds/music_menu.ogg
LFS
Binary file not shown.
BIN
sounds/player_broom_attack.ogg
LFS
BIN
sounds/player_broom_attack.ogg
LFS
Binary file not shown.
BIN
sounds/player_broom_hit.ogg
LFS
BIN
sounds/player_broom_hit.ogg
LFS
Binary file not shown.
Binary file not shown.
BIN
sounds/player_nunchuck_hit.ogg
LFS
BIN
sounds/player_nunchuck_hit.ogg
LFS
Binary file not shown.
BIN
sounds/player_shovel_attack.ogg
LFS
BIN
sounds/player_shovel_attack.ogg
LFS
Binary file not shown.
BIN
sounds/player_shovel_hit.ogg
LFS
BIN
sounds/player_shovel_hit.ogg
LFS
Binary file not shown.
BIN
sounds/player_takes_damage.ogg
LFS
BIN
sounds/player_takes_damage.ogg
LFS
Binary file not shown.
BIN
sounds/pumpkin_high.ogg
LFS
BIN
sounds/pumpkin_high.ogg
LFS
Binary file not shown.
BIN
sounds/pumpkin_low.ogg
LFS
BIN
sounds/pumpkin_low.ogg
LFS
Binary file not shown.
BIN
sounds/pumpkin_splat.ogg
LFS
BIN
sounds/pumpkin_splat.ogg
LFS
Binary file not shown.
BIN
sounds/revenant.ogg
LFS
BIN
sounds/revenant.ogg
LFS
Binary file not shown.
BIN
sounds/revenant_dies.ogg
LFS
BIN
sounds/revenant_dies.ogg
LFS
Binary file not shown.
BIN
sounds/save.ogg
LFS
BIN
sounds/save.ogg
LFS
Binary file not shown.
BIN
sounds/skull_lands.ogg
LFS
BIN
sounds/skull_lands.ogg
LFS
Binary file not shown.
BIN
sounds/skull_storm.ogg
LFS
BIN
sounds/skull_storm.ogg
LFS
Binary file not shown.
BIN
sounds/skull_storm_ends.ogg
LFS
BIN
sounds/skull_storm_ends.ogg
LFS
Binary file not shown.
BIN
sounds/spider.ogg
LFS
BIN
sounds/spider.ogg
LFS
Binary file not shown.
BIN
sounds/spider_dies.ogg
LFS
BIN
sounds/spider_dies.ogg
LFS
Binary file not shown.
BIN
sounds/spiderweb.ogg
LFS
BIN
sounds/spiderweb.ogg
LFS
Binary file not shown.
BIN
sounds/stand.ogg
LFS
BIN
sounds/stand.ogg
LFS
Binary file not shown.
BIN
sounds/survivor_bonus.ogg
LFS
BIN
sounds/survivor_bonus.ogg
LFS
Binary file not shown.
BIN
sounds/throw_jack_o_lantern.ogg
LFS
BIN
sounds/throw_jack_o_lantern.ogg
LFS
Binary file not shown.
BIN
sounds/witch.ogg
LFS
BIN
sounds/witch.ogg
LFS
Binary file not shown.
BIN
sounds/witch_dies.ogg
LFS
BIN
sounds/witch_dies.ogg
LFS
Binary file not shown.
BIN
sounds/zombie.ogg
LFS
BIN
sounds/zombie.ogg
LFS
Binary file not shown.
BIN
sounds/zombie_dies.ogg
LFS
BIN
sounds/zombie_dies.ogg
LFS
Binary file not shown.
@@ -6,7 +6,7 @@ import random
|
||||
|
||||
|
||||
class Pumpkin:
|
||||
def __init__(self, x, isHigh, direction, playerMaxHealth, soundOverrides=None):
|
||||
def __init__(self, x, isHigh, direction, playerMaxHealth):
|
||||
self.x = x
|
||||
self.isHigh = isHigh
|
||||
self.direction = direction
|
||||
@@ -14,17 +14,7 @@ class Pumpkin:
|
||||
self.isActive = True
|
||||
self.damage = playerMaxHealth // 2 # Half of player's max health
|
||||
self.soundChannel = None
|
||||
|
||||
# Apply sound overrides if provided
|
||||
if soundOverrides:
|
||||
if isHigh and "pumpkin_high" in soundOverrides:
|
||||
self.soundName = soundOverrides["pumpkin_low"] # Still inverted for overrides
|
||||
elif not isHigh and "pumpkin_low" in soundOverrides:
|
||||
self.soundName = soundOverrides["pumpkin_high"] # Still inverted for overrides
|
||||
else:
|
||||
self.soundName = "pumpkin_low" if isHigh else "pumpkin_high" # Inverted mapping
|
||||
else:
|
||||
self.soundName = "pumpkin_low" if isHigh else "pumpkin_high" # Inverted mapping
|
||||
self.soundName = "pumpkin_low" if isHigh else "pumpkin_high" # Inverted mapping
|
||||
|
||||
def update(self, sounds, playerX):
|
||||
"""Update pumpkin position and sound"""
|
||||
@@ -83,24 +73,20 @@ class Catapult(Object):
|
||||
self.launchDelay = 900 # Time between launch sound and pumpkin firing
|
||||
self.pendingPumpkin = None # Store pending pumpkin data
|
||||
self.pumpkinLaunchTime = 0 # When to launch the pending pumpkin
|
||||
self.launchSound = "catapult_launch" # Configurable launch sound
|
||||
self.soundOverrides = {} # Store sound overrides for pumpkins
|
||||
self.activateMessage = "Pumpkin catapult activates!"
|
||||
self.deactivateMessage = "Out of pumpkin catapult range."
|
||||
|
||||
def fire(self, currentTime, player):
|
||||
"""Start the firing sequence"""
|
||||
self.lastFireTime = currentTime
|
||||
|
||||
# Play launch sound using directional audio
|
||||
play_directional_sound(self.sounds, self.launchSound, player.xPos, self.xPos)
|
||||
play_directional_sound(self.sounds, "catapult_launch", player.xPos, self.xPos)
|
||||
|
||||
# Set up pending pumpkin
|
||||
isHigh = random.choice([True, False])
|
||||
fireDirection = 1 if player.xPos > self.xPos else -1
|
||||
|
||||
# Store pumpkin data for later creation
|
||||
self.pendingPumpkin = {"isHigh": isHigh, "direction": fireDirection, "playerMaxHealth": player.get_max_health(), "soundOverrides": self.soundOverrides}
|
||||
self.pendingPumpkin = {"isHigh": isHigh, "direction": fireDirection, "playerMaxHealth": player.get_max_health()}
|
||||
|
||||
# Set when to actually launch the pumpkin
|
||||
self.pumpkinLaunchTime = currentTime + self.launchDelay
|
||||
@@ -118,10 +104,10 @@ class Catapult(Object):
|
||||
if inRange and not self.isFiring:
|
||||
self.isFiring = True
|
||||
self.lastFireTime = currentTime # Reset timer when entering range
|
||||
speak(self.activateMessage)
|
||||
speak("Pumpkin catapult activates!")
|
||||
elif not inRange and self.isFiring:
|
||||
self.isFiring = False
|
||||
speak(self.deactivateMessage)
|
||||
speak("Out of pumpkin catapult range.")
|
||||
|
||||
# Check for pending pumpkin launch
|
||||
if self.pendingPumpkin and currentTime >= self.pumpkinLaunchTime:
|
||||
@@ -131,7 +117,6 @@ class Catapult(Object):
|
||||
self.pendingPumpkin["isHigh"],
|
||||
self.pendingPumpkin["direction"],
|
||||
self.pendingPumpkin["playerMaxHealth"],
|
||||
self.pendingPumpkin["soundOverrides"],
|
||||
)
|
||||
self.activePumpkins.append(pumpkin)
|
||||
self.pendingPumpkin = None
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user