Updated libstormgames submodule. Added skull storms. Fixed no jack-o-lanterns reported when there actually were. Fixed player being recreated on each new level thus resetting all stats.
This commit is contained in:
		
							
								
								
									
										112
									
								
								src/skull_storm.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								src/skull_storm.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | ||||
| from libstormgames import * | ||||
| from src.object import Object | ||||
|  | ||||
| class SkullStorm(Object): | ||||
|     """Handles falling skulls within a specified range.""" | ||||
|      | ||||
|     def __init__(self, xRange, y, sounds, damage, maxSkulls=3, minFreq=2, maxFreq=5): | ||||
|         super().__init__( | ||||
|             xRange, | ||||
|             y, | ||||
|             "",  # No ambient sound for the skull storm | ||||
|             isStatic=True, | ||||
|             isCollectible=False, | ||||
|             isHazard=False | ||||
|         ) | ||||
|         self.sounds = sounds | ||||
|         self.damage = damage | ||||
|         self.maxSkulls = maxSkulls | ||||
|         self.minFreq = minFreq * 1000  # Convert to milliseconds | ||||
|         self.maxFreq = maxFreq * 1000 | ||||
|          | ||||
|         self.activeSkulls = []  # List of currently falling skulls | ||||
|         self.lastSkullTime = 0 | ||||
|         self.nextSkullDelay = random.randint(self.minFreq, self.maxFreq) | ||||
|         self.playerInRange = False | ||||
|  | ||||
|     def update(self, currentTime, player): | ||||
|         """Update all active skulls and potentially spawn new ones.""" | ||||
|         if not self.isActive: | ||||
|             return | ||||
|          | ||||
|         # Check if player has entered range | ||||
|         inRange = self.xRange[0] <= player.xPos <= self.xRange[1] | ||||
|         if inRange and not self.playerInRange: | ||||
|             # Player just entered range - play the warning sound | ||||
|             self.sounds['skull_storm'].play() | ||||
|             self.playerInRange = True | ||||
|         elif not inRange and self.playerInRange:  # Only speak when actually leaving range | ||||
|             # Player just left range | ||||
|             self.playerInRange = False | ||||
|             speak("Skull storm ended.") | ||||
|              | ||||
|         if not inRange: | ||||
|             return | ||||
|              | ||||
|         # Update existing skulls | ||||
|         for skull in self.activeSkulls[:]:  # Copy list to allow removal | ||||
|             if currentTime >= skull['land_time']: | ||||
|                 # Skull has landed | ||||
|                 self.handle_landing(skull, player) | ||||
|                 self.activeSkulls.remove(skull) | ||||
|             else: | ||||
|                 # Update falling sound | ||||
|                 timeElapsed = currentTime - skull['start_time'] | ||||
|                 fallProgress = timeElapsed / skull['fall_duration'] | ||||
|                 currentY = self.yPos * (1 - fallProgress) | ||||
|                  | ||||
|                 if skull['channel'] is None or not skull['channel'].get_busy(): | ||||
|                     skull['channel'] = play_random_falling( | ||||
|                         self.sounds, | ||||
|                         'falling_skull', | ||||
|                         player.xPos, | ||||
|                         skull['x'], | ||||
|                         self.yPos, | ||||
|                         currentY | ||||
|                     ) | ||||
|          | ||||
|         # Check if we should spawn a new skull | ||||
|         if (len(self.activeSkulls) < self.maxSkulls and  | ||||
|             currentTime - self.lastSkullTime >= self.nextSkullDelay): | ||||
|             self.spawn_skull(currentTime) | ||||
|              | ||||
|     def spawn_skull(self, currentTime): | ||||
|         """Spawn a new falling skull at a random position within range.""" | ||||
|         # Reset timing | ||||
|         self.lastSkullTime = currentTime | ||||
|         self.nextSkullDelay = random.randint(self.minFreq, self.maxFreq) | ||||
|          | ||||
|         # Calculate fall duration based on height (higher = longer fall) | ||||
|         fallDuration = self.yPos * 100  # 100ms per unit of height | ||||
|          | ||||
|         # Create new skull | ||||
|         skull = { | ||||
|             'x': random.uniform(self.xRange[0], self.xRange[1]), | ||||
|             'start_time': currentTime, | ||||
|             'fall_duration': fallDuration, | ||||
|             'land_time': currentTime + fallDuration, | ||||
|             'channel': None | ||||
|         } | ||||
|  | ||||
|         self.activeSkulls.append(skull) | ||||
|  | ||||
|     def handle_landing(self, skull, player): | ||||
|         """Handle a skull landing.""" | ||||
|         # Stop falling sound | ||||
|         if skull['channel']: | ||||
|             obj_stop(skull['channel']) | ||||
|  | ||||
|         # Play landing sound with positional audio once | ||||
|         channel = pygame.mixer.find_channel(True)  # Find an available channel | ||||
|         if channel: | ||||
|             soundObj = self.sounds['skull_lands'] | ||||
|             channel.play(soundObj, 0)  # Play once (0 = no loops) | ||||
|             # Apply positional audio | ||||
|             volume, left, right = calculate_volume_and_pan(player.xPos, skull['x']) | ||||
|             channel.set_volume(volume * left, volume * right) | ||||
|  | ||||
|         # Check if player was hit | ||||
|         if abs(player.xPos - skull['x']) < 1:  # Within 1 tile | ||||
|             if not player.isJumping:  # Only hit if not jumping | ||||
|                 player.set_health(player.get_health() - self.damage) | ||||
|                 speak("Hit by falling skull!") | ||||
		Reference in New Issue
	
	Block a user