Catapults working. At this point most of the basic game is set up, now I have to get down to level and enemy creation, and sounds for when things happen.
This commit is contained in:
@@ -120,6 +120,14 @@
|
|||||||
"sound": "grave",
|
"sound": "grave",
|
||||||
"static": true,
|
"static": true,
|
||||||
"zombie_spawn_chance": 30
|
"zombie_spawn_chance": 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 145,
|
||||||
|
"y": 0,
|
||||||
|
"type": "catapult",
|
||||||
|
"direction": -1,
|
||||||
|
"fire_interval": 5000,
|
||||||
|
"range": 15
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"boundaries": {
|
"boundaries": {
|
||||||
|
BIN
sounds/catapult.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/catapult.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/catapult_launch.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/catapult_launch.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/pumpkin_high.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/pumpkin_high.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/pumpkin_low.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/pumpkin_low.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
137
src/catapult.py
Normal file
137
src/catapult.py
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
from libstormgames import *
|
||||||
|
from src.object import Object
|
||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
class Pumpkin:
|
||||||
|
def __init__(self, x, isHigh, direction, playerMaxHealth):
|
||||||
|
self.x = x
|
||||||
|
self.isHigh = isHigh
|
||||||
|
self.direction = direction # 1 for right, -1 for left
|
||||||
|
self.speed = 0.15
|
||||||
|
self.isActive = True
|
||||||
|
self.damage = playerMaxHealth // 2 # Half of player's max health
|
||||||
|
self.soundChannel = None
|
||||||
|
self.soundName = 'pumpkin_low' if isHigh else 'pumpkin_high' # Inverted mapping
|
||||||
|
|
||||||
|
def update(self, sounds, playerX):
|
||||||
|
"""Update pumpkin position and sound"""
|
||||||
|
if not self.isActive:
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.x += self.direction * self.speed
|
||||||
|
|
||||||
|
# Update or start positional audio
|
||||||
|
if self.soundChannel is None or not self.soundChannel.get_busy():
|
||||||
|
self.soundChannel = obj_play(sounds, self.soundName, playerX, self.x)
|
||||||
|
else:
|
||||||
|
self.soundChannel = obj_update(self.soundChannel, playerX, self.x)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def stop_sound(self):
|
||||||
|
"""Stop the pumpkin's sound"""
|
||||||
|
if self.soundChannel:
|
||||||
|
obj_stop(self.soundChannel)
|
||||||
|
self.soundChannel = None
|
||||||
|
|
||||||
|
def check_collision(self, player):
|
||||||
|
"""Check if pumpkin hits player"""
|
||||||
|
if not self.isActive:
|
||||||
|
return False
|
||||||
|
|
||||||
|
distance = abs(player.xPos - self.x)
|
||||||
|
if distance < 1: # Within 1 tile
|
||||||
|
if self.isHigh and not player.isJumping:
|
||||||
|
return True # Hit by high pumpkin while on ground
|
||||||
|
elif not self.isHigh and player.isJumping:
|
||||||
|
return True # Hit by low pumpkin while jumping
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class Catapult(Object):
|
||||||
|
def __init__(self, x, y, sounds, direction=1, fireInterval=5000, firingRange=20):
|
||||||
|
super().__init__(
|
||||||
|
x, y, "catapult",
|
||||||
|
isStatic=True,
|
||||||
|
isCollectible=False,
|
||||||
|
)
|
||||||
|
self.sounds = sounds
|
||||||
|
self.direction = direction
|
||||||
|
self.fireInterval = fireInterval # Time between shots in milliseconds
|
||||||
|
self.firingRange = firingRange # How close player needs to be to trigger firing
|
||||||
|
self.lastFireTime = 0
|
||||||
|
self.activePumpkins = []
|
||||||
|
self.isFiring = False # Track if we're currently in firing mode
|
||||||
|
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
|
||||||
|
|
||||||
|
def fire(self, currentTime, player):
|
||||||
|
"""Start the firing sequence"""
|
||||||
|
self.lastFireTime = currentTime
|
||||||
|
|
||||||
|
# Play launch sound
|
||||||
|
self.sounds['catapult_launch'].play()
|
||||||
|
|
||||||
|
# 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set when to actually launch the pumpkin
|
||||||
|
self.pumpkinLaunchTime = currentTime + self.launchDelay
|
||||||
|
|
||||||
|
def update(self, currentTime, player):
|
||||||
|
"""Update catapult and its pumpkins"""
|
||||||
|
if not self.isActive:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check if player is in range
|
||||||
|
distance = abs(player.xPos - self.xPos)
|
||||||
|
inRange = distance <= self.firingRange
|
||||||
|
|
||||||
|
# Handle entering/leaving range
|
||||||
|
if inRange and not self.isFiring:
|
||||||
|
self.isFiring = True
|
||||||
|
self.lastFireTime = currentTime # Reset timer when entering range
|
||||||
|
speak("Pumpkin catapult activates!")
|
||||||
|
elif not inRange and self.isFiring:
|
||||||
|
self.isFiring = False
|
||||||
|
speak("Out of pumpkin catapult range.")
|
||||||
|
|
||||||
|
# Check for pending pumpkin launch
|
||||||
|
if self.pendingPumpkin and currentTime >= self.pumpkinLaunchTime:
|
||||||
|
# Create and fire the pending pumpkin
|
||||||
|
pumpkin = Pumpkin(
|
||||||
|
self.xPos,
|
||||||
|
self.pendingPumpkin['isHigh'],
|
||||||
|
self.pendingPumpkin['direction'],
|
||||||
|
self.pendingPumpkin['playerMaxHealth']
|
||||||
|
)
|
||||||
|
self.activePumpkins.append(pumpkin)
|
||||||
|
self.pendingPumpkin = None
|
||||||
|
|
||||||
|
# Only start new fire sequence if in range and enough time has passed
|
||||||
|
if self.isFiring and currentTime - self.lastFireTime >= self.fireInterval:
|
||||||
|
self.fire(currentTime, player)
|
||||||
|
|
||||||
|
# Always update existing pumpkins
|
||||||
|
for pumpkin in self.activePumpkins[:]: # Copy list to allow removal
|
||||||
|
if not pumpkin.update(self.sounds, player.xPos):
|
||||||
|
pumpkin.stop_sound()
|
||||||
|
self.activePumpkins.remove(pumpkin)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if pumpkin.check_collision(player):
|
||||||
|
player.set_health(player.get_health() - pumpkin.damage)
|
||||||
|
pumpkin.stop_sound()
|
||||||
|
pumpkin.isActive = False
|
||||||
|
self.activePumpkins.remove(pumpkin)
|
||||||
|
speak("Hit by a pumpkin!")
|
25
src/level.py
25
src/level.py
@@ -1,6 +1,8 @@
|
|||||||
import pygame
|
import pygame
|
||||||
import random
|
import random
|
||||||
from libstormgames import *
|
from libstormgames import *
|
||||||
|
from src.catapult import Catapult
|
||||||
|
from src.coffin import CoffinObject
|
||||||
from src.enemy import Enemy
|
from src.enemy import Enemy
|
||||||
from src.object import Object
|
from src.object import Object
|
||||||
from src.player import Player
|
from src.player import Player
|
||||||
@@ -18,7 +20,7 @@ class Level:
|
|||||||
self.player = Player(levelData["player_start"]["x"], levelData["player_start"]["y"])
|
self.player = Player(levelData["player_start"]["x"], levelData["player_start"]["y"])
|
||||||
self.edge_warning_channel = None
|
self.edge_warning_channel = None
|
||||||
self.weapon_hit_channel = None
|
self.weapon_hit_channel = None
|
||||||
|
|
||||||
# Load objects and enemies from level data
|
# Load objects and enemies from level data
|
||||||
for obj in levelData["objects"]:
|
for obj in levelData["objects"]:
|
||||||
# Handle x position or range
|
# Handle x position or range
|
||||||
@@ -26,9 +28,20 @@ class Level:
|
|||||||
xPos = obj["x_range"]
|
xPos = obj["x_range"]
|
||||||
else:
|
else:
|
||||||
xPos = [obj["x"], obj["x"]] # Single position as range
|
xPos = [obj["x"], obj["x"]] # Single position as range
|
||||||
|
|
||||||
|
# Check if this is a catapult
|
||||||
|
if obj.get("type") == "catapult":
|
||||||
|
catapult = Catapult(
|
||||||
|
xPos[0],
|
||||||
|
obj["y"],
|
||||||
|
self.sounds,
|
||||||
|
direction=obj.get("direction", 1),
|
||||||
|
fireInterval=obj.get("fireInterval", 5000),
|
||||||
|
firingRange=obj.get("range", 20)
|
||||||
|
)
|
||||||
|
self.objects.append(catapult)
|
||||||
# Check if this is an enemy
|
# Check if this is an enemy
|
||||||
if "enemy_type" in obj:
|
elif "enemy_type" in obj:
|
||||||
enemy = Enemy(
|
enemy = Enemy(
|
||||||
xPos,
|
xPos,
|
||||||
obj["y"],
|
obj["y"],
|
||||||
@@ -71,7 +84,6 @@ class Level:
|
|||||||
obj.has_spawned = True
|
obj.has_spawned = True
|
||||||
|
|
||||||
roll = random.randint(1, 100)
|
roll = random.randint(1, 100)
|
||||||
speak(f"Near grave, chance to spawn zombie")
|
|
||||||
if roll <= obj.zombie_spawn_chance:
|
if roll <= obj.zombie_spawn_chance:
|
||||||
zombie = Enemy(
|
zombie = Enemy(
|
||||||
[obj.xPos, obj.xPos],
|
[obj.xPos, obj.xPos],
|
||||||
@@ -108,6 +120,11 @@ class Level:
|
|||||||
if enemy.channel is not None:
|
if enemy.channel is not None:
|
||||||
enemy.channel = obj_update(enemy.channel, self.player.xPos, enemy.xPos)
|
enemy.channel = obj_update(enemy.channel, self.player.xPos, enemy.xPos)
|
||||||
|
|
||||||
|
# Update catapults
|
||||||
|
for obj in self.objects:
|
||||||
|
if isinstance(obj, Catapult):
|
||||||
|
obj.update(currentTime, self.player)
|
||||||
|
|
||||||
# Update bouncing items
|
# Update bouncing items
|
||||||
for item in self.bouncing_items[:]: # Copy list to allow removal
|
for item in self.bouncing_items[:]: # Copy list to allow removal
|
||||||
if not item.update(currentTime):
|
if not item.update(currentTime):
|
||||||
|
@@ -12,6 +12,7 @@ class Player:
|
|||||||
|
|
||||||
# Stats and tracking
|
# Stats and tracking
|
||||||
self._health = 10
|
self._health = 10
|
||||||
|
self._maxHealth = 10
|
||||||
self._lives = 1
|
self._lives = 1
|
||||||
self.distanceSinceLastStep = 0
|
self.distanceSinceLastStep = 0
|
||||||
self.stepDistance = 0.5
|
self.stepDistance = 0.5
|
||||||
@@ -92,6 +93,10 @@ class Player:
|
|||||||
"""Get current health"""
|
"""Get current health"""
|
||||||
return self._health
|
return self._health
|
||||||
|
|
||||||
|
def get_max_health(self):
|
||||||
|
"""Get current max health"""
|
||||||
|
return self._maxHealth
|
||||||
|
|
||||||
def set_health(self, value):
|
def set_health(self, value):
|
||||||
"""Set health and handle death if needed"""
|
"""Set health and handle death if needed"""
|
||||||
if self.isInvincible:
|
if self.isInvincible:
|
||||||
@@ -103,6 +108,10 @@ class Player:
|
|||||||
if self._lives > 0:
|
if self._lives > 0:
|
||||||
self._health = 10 # Reset health if we still have lives
|
self._health = 10 # Reset health if we still have lives
|
||||||
|
|
||||||
|
def set_max_health(self, value):
|
||||||
|
"""Set max health"""
|
||||||
|
self._maxHealth = value
|
||||||
|
|
||||||
def get_coins(self):
|
def get_coins(self):
|
||||||
"""Get remaining coins"""
|
"""Get remaining coins"""
|
||||||
return self._coins
|
return self._coins
|
||||||
|
Reference in New Issue
Block a user