|
1 | | -import enum |
2 | | -from typing import List, Optional, Tuple |
| 1 | +from typing import List |
3 | 2 |
|
4 | 3 | import pygame |
5 | | -from pygame import Color |
| 4 | +from entities import NPC, GameItem, GameStatus, Player, Robot |
6 | 5 | from pygame.surface import Surface |
| 6 | +from utils import overlap |
| 7 | + |
| 8 | +from common import ( |
| 9 | + BACKGROUND_SPRITE, |
| 10 | + DIAMOND_BLUE_SPRITE, |
| 11 | + DIAMOND_RED_SPRITE, |
| 12 | + FPS, |
| 13 | + PLAYER_SPRITE, |
| 14 | + ROBOT_SPRITE, |
| 15 | + SCREEN_HEIGHT, |
| 16 | + SCREEN_WIDTH, |
| 17 | + TO_MO_SPRITE, |
| 18 | + WHITE, |
| 19 | + GameState, |
| 20 | +) |
7 | 21 |
|
8 | 22 | pygame.init() |
9 | 23 |
|
10 | | -SCREEN_WIDTH: int = 1280 |
11 | | -SCREEN_HEIGHT: int = 768 |
12 | | - |
13 | | -WHITE: Color = Color(255, 255, 255) |
14 | | -RED: Color = Color(255, 0, 0) # Màu Đỏ |
15 | | -BLUE: Color = Color(0, 0, 255) # Màu Xanh |
16 | | - |
17 | | -FPS: int = 30 # Số cảnh mỗi giây (frame per second) |
18 | | - |
19 | | -# Fonts |
20 | | -pygame.font.init() |
21 | | -FREESANSBOLD_24 = pygame.font.Font("freesansbold.ttf", 24) |
22 | | -FREESANSBOLD_48 = pygame.font.Font("freesansbold.ttf", 48) |
23 | | - |
24 | | -# Hình nền: |
25 | | -BACKGROUND_SPRITE: Surface = pygame.image.load("assets/background.png") |
26 | | -BACKGROUND_SPRITE.set_alpha(128) |
27 | | -BACKGROUND_SPRITE = pygame.transform.scale(BACKGROUND_SPRITE, [SCREEN_WIDTH, SCREEN_HEIGHT]) |
28 | | - |
29 | | - |
30 | | -# Hàm tiện ích (utility) |
31 | | -def scale_image(image: Surface, scale: float) -> Surface: |
32 | | - """Resizes image by a factor of input arg `scale`.""" |
33 | | - new_dimension: Tuple[int, int] = ( |
34 | | - int(image.get_width() * scale), |
35 | | - int(image.get_height() * scale), |
36 | | - ) |
37 | | - return pygame.transform.scale(image, new_dimension) |
38 | | - |
39 | | - |
40 | | -# Game Entities Sprites |
41 | | -PLAYER_SPRITE: Surface = scale_image(pygame.image.load("assets/player.png"), 0.2) |
42 | | -ROBOT_SPRITE: Surface = scale_image(pygame.image.load("assets/robot.png"), 0.08) |
43 | | -DIAMOND_BLUE_SPRITE: Surface = scale_image(pygame.image.load("assets/diamond_blue.png"), 0.02) |
44 | | -DIAMOND_RED_SPRITE: Surface = scale_image(pygame.image.load("assets/diamond_red.png"), 0.02) |
45 | | -TO_MO_SPRITE: Surface = scale_image(pygame.image.load("assets/to_mo.png"), 0.2) |
46 | | - |
47 | | - |
48 | | -# Hàm tiện ích (utility) |
49 | | -def overlap(x1: int, y1: int, image1: Surface, x2: int, y2: int, image2: Surface) -> bool: |
50 | | - """Returns True if 2 items overlap.""" |
51 | | - mask1 = pygame.mask.from_surface(image1) |
52 | | - mask2 = pygame.mask.from_surface(image2) |
53 | | - offset_x = x2 - x1 |
54 | | - offset_y = y2 - y1 |
55 | | - return bool(mask1.overlap(mask2, (offset_x, offset_y))) |
56 | | - |
57 | | - |
58 | | -class EntityType(enum.Enum): |
59 | | - PLAYER = enum.auto() |
60 | | - ROBOT = enum.auto() |
61 | | - DIAMOND_BLUE = enum.auto() |
62 | | - DIAMOND_RED = enum.auto() |
63 | | - |
64 | | - |
65 | | -class GameState(enum.Enum): |
66 | | - RUNNING = enum.auto() |
67 | | - WON = enum.auto() |
68 | | - LOST = enum.auto() |
69 | | - |
70 | | - |
71 | | -class Player: |
72 | | - def __init__(self, x: int, y: int, image: Surface) -> None: |
73 | | - self.x = x |
74 | | - self.y = y |
75 | | - self.image = image |
76 | | - |
77 | | - def _move(self, dx: int, dy: int): |
78 | | - new_x = self.x + dx |
79 | | - new_y = self.y + dy |
80 | | - |
81 | | - if 0 < new_x < SCREEN_WIDTH - self.image.get_width(): |
82 | | - self.x = new_x |
83 | | - if 0 < new_y < SCREEN_HEIGHT - self.image.get_height(): |
84 | | - self.y = new_y |
85 | | - |
86 | | - def update(self) -> None: |
87 | | - pressed = pygame.key.get_pressed() |
88 | | - if pressed[pygame.K_UP] or pressed[pygame.K_w]: |
89 | | - self._move(0, -10) |
90 | | - if pressed[pygame.K_DOWN] or pressed[pygame.K_s]: |
91 | | - self._move(0, 10) |
92 | | - if pressed[pygame.K_LEFT] or pressed[pygame.K_a]: |
93 | | - self._move(-10, 0) |
94 | | - if pressed[pygame.K_RIGHT] or pressed[pygame.K_d]: |
95 | | - self._move(10, 0) |
96 | | - |
97 | | - def render(self, screen: Surface) -> None: |
98 | | - screen.blit(self.image, (self.x, self.y)) |
99 | | - |
100 | | - |
101 | | -class Robot: |
102 | | - def __init__(self, x: int, y: int, image: Surface, x_heading: int, y_heading: int) -> None: |
103 | | - self.x = x |
104 | | - self.y = y |
105 | | - self.image = image |
106 | | - self.x_heading = x_heading |
107 | | - self.y_heading = y_heading |
108 | | - |
109 | | - def update(self): |
110 | | - self.x = self.x + self.x_heading |
111 | | - self.y = self.y + self.y_heading |
112 | | - |
113 | | - if self.x > SCREEN_WIDTH - self.image.get_width(): |
114 | | - self.x_heading = -self.x_heading |
115 | | - if self.x < 0: |
116 | | - self.x_heading = -self.x_heading |
117 | | - if self.y > SCREEN_HEIGHT - self.image.get_height(): |
118 | | - self.y_heading = -self.y_heading |
119 | | - if self.y < 0: |
120 | | - self.y_heading = -self.y_heading |
121 | | - |
122 | | - def render(self, screen: Surface) -> None: |
123 | | - screen.blit(self.image, (self.x, self.y)) |
124 | | - |
125 | | - |
126 | | -class NPC: |
127 | | - def __init__(self, x: int, y: int, image: Surface) -> None: |
128 | | - self.x = x |
129 | | - self.y = y |
130 | | - self.image = image |
131 | | - |
132 | | - def render(self, screen: Surface) -> None: |
133 | | - screen.blit(self.image, (self.x, self.y)) |
134 | | - |
135 | | - |
136 | | -class GameItem: |
137 | | - def __init__(self, x: int, y: int, image: Surface) -> None: |
138 | | - self.x = x |
139 | | - self.y = y |
140 | | - self.image = image |
141 | | - self.hidden = False |
142 | | - |
143 | | - def set_hidden(self): |
144 | | - self.hidden = True |
145 | | - |
146 | | - def render(self, screen: Surface) -> None: |
147 | | - if not self.hidden: |
148 | | - screen.blit(self.image, (self.x, self.y)) |
149 | | - |
150 | | - |
151 | | -class GameStatus: |
152 | | - def __init__(self) -> None: |
153 | | - self.score: int = 0 |
154 | | - self.state: GameState = GameState.RUNNING |
155 | | - |
156 | | - def update_state(self, state: GameState) -> None: |
157 | | - self.state = state |
158 | | - |
159 | | - def update_score(self, delta: int) -> None: |
160 | | - self.score += delta |
161 | | - |
162 | | - def render(self, screen: Surface) -> None: |
163 | | - # Score Text Render on Top Left Corner |
164 | | - score_text = FREESANSBOLD_24.render("Score: %d" % self.score, True, BLUE) |
165 | | - screen.blit(score_text, (10, 10)) |
166 | | - |
167 | | - # Status Text Render in Middle of Screen |
168 | | - status_text = None |
169 | | - if self.state == GameState.WON: |
170 | | - status_text = FREESANSBOLD_48.render("YOU WON!!", True, RED) |
171 | | - elif self.state == GameState.LOST: |
172 | | - status_text = FREESANSBOLD_48.render("YOU LOST!!", True, RED) |
173 | | - |
174 | | - if status_text: |
175 | | - position = ( |
176 | | - SCREEN_WIDTH // 2 - status_text.get_width() // 2, |
177 | | - SCREEN_HEIGHT // 2 - status_text.get_height() // 2, |
178 | | - ) |
179 | | - |
180 | | - screen.blit(status_text, position) |
181 | | - |
182 | | - def is_running(self): |
183 | | - return self.state == GameState.RUNNING |
184 | | - |
185 | | - |
186 | 24 | screen: Surface = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT]) |
187 | 25 | clock = pygame.time.Clock() |
188 | 26 |
|
|
0 commit comments