1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
|
# -*- coding: utf-8 -*-
import pygame
import time
import classes.board
import classes.dialog
import classes.layout
import classes.level_controller
class GameBase:
def __init__(self):
self.screen_tick = 0 # screen frame count
self.screen_speed = 3 # execute every 3 frames
self.move_tick = 0 # object motion frame count
self.move_speed = 5 # move every 3 frames
self.ai_enabled = False
self.ai_speed = 10 # move every ai_speed frames
self.ai_tick = 0 # ai motion frame count
self.show_msg = False
self.auto_checking = False
self.ships_count = 0
self.lvlc = None
def game_restart(self, screen):
pass
def handle(self, event):
pass
def display(self, screen):
pass
class BoardGame(GameBase):
def __init__(self, mainloop, speaker, config, screen_w, screen_h, x_count, y_count):
GameBase.__init__(self)
self.mainloop = mainloop
self.speaker = speaker
self.lang = self.mainloop.lang
self.min_level = 1
self.d = self.mainloop.lang.d
self.dp = self.mainloop.lang.dp
self.uage = self.mainloop.config.user_age_group
self.config = config
self.move = False # used to start moving the square when pressed
self.mouse_over = False
self.direction = [0, 0]
self.ship_id = -1
self.drag = False # used to control draging objects around
self.gridpos_now_top = (-1, -1)
self.gridpos_prev_top = (-1, -1)
self.x_diff = 0
self.y_diff = 0
self.circle_lock_pos = (0, 0)
self.game_type = "Board"
self.layout = classes.layout.Layout(mainloop, screen_w, screen_h, x_count, y_count)
self.sizer = mainloop.sizer
self.dialog = classes.dialog.Dialog(self)
self.screen_w = self.sizer.screen_w
self.screen_h = self.sizer.screen_h
self.mainloop.info.reset_buttons()
self.changed_since_check = True
self.mouse_entered_new = False
self.show_info_btn = False
# allow walking through walls and allow dragging items freely
self.allow_teleport = True
self.allow_unit_animations = True
self.active_game = self.mainloop.m.games[self.mainloop.m.active_game_id]
# self.level.lvl = self.mainloop.m.saved_levels[self.active_game.game_constructor][str(self.active_game.variant)]
self.level.lvl = self.mainloop.m.saved_levels[self.active_game.dbgameid]
if self.level.lvl > self.level.lvl_count:
self.level.lvl = self.level.lvl_count
# if one game has more than one category of tasks on different levels - the beginning of a category
# can be marked on chapter list and jumped between with right click on the next level button
self.chapters = [1]
self.unit_mouse_over = None
self.units = []
# create game board
self.data = [0, 0]
#self.board = classes.board.Board(self.mainloop, self.layout.x_count, self.layout.y_count, self.layout.scale)
self.board = classes.board.Board(self.mainloop, 3, 3, 50)
self.create_game_objects()
if not self.board.animation_c_set:
self.board.set_animation_constraints(0, self.data[0], 0, self.data[1])
# used for a line around game area
self.line_color = self.board.board_bg.line_color # (240, 240, 240)
self.screen_wx = self.board.x_count * self.board.scale
self.screen_hx = self.board.y_count * self.board.scale
self.len_ships = len(self.board.ships)
self.board.all_sprites_list.move_to_back(self.board.board_bg)
self.board.board_bg.update(self.board) # in case colour of background changed during game creation
self.movingsprites = pygame.sprite.RenderPlain((self.board.ship_list))
self.staticsprites = pygame.sprite.RenderPlain((self.board.unit_list))
self.cl_grid_line = (240, 240, 240)
try:
if self.mainloop.info.hidden == True:
self.mainloop.info.title_only()
except:
pass
self.level.completed = self.mainloop.db.query_completion(self.mainloop.userid, self.active_game.dbgameid,
self.level.lvl)
self.level.update_level_dict()
# to make sure game gets updated after starting
self.mainloop.redraw_needed = [True, True, True]
def board_layout_update(self):
self.screen_w = self.sizer.screen_w
self.screen_h = self.sizer.screen_h
self.board.scale = self.layout.scale
def say(self, text, voice="1"):
self.speaker.say(text, voice)
def show_info_dialog(self):
pass
def create_game_objects(self):
pass
def get_x_count(self, y_count, even=None):
"""method used to calculate the number of horizontal squares needed
to fill all available area making games wider on wide screens
it may make games a little bit harder on larger screens,
patches my mistake to make this game optimized for 1024x786 screens only"""
scale = self.sizer.avail_game_h // y_count
x_count = self.sizer.avail_game_w // scale
if even is None:
# if number of squares does not matter make it fill whole width rather than height
return x_count + 1
else:
m = x_count % 2
if even == True and m == 0 or even == False and m == 1:
# if the number we have is the number we need -> return it
return x_count
else:
# else: increase it to make it match the criteria set (why in- and not decrease -> see: even is None)
return x_count + 1
def get_y_count(self, x_count, even=None):
"""method used to calculate the number of horizontal squares needed
to fill all available area making games wider on wide screens
it may make games a little bit harder on larger screens,
patches my mistake to make this game optimized for 1024x786 screens only"""
scale = self.sizer.avail_game_w // x_count
y_count = self.sizer.avail_game_h // scale
if even is None:
# if number of squares does not matter make it fill whole width rather than height
return y_count + 1
else:
m = y_count % 2
if even == True and m == 0 or even == False and m == 1:
# if the number we have is the number we need -> return it
return y_count
else:
# else: increase it to make it match the criteria set (why in- and not decrease -> see: even is None)
return y_count + 1
def outline_all(self, color, width, units=True, ships=True):
# mark to draw outline around all units/ships on the board
if units == True:
for each in self.board.units:
each.set_outline(color, width)
if ships == True:
for each in self.board.ships:
each.set_outline(color, width)
def handle(self, event):
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
self.mainloop.mbtndno = None
# Change the x/y screen coordinates to grid coordinates
pos = event.pos
column = (pos[0] - self.layout.game_left) // (self.layout.width)
row = (pos[1] - self.layout.top_margin) // (self.layout.height)
if self.ships_count == 1:
# if only one movable unit found - activate it
self.board.active_ship = 0
else:
# check if we have clicked on any movable unit
self.board.activate_ship(column, row)
self.ship_id = self.board.active_ship
self.mainloop.redraw_needed[0] = True
if self.ship_id >= 0:
self.changed_since_check = True
if self.board.ships[self.ship_id].draggable == True:
self.drag = True
self.x_diff = column - self.board.active_ship_pos[0]
self.y_diff = row - self.board.active_ship_pos[1]
self.offset_x = pos[0] - self.layout.game_left - self.board.ships[self.ship_id].rect.left
self.offset_y = pos[1] - self.layout.top_margin - self.board.ships[self.ship_id].rect.top
if self.allow_unit_animations and self.board.ships[self.ship_id].animable:
self.board.all_sprites_list.move_to_front(self.board.ships[self.ship_id])
self.gridpos_prev_top = (
column - self.x_diff, row - self.y_diff) # used to store the mouse coords on previous position in grid
self.gridpos_now_top = (
column - self.x_diff, row - self.y_diff) # used to store current position on grid
self.circle_lock_pos = (self.x_diff * self.layout.scale, self.y_diff * self.layout.scale)
if self.board.active_sval_len > 0 and self.board.ships[self.board.active_ship].readable:
if isinstance(self.board.ships[self.board.active_ship].speaker_val, list):
value = ', '.join(self.board.ships[self.board.active_ship].speaker_val)
else:
value = self.board.ships[self.board.active_ship].speaker_val
self.say(value, 6)
elif event.type == pygame.MOUSEMOTION:
self.on_mouse_over()
# make sure the current game title is displayed
if self.mainloop.info.hidden == True:
self.mainloop.info.buttons_restore()
if self.drag:
pos = event.pos
if self.allow_unit_animations:
self.board.follow_cursor(self.ship_id, pos[0] - self.offset_x, pos[1] - self.offset_y)
self.mainloop.redraw_needed[0] = True
column = (pos[0] - self.layout.game_left) // (self.layout.width)
row = (pos[1] - self.layout.top_margin) // (self.layout.height)
column = column - self.x_diff
row = row - self.y_diff
self.board.anim_hover(column, row)
if pos[0] > self.layout.game_left and self.layout.top_margin < pos[
1] < self.layout.game_h + self.layout.top_margin: # if still on game board
if not self.allow_unit_animations:
self.mouse_entered_new = False
# Change the x/y screen coordinates to grid coordinates
column = (pos[0] - self.layout.game_left) // (self.layout.width)
row = (pos[1] - self.layout.top_margin) // (self.layout.height)
mdir = [0, 0] # mouse drag direction
i = 0
if self.gridpos_prev_top != (
column - self.x_diff, row - self.y_diff): # on_mouse_enter on a grid square simulation
while self.gridpos_now_top != (column - self.x_diff, row - self.y_diff) and i < 5:
if self.board.ships[self.ship_id].grid_w > 1 or self.board.ships[
self.ship_id].grid_h > 1:
i = 4
i += 1
# mouse entered a new square
self.mouse_entered_new = True
self.mainloop.redraw_needed[0] = True
self.gridpos_prev_top = (column - self.x_diff, row - self.y_diff)
column = column - self.x_diff
row = row - self.y_diff
x_change = column - self.gridpos_now_top[0]
y_change = row - self.gridpos_now_top[1]
if -1 <= x_change <= 1 and -1 <= y_change <= 1:
mdir = [x_change, y_change]
# else if mouse is out of the range try to follow in one direction
else:
if self.allow_teleport:
mdir[0] = x_change
mdir[1] = y_change
else:
if (self.gridpos_now_top[0] != column):
if x_change >= 1:
mdir[0] = 1
elif x_change <= -1:
mdir[0] = -1
if (self.gridpos_now_top[1] != row):
if y_change >= 1:
mdir[1] = 1
elif y_change <= -1:
mdir[1] = -1
if mdir[0] != 0 or mdir[1] != 0:
self.board.move(self.ship_id, *mdir)
self.board.ships[self.ship_id].turn(mdir)
self.gridpos_now_top = self.board.active_ship_pos
self.circle_lock_pos = (self.x_diff * self.layout.scale,
self.y_diff * self.layout.scale)
elif event.type == pygame.MOUSEBUTTONUP and event.button == 1:
if self.drag:
pos = event.pos
if pos[0] > self.layout.game_left and self.layout.top_margin < pos[
1] < self.layout.game_h + self.layout.top_margin: # if still on game board
if self.allow_unit_animations:
column = (pos[0] - self.layout.game_left) // (self.layout.width)
row = (pos[1] - self.layout.top_margin) // (self.layout.height)
column = column - self.x_diff
row = row - self.y_diff
self.board.anim_land(column, row)
self.mainloop.redraw_needed[0] = True
else:
self.mouse_entered_new = False
# Change the x/y screen coordinates to grid coordinates
column = (pos[0] - self.layout.game_left) // (self.layout.width)
row = (pos[1] - self.layout.top_margin) // (self.layout.height)
mdir = [0, 0] # mouse drag direction
i = 0
if self.gridpos_prev_top != (column - self.x_diff, row - self.y_diff):
while self.gridpos_now_top != (column - self.x_diff, row - self.y_diff) and i < 5:
if self.board.ships[self.ship_id].grid_w > 1 \
or self.board.ships[self.ship_id].grid_h > 1:
i = 4
i += 1
# mouse entered a new square
self.mouse_entered_new = True
self.mainloop.redraw_needed[0] = True
self.gridpos_prev_top = (column - self.x_diff, row - self.y_diff)
column = column - self.x_diff
row = row - self.y_diff
x_change = column - self.gridpos_now_top[0]
y_change = row - self.gridpos_now_top[1]
if -1 <= x_change <= 1 and -1 <= y_change <= 1:
mdir = [x_change, y_change]
# else if mouse is out of the range try to follow in one direction
else:
if self.allow_teleport:
mdir[0] = x_change
mdir[1] = y_change
else:
if self.gridpos_now_top[0] != column:
if x_change >= 1:
mdir[0] = 1
elif x_change <= -1:
mdir[0] = -1
if self.gridpos_now_top[1] != row:
if y_change >= 1:
mdir[1] = 1
elif y_change <= -1:
mdir[1] = -1
if mdir[0] != 0 or mdir[1] != 0:
self.board.move(self.ship_id, *mdir)
self.board.ships[self.ship_id].turn(mdir)
self.gridpos_now_top = self.board.active_ship_pos
self.circle_lock_pos = (self.x_diff * self.layout.scale,
self.y_diff * self.layout.scale)
else:
if self.allow_unit_animations:
ship = self.board.ships[self.board.active_ship]
self.board.anim_land(ship.grid_last_x, ship.grid_last_y)
self.drag = False
#self.board.ships[self.board.active_ship].disable_circle()
self.mainloop.redraw_needed[0] = True
elif event.type == pygame.KEYDOWN and self.len_ships > self.ship_id >= 0 \
and self.board.ships[self.ship_id].keyable:
if event.key == pygame.K_LEFT:
self.direction[0] = -1
elif event.key == pygame.K_RIGHT:
self.direction[0] = 1
elif event.key == pygame.K_UP:
self.direction[1] = -1
elif event.key == pygame.K_DOWN:
self.direction[1] = 1
self.check_direction_kdown()
elif event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
self.direction[0] = 0
elif event.key == pygame.K_RIGHT:
self.direction[0] = 0
elif event.key == pygame.K_UP:
self.direction[1] = 0
elif event.key == pygame.K_DOWN:
self.direction[1] = 0
self.check_direction_kup()
if event.type == pygame.KEYDOWN and (event.key == pygame.K_RETURN or event.key == pygame.K_KP_ENTER):
if self.changed_since_check or self.show_msg == True:
self.mainloop.redraw_needed[0] = True
self.mainloop.redraw_needed[1] = True
if self.show_msg == False:
self.mainloop.info.btns[0].img = self.mainloop.info.btns[0].img_2
self.mainloop.redraw_needed[1] = True
self.check_result()
else:
self.show_msg = False
self.level.next_board_load()
self.changed_since_check = False
def default_hover(self, event):
if not self.drag:
pos = [event.pos[0] - self.layout.game_left, event.pos[1] - self.layout.top_margin]
found = False
for each in self.units:
if each.rect.left < pos[0] < each.rect.right and each.rect.top < pos[1] < each.rect.bottom:
if each != self.unit_mouse_over:
if self.unit_mouse_over is not None:
self.unit_mouse_over.mouse_out()
self.unit_mouse_over = each
found = True
each.handle(event)
break
if not found:
if self.unit_mouse_over is not None:
self.unit_mouse_over.mouse_out()
self.unit_mouse_over = None
def on_mouse_over(self):
if not self.mouse_over:
self.on_mouse_enter()
def on_mouse_enter(self):
if self.mainloop.mouse_over[0] is not None:
self.mainloop.mouse_over[0].on_mouse_out()
self.mainloop.mouse_over[0] = self
if self.mainloop.mouse_over[1] is not None:
self.mainloop.mouse_over[1].on_mouse_out()
self.mainloop.mouse_over[1] = None
if self.mainloop.mouse_over[2] is not None:
self.mainloop.mouse_over[2].on_mouse_out()
self.mainloop.mouse_over[2] = None
self.mouse_over = True
def on_mouse_out(self):
if self.mouse_over:
self.mouse_over = False
def check_direction_kdown(self):
if self.direction[0] != 0 or self.direction[1] != 0:
self.move = True
self.changed_since_check = True
self.board.ships[self.ship_id].turn(self.direction)
self.mainloop.redraw_needed[0] = True
def check_direction_kup(self):
if self.direction == [0, 0]:
self.move = False
else:
self.board.ships[self.ship_id].turn(self.direction)
self.mainloop.redraw_needed[0] = True
def process_keydown(self):
self.move_tick += 1 # if key pressed execute this every move_speed frames
if self.move_tick > self.move_speed:
if self.move:
self.move_tick = 0
self.board.move(self.ship_id, self.direction[0], self.direction[1])
self.after_keydown_move()
self.mainloop.redraw_needed[0] = True
self.screen_tick += 1 # used to limit the display update rate, without limiting responsivenes to key presses
if self.screen_tick >= self.screen_speed:
self.screen_tick = 0
def after_keydown_move(self):
pass
def process_ai(self):
# process ai and move unit if arrow button is pressed
if self.show_msg == False:
self.process_keydown()
if self.ai_enabled == True:
self.ai_tick += 1 # if key pressed execute this every move_speed frames
if self.ai_tick > self.ai_speed:
self.ai_walk()
self.mainloop.redraw_needed[0] = True
self.ai_tick = 0
def update(self, game):
if self.board.mainloop.scheme is not None: # and self.decolorable and self.board.mainloop.game_board is not None and (isinstance(self, Letter) or isinstance(self, Label)):
self.mainloop.game_bg.fill(self.mainloop.scheme.u_color)
self.board.board_bg.initcolor = self.mainloop.scheme.u_color
self.board.board_bg.color = self.mainloop.scheme.u_color
else:
self.mainloop.game_bg.fill((255, 255, 255))
self.board.update_ships(self.circle_lock_pos)
self.board.all_sprites_list.draw(game)
if self.show_msg:
if self.level is not None:
if time.time() - self.level.completed_time > 0.5:
self.dialog.update(self.mainloop.dialogbg)
if self.board.draw_grid:
self.screen_wx = self.board.x_count * self.board.scale
self.screen_hx = self.board.y_count * self.board.scale
if not self.show_msg:
pygame.draw.line(self.mainloop.game_bg, self.line_color,
[self.screen_wx + self.layout.game_left, 0],
[self.screen_wx + self.layout.game_left, self.screen_hx], 1)
pygame.draw.line(self.mainloop.game_bg, self.line_color,
[self.layout.game_left, self.screen_hx],
[self.screen_wx + self.layout.game_left - 1, self.screen_hx], 1)
def update_score(self, points):
pass
def check_result(self):
pass
|