|
app.py
from random import randint, choice, random
import pyxel
ALIVE = 0
TOUCHED = 1
EXPLODED = 2
BOSS_TOUCHED = 3
class Tile:
grasses_img = [(16, 24), (24, 24), (24, 32), (16, 32)]
rocks_img = [(0, 32), (8, 32), (0, 40), (8, 40)]
def __init__(self):
self.tile_size = 8
self.is_grass = True # possibilité de mettre des roches
if self.is_grass:
img = choice(Tile.grasses_img)
self.u = img[0]
self.v = img[1]
else:
img = choice(Tile.rocks_img)
self.u = img[0]
self.v = img[1]
class Enemy:
def __init__(self, x, y):
self.x = x
self.y = y
self.u = 0
self.v = 8
self.tile_size = 8
self.direction = (0, 1)
self.status = ALIVE
self.explosion_animation = 0
self.is_boss = False
def move(self, windows_width, windows_height, player_x, player_y):
# il va vers le joueur seulement à partir d'une certaine distance
if self.distance(player_x, player_y) < 1500 or self.is_boss:
if self.x < player_x:
if self.y < player_y:
self.direction = (1, 1)
elif self.y > player_y:
self.direction = (1, -1)
else:
self.direction = (1, 0)
elif self.x > player_x:
if self.y < player_y:
self.direction = (-1, 1)
elif self.y > player_y:
self.direction = (-1, -1)
else:
self.direction = (-1, 0)
else:
if self.y < player_y:
self.direction = (0, 1)
elif self.y > player_y:
self.direction = (0, -1)
else:
self.direction = (0, 0)
else:
if random() > 0.90:
self.direction = choice([(1, 0), (-1, 0), (0, 1), (0, -1), (-1, -1), (1, 1), (-1, 1), (1, -1)])
self.x = min(self.x + self.direction[0], windows_width - self.tile_size)
self.x = max(self.x, 0)
self.y = min(self.y + self.direction[1], windows_height - self.tile_size)
self.y = max(self.y, 0)
def draw(self):
if self.status == ALIVE:
pyxel.blt(self.x, self.y, 0, self.u, self.v, self.tile_size, self.tile_size, 11)
elif self.status == TOUCHED and self.explosion_animation < 6:
circle_size = self.tile_size // 2
pyxel.circ(x=self.x + circle_size,
y=self.y + circle_size,
r=2 * (self.explosion_animation // circle_size),
col=self.tile_size + self.explosion_animation % 3)
elif self.status == TOUCHED:
self.status = EXPLODED
def is_touched(self, shoot_x, shoot_y):
return self.x + self.tile_size >= shoot_x >= self.x and self.y <= shoot_y <= self.y + self.tile_size
def touched(self):
self.status = TOUCHED
return True
def update_explosion(self):
self.explosion_animation += 1
def __repr__(self):
return f"Ennemi en {self.x}, {self.y}"
def distance(self, player_x, player_y):
return (self.x - player_x) ** 2 + (self.y - player_y) ** 2
class Boss(Enemy):
def __init__(self, x, y, hp):
super().__init__(x, y)
self.max_hp = hp
self.hp = hp
self.u = 0
self.v = 16
self.is_boss = True
self.nb_lives = 10
def touched(self):
self.hp -= 10
self.nb_lives = int(((self.hp / self.max_hp) * 100) // 10)
if self.hp <= 0:
self.status = TOUCHED
return True
return False
def draw(self):
if self.status == ALIVE:
pyxel.blt(self.x, self.y, 0, self.u, self.v, self.tile_size, self.tile_size, 11)
elif self.status == TOUCHED and self.explosion_animation < 6:
circle_size = self.tile_size // 2
pyxel.circ(x=self.x + circle_size,
y=self.y + circle_size,
r=2 * (self.explosion_animation // circle_size),
col=self.tile_size + self.explosion_animation % 3)
elif self.status == TOUCHED:
self.status = EXPLODED
elif self.status == BOSS_TOUCHED:
pyxel.blt(self.x, self.y, 0, 0, 24, self.tile_size, self.tile_size, 11)
class Player:
def __init__(self, x, y):
self.x = x
self.y = y
self.u = 0
self.v = 0
self.tile_size = 8
self.lives = 3
self.is_vulnerable = True
def draw(self):
pyxel.blt(self.x, self.y, 0, self.u, self.v, self.tile_size, self.tile_size, 11)
def move(self, window_width, window_height):
if pyxel.btn(pyxel.KEY_RIGHT) and self.x < window_width - self.tile_size:
self.x += 1
if pyxel.btn(pyxel.KEY_LEFT) and self.x > 0:
self.x += -1
if pyxel.btn(pyxel.KEY_DOWN) and self.y < window_height - self.tile_size:
self.y += 1
if pyxel.btn(pyxel.KEY_UP) and self.y > 0:
self.y += -1
def touched(self):
self.lives -= 1
self.is_vulnerable = False
def vulnerability(self):
if pyxel.frame_count % 60 == 0:
self.is_vulnerable = True
class Game:
def __init__(self):
self.height = 176
self.width = 176
self.map_size = 22
self.player = Player(self.width // 2, self.height // 2)
self.enemies_base = (randint(0, 22) * 8, randint(0, 22) * 8)
self.all_enemies = list()
self.game_map = self.create_map()
self.shoots = list()
self.score = 0
self.paints_spots = list()
self.all_ground_pixels = self.get_ground_pixel()
self.touched_enemies = set()
self.frequency = 60
self.best_score = 0
self.nb_enemies_touched = 0
self.boss = None
self.nb_boss = 1
pyxel.init(self.width, self.height, title="PaintGame")
pyxel.load("3.pyxres")
pyxel.run(self.update, self.draw)
def update(self):
if pyxel.frame_count % 30 == 0:
self.frequency = max(self.frequency - 1, 10)
if self.player.lives > 0:
self.update_player()
self.get_shoots()
self.generate_enemies()
self.update_enemies()
self.update_shoots()
self.check_collisions()
self.count_score()
else:
if self.score > self.best_score:
self.best_score = self.score
if pyxel.btn(pyxel.KEY_R):
self.restart()
def draw(self):
pyxel.cls(0)
self.draw_map()
self.draw_paints()
if self.player.lives > 0:
self.draw_base()
for enemy in self.all_enemies:
enemy.draw()
self.player.draw()
self.draw_shoots()
self.draw_score()
self.draw_lives()
else:
text = f"Perdu...\nScore : {self.score:.2f}%\nMeilleur score: {self.best_score:.2f}%"
pyxel.text((self.width // 2) - len(text), self.height // 2, text, 0)
def create_map(self):
game_map = [[Tile() for _ in range(self.map_size)] for _ in range(self.map_size)]
return game_map
def draw_map(self):
for i in range(0, len(self.game_map)):
for j in range(0, len(self.game_map[i])):
pyxel.blt(0 + (i * 8), 0 + (j * 8), 0, self.game_map[i][j].u, self.game_map[i][j].v, 8, 8)
def generate_enemies(self):
if pyxel.frame_count % self.frequency != 0 or len(
self.all_enemies) > 50: # si ça ne fait pas une seconde on attend
return
x = self.enemies_base[0]
y = self.enemies_base[1]
if (self.nb_enemies_touched % (15 * self.nb_boss) == 0) and (self.boss is None) and (
self.nb_enemies_touched > 0):
self.boss = Boss(x, y, 150 * (self.nb_enemies_touched // 15))
self.all_enemies.append(self.boss)
self.nb_boss += 1
else:
self.all_enemies.append(Enemy(x, y))
def update_enemies(self):
for enemy in self.all_enemies:
if enemy.status == ALIVE and pyxel.frame_count % 2 == 0:
enemy.move(self.width, self.height, self.player.x, self.player.y)
elif enemy.status == TOUCHED:
enemy.update_explosion()
elif enemy.status == EXPLODED:
self.all_enemies.remove(enemy)
def get_shoots(self):
if pyxel.btn(pyxel.KEY_Q): # shoot à gauche
self.shoots.append(
(self.player.x + (self.player.tile_size // 2), self.player.y + (self.player.tile_size // 2), (-1, 0)))
if pyxel.btn(pyxel.KEY_D): # shoot à droite
self.shoots.append(
(self.player.x + (self.player.tile_size // 2), self.player.y + (self.player.tile_size // 2), (1, 0)))
if pyxel.btn(pyxel.KEY_Z): # shoot en haut
self.shoots.append(
(self.player.x + (self.player.tile_size // 2), self.player.y + (self.player.tile_size // 2), (0, -1)))
if pyxel.btn(pyxel.KEY_S): # shoot en bas
self.shoots.append(
(self.player.x + (self.player.tile_size // 2), self.player.y + (self.player.tile_size // 2), (0, 1)))
def update_shoots(self):
new_shoots = []
for shoot in self.shoots:
dir_x, dir_y = shoot[2]
new_x = shoot[0] + dir_x
new_y = shoot[1] + dir_y
new_shoots.append((new_x, new_y, (dir_x, dir_y)))
self.shoots = new_shoots
def draw_shoots(self):
for shoot_x, shoot_y, shoot_dir in self.shoots:
pyxel.rect(shoot_x, shoot_y, 1, 1, 10)
def check_collisions(self):
# shoots / enemy
for enemy in self.all_enemies:
for shoot in self.shoots:
if enemy.is_touched(shoot[0], shoot[1]):
self.shoots.remove(shoot)
killed = enemy.touched()
self.paints_spots.append((enemy.x, enemy.y, enemy.tile_size))
if killed:
self.nb_enemies_touched += 1
if enemy.is_boss:
self.boss = None
# enemy / player
for enemy in self.all_enemies:
if enemy not in self.touched_enemies and enemy.x <= self.player.x + self.player.tile_size and \
enemy.y <= self.player.y + self.player.tile_size and enemy.x + enemy.tile_size >= self.player.x and \
enemy.y + enemy.tile_size >= self.player.y and self.player.is_vulnerable:
self.player.touched()
enemy.touched()
self.touched_enemies.add(enemy)
def draw_paints(self):
for paint_x, paint_y, size in self.paints_spots:
pyxel.circ(paint_x, paint_y, size, 8)
def draw_base(self):
pyxel.blt(self.enemies_base[0], self.enemies_base[1], 0, 24, 16, 8, 8, 11)
def count_score(self):
actual_score = 0
for x in range(self.width):
for y in range(self.height):
if pyxel.pget(x, y) == 8 and not self.all_ground_pixels[x][y]:
self.all_ground_pixels[x][y] = True
for line in self.all_ground_pixels:
actual_score += line.count(True)
self.score = actual_score / (self.width * self.height) * 100
def draw_score(self):
text = f"Score : {self.score:.2f}%"
pyxel.text(0, self.height - 10, text, 0)
def get_ground_pixel(self):
return [[False for _ in range(self.height)] for _ in range(self.width)]
def draw_lives(self):
# player
for i in range(self.player.lives):
pyxel.blt(0 + i * 8, 0, 0, 8, 16, 8, 8, 11)
# boss
if self.boss is not None:
for i in range(1, self.boss.nb_lives+1):
pyxel.rect(self.width - (i*4), 0, 4, 4, 9)
def restart(self):
self.score = 0
self.player = Player(self.width // 2, self.height // 2)
self.enemies_base = (randint(0, 22) * 8, randint(0, 22) * 8)
self.all_enemies = list()
self.game_map = self.create_map()
self.shoots = list()
self.paints_spots = list()
self.all_ground_pixels = self.get_ground_pixel()
self.touched_enemies = set()
self.frequency = 60
def update_player(self):
self.player.move(self.width, self.height)
self.player.vulnerability()
Game()
Description
Documentation
PACKAGES
If your project uses packages, list them bellow (names separated by commas). Packages that can be added are only packages built in Pyodide. Example: numpy,pandas Layout
Packages
If your project uses packages, list them bellow (names separated by commas). Packages that can be added are only packages built in Pyodide.
Example: numpy,pandas File to execute with the public link
PYXEL DOCUMENTATION
|