1'''The main game module. One big event loop in the run function plus a few helper functions.''' 2 3import pygame 4from pygame.locals import * 5import os 6import random 7 8from locals import * 9 10from player import Player 11from spider import Spider 12from particle import Particle 13from level import Level 14from sound import play_sound 15from edit_utils import Edit_utils 16from util import * 17from variables import Variables 18from log import error_message 19from trigger import Trigger 20from visibleobject import flip_direction_from_position 21 22import data 23 24keys_released = {} 25 26def render_edit_utilities(screen): 27 return 28 29#This function renders the in-game GUI on the screen. 30def render_gui(screen, life, score, topleft): 31 score_image = render_text("Score: " + str(score) ) 32 life_image = render_text("Life:") 33 #life_bar_bg_image = Util.cached_images["health_bar_empty"] 34 #life_bar_image = Util.cached_images["health_bar_fill"] 35 version_image = render_text("0.7.9") 36 37 rect = score_image.get_rect() 38 rect.left = topleft[0] 39 rect.top = topleft[1] 40 screen.blit(score_image, rect) 41 42 rect.left = topleft[0] + 26 43 rect.top = topleft[1] + 26 44 rect.width = 38 45 rect.height = 8 46 pygame.draw.rect(screen, COLOR_GUI_BG, rect) 47 if life > 0: 48 rect.left = topleft[0] + 27 49 rect.top = topleft[1] + 27 50 rect.width = life 51 rect.height = 6 52 pygame.draw.rect(screen, COLOR_BLOOD, rect) 53 54 rect = life_image.get_rect() 55 rect.left = topleft[0] 56 rect.top = topleft[1] + 20 57 screen.blit(life_image, rect) 58 59 rect = version_image.get_rect() 60 rect.right = SCREEN_WIDTH - 2 61 rect.bottom = SCREEN_HEIGHT - 2 62 screen.blit(version_image, rect) 63 return 64 65#This function parses inputs from the keyboard and returns them as an array 66def parse_inputs(joystick = None): 67 keys = pygame.key.get_pressed() 68 inputs = {} 69 70 if keys[K_LEFT]: 71 inputs["LEFT"] = True 72 if keys_released["K_LEFT"]: 73 inputs["EDIT_LEFT"] = True 74 keys_released["K_LEFT"] = False 75 else: 76 keys_released["K_LEFT"] = True 77 78 if keys[K_RIGHT]: 79 inputs["RIGHT"] = True 80 if keys_released["K_RIGHT"]: 81 inputs["EDIT_RIGHT"] = True 82 keys_released["K_RIGHT"] = False 83 else: 84 keys_released["K_RIGHT"] = True 85 86 if keys[K_DOWN]: 87 if keys_released["K_DOWN"]: 88 inputs["DOWN"] = True 89 inputs["EDIT_DOWN"] = True 90 keys_released["K_DOWN"] = False 91 else: 92 keys_released["K_DOWN"] = True 93 94 if keys[K_z]: 95 inputs["UP"] = True 96 if keys_released["K_z"]: 97 inputs["JUMP"] = True 98 keys_released["K_z"] = False 99 else: 100 keys_released["K_z"] = True 101 102 if keys[K_p]: 103 if keys_released["K_p"]: 104 inputs["PAUSE"] = True 105 keys_released["K_p"] = False 106 else: 107 keys_released["K_p"] = True 108 109 if keys[K_w]: 110 inputs["ADD_TILE_WALL"] = True 111 112 if keys[K_s]: 113 if keys_released["K_s"]: 114 inputs["ADD_TILE_SPIKES"] = True 115 if (keys[K_RCTRL] or keys[K_LCTRL]): 116 inputs["SAVE_TILES"] = True 117 del inputs["ADD_TILE_SPIKES"] 118 keys_released["K_s"] = False 119 else: 120 keys_released["K_s"] = True 121 122 if keys[K_b]: 123 inputs["ADD_TILE_BARS"] = True 124 125 if keys[K_d]: 126 inputs["REMOVE_TILE"] = True 127 128 129 if keys[K_UP]: 130 inputs["UP"] = True 131 if keys_released["K_UP"]: 132 inputs["JUMP"] = True 133 inputs["EDIT_UP"] = True 134 keys_released["K_UP"] = False 135 else: 136 keys_released["K_UP"] = True 137 138 if keys[K_F10]: 139 inputs["SPECIAL"] = True 140 141 if joystick != None: # Parse joystick input 142 143# axis0 = joystick.get_axis(0) 144 145# if axis0 < -0.1: 146# inputs["LEFT"] = True 147# inputs["ANALOG"] = -axis0 148 149# if axis0 > 0.1: 150# inputs["RIGHT"] = True 151# inputs["ANALOG"] = axis0 152 153 if joystick.get_numbuttons() > 1: 154 if joystick.get_button(0): 155 inputs["UP"] = True 156 if keys_released["J_B0"]: 157 inputs["JUMP"] = True 158 keys_released["J_B0"] = False 159 else: 160 keys_released["J_B0"] = True 161 162 if joystick.get_button(1): 163 if keys_released["J_B1"]: 164 inputs["DOWN"] = True 165 keys_released["J_B1"] = False 166 else: 167 keys_released["J_B1"] = True 168# else: 169# axis1 = joystick.get_axis(1) 170 171# if axis1 < -0.1: 172# inputs["UP"] = True 173# if keys_released["J_A1U"]: 174# inputs["JUMP"] = True 175# keys_released["J_A1U"] = False 176# else: 177# keys_released["J_A1U"] = True 178 179 if axis1 > 0.1: 180 if keys_released["J_A1D"]: 181 inputs["DOWN"] = True 182 keys_released["J_A1D"] = False 183 else: 184 keys_released["J_A1D"] = True 185 186 return inputs 187 188 189def run(screen, level_name = "w0-l0", score_mod = 0, score = None, joystick = None): 190 191 if (Variables.vdict["devmode"]): 192 edit_utils = Edit_utils() 193 194 done = False 195 objects = [] 196 particles = [] 197 198 if score == None: 199 score = Score(0) 200 201 #try: 202 level = Level(screen, level_name) 203 #except: 204 # error_message("Couldn't open level '" + level_name + "'") 205 # return END_QUIT 206 207 objects = level.get_objects() 208 player = level.get_player() 209 objects.append(player) 210 211 player.life = score.life 212 213 clock = pygame.time.Clock() 214 215 end_trigger = END_NONE 216 scripted_event_on = False 217 218 #There's no music at the moment: 219 #pygame.mixer.music.load( data.filepath(os.path.join("music", "music.ogg")) ) 220 #pygame.mixer.music.play(-1) 221 222 scripted_events = level.get_scripted_events() 223 current_scripted_event = None 224 225 scripted_event_trigger = TRIGGER_LEVEL_BEGIN 226 227 flip_wait = -1 228 229 keys_released["K_z"] = True 230 keys_released["K_p"] = True 231 keys_released["K_s"] = True 232 keys_released["K_DOWN"] = True 233 keys_released["K_LEFT"] = True 234 keys_released["K_RIGHT"] = True 235 keys_released["K_UP"] = True 236 keys_released["J_B0"] = True 237 keys_released["J_B1"] = True 238 239 fading = True 240 fade_target = FADE_STATE_NONE 241 Util.fade_state = FADE_STATE_BLACK 242 243 flip_trigger_position = (0, 0) 244 245 changing_level = False 246 247 paused = False 248 249 #Main game loop 250 251 while (end_trigger == END_NONE or fading): 252 253 # Pygame event and keyboard input processing 254 for event in pygame.event.get(): 255 if event.type == QUIT: 256 end_trigger = END_HARD_QUIT 257 if (event.type == KEYDOWN and event.key == K_ESCAPE): 258 end_trigger = END_QUIT 259 if fading == False: 260 fading = True 261 fade_target = FADE_STATE_HALF 262 263 inputs = parse_inputs(joystick) 264 265 trigger = None 266 267 if scripted_event_on: 268 if inputs.has_key("JUMP") or inputs.has_key("DOWN"): 269 cleared = True 270 271 moved = False 272 273 add_time = False #The ingame time counter toggle - this is False when the player can't control the character 274 275 if not scripted_event_on and not level.flipping and not fading and not paused \ 276 and player.current_animation != "dying" and player.current_animation != "exit": 277 #There isn't anything special going on: player can control the character 278 #Translates input to commands to the player object 279 add_time = True 280 if inputs.has_key("LEFT"): 281 player.move((-PLAYER_MAX_ACC, 0)) 282 moved = True 283 284 if inputs.has_key("RIGHT"): 285 player.move((PLAYER_MAX_ACC, 0)) 286 moved = True 287 288 if inputs.has_key("JUMP"): 289 if (player.on_ground): 290 count = 0 291 while (count < 5): 292 count += 1 293 particles.append(Particle(screen, 10, player.rect.centerx - player.dx / 4 + random.uniform(-3, 3), player.rect.bottom, -player.dx * 0.1, -0.5, 0.3, level.dust_color, 4)) 294 player.jump() 295 296 #The blobs always try to jump when the player jumps 297 298 for o in objects: 299 if o.itemclass == "blob": 300 o.jump() 301 302 if inputs.has_key("UP") and not player.on_ground: 303 player.jump() 304 305 if inputs.has_key("DOWN"): 306 pick_up_item = level.pick_up(player.x, player.y) 307 if pick_up_item != None: 308 play_sound("coins") 309 player.inventory.append(pick_up_item) 310 scripted_event_trigger = pick_up_item.itemclass 311 312 #If the level is not flipping at the moment, the player can trigger stuff in the level 313 if flip_wait == -1: 314 trigger = level.trigger(player.x, player.y) 315 316 #Debug command for flipping: 317 if inputs.has_key("SPECIAL"): 318 trigger = Trigger(TRIGGER_FLIP, player.x, player.y) 319 320 if inputs.has_key("PAUSE") and player.current_animation != "dying": 321 paused = not paused 322 323 #Decelerates the player, if he doesn't press any movement keys or when he is dead and on the ground 324 if ((player.current_animation != "dying" and not moved) or (player.current_animation == "dying" and player.on_ground)) and not paused: 325 player.dec((PLAYER_MAX_ACC, 0)) 326 327 if trigger != None and trigger.trigger_type == TRIGGER_FLIP: 328 if flip_wait == -1: 329 flip_wait = 0 330 flip_trigger_position = (trigger.x, trigger.y) 331 play_sound("woosh") 332 333 if flip_wait != -1 and not paused: 334 flip_wait += 1 335 if flip_wait > FLIP_DELAY: 336 flip_direction = flip_direction_from_position(flip_trigger_position) 337 flip_wait = -1 338 level.flip(flip_direction) 339 for o in objects: 340 o.flip(flip_direction) 341 for p in particles: 342 p.flip() 343 344 #Dust effect rising from the character's feet: 345 346 if (player.current_animation == "walking"): 347 particles.append(Particle(screen, 10, player.rect.centerx - player.dx / 2 + random.uniform(-2, 2), player.rect.bottom, -player.dx * 0.1, 0.1, 0.3, level.dust_color)) 348 349 #Updating level and objects: 350 351 if scripted_event_trigger == None: 352 scripted_event_trigger = level.update() 353 else: 354 level.update() 355 356 #Objects are only updated when there's not a scripted event going on 357 358 normal_updating = not scripted_event_on and not fading and not paused 359 360 if changing_level: 361 player.update(level) 362 elif normal_updating: 363 for o in objects: 364 if o.dead and o.itemclass != "player": 365 objects.remove(o) 366 continue 367 new_particles = o.update(level) 368 if o.itemclass == "projectile": 369 if player.rect.collidepoint(o.x, o.y) and o.current_animation == "default": 370 new_particles = player.take_damage(o.damage) 371 o.die() 372 if type(new_particles) == list: #Sometimes the type of the return value is int (hackity hack) 373 if new_particles != None: 374 for p in new_particles: 375 particles.append(p) 376 377 if normal_updating or changing_level: 378 for p in particles: 379 p.update() 380 if p.dead: 381 particles.remove(p) 382 383 #Rendering level - background and tiles 384 level.render() 385 386 #Rendering objects and particles 387 for o in objects: 388 if o.itemclass == "player": 389 o.render(None, None, (fading or paused) ) 390 else: 391 o.render(None, None, (scripted_event_on or fading or paused) ) 392 #On special conditions the animations aren't updated. The player is updated on a scripted event, others are not. 393 394 for p in particles: 395 p.render() 396 397 #Rendering GUI on top of game graphics: 398 if (not paused) or (not Variables.vdict["devmode"]): 399 render_gui(screen, player.life, score.score, (5, 5)) 400 401 # Scripted event triggering: 402 403 if scripted_event_trigger != None: 404 if player.on_ground: 405 for ev in scripted_events: 406 if ev.trigger_type == scripted_event_trigger: 407 scripted_event_on = True 408 current_scripted_event = ev 409 current_scripted_event_element = None 410 text = None 411 phase = 0 412 cleared = False # Clearing dialog boxes 413 player.dy = 0 414 player.dx = 0 415 player.update() 416 scripted_event_trigger = None 417 418 # Scripted event processing: 419 420 if scripted_event_on and not fading and not paused: 421 if (current_scripted_event_element == None) or (current_scripted_event_element.finished): 422 423 current_scripted_event_element = current_scripted_event.next_element() 424 425 if current_scripted_event_element.event_type == "end": 426 scripted_event_on = False 427 current_scripted_event_element = None 428 429 else: 430 431 if not Variables.vdict["dialogue"]: #Dialogue skipping 432 while (current_scripted_event_element.event_type == "dialogue" or current_scripted_event_element.event_type == "player"): 433 current_scripted_event_element.finished = True 434 current_scripted_event_element = current_scripted_event.next_element() 435 if current_scripted_event_element.event_type == "end": 436 current_scripted_event_element.finished = True 437 438 if current_scripted_event_element.event_type == "wait": 439 current_scripted_event_element.finished = True 440 elif current_scripted_event_element.event_type == "dialogue": 441 if text == None: 442 text = current_scripted_event_element.text 443 phase = 0 444 phase = render_text_dialogue(screen, text, phase) 445 if (phase == -1) and cleared: 446 current_scripted_event_element.finished = True 447 phase = 0 448 cleared = False 449 text = None 450 if cleared: 451 phase = -1 452 cleared = False 453 elif current_scripted_event_element.event_type == "player": 454 if current_scripted_event_element.text == "orientation": 455 player.orientation = current_scripted_event_element.orientation 456 current_scripted_event_element.finished = True 457 elif current_scripted_event_element.event_type == "change_level": 458 score.score += (5 + score_mod) * ((player.life + 4) / 5 + 12) 459 score.levels += 1 460 current_scripted_event_element.finished = True 461 if player.current_animation != "gone": 462 player.exit() 463 464 if player.current_animation == "exit": 465 changing_level = True 466 elif changing_level: 467 end_trigger = END_NEXT_LEVEL 468 fading = True 469 fade_target = FADE_STATE_BLACK 470 471 472 if player.dead: 473 end_trigger = END_LOSE 474 fading = True 475 fade_target = FADE_STATE_HALF 476 477 #And finally, rendering the pause button: 478 479 if paused: 480 if (Variables.vdict["devmode"]): 481 change = edit_utils.update(inputs) 482 level.change(change) 483 edit_utils.render(screen) 484 else: 485 render_text_dialogue(screen, "Game paused. Press P to continue.", -1, "p") 486 487 #Render fading on top of everything else: 488 489 if (fading or Util.fade_state != FADE_STATE_NONE): 490 if fade_to_black(screen, fade_target): 491 #Fading finished 492 fading = False 493 494 if(add_time): 495 score.time += 1 496 497 #Display, clock 498 499 pygame.display.flip() 500 501 clock.tick(FPS) 502 503 #Main game loop finished 504 505 score.life = player.life #To make the player's health stay the same to the next level 506 507 return end_trigger 508