To much gap between last commit

Im now working next on shoorting for turret

Added new sand map and new cannon tower images aswell as function to play animations, showing range, placing turrets on allowed tiles for towers

Buy and cancel button to buy towers
This commit is contained in:
2026-07-04 23:50:07 +02:00
parent 0171a5c845
commit f3a5647e5b
40 changed files with 248 additions and 24 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 B

File diff suppressed because one or more lines are too long
+3 -2
View File
@@ -18,7 +18,7 @@ class Editor():
self.manager = pgi.UIManager(self.screen.get_size(), "assets/theme/editor_theme.json") self.manager = pgi.UIManager(self.screen.get_size(), "assets/theme/editor_theme.json")
self.map = 'test' self.map = 'desert'
self.path = 'assets/maps/' + str(self.map) + '.json' self.path = 'assets/maps/' + str(self.map) + '.json'
try: try:
@@ -30,7 +30,8 @@ class Editor():
self.assets = { self.assets = {
"base": load_images("/tiles/test"), "base": load_images("/tiles/test"),
"jungle": load_images("/tiles/jungle") "jungle": load_images("/tiles/jungle"),
"desert": load_images('/tiles/desert')
} }
self.tile_list = list(self.assets) self.tile_list = list(self.assets)
+13 -2
View File
@@ -2,7 +2,7 @@ import pygame as pg
class Camera(): class Camera():
def __init__(self): def __init__(self):
self.zoom = 1 self.zoom = 2
self.offset = pg.math.Vector2(0, 0) self.offset = pg.math.Vector2(0, 0)
def world_to_screen(self, pos): def world_to_screen(self, pos):
@@ -23,4 +23,15 @@ class Camera():
def change_zoom(self, amount): def change_zoom(self, amount):
self.zoom += amount self.zoom += amount
self.zoom = max(0.5, min(self.zoom, 4.0)) self.zoom = max(0.5, min(self.zoom, 4.0))
def zoom_offset(self, screen_pos, zoom_factor):
world_before = self.screen_to_world(screen_pos)
self.change_zoom(zoom_factor)
world_after = self.screen_to_world(screen_pos)
self.offset.x += world_before[0] - world_after[0]
self.offset.y += world_before[1] - world_after[1]
+16 -5
View File
@@ -1,17 +1,18 @@
from game.util import * from game.util import *
import pygame as pg import pygame as pg
import math
class Enemy(pg.sprite.Group): class Enemy(pg.sprite.Group):
def __init__(self, game, speed, health): def __init__(self, game, speed, health):
super().__init__() super().__init__()
self.game = game self.game = game
self.waypoints = self.game.tilemap.waypoints self.waypoints = self.game.tilemap.waypoints
print(self.waypoints)
self.speed = speed self.speed = speed
self.angle = 0
self.health = health self.health = health
self.image = self.game.assets['enemy'] self.image = pg.transform.rotate(self.game.assets['enemy'].copy(), self.angle)
self.rect = self.image.get_rect() self.rect = self.image.get_rect()
self.pos = pg.Vector2( self.pos = pg.Vector2(
@@ -23,12 +24,16 @@ class Enemy(pg.sprite.Group):
self.rect.center = self.pos self.rect.center = self.pos
def update(self): def update(self):
self.move()
self.rotate()
def move(self):
if self.target_index >= len(self.waypoints): if self.target_index >= len(self.waypoints):
return return
tile_size = self.game.tilemap.tile_size tile_size = self.game.tilemap.tile_size
target = pg.Vector2( self.target = pg.Vector2(
tile_to_pixel( tile_to_pixel(
self.waypoints[self.target_index], self.waypoints[self.target_index],
tile_size, tile_size,
@@ -36,16 +41,22 @@ class Enemy(pg.sprite.Group):
) )
) )
direction = target - self.pos direction = self.target - self.pos
distance = direction.length() distance = direction.length()
if distance <= self.speed: if distance <= self.speed:
self.pos = target self.pos = self.target
self.target_index += 1 self.target_index += 1
else: else:
direction = direction.normalize() direction = direction.normalize()
self.pos += direction * self.speed self.pos += direction * self.speed
def rotate(self):
dist = self.target - self.pos
self.angle = math.degrees(math.atan2(-dist[1], dist[0]))
self.image = pg.transform.rotate(self.game.assets['enemy'].copy(), self.angle)
self.rect = self.image.get_rect()
self.rect.center = self.pos self.rect.center = self.pos
def render(self, surf, camera): def render(self, surf, camera):
+5 -8
View File
@@ -23,18 +23,15 @@ class Game:
"assets/theme/theme.json" "assets/theme/theme.json"
) )
self.tilemap = Tilemap(self) self.tilemap = Tilemap(self)
self.map = 'test'
self.path = 'assets/maps/' + str(self.map) + '.json'
try:
self.tilemap.load(self.path)
except FileNotFoundError:
print('File not Found')
print(self.tilemap.waypoints)
self.assets={ self.assets={
"base": load_images('/tiles/test'), "base": load_images('/tiles/test'),
"jungle": load_images('/tiles/jungle'), "jungle": load_images('/tiles/jungle'),
"enemy": load_image('/entities/enemies/sand_monster.png') "desert": load_images('/tiles/desert'),
"enemy": load_image('/entities/enemies/enemy.png'),
"cannon": load_image('/entities/towers/cannon/shoot/0.png'),
"red_cannon": load_image('/entities/towers/cannon/red_cannon.png'),
"cannon/shoot": Animation(load_images('/entities/towers/cannon/shoot'), 10, False)
} }
self.screens = { self.screens = {
+107 -6
View File
@@ -1,6 +1,7 @@
from game.screens.base_screen import BaseScreen from game.screens.base_screen import BaseScreen
from game.enemy import Enemy from game.enemy import Enemy
from game.camera import Camera from game.camera import Camera
from game.tower import Tower, TowerGroup
from game.util import * from game.util import *
import pygame as pg import pygame as pg
import pygame_gui as pgi import pygame_gui as pgi
@@ -15,19 +16,99 @@ class GamePlay(BaseScreen):
# self.map_surface = pg.surface.Surface((self.game_surface.get_width()/self.tile_multiplicator,self.game_surface.get_height()/self.tile_multiplicator)) # self.map_surface = pg.surface.Surface((self.game_surface.get_width()/self.tile_multiplicator,self.game_surface.get_height()/self.tile_multiplicator))
self.camera = Camera() self.camera = Camera()
self.center = (self.game_surface.get_width() /2, self.game_surface.get_height() /2)
self.movment = [False, False, False, False] self.movment = [False, False, False, False]
self.selected = None
self.notunselect = True
self.map = 'test' self.map = 'desert'
self.path = 'assets/maps/' + str(self.map) + '.json' self.path = 'assets/maps/' + str(self.map) + '.json'
self.enemy = Enemy(self.game, 0.5, 100)
try: try:
self.game.tilemap.load(self.path) self.game.tilemap.load(self.path)
except FileNotFoundError: except FileNotFoundError:
print('File not Found') print('File not Found')
self.enemy_group = pg.sprite.Group()
self.tower_group = TowerGroup()
self.enemy = Enemy(self.game, 0.5, 100)
self.buy_button = pgi.elements.UIButton(
relative_rect=pg.Rect(
self.width - 200,
self.height -250,
150,
50
),
text="Buy Tower",
manager=self.game.ui_manager
)
self.cancel_button = pgi.elements.UIButton(
relative_rect=pg.Rect(
self.width - 200,
self.height -175,
150,
50
),
text="Cancel",
manager=self.game.ui_manager
)
self.buy_button.hide()
self.cancel_button.hide()
self.bought = False
self.elements = [
self.buy_button,
self.cancel_button
]
def process_event(self, event): def process_event(self, event):
#calculate tile position from mouse position
self.mpos = pg.mouse.get_pos()
game_rect = self.game_surface.get_rect(topleft=(0, self.height / 6))
if game_rect.collidepoint(self.mpos):
local_mpos = (
self.mpos[0] - game_rect.x,
self.mpos[1] - game_rect.y
)
world_pos = self.camera.screen_to_world(local_mpos)
self.tile_pos_decimal = pixel_to_tile(world_pos, self.game.tilemap.tile_size)
self.tile_pos = (int(self.tile_pos_decimal[0]), int(self.tile_pos_decimal[1]))
else:
self.tile_pos = None
if event.type == pgi.UI_BUTTON_PRESSED:
if event.ui_element == self.buy_button:
self.bought = True
# self.tower = Tower(self.game, self.mpos, self.tile_pos, 'cannon')
if event.ui_element == self.cancel_button:
self.bought = False
self.tile_pos = (int(self.tile_pos_decimal[0]), int(self.tile_pos_decimal[1]))
if event.type == pg.MOUSEBUTTONDOWN:
if self.tile_pos is not None:
if event.button == 1:
self.selected = self.select_turret(self.tile_pos)
self.clear_selection()
if self.selected is not None:
print(f'selected: {self.selected}')
self.selected.selected = True
print(f'{self.selected}')
if self.bought:
if self.game.tilemap.placeable(self.tile_pos):
if not any(tower.tile_pos == self.tile_pos for tower in self.tower_group):
tower = Tower(self.game, self.mpos, self.tile_pos, 'cannon', 40)
self.tower_group.add(tower)
self.bought = False
if event.button == 3:
for tower in self.tower_group:
if tower.tile_pos == self.tile_pos:
tower.kill()
if event.type == pg.KEYDOWN: if event.type == pg.KEYDOWN:
if event.key == pg.K_a: if event.key == pg.K_a:
self.movment[0] = True self.movment[0] = True
@@ -39,9 +120,9 @@ class GamePlay(BaseScreen):
self.movment[3] = True self.movment[3] = True
if event.key == pg.K_q: if event.key == pg.K_q:
self.camera.change_zoom(-0.25) self.camera.zoom_offset(self.center, -0.25)
if event.key == pg.K_e: if event.key == pg.K_e:
self.camera.change_zoom(0.25) self.camera.zoom_offset(self.center ,0.25)
if event.type == pg.KEYUP: if event.type == pg.KEYUP:
if event.key == pg.K_a: if event.key == pg.K_a:
self.movment[0] = False self.movment[0] = False
@@ -54,6 +135,7 @@ class GamePlay(BaseScreen):
def update(self, dt): def update(self, dt):
self.enemy.update() self.enemy.update()
self.tower_group.update("sigma")
self.camera.move((self.movment[1] - self.movment[0]) * 2, (self.movment[3] - self.movment[2]) * 2) self.camera.move((self.movment[1] - self.movment[0]) * 2, (self.movment[3] - self.movment[2]) * 2)
@@ -64,7 +146,26 @@ class GamePlay(BaseScreen):
self.game.tilemap.render_game(self.game_surface, self.camera) self.game.tilemap.render_game(self.game_surface, self.camera)
self.enemy.render(self.game_surface, self.camera) self.enemy.render(self.game_surface, self.camera)
self.tower_group.render(self.game_surface, self.camera)
if self.bought:
if self.game.tilemap.placeable(self.tile_pos):
self.game_surface.blit(pg.transform.scale2x(self.game.assets["cannon"]), (self.mpos[0] - (self.game.tilemap.tile_size //2), self.mpos[1] - (self.game.tilemap.tile_size //2 + self.height/6)))
else:
self.game_surface.blit(pg.transform.scale2x(self.game.assets["red_cannon"]), (self.mpos[0] - (self.game.tilemap.tile_size //2), self.mpos[1] - (self.game.tilemap.tile_size //2 + self.height/6)))
# self.game_surface.blit(pg.transform.scale(self.map_surface, self.game_surface.get_size())) # self.game_surface.blit(pg.transform.scale(self.map_surface, self.game_surface.get_size()))
surface.blit(self.game_surface, (0, self.height/6)) surface.blit(self.game_surface, (0, self.height/6))
# if self.bought:
# self.game_surface.blit(pg.transform.scale2x(self.game.assets["cannon"]), (self.mpos[0] - (self.game.tilemap.tile_size //2), self.mpos[1] - (self.game.tilemap.tile_size //2)))
def select_turret(self, tile_pos):
for tower in self.tower_group:
if (tile_pos[0], tile_pos[1]) == (tower.tile_pos[0], tower.tile_pos[1]):
return tower
def clear_selection(self):
for tower in self.tower_group:
tower.selected = False
-1
View File
@@ -70,7 +70,6 @@ class MainMenu(BaseScreen):
if event.type == pgi.UI_BUTTON_PRESSED: if event.type == pgi.UI_BUTTON_PRESSED:
if event.ui_element == self.play_button: if event.ui_element == self.play_button:
# Zum Gameplay wechseln # Zum Gameplay wechseln
self.game.change_screen("game") self.game.change_screen("game")
+13
View File
@@ -13,6 +13,8 @@ class Tilemap:
self.offgrid_tiles = [] self.offgrid_tiles = []
self.waypoints=[] self.waypoints=[]
self.tower_placeable = [{"type": "desert", "variant": 0}]
def save(self, path): def save(self, path):
f = open(path, 'w') f = open(path, 'w')
json.dump({'tilemap': self.tilemap, 'tile_size': self.tile_size, 'waypoints': self.waypoints}, f) json.dump({'tilemap': self.tilemap, 'tile_size': self.tile_size, 'waypoints': self.waypoints}, f)
@@ -27,6 +29,17 @@ class Tilemap:
self.tile_size = map_data['tile_size'] self.tile_size = map_data['tile_size']
self.waypoints = map_data['waypoints'] self.waypoints = map_data['waypoints']
def placeable(self, tile_pos):
str_tile_pos = f'{tile_pos[0]};{tile_pos[1]}'
if str_tile_pos not in self.tilemap:
return False
tile = self.tilemap[str_tile_pos]
for ti in self.tower_placeable:
if tile['type'] == ti['type'] and tile['variant'] == ti['variant']:
return True
return False
def render(self, surf, offset=(0, 0)): def render(self, surf, offset=(0, 0)):
for x in range(offset[0] // self.tile_size, (offset[0] + surf.get_width()) // self.tile_size + 1): for x in range(offset[0] // self.tile_size, (offset[0] + surf.get_width()) // self.tile_size + 1):
for y in range(offset[1] // self.tile_size, (offset[1] + surf.get_height()) // self.tile_size + 1): for y in range(offset[1] // self.tile_size, (offset[1] + surf.get_height()) // self.tile_size + 1):
+62
View File
@@ -0,0 +1,62 @@
import pygame as pg
from game.util import *
class Tower(pg.sprite.Sprite):
def __init__(self, game, pos, tile_pos, type, range, level=1):
super().__init__()
self.game = game
self.type = type
self.image = self.game.assets[type]
self.rect = self.image.get_rect()
self.rect.center = pos
self.range = range
self.selected = False
self.pos = pos
self.tile_pos = tile_pos
self.level = level
self.action = ""
self.set_action('shoot')
def update(self, enemies):
self.animation.update()
def set_action(self, action):
if action != self.action:
self.action = action
self.animation = self.game.assets[self.type + '/' + self.action].copy()
def render(self, surf, camera):
screen_pos = camera.world_to_screen(pg.Vector2(
tile_to_pixel(self.tile_pos, self.game.tilemap.tile_size, True)
))
anim_img = self.animation.img()
scaled_image = pg.transform.scale(
anim_img,
(
int(anim_img.get_width() * camera.zoom),
int(anim_img.get_height() * camera.zoom)
)
)
rect = scaled_image.get_rect(center=screen_pos)
if self.selected:
scaled_range = int(self.range * camera.zoom)
range_surf = pg.Surface((scaled_range * 2, scaled_range * 2), pg.SRCALPHA)
pg.draw.circle(range_surf, (200, 200, 200, 100), (scaled_range, scaled_range), scaled_range)
range_rect = range_surf.get_rect(center=screen_pos)
surf.blit(range_surf, range_rect)
surf.blit(scaled_image, rect)
class TowerGroup(pg.sprite.Group):
def render(self, surf, camera):
for tower in self.sprites():
tower.render(surf, camera)
def update(self, enemies):
for tower in self.sprites():
tower.update(enemies)
+28
View File
@@ -26,3 +26,31 @@ def tile_to_pixel(waypoint, tile_size, center=False):
waypoint[0] * tile_size, waypoint[0] * tile_size,
waypoint[1] * tile_size waypoint[1] * tile_size
) )
def pixel_to_tile(waypoint, tile_size):
return (
waypoint[0] / tile_size,
waypoint[1] / tile_size
)
class Animation:
def __init__(self, images, img_dur=5, loop=True):
self.images = images
self.loop = loop
self.img_duration = img_dur
self.done = False
self.frame = 0
def copy(self):
return Animation(self.images, self.img_duration, self.loop)
def update(self):
if self.loop:
self.frame = (self.frame + 1) % (self.img_duration * len(self.images))
else:
self.frame = min(self.frame + 1, self.img_duration * len(self.images))
if self.frame >= self.img_duration * len(self.images) - 1:
self.done = True
def img(self):
return self.images[int(self.frame / self.img_duration) - 1]