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:
		
							
								
								
									
										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 random | ||||
| from libstormgames import * | ||||
| from src.catapult import Catapult | ||||
| from src.coffin import CoffinObject | ||||
| from src.enemy import Enemy | ||||
| from src.object import Object | ||||
| from src.player import Player | ||||
| @@ -18,7 +20,7 @@ class Level: | ||||
|         self.player = Player(levelData["player_start"]["x"], levelData["player_start"]["y"]) | ||||
|         self.edge_warning_channel = None | ||||
|         self.weapon_hit_channel = None | ||||
|          | ||||
|      | ||||
|         # Load objects and enemies from level data | ||||
|         for obj in levelData["objects"]: | ||||
|             # Handle x position or range | ||||
| @@ -26,9 +28,20 @@ class Level: | ||||
|                 xPos = obj["x_range"] | ||||
|             else: | ||||
|                 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 | ||||
|             if "enemy_type" in obj: | ||||
|             elif "enemy_type" in obj: | ||||
|                 enemy = Enemy( | ||||
|                     xPos, | ||||
|                     obj["y"], | ||||
| @@ -71,7 +84,6 @@ class Level: | ||||
|                     obj.has_spawned = True | ||||
|                      | ||||
|                     roll = random.randint(1, 100) | ||||
|                     speak(f"Near grave, chance to spawn zombie") | ||||
|                     if roll <= obj.zombie_spawn_chance: | ||||
|                         zombie = Enemy( | ||||
|                             [obj.xPos, obj.xPos], | ||||
| @@ -108,6 +120,11 @@ class Level: | ||||
|             if enemy.channel is not None: | ||||
|                 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 | ||||
|         for item in self.bouncing_items[:]:  # Copy list to allow removal | ||||
|             if not item.update(currentTime): | ||||
|   | ||||
| @@ -12,6 +12,7 @@ class Player: | ||||
|          | ||||
|         # Stats and tracking | ||||
|         self._health = 10 | ||||
|         self._maxHealth = 10 | ||||
|         self._lives = 1 | ||||
|         self.distanceSinceLastStep = 0 | ||||
|         self.stepDistance = 0.5 | ||||
| @@ -92,6 +93,10 @@ class Player: | ||||
|         """Get current health""" | ||||
|         return self._health | ||||
|          | ||||
|     def get_max_health(self): | ||||
|         """Get current max health""" | ||||
|         return self._maxHealth | ||||
|          | ||||
|     def set_health(self, value): | ||||
|         """Set health and handle death if needed""" | ||||
|         if self.isInvincible: | ||||
| @@ -103,6 +108,10 @@ class Player: | ||||
|             if self._lives > 0: | ||||
|                 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): | ||||
|         """Get remaining coins""" | ||||
|         return self._coins | ||||
|   | ||||
		Reference in New Issue
	
	Block a user