add tip list

This commit is contained in:
Aron Petau 2025-10-12 08:54:30 +02:00
parent 7b9d7734ab
commit c733cf64e1
12 changed files with 598 additions and 187 deletions

View file

@ -1,32 +1,22 @@
# Simple top-down shooter with WASD player, arrow-key enemy, health bar, minimap, and raster background
import pygame, sys, numpy as np, rasterio, random
import sys
import random
import pygame
import numpy as np
import rasterio
from player import Player
from enemy import Enemy
from tapwater import TapWater
from settings import PLAYER_SIZE, MAP_PATH, WIDTH, HEIGHT, MINIMAP_MARGIN
DEBUG = False
pygame.init()
WIDTH, HEIGHT = 800, 600
# logo_img = pygame.image.load("images/logo.png")
# pygame.display.set_icon(logo_img)
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Water Bottle Shooter")
PLAYER_SIZE, PLAYER_SPEED = 40, 5
WATER_SIZE, WATER_SPEED = 10, 10
KILL_RADIUS = 30
MINIMAP_SIZE, MINIMAP_MARGIN = 200, 20
PLAYER_IMG_PATH = "images/player.png"
ENEMY_IMG_PATH = "images/enemy.png"
HUMAN_IMG_PATH = "images/human.png"
MAP_PATH = "results/s2_2025.tif"
# Load images
def load_img(path, size):
try:
img = pygame.image.load(path).convert_alpha()
return pygame.transform.scale(img, size)
except:
return None
PLAYER_IMAGE = load_img(PLAYER_IMG_PATH, (PLAYER_SIZE, PLAYER_SIZE))
ENEMY_IMAGE = load_img(ENEMY_IMG_PATH, (PLAYER_SIZE, PLAYER_SIZE))
HUMAN_IMAGE = load_img(HUMAN_IMG_PATH, (PLAYER_SIZE, PLAYER_SIZE))
# Load raster map
def load_map(path):
try:
arr = rasterio.open(path).read(1)
@ -34,156 +24,41 @@ def load_map(path):
arr = np.clip(arr, np.percentile(arr, 5), np.percentile(arr, 95))
arr = ((arr - arr.min()) / (arr.max() - arr.min()) * 255).astype(np.uint8)
arr = np.stack([arr, arr, arr], -1)
arr = np.rot90(arr, 3)
arr = np.fliplr(arr)
surf = pygame.surfarray.make_surface(arr)
return surf, arr.shape[1], arr.shape[0]
except Exception as e:
print(f"Map load error: {e}"); return None, WIDTH, HEIGHT
print(f"Map load error: {e}")
return None, WIDTH, HEIGHT
map_surface, MAP_W, MAP_H = load_map(MAP_PATH)
class Player:
def __init__(self, x, y):
self.x, self.y, self.alive = x, y, True
self.rect = pygame.Rect(x, y, PLAYER_SIZE, PLAYER_SIZE)
self.dir = "up"
def move(self, keys):
if not self.alive: return
dx = dy = 0
if keys[pygame.K_w]: dy -= PLAYER_SPEED; self.dir = "up"
if keys[pygame.K_s]: dy += PLAYER_SPEED; self.dir = "down"
if keys[pygame.K_a]: dx -= PLAYER_SPEED; self.dir = "left"
if keys[pygame.K_d]: dx += PLAYER_SPEED; self.dir = "right"
self.x = max(0, min(MAP_W-PLAYER_SIZE, self.x+dx))
self.y = max(0, min(MAP_H-PLAYER_SIZE, self.y+dy))
self.rect.topleft = (self.x, self.y)
def draw(self, win, ox, oy):
if not self.alive: return
img = PLAYER_IMAGE or None
angle = {"up": 90, "right": 0, "down": -90, "left": 180}[self.dir]
if img:
rotated_img = pygame.transform.rotate(img, angle)
win.blit(rotated_img, (self.x-ox, self.y-oy))
else: pygame.draw.rect(win, (200,200,0), (self.x-ox, self.y-oy, PLAYER_SIZE, PLAYER_SIZE))
class Enemy:
def __init__(self, x, y,controlled=False):
self.x, self.y = x, y
self.rect = pygame.Rect(x, y, PLAYER_SIZE, PLAYER_SIZE)
self.dir = "up"
self.max_health, self.health = 100, 100
self.is_human = False
self.controlled = controlled
def move(self, keys, player=None):
if self.is_human:
# Move randomly
if random.random() < 0.02:
self.dir = random.choice(["up", "down", "left", "right"])
dx = dy = 0
if self.dir == "up": dy -= PLAYER_SPEED//1.5
if self.dir == "down": dy += PLAYER_SPEED//2
if self.dir == "left": dx -= PLAYER_SPEED//2
if self.dir == "right": dx += PLAYER_SPEED//2
self.x = max(0, min(MAP_W-PLAYER_SIZE//2, self.x+dx))
self.y = max(0, min(MAP_H-PLAYER_SIZE//2, self.y+dy))
self.rect.topleft = (self.x, self.y)
elif self.controlled:
dx = dy = 0
if keys[pygame.K_UP]: dy -= PLAYER_SPEED//1.5; self.dir = "up"
if keys[pygame.K_DOWN]: dy += PLAYER_SPEED//1.5; self.dir = "down"
if keys[pygame.K_LEFT]: dx -= PLAYER_SPEED//1.5; self.dir = "left"
if keys[pygame.K_RIGHT]: dx += PLAYER_SPEED//1.5; self.dir = "right"
self.x = max(0, min(MAP_W-PLAYER_SIZE//2, self.x+dx))
self.y = max(0, min(MAP_H-PLAYER_SIZE//2, self.y+dy))
self.rect.topleft = (self.x, self.y)
else:
# Chase the player
if player and player.alive:
dx = player.x - self.x
dy = player.y - self.y
dist = max(1, (dx**2 + dy**2)**0.5)
speed = PLAYER_SPEED//2
self.x += int(speed * dx / dist)
self.y += int(speed * dy / dist)
self.x = max(0, min(MAP_W-PLAYER_SIZE//2, self.x))
self.y = max(0, min(MAP_H-PLAYER_SIZE//2, self.y))
self.rect.topleft = (self.x, self.y)
def draw(self, win, ox, oy):
enemy_img = ENEMY_IMAGE or None
human_img = HUMAN_IMAGE or None
color = (0, 128, 255) if self.is_human else (200, 200, 0)
if enemy_img and not self.is_human:
win.blit(enemy_img, (self.x-ox, self.y-oy))
elif human_img and self.is_human:
win.blit(human_img, (self.x-ox, self.y-oy))
else:
pygame.draw.rect(win, color, (self.x-ox, self.y-oy, PLAYER_SIZE//2, PLAYER_SIZE//2))
# Health bar (only for zombie)
if not self.is_human:
bw, bh = PLAYER_SIZE//2, 6
ratio = self.health/self.max_health
bx, by = self.x-ox, self.y-oy-bh-2
pygame.draw.rect(win, (255,0,0), (bx,by,bw,bh))
pygame.draw.rect(win, (0,255,0), (bx,by,int(bw*ratio),bh))
def take_damage(self, amt):
if not self.is_human:
self.health = max(0, self.health-amt)
if self.health == 0:
self.is_human = True
def check_kill_player(self, player):
if not self.is_human:
dx = (self.x+PLAYER_SIZE//4)-(player.x+PLAYER_SIZE//2)
dy = (self.y+PLAYER_SIZE//4)-(player.y+PLAYER_SIZE//2)
if (dx**2+dy**2)**0.5 < KILL_RADIUS: player.alive = False
class TapWater:
def __init__(self, x, y, dir):
self.x = x+PLAYER_SIZE//2-WATER_SIZE//2
self.y = y+PLAYER_SIZE//2-WATER_SIZE//2
self.dir = dir
self.rect = pygame.Rect(self.x, self.y, WATER_SIZE, WATER_SIZE)
def move(self):
if self.dir=="up": self.y -= WATER_SPEED
elif self.dir=="down": self.y += WATER_SPEED
elif self.dir=="left": self.x -= WATER_SPEED
elif self.dir=="right": self.x += WATER_SPEED
self.rect.topleft = (self.x, self.y)
def draw(self, win, ox, oy):
pygame.draw.rect(win, (0,180,255), (self.x-ox, self.y-oy, WATER_SIZE, WATER_SIZE))
MINIMAP_WIDTH = 200
MINIMAP_HEIGHT = int(MAP_H / MAP_W * MINIMAP_WIDTH)
def draw_minimap(win, map_surface, player, enemies, bottles):
mini = pygame.transform.smoothscale(map_surface, (MINIMAP_SIZE, MINIMAP_SIZE))
win.blit(mini, (WIDTH-MINIMAP_SIZE-MINIMAP_MARGIN, MINIMAP_MARGIN))
def map2mini(x,y):
mx = int(x/MAP_W*MINIMAP_SIZE)
my = int(y/MAP_H*MINIMAP_SIZE)
return WIDTH-MINIMAP_SIZE-MINIMAP_MARGIN+mx, MINIMAP_MARGIN+my
pygame.draw.circle(win, (0,255,0), map2mini(player.x+PLAYER_SIZE//2, player.y+PLAYER_SIZE//2), 5)
mini = pygame.transform.smoothscale(map_surface, (MINIMAP_WIDTH, MINIMAP_HEIGHT))
win.blit(mini, (WIDTH - MINIMAP_WIDTH - MINIMAP_MARGIN, MINIMAP_MARGIN))
def map2mini(x, y):
mx = int(x / MAP_W * MINIMAP_WIDTH)
my = int(y / MAP_H * MINIMAP_HEIGHT)
return WIDTH - MINIMAP_WIDTH - MINIMAP_MARGIN + mx, MINIMAP_MARGIN + my
pygame.draw.circle(win, (0, 255, 0), map2mini(player.x + PLAYER_SIZE // 2, player.y + PLAYER_SIZE // 2), 5)
for enemy in enemies:
color = (0,128,255) if enemy.is_human else (255,0,0) if enemy.controlled else (200,200,0)
ex, ey = map2mini(enemy.x+PLAYER_SIZE//4, enemy.y+PLAYER_SIZE//4)
color = (0, 128, 255) if enemy.is_human else (255, 0, 0) if enemy.controlled else (200, 200, 0)
ex, ey = map2mini(enemy.x + PLAYER_SIZE // 4, enemy.y + PLAYER_SIZE // 4)
pygame.draw.circle(win, color, (ex, ey), 5)
for b in bottles:
pygame.draw.circle(win, (0,180,255), map2mini(b.x, b.y), 3)
for bottle in bottles:
pygame.draw.circle(win, (0, 180, 255), map2mini(bottle.x, bottle.y), 3)
def main():
player = Player(MAP_W//2, MAP_H//2)
# Spawn 20 zombies, only the first is controlled
enemies = []
for i in range(20):
x = random.randint(0, MAP_W-PLAYER_SIZE)
y = random.randint(0, MAP_H-PLAYER_SIZE)
controlled = (i == 0)
enemies.append(Enemy(x, y, controlled=controlled))
from settings import PLAYER_IMG_PATH, ENEMY_IMG_PATH, HUMAN_IMG_PATH, PLAYER_SIZE, load_img
player_img = load_img(PLAYER_IMG_PATH, (PLAYER_SIZE, PLAYER_SIZE))
enemy_img = load_img(ENEMY_IMG_PATH, (PLAYER_SIZE, PLAYER_SIZE))
human_img = load_img(HUMAN_IMG_PATH, (PLAYER_SIZE, PLAYER_SIZE))
player = Player(MAP_W // 2, MAP_H // 2, player_img)
enemies = [Enemy(random.randint(0, MAP_W - PLAYER_SIZE), random.randint(0, MAP_H - PLAYER_SIZE), enemy_img, human_img, i == 0) for i in range(20)]
bottles = []
minimap_visible = True
clock = pygame.time.Clock()
@ -191,35 +66,48 @@ def main():
while running:
clock.tick(60)
for event in pygame.event.get():
if event.type==pygame.QUIT: running=False
if event.type==pygame.KEYDOWN:
if event.key==pygame.K_SPACE: bottles.append(TapWater(player.x, player.y, player.dir))
if event.key==pygame.K_m: minimap_visible = not minimap_visible
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
bottles.append(TapWater(player.x, player.y, player.dir))
if event.key == pygame.K_m:
minimap_visible = not minimap_visible
keys = pygame.key.get_pressed()
player.move(keys)
player.move(keys, MAP_W, MAP_H)
for enemy in enemies:
enemy.move(keys, player)
enemy.move(keys, player, MAP_W, MAP_H)
enemy.check_kill_player(player)
for b in bottles[:]:
for bottle in bottles[:]:
for enemy in enemies:
if enemy.rect.colliderect(b.rect): enemy.take_damage(20); bottles.remove(b); break
bottles = [b for b in bottles if 0<=b.x<=MAP_W and 0<=b.y<=MAP_H]
ox = max(0, min(player.x+PLAYER_SIZE//2-WIDTH//2, MAP_W-WIDTH))
oy = max(0, min(player.y+PLAYER_SIZE//2-HEIGHT//2, MAP_H-HEIGHT))
if map_surface: WIN.blit(map_surface, (0,0), area=pygame.Rect(ox,oy,WIDTH,HEIGHT))
else: WIN.fill((255,255,255))
if enemy.rect.colliderect(bottle.rect):
enemy.take_damage(20)
bottles.remove(bottle)
break
bottles = [b for b in bottles if 0 <= b.x <= MAP_W and 0 <= b.y <= MAP_H]
ox = max(0, min(player.x + PLAYER_SIZE // 2 - WIDTH // 2, MAP_W - WIDTH))
oy = max(0, min(player.y + PLAYER_SIZE // 2 - HEIGHT // 2, MAP_H - HEIGHT))
if map_surface:
WIN.blit(map_surface, (0, 0), area=pygame.Rect(ox, oy, WIDTH, HEIGHT))
else:
WIN.fill((255, 255, 255))
player.draw(WIN, ox, oy)
for b in bottles: b.draw(WIN, ox, oy)
for enemy in enemies: enemy.draw(WIN, ox, oy)
for bottle in bottles:
bottle.draw(WIN, ox, oy)
for enemy in enemies:
enemy.draw(WIN, ox, oy)
if minimap_visible:
# Draw all enemies on minimap
draw_minimap(WIN, map_surface, player, enemies, bottles)
pygame.display.update()
if not player.alive:
font = pygame.font.SysFont(None, 48)
text = font.render("Game Over!", True, (255,0,0))
WIN.blit(text, (WIDTH//2-text.get_width()//2, HEIGHT//2-text.get_height()//2))
pygame.display.update(); pygame.time.wait(2000); running=False
pygame.quit(); sys.exit()
# Remove win/lose condition checks
# if not player.alive:
# show_lose_screen(WIN)
# running = False
# elif all(not e.alive or e.health <= 0 for e in enemies):
# show_win_screen(WIN)
# running = False
pygame.quit()
sys.exit()
if __name__=="__main__": main()
if __name__ == "__main__":
main()